/ node_modules / ecdsa-sig-formatter / src / ecdsa-sig-formatter.js
ecdsa-sig-formatter.js
  1  'use strict';
  2  
  3  var Buffer = require('safe-buffer').Buffer;
  4  
  5  var getParamBytesForAlg = require('./param-bytes-for-alg');
  6  
  7  var MAX_OCTET = 0x80,
  8  	CLASS_UNIVERSAL = 0,
  9  	PRIMITIVE_BIT = 0x20,
 10  	TAG_SEQ = 0x10,
 11  	TAG_INT = 0x02,
 12  	ENCODED_TAG_SEQ = (TAG_SEQ | PRIMITIVE_BIT) | (CLASS_UNIVERSAL << 6),
 13  	ENCODED_TAG_INT = TAG_INT | (CLASS_UNIVERSAL << 6);
 14  
 15  function base64Url(base64) {
 16  	return base64
 17  		.replace(/=/g, '')
 18  		.replace(/\+/g, '-')
 19  		.replace(/\//g, '_');
 20  }
 21  
 22  function signatureAsBuffer(signature) {
 23  	if (Buffer.isBuffer(signature)) {
 24  		return signature;
 25  	} else if ('string' === typeof signature) {
 26  		return Buffer.from(signature, 'base64');
 27  	}
 28  
 29  	throw new TypeError('ECDSA signature must be a Base64 string or a Buffer');
 30  }
 31  
 32  function derToJose(signature, alg) {
 33  	signature = signatureAsBuffer(signature);
 34  	var paramBytes = getParamBytesForAlg(alg);
 35  
 36  	// the DER encoded param should at most be the param size, plus a padding
 37  	// zero, since due to being a signed integer
 38  	var maxEncodedParamLength = paramBytes + 1;
 39  
 40  	var inputLength = signature.length;
 41  
 42  	var offset = 0;
 43  	if (signature[offset++] !== ENCODED_TAG_SEQ) {
 44  		throw new Error('Could not find expected "seq"');
 45  	}
 46  
 47  	var seqLength = signature[offset++];
 48  	if (seqLength === (MAX_OCTET | 1)) {
 49  		seqLength = signature[offset++];
 50  	}
 51  
 52  	if (inputLength - offset < seqLength) {
 53  		throw new Error('"seq" specified length of "' + seqLength + '", only "' + (inputLength - offset) + '" remaining');
 54  	}
 55  
 56  	if (signature[offset++] !== ENCODED_TAG_INT) {
 57  		throw new Error('Could not find expected "int" for "r"');
 58  	}
 59  
 60  	var rLength = signature[offset++];
 61  
 62  	if (inputLength - offset - 2 < rLength) {
 63  		throw new Error('"r" specified length of "' + rLength + '", only "' + (inputLength - offset - 2) + '" available');
 64  	}
 65  
 66  	if (maxEncodedParamLength < rLength) {
 67  		throw new Error('"r" specified length of "' + rLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
 68  	}
 69  
 70  	var rOffset = offset;
 71  	offset += rLength;
 72  
 73  	if (signature[offset++] !== ENCODED_TAG_INT) {
 74  		throw new Error('Could not find expected "int" for "s"');
 75  	}
 76  
 77  	var sLength = signature[offset++];
 78  
 79  	if (inputLength - offset !== sLength) {
 80  		throw new Error('"s" specified length of "' + sLength + '", expected "' + (inputLength - offset) + '"');
 81  	}
 82  
 83  	if (maxEncodedParamLength < sLength) {
 84  		throw new Error('"s" specified length of "' + sLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
 85  	}
 86  
 87  	var sOffset = offset;
 88  	offset += sLength;
 89  
 90  	if (offset !== inputLength) {
 91  		throw new Error('Expected to consume entire buffer, but "' + (inputLength - offset) + '" bytes remain');
 92  	}
 93  
 94  	var rPadding = paramBytes - rLength,
 95  		sPadding = paramBytes - sLength;
 96  
 97  	var dst = Buffer.allocUnsafe(rPadding + rLength + sPadding + sLength);
 98  
 99  	for (offset = 0; offset < rPadding; ++offset) {
100  		dst[offset] = 0;
101  	}
102  	signature.copy(dst, offset, rOffset + Math.max(-rPadding, 0), rOffset + rLength);
103  
104  	offset = paramBytes;
105  
106  	for (var o = offset; offset < o + sPadding; ++offset) {
107  		dst[offset] = 0;
108  	}
109  	signature.copy(dst, offset, sOffset + Math.max(-sPadding, 0), sOffset + sLength);
110  
111  	dst = dst.toString('base64');
112  	dst = base64Url(dst);
113  
114  	return dst;
115  }
116  
117  function countPadding(buf, start, stop) {
118  	var padding = 0;
119  	while (start + padding < stop && buf[start + padding] === 0) {
120  		++padding;
121  	}
122  
123  	var needsSign = buf[start + padding] >= MAX_OCTET;
124  	if (needsSign) {
125  		--padding;
126  	}
127  
128  	return padding;
129  }
130  
131  function joseToDer(signature, alg) {
132  	signature = signatureAsBuffer(signature);
133  	var paramBytes = getParamBytesForAlg(alg);
134  
135  	var signatureBytes = signature.length;
136  	if (signatureBytes !== paramBytes * 2) {
137  		throw new TypeError('"' + alg + '" signatures must be "' + paramBytes * 2 + '" bytes, saw "' + signatureBytes + '"');
138  	}
139  
140  	var rPadding = countPadding(signature, 0, paramBytes);
141  	var sPadding = countPadding(signature, paramBytes, signature.length);
142  	var rLength = paramBytes - rPadding;
143  	var sLength = paramBytes - sPadding;
144  
145  	var rsBytes = 1 + 1 + rLength + 1 + 1 + sLength;
146  
147  	var shortLength = rsBytes < MAX_OCTET;
148  
149  	var dst = Buffer.allocUnsafe((shortLength ? 2 : 3) + rsBytes);
150  
151  	var offset = 0;
152  	dst[offset++] = ENCODED_TAG_SEQ;
153  	if (shortLength) {
154  		// Bit 8 has value "0"
155  		// bits 7-1 give the length.
156  		dst[offset++] = rsBytes;
157  	} else {
158  		// Bit 8 of first octet has value "1"
159  		// bits 7-1 give the number of additional length octets.
160  		dst[offset++] = MAX_OCTET	| 1;
161  		// length, base 256
162  		dst[offset++] = rsBytes & 0xff;
163  	}
164  	dst[offset++] = ENCODED_TAG_INT;
165  	dst[offset++] = rLength;
166  	if (rPadding < 0) {
167  		dst[offset++] = 0;
168  		offset += signature.copy(dst, offset, 0, paramBytes);
169  	} else {
170  		offset += signature.copy(dst, offset, rPadding, paramBytes);
171  	}
172  	dst[offset++] = ENCODED_TAG_INT;
173  	dst[offset++] = sLength;
174  	if (sPadding < 0) {
175  		dst[offset++] = 0;
176  		signature.copy(dst, offset, paramBytes);
177  	} else {
178  		signature.copy(dst, offset, paramBytes + sPadding);
179  	}
180  
181  	return dst;
182  }
183  
184  module.exports = {
185  	derToJose: derToJose,
186  	joseToDer: joseToDer
187  };