JAVASCRIPT   49

ecdsa-sig-formatter.js

Guest on 1st September 2021 10:38:54 AM

  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. };

Raw Paste


Login or Register to edit or fork this paste. It's free.