Skip to content

Commit

Permalink
FAB-1408 enhance ecdsa/key.js for public key
Browse files Browse the repository at this point in the history
as part of supporting signature verification, we need public keys
extracted from certificates to be represented by the standard
api.Key interface. as such we need the lib/impl/ecdsa/key.js to
be enhanced so it can be seeded with a public key object

Patch 2: re-implemented key.getSKI() to use EC standard as
described in X9.62-1988 section 4.3.6

Change-Id: I487750bee67a86e6dbbe7caf08ed46510b24aca7
Signed-off-by: Jim Zhang <[email protected]>
  • Loading branch information
jimthematrix committed Dec 17, 2016
1 parent e1ffe93 commit 9c3e33f
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 27 deletions.
24 changes: 23 additions & 1 deletion hfc/lib/impl/CryptoSuite_ECDSA_AES.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,29 @@ var CryptoSuite_ECDSA_AES = class extends api.CryptoSuite {
* To be implemented
*/
importKey(raw, opts) {
throw new Error('Not implemented yet');
if (!opts)
throw new Error('Missing required parameter "opts"');

if (!opts.algorithm)
throw new Error('Parameter "opts" missing required field "algorithm"');

if (opts.algorithm === 'X509Certificate') {
// importing public key from an x.509 certificate
var pemString = Buffer.from(raw).toString();
try {
var publicKey = KEYUTIL.getKey(raw);

if (publicKey.type === 'EC') {
return new ECDSAKey(publicKey, publicKey.ecparams.keylen);
} else {
// TODO PEM encoded private keys, DER encoded public certs and private keys, etc
throw new Error('Does not understand certificates other than ECDSA public keys');
}
} catch(err) {
logger.error('Failed to parse public key from PEM: ' + err);
throw err;
}
}
}

/**
Expand Down
60 changes: 35 additions & 25 deletions hfc/lib/impl/ecdsa/key.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,23 @@

var Ber = require('asn1').Ber;
var Hash = require('../../hash.js');
var utils = require('../../utils.js');
var jsrsa = require('jsrsasign');
var asn1 = jsrsa.asn1;
var KEYUTIL = jsrsa.KEYUTIL;
var ECDSA = jsrsa.ECDSA;

var logger = utils.getLogger('ecdsa/key.js');

/*
* This module implements the {@link module:api.Key} interface, for ECDSA.
* @module ECDSA_KEY
*/
module.exports = class ECDSA_KEY {
/*
* this class represents the private key of an ECDSA key pair.
* this class represents the private or public key of an ECDSA key pair.
*
* @param {Object} key This must be the "privKeyObj" part of the object generated by KEYUTIL.generateKeypair()
* @param {Object} key This must be the "privKeyObj" or "pubKeyObj" part of the object generated by jsrsasign.KEYUTIL.generateKeypair()
*/
constructor(key, keySize) {
if (typeof key === 'undefined' || key === null) {
Expand All @@ -54,27 +57,30 @@ module.exports = class ECDSA_KEY {
* @returns {string} a string representation of the hash from a sequence based on the private key bytes
*/
getSKI() {
var sk = new Ber.Writer();
sk.startSequence();
sk.writeInt(1);
sk.writeBuffer(new Buffer(this._key.prvKeyHex, 'hex'), 4);
sk.writeByte(160);
sk.writeByte(7);
if (this._keySize === 384) {
// OID of P384
sk.writeOID('1.3.132.0.34');
} else {
// OID of P256
sk.writeOID('1.2.840.10045.3.1.7');
}

sk.endSequence();
var buff;

var pointToOctet = function(key) {
var byteLen = (key.ecparams.keylen + 7) >> 3;
let buff = Buffer.allocUnsafe(1 + 2 * byteLen);
buff[0] = 4; // uncompressed point (https://www.security-audit.com/files/x9-62-09-20-98.pdf, section 4.3.6)
var xyhex = key.getPublicKeyXYHex();
var xBuffer = Buffer.from(xyhex.x, 'hex');
var yBuffer = Buffer.from(xyhex.y, 'hex');
logger.debug('ECDSA curve param X: %s', xBuffer.toString('hex'));
logger.debug('ECDSA curve param Y: %s', yBuffer.toString('hex'));
xBuffer.copy(buff, 1 + byteLen - xBuffer.length);
yBuffer.copy(buff, 1 + 2 * byteLen - yBuffer.length);
return buff;
};

if (this._keySize === 256) {
return Hash.sha3_256(sk.buffer);
if (this._key.isPublic) {
// referencing implementation of the Marshal() method of https://golang.org/src/crypto/elliptic/elliptic.go
buff = pointToOctet(this._key);
} else {
return Hash.sha3_384(sk.buffer);
buff = pointToOctet(this.getPublicKey()._key);
}

return Hash.sha2_256(buff);
}

isSymmetric() {
Expand All @@ -89,11 +95,15 @@ module.exports = class ECDSA_KEY {
}

getPublicKey() {
var f = new ECDSA({ curve: this._key.curveName });
f.setPublicKeyHex(this._key.pubKeyHex);
f.isPrivate = false;
f.isPublic = true;
return new ECDSA_KEY(f, this._keySize);
if (this._key.isPublic)
return this;
else {
var f = new ECDSA({ curve: this._key.curveName });
f.setPublicKeyHex(this._key.pubKeyHex);
f.isPrivate = false;
f.isPublic = true;
return new ECDSA_KEY(f, this._keySize);
}
}

/**
Expand Down
62 changes: 61 additions & 1 deletion test/unit/headless-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,34 @@ var TEST_KEY_PUBLIC = '04f46815aa00fe2ba2814b906aa4ef1755caf152658de8997a6a85808
var TEST_MSG_SIGNATURE_SHA2_256 = '3046022100a6460b29373fa16ee96172bfe04666140405fdef78182280545d451f08547736022100d9022fe620ceadabbef1714b894b8d6be4b74c0f9c573bd774871764f4f789c9';
var TEST_LONG_MSG_SIGNATURE_SHA2_256 = '3045022073266302d730b07499aabd0f88f12c8749a0f90144034dbc86a8cd742722ad29022100852346f93e50911008ab97afc452f83c5985a19fa3aa6d58f615c03bddaa90a1';

var TEST_PUBLICKEY_PEM = '-----BEGIN ECDSA PUBLIC KEY-----\n' +
'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEp53zR7+0ZkYyHw1jattcyl6S0Es9uaqc\n' +
'lABdfMBflwZWBB8jOj+CGW3l4v+qYnAaEJl/TFG73GGtCY5/FNx9E0FkmjCtXsUY\n' +
'tDKOY53CUoBQParnUL0mgpYkBRlguYIG\n' +
'-----END ECDSA PUBLIC KEY-----';
var TEST_PRIVATEKEY_PEM = '-----BEGIN PRIVATE KEY-----MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgzXIgbq9dtAzwK1yknvFTyQmZKmoLkipQHZUjfE2ILb2hRANCAASaQgfH/7XGn9mQ261INTENal0rLGzZroTK7oKHp5IAPK1nDPu+WovQwiDuaL6CzinkufHxvoeZ3XEZOonRP3qP-----END PRIVATE KEY-----';
// var TEST_CERT_PEM = '-----BEGIN CERTIFICATE-----MIIDVDCCAvqgAwIBAgIBATAKBggqhkjOPQQDAjBOMRMwEQYDVQQKDArOoyBBY21lIENvMRkwFwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMQ8wDQYDVQQqEwZHb3BoZXIxCzAJBgNVBAYTAk5MMB4XDTE2MTIxNjIzMTAxM1oXDTE2MTIxNzAxMTAxM1owTjETMBEGA1UECgwKzqMgQWNtZSBDbzEZMBcGA1UEAxMQdGVzdC5leGFtcGxlLmNvbTEPMA0GA1UEKhMGR29waGVyMQswCQYDVQQGEwJOTDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFKnXh7hBdp6s9OJ/aadigT1z2WzBbSc7Hzb3rkaWFz4e+9alqqWg9lrur/mDYzG9dudC8jFjVa7KIh+2BxgBayjggHHMIIBwzAOBgNVHQ8BAf8EBAMCAgQwJgYDVR0lBB8wHQYIKwYBBQUHAwIGCCsGAQUFBwMBBgIqAwYDgQsBMA8GA1UdEwEB/wQFMAMBAf8wDQYDVR0OBAYEBAECAwQwDwYDVR0jBAgwBoAEAQIDBDBiBggrBgEFBQcBAQRWMFQwJgYIKwYBBQUHMAGGGmh0dHA6Ly9vY0JDQ1NQLmV4YW1wbGUuY29tMCoGCCsGAQUFBzAChh5odHRwOi8vY3J0LmV4YW1wbGUuY29tL2NhMS5jcnQwRgYDVR0RBD8wPYIQdGVzdC5leGFtcGxlLmNvbYERZ29waGVyQGdvbGFuZy5vcmeHBH8AAAGHECABSGAAACABAAAAAAAAAGgwDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAhoB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4YW1wbGUuY29tMFcGA1UdHwRQME4wJaAjoCGGH2h0dHA6Ly9jcmwxLmV4YW1wbGUuY29tL2NhMS5jcmwwJaAjoCGGH2h0dHA6Ly9jcmwyLmV4YW1wbGUuY29tL2NhMS5jcmwwFgYDKgMEBA9leHRyYSBleHRlbnNpb24wCgYIKoZIzj0EAwIDSAAwRQIgcguBb6FUxO+X8DbY17gpqSGuNC4NT4BddPg1UWUxIC0CIQDNyHQAwzhw+512meXRwG92GfpzSBssDKLdwlrqiHOu5A==-----END CERTIFICATE-----';
var TEST_CERT_PEM = '-----BEGIN CERTIFICATE-----' +
'MIIDVDCCAvqgAwIBAgIBATAKBggqhkjOPQQDAjBOMRMwEQYDVQQKDArOoyBBY21l' +
'IENvMRkwFwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMQ8wDQYDVQQqEwZHb3BoZXIx' +
'CzAJBgNVBAYTAk5MMB4XDTE2MTIxNjIzMTAxM1oXDTE2MTIxNzAxMTAxM1owTjET' +
'MBEGA1UECgwKzqMgQWNtZSBDbzEZMBcGA1UEAxMQdGVzdC5leGFtcGxlLmNvbTEP' +
'MA0GA1UEKhMGR29waGVyMQswCQYDVQQGEwJOTDBZMBMGByqGSM49AgEGCCqGSM49' +
'AwEHA0IABFKnXh7hBdp6s9OJ/aadigT1z2WzBbSc7Hzb3rkaWFz4e+9alqqWg9lr' +
'ur/mDYzG9dudC8jFjVa7KIh+2BxgBayjggHHMIIBwzAOBgNVHQ8BAf8EBAMCAgQw' +
'JgYDVR0lBB8wHQYIKwYBBQUHAwIGCCsGAQUFBwMBBgIqAwYDgQsBMA8GA1UdEwEB' +
'/wQFMAMBAf8wDQYDVR0OBAYEBAECAwQwDwYDVR0jBAgwBoAEAQIDBDBiBggrBgEF' +
'BQcBAQRWMFQwJgYIKwYBBQUHMAGGGmh0dHA6Ly9vY0JDQ1NQLmV4YW1wbGUuY29t' +
'MCoGCCsGAQUFBzAChh5odHRwOi8vY3J0LmV4YW1wbGUuY29tL2NhMS5jcnQwRgYD' +
'VR0RBD8wPYIQdGVzdC5leGFtcGxlLmNvbYERZ29waGVyQGdvbGFuZy5vcmeHBH8A' +
'AAGHECABSGAAACABAAAAAAAAAGgwDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAh' +
'oB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4YW1wbGUuY29tMFcGA1UdHwRQME4wJaAj' +
'oCGGH2h0dHA6Ly9jcmwxLmV4YW1wbGUuY29tL2NhMS5jcmwwJaAjoCGGH2h0dHA6' +
'Ly9jcmwyLmV4YW1wbGUuY29tL2NhMS5jcmwwFgYDKgMEBA9leHRyYSBleHRlbnNp' +
'b24wCgYIKoZIzj0EAwIDSAAwRQIgcguBb6FUxO+X8DbY17gpqSGuNC4NT4BddPg1' +
'UWUxIC0CIQDNyHQAwzhw+512meXRwG92GfpzSBssDKLdwlrqiHOu5A==' +
'-----END CERTIFICATE-----';

var jsrsa = require('jsrsasign');
var KEYUTIL = jsrsa.KEYUTIL;
var ECDSA = jsrsa.ECDSA;
Expand Down Expand Up @@ -1263,6 +1291,11 @@ test('\n\n ** CryptoSuite_ECDSA_AES - function tests **\n\n', function (t) {
testVerify(TEST_MSG_SIGNATURE_SHA2_256, TEST_MSG);
testVerify(TEST_LONG_MSG_SIGNATURE_SHA2_256, TEST_LONG_MSG);

// test importKey()
var pubKey = cryptoUtils.importKey(TEST_CERT_PEM, { algorithm: 'X509Certificate' });
t.equal(pubKey.isPrivate(), false, 'Test imported public key isPrivate()');
t.equal(pubKey.getSKI(), 'b5cb4942005c4ecaa9f73a49e1936a58baf549773db213cf1e22a1db39d9dbef', 'Test imported public key SKI');

t.end();
})
.catch(function (err) {
Expand Down Expand Up @@ -1319,14 +1352,41 @@ test('\n\n ** ECDSA Key Impl tests **\n\n', function (t) {

var pair2 = KEYUTIL.generateKeypair('EC', 'secp384r1');
var key2 = new ecdsaKey(pair2.prvKeyObj, 384);
t.equal(key2.getSKI().length, 96, 'Checking generated SKI hash string for 384 curve keys');
t.equal(key2.getSKI().length, 64, 'Checking generated SKI hash string for 384 curve keys');

t.equal(key1.isSymmetric() || key2.isSymmetric(), false, 'Checking if key is symmetric');
t.equal(key1.isPrivate() && key2.isPrivate(), true, 'Checking if key is private');

t.equal(key1.getPublicKey().isPrivate(), false, 'Checking isPrivate() logic');
t.equal(key1.getPublicKey().toBytes().length, 182, 'Checking toBytes() output');

// test public keys
var key3 = new ecdsaKey(pair1.pubKeyObj, 256);
t.equal(key3.getSKI().length, 64, 'Checking generated SKI hash string for 256 curve public key');

t.doesNotThrow(
function() {
key3.toBytes();
},
null,
'Checking to dump a public ECDSAKey object to bytes'
);

var key4 = new ecdsaKey(pair2.pubKeyObj, 384);
t.equal(key4.getSKI().length, 64, 'Checking generated SKI hash string for 384 curve public key');

t.doesNotThrow(
function() {
key4.toBytes();
},
null,
'Checking to dump a public ECDSAKey object to bytes'
);

t.equal(!key3.isPrivate() && !key4.isPrivate(), true, 'Checking if both keys are public');
t.equal(key3.getPublicKey().isPrivate(), false, 'Checking getPublicKey() logic');
t.equal(key4.getPublicKey().toBytes().length, 220, 'Checking toBytes() output');

//test CSR generation
var pair3 = KEYUTIL.generateKeypair('EC', 'secp256r1');
var key3 = new ecdsaKey(pair3.prvKeyObj, 256);
Expand Down

0 comments on commit 9c3e33f

Please sign in to comment.