ecdsa-sig-formatter.js 4.9 KB

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