UNPKG

@cdottori/ecdsa-node

Version:

fast openSSL-compatible implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA)

338 lines (242 loc) 7.88 kB
var Base64 = require("./base"); var BinaryAscii = require("./binary"); var modulo = require("./integer").modulo; var BigInt = require("big-integer"); var hexAt = "\x00"; var hexB = "\x02"; var hexC = "\x03"; var hexD = "\x04"; var hexF = "\x06"; var hex0 = "\x30"; var hex31 = 0x1f; var hex127 = 0x7f; var hex129 = 0xa0; var hex160 = 0x80; var hex224 = 0xe0; var bytesHex0 = Buffer.from(hex0).toString('binary'); var bytesHexB = Buffer.from(hexB).toString('binary'); var bytesHexC = Buffer.from(hexC).toString('binary'); var bytesHexD = Buffer.from(hexD).toString('binary'); var bytesHexF = Buffer.from(hexF).toString('binary'); exports.encodeSequence = function () { let sequence = []; let totalLengthLen = 0; for (i=0; i < arguments.length; i++) { sequence.push(arguments[i]); totalLengthLen += arguments[i].length; } return hex0 + _encodeLength(totalLengthLen) + sequence.join(""); } exports.encodeInteger = function (x) { if (x.lesser(0)) { throw new Error("x cannot be negative"); } let t = x.toString(16); if (t.length % 2) { t = "0" + t; } x = BinaryAscii.binaryFromHex(t); let num; if (typeof(x[0]) === "number") { num = x[0]; } else { num = x.charCodeAt(0); } if (num <= hex127) { return hexB + String.fromCharCode(x.length) + x; } return hexB + String.fromCharCode(x.length + 1) + hexAt + x; } exports.encodeOid = function (pieces) { let first = pieces.shift(); let second = pieces.shift(); if (first > 2) { throw new Error("first has to be <= 2"); } if (second > 39) { throw new Error("second has to be <= 39"); } let encodedPieces = []; pieces.forEach((x) => {encodedPieces.push(_encodeNumber(x))}) let body = [String.fromCharCode(40 * first + second)].concat(encodedPieces).join(""); return hexF + _encodeLength(body.length) + body; } exports.encodeBitstring = function (t) { return hexC + _encodeLength(t.length) + t; } exports.encodeOctetString = function (t) { return hexD + _encodeLength(t.length) + t; } exports.encodeConstructed = function (tag, value) { return String.fromCharCode(hex129 + tag) + _encodeLength(value.length) + value; } exports.removeSequence = function (string) { _checkSequenceError(string, bytesHex0, "03"); let result = _readLength(string.slice(1)); let length = result[0]; let lengthLen = result[1]; endSeq = 1 + lengthLen + length; return [string.slice(1 + lengthLen, endSeq), string.slice(endSeq)]; } exports.removeInteger = function (string) { _checkSequenceError(string, bytesHexB, "02"); let result = _readLength(string.slice(1)); let length = result[0]; let lengthLen = result[1]; let numberBytes = string.slice(1 + lengthLen, 1 + lengthLen + length); let rest = string.slice(1 + lengthLen + length); let nBytes; if (typeof(numberBytes[0]) === "number") { nBytes = numberBytes[0]; } else { nBytes = numberBytes.charCodeAt(0); } if (nBytes >= hex160) { throw new Error("nBytes must be < 160"); } return [BigInt(BinaryAscii.hexFromBinary(numberBytes), 16), rest]; } exports.removeObject = function (string) { _checkSequenceError(string, bytesHexF, "06"); let result = _readLength(string.slice(1)); let length = result[0]; let lengthLen = result[1]; let body = string.slice(1 + lengthLen, 1 + lengthLen + length); let rest = string.slice(1 + lengthLen + length); let numbers = []; while (body) { let results = _readNumber(body); let n = results[0]; let lengthLength = results[1]; numbers.push(n); body = body.slice(lengthLength); } let n0 = numbers.shift(); let first = Math.floor(n0 / 40); let second = n0 - (40 * first); numbers = [first, second].concat(numbers); return [numbers, rest]; } exports.removeBitString = function (string) { _checkSequenceError(string, bytesHexC, "03"); let result = _readLength(string.slice(1)); let length = result[0]; let lengthLen = result[1]; let body = string.slice(1 + lengthLen, 1 + lengthLen + length); let rest = string.slice(1 + lengthLen + length); return [body, rest]; } exports.removeOctetString = function (string) { _checkSequenceError(string, bytesHexD, "04"); let result = _readLength(string.slice(1)); let length = result[0]; let lengthLen = result[1]; body = string.slice(1 + lengthLen, 1 + lengthLen + length); rest = string.slice(1 + lengthLen + length); return [body, rest]; } exports.removeConstructed = function (string) { s0 = _extractFirstInt(string); if ((s0 & hex224) != hex129) { throw new Error("wanted constructed tag (0xa0-0xbf), got 0x" + s0); } tag = s0 & hex31 let result = _readLength(string.slice(1)); let length = result[0]; let lengthLen = result[1]; let body = string.slice(1 + lengthLen, 1 + lengthLen + length); let rest = string.slice(1 + lengthLen + length); return [tag, body, rest]; } exports.fromPem = function (pem) { let split = pem.split("\n"); let stripped = ""; let i; for (i = 0; i < split.length; i++) { if (!split[i].startsWith("-----")) { stripped += split[i].trim(); } } return Base64.decode(stripped); } exports.toPem = function (der, name) { let b64 = Base64.encode(der); lines = [("-----BEGIN " + name + "-----\n")]; for (start = 0; start <= b64.length; start += 64) { lines.push(b64.slice(start, start + 64) + "\n") } lines.push("-----END " + name + "-----\n"); return lines.join(""); } function _encodeLength (length) { if (length < 0) { throw new Error("length cannot be negative"); } if (length < hex160) { return String.fromCharCode(length); } let s = length.toString(16); if (modulo(s.length, 2)) { s = "0" + s; } s = BinaryAscii.binaryFromHex(s); let lengthLen = s.length; return String.fromCharCode(hex160 | lengthLen) + toString(lengthLen); } function _encodeNumber (n) { let b128Digits = []; while (n) { b128Digits.unshift((n & hex127) | hex160); n = n >> 7; } if (!b128Digits.length) { b128Digits.push(0); } b128Digits[b128Digits.length - 1] &= hex127; let encodedDigits = []; b128Digits.forEach((d) => {encodedDigits.push(String.fromCharCode(d))}) return encodedDigits.join(""); } function _readLength (string) { num = _extractFirstInt(string); if (!(num & hex160)) { return [(num & hex127), 1]; } let lengthLen = num & hex127; if (lengthLen > string.length - 1) { throw new Error("ran out of length bytes"); } return [parseInt(BinaryAscii.hexFromBinary(string.slice(1, 1 + lengthLen)), 16), 1 + lengthLen]; } function _readNumber (string) { let number = 0; let lengthLen = 0; let d; while (true) { if (lengthLen > string.length) { throw new Error("ran out of length bytes"); } number = number << 7; d = string.charAt(lengthLen); if (typeof(d) !== "number") { d = d.charCodeAt(0); } number += (d & hex127); lengthLen += 1; if (!(d & hex160)) { break; } } return [number, lengthLen]; } function _checkSequenceError(string, start, expected) { if (!string.startsWith(start)) { throw new Error("wanted sequence (0x" + expected + "), got 0x" + _extractFirstInt(string).toString(16)); } } function _extractFirstInt(string) { if (typeof(string[0]) === "number") { return string[0]; } return string.charCodeAt(0); }