UNPKG

@luckxudong/nodeforge

Version:

JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.

397 lines (367 loc) 10.9 kB
/** * JavaScript implementation of ECDSA. * * Copyright (c) 2021 HAMANO Tsukasa <hamano@osstech.co.jp> * * This implementation is based on the elliptic * * https://github.com/indutny/elliptic/ */ var forge = require('./forge'); require('./asn1'); require('./jsbn'); require('./random'); require('./sha512'); var util = require('./util'); var elliptic = require('elliptic'); var asn1Validator = require('./asn1-validator'); var publicKeyInfoValidator = asn1Validator.publicKeyInfoValidator; var privateKeyValidator = asn1Validator.privateKeyValidator; var asn1 = forge.asn1; if(typeof BigInteger === 'undefined') { var BigInteger = forge.jsbn.BigInteger; } var ByteBuffer = util.ByteBuffer; var NativeBuffer = typeof Buffer === 'undefined' ? Uint8Array : Buffer; forge.pki = forge.pki || {}; module.exports = forge.pki.ecdsa = forge.ecdsa = forge.ecdsa || {}; var ecdsa = forge.ecdsa; ecdsa.constants = {}; /* * Supported namedCurve listed here: * https://github.com/indutny/elliptic/blob/master/lib/elliptic/curves.js */ ecdsa.supportedCueves = [ 'p192', // secp192r1, prime192v1 'p256', // secp256r1, prime256v1 'p224', // secp224r1, 'p384', // secp384r1 'p521', // secp521r1 'secp256k1',// secp256k1 ]; /* * RCF5915: Elliptic Curve Private Key Format * https://datatracker.ietf.org/doc/html/rfc5915 * * ECPrivateKey ::= SEQUENCE { * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), * privateKey OCTET STRING, * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, * publicKey [1] BIT STRING OPTIONAL * } */ var ecPrivateKeyValidator = { name: 'ECPrivateKey', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'ECPrivateKey.version', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, capture: 'version', }, { name: 'ECPrivateKey.privateKey', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OCTETSTRING, capture: 'privateKey', }, { tagClass: asn1.Class.CONTEXT_SPECIFIC, type: 0x0, optional: true, value: [{ name: 'ECPrivateKey.parameters', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OID, captureAsn1: 'parameters', }], }, { tagClass: asn1.Class.CONTEXT_SPECIFIC, type: 0x1, optional: true, value: [{ name: 'ECPrivateKey.publicKey', type: asn1.Type.BITSTRING, captureAsn1: 'publicKey', }], }], }; var ecSpecifiedCurveValidator = { name: 'SpecifiedCurve', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'SpecifiedCurveVersion', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, capture: 'version', }, { name: 'SpecifiedCurve.FieldID', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'SpecifiedCurve.FieldID.fieldType', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OID, capture: 'fieldType', }, { name: 'SpecifiedCurve.FieldID.prime', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, capture: 'p', }], }, { name: 'SpecifiedCurve.Curve', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'SpecifiedCurve.Curve.a', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OCTETSTRING, capture: 'a', }, { name: 'SpecifiedCurve.Curve.b', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OCTETSTRING, capture: 'b', }], }, { name: 'SpecifiedCurve.Generator', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OCTETSTRING, capture: 'g', }, { name: 'SpecifiedCurve.Order', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, capture: 'n', }, { name: 'SpecifiedCurve.Confactor', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, capture: 'c', optional: true, }], }; ecdsa.generateKeyPair = function(options) { options = options || {}; var curveName = options.name || 'p256'; var seed = options.seed; var errors = []; if(!(ecdsa.supportedCueves.includes(curveName))) { var error = new Error('unsupported curveName: ' + curveName); error.errors = errors; throw error; } var curve = elliptic.curves[curveName]; var ec = new elliptic.ec(curve); ec.curveName = curveName; var kp = ec.genKeyPair({ entropy: seed, }); var privateKey = kp.getPrivate(); var publicKey = kp.getPublic(); return { publicKey: new ecdsa.ECPublicKey(kp.ec, publicKey), privateKey: new ecdsa.ECPrivateKey(kp.ec, privateKey), }; }; /** * Converts a ECPrivateKey to an ASN.1 representation. * * @param key the ECPrivateKey. * * @return the ASN.1 representation of an ECPrivateKey. */ ecdsa.privateKeyToAsn1 = function(key, options) { return key.toAsn1(options); }; class ECPublicKey { constructor(ec, publicKey) { this._ec = ec; this._publicKey = publicKey; } verify(msg, signature) { var hexMsg = util.bytesToHex(msg); var hexSignature = util.bytesToHex(signature); return this._ec.verify(hexMsg, hexSignature, this._publicKey, 'hex'); } getBytes() { return String.fromCharCode.apply(null, this._publicKey.encode()); }; toAsn1(options) { var curveOID = forge.oids[this._ec.curveName]; if(!curveOID) { var error = new Error('unsupported namedCurve or specifiedCurve.'); error.errors = errors; throw error; } var obj = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); var aid = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(forge.oids['ecPublicKey']).getBytes()), asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(curveOID).getBytes())]); obj.value.push(aid); obj.value.push( asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, '\0' + this.getBytes())); return obj; }; toDer() { return asn1.toDer(this.toAsn1()).getBytes(); }; toPem() { return '-----BEGIN PUBLIC KEY-----\n' + util.encode64(this.toDer(), 64) + '\n-----END PUBLIC KEY-----\n'; }; toString() { return this._publicKey.encode('hex'); } } /** * Converts a public key from a RFC8410 ASN.1 encoding. * * @param obj - The asn1 representation of a public key. * * @return {ECPublicKey} - ECPublicKey object. */ ECPublicKey.fromAsn1 = function(obj) { var capture = {}; var errors = []; if(!forge.asn1.validate(obj, publicKeyInfoValidator, capture, errors)) { var error = new Error('Cannot read PublicKeyInfo ASN.1 object.'); error.errors = errors; throw error; } var publicKey = capture.subjectPublicKeyRaw; var params = capture.parameters; var curve; var curveName; if(params && params.type === forge.asn1.Type.OID) { var oid = forge.asn1.derToOid(params.value); curveName = forge.oids[oid]; if(!ecdsa.supportedCueves.includes(curveName)) { var error = new Error('Unsupported curveName: ' + curveName); error.errors = errors; throw error; } curve = elliptic.curves[curveName]; } else if(params && params.type === forge.asn1.Type.SEQUENCE) { var capture = {}; if(!forge.asn1.validate(params, ecSpecifiedCurveValidator, capture, errors)) { var error = new Error('Cannot read specified curve ASN.1 object.'); error.errors = errors; throw error; } var options = { p: util.bytesToHex(capture.p), a: util.bytesToHex(capture.a), b: util.bytesToHex(capture.b), n: util.bytesToHex(capture.n), }; var _curve = new elliptic.curve.short(options); var g = _curve.decodePoint(util.bytesToHex(capture.g), 'hex'); curve = { curve: _curve, n: _curve.n, g: g, }; } else { var error = new Error('no ECParameters'); error.errors = errors; throw error; } var ec = new elliptic.ec({curve: curve}); ec.curveName = curveName; var kp = ec.keyFromPublic(publicKey); return new ECPublicKey(ec, kp.getPublic()); }; ecdsa.ECPublicKey = ECPublicKey; forge.ecdsa.ECPublicKey = ECPublicKey; class ECPrivateKey { constructor(ec, privateKey) { this._ec = ec; this._privateKey = privateKey; } sign(msg) { var hexMsg = util.bytesToHex(msg); var signature = this._ec.sign(hexMsg, this._privateKey); return String.fromCharCode.apply(null, signature.toDER()); }; getBytes() { return String.fromCharCode.apply(null, this._privateKey.toArray()); }; toAsn1(options) { var curveOID = forge.oids[this._ec.curveName]; if(!curveOID) { var error = new Error('unsupported namedCurve'); error.errors = errors; throw error; } return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(1).getBytes()), asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, this.getBytes()), asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0x0, true, [ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(curveOID).getBytes()), ]), ]); }; toDer(options) { return asn1.toDer(this.toAsn1(options)).getBytes(); }; toPem(options) { return '-----BEGIN EC PRIVATE KEY-----\n' + util.encode64(this.toDer(options), 64) + '\n-----END EC PRIVATE KEY-----\n'; }; toString() { return this._privateKey.toString('hex'); }; } /** * Converts a private key from a RFC5915 ASN.1 Object. * * @param obj - The asn1 representation of a private key. * * @returns {Object} obj - The ASN.1 key object. * @returns {ECPrivateKey} ECPrivateKey object. */ ECPrivateKey.fromAsn1 = function(obj) { var capture = {}; var errors = []; var valid = forge.asn1.validate(obj, ecPrivateKeyValidator, capture, errors); if(!valid) { var error = new Error('Invalid ECPrivateKey object.'); error.errors = errors; throw error; } var params; if(!capture.parameters) { var error = new Error('no ECPrivateKey.parameters.'); error.errors = errors; throw error; } var oid = asn1.derToOid(capture.parameters.value); var curveName = forge.oids[oid]; if(!ecdsa.supportedCueves.includes(curveName)) { var error = new Error('unsupported curveName: ' + curveName); error.errors = errors; throw error; } var curve = elliptic.curves[curveName]; var ec = new elliptic.ec({curve: curve}); ec.curveName = curveName; var kp = ec.keyFromPrivate(util.bytesToHex(capture.privateKey)); return new ECPrivateKey(ec, kp.getPrivate()); }; ecdsa.ECPrivateKey = ECPrivateKey; forge.ecdsa.ECPrivateKey = ECPrivateKey;