Skip to content

Commit

Permalink
Refactored crypto-related APIs to be algorithm-agnostic
Browse files Browse the repository at this point in the history
- pass I: APIs used by interacting with member services for registering and enrollment.
- Fixed jsdoc per comments by Anya in code review

Change-Id: Icae39cae3143a11a6076ad12f4b6ac5fa65df718
Signed-off-by: Jim Zhang <[email protected]>
  • Loading branch information
jimthematrix committed Sep 28, 2016
1 parent 4d9b475 commit 0b2d441
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 94 deletions.
139 changes: 73 additions & 66 deletions lib/CryptoSuite_ECDSA_SHA.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ var ECDSA_SHA = api.CryptoSuite.extend({
this._initialize();
},

/**
* must match one of the values in ca.proto
*/
getPublicKeyAlgorithm: function() {
return "ECDSA";
},

/**
* Get the security level
* @returns The security level
Expand Down Expand Up @@ -115,24 +122,85 @@ var ECDSA_SHA = api.CryptoSuite.extend({
return crypto.randomBytes(NonceSize);
},

ecdsaKeyGen: function() {
generateKeyPair: function() {
return KEYUTIL.generateKeypair("EC", this._curveName);
},

ecdsaKeyFromPrivate: function(key, encoding) {
asymmetricDecrypt: function(recipientPrivateKey, cipherText) {
var self = this;
// debug("recipientPrivateKey=%s", util.inspect(recipientPrivateKey));//XXX
var level = recipientPrivateKey.ecparams.keylen;
var curveName = recipientPrivateKey.curveName;
// debug("=============> %d", level);
if (this._securityLevel != level) {
throw Error("Invalid key. It's security does not match the current security level " + this._securityLevel + " " + level);
}
//cipherText = ephemeralPubKeyBytes + encryptedTokBytes + macBytes
//ephemeralPubKeyBytes = first ((384+7)/8)*2 + 1 bytes = first 97 bytes
//hmac is sha3_384 = 48 bytes or sha3_256 = 32 bytes
var Rb_len = Math.floor((level + 7) / 8) * 2 + 1;
var D_len = level >> 3;
var ct_len = cipherText.length;

if (ct_len <= Rb_len + D_len)
throw new Error("Illegal cipherText length: " + ct_len + " must be > " + (Rb_len + D_len));

var Rb = cipherText.slice(0, Rb_len); // ephemeral public key bytes
var EM = cipherText.slice(Rb_len, ct_len - D_len); // encrypted content bytes
var D = cipherText.slice(ct_len - D_len);

// debug("Rb :\n", new Buffer(Rb).toString('hex'));
// debug("EM :\n", new Buffer(EM).toString('hex'));
// debug("D :\n", new Buffer(D).toString('hex'));

var EC = elliptic.ec;
//var curve = elliptic.curves['p'+level];
var ecdsa = new EC('p' + level);

//convert bytes to usable key object
var ephPubKey = ecdsa.keyFromPublic(new Buffer(Rb, 'hex'), 'hex');
//var encPrivKey = ecdsa.keyFromPrivate(ecKeypair2.prvKeyObj.prvKeyHex, 'hex');
var privKey = ecdsa.keyFromPrivate(recipientPrivateKey.prvKeyHex, 'hex');
// debug('computing Z...', privKey, ephPubKey);

var Z = privKey.derive(ephPubKey.pub);
// debug('Z computed', Z);
// debug('secret: ', new Buffer(Z.toArray(), 'hex'));
var kdfOutput = self._hkdf(Z.toArray(), ECIESKDFOutput, null, null);
var aesKey = kdfOutput.slice(0, AESKeyLength);
var hmacKey = kdfOutput.slice(AESKeyLength, AESKeyLength + HMACKeyLength);
// debug('secret: ', new Buffer(Z.toArray(), 'hex'));
// debug('aesKey: ', new Buffer(aesKey, 'hex'));
// debug('hmacKey: ', new Buffer(hmacKey, 'hex'));

var recoveredD = self.hmac(hmacKey, EM);
debug('recoveredD: ', new Buffer(recoveredD).toString('hex'));

if (D.compare(new Buffer(recoveredD)) != 0) {
// debug("D="+D.toString('hex')+" vs "+new Buffer(recoveredD).toString('hex'));
throw new Error("HMAC verify failed");
}
var iv = EM.slice(0, IVLength);
var cipher = crypto.createDecipheriv('aes-256-cfb', new Buffer(aesKey), iv);
var decryptedBytes = cipher.update(EM.slice(IVLength));
// debug("decryptedBytes: ",new Buffer(decryptedBytes).toString('hex'));
return decryptedBytes;
},

getKeyPairForSigning: function(key, encoding) {
// select curve and hash algo based on level
var keypair = new EC(this._ecdsaCurve).keyFromPrivate(key, encoding);
debug('keypair: ', keypair);
return keypair;
},

ecdsaKeyFromPublic: function(key, encoding) {
getKeyPairForEncryption: function(key, encoding) {
var publicKey = new EC(this._ecdsaCurve).keyFromPublic(key, encoding);
// debug('publicKey: [%j]', publicKey);
return publicKey;
},

ecdsaSign: function(key, msg) {
sign: function(key, msg) {
var ecdsa = new EC(this._ecdsaCurve);
var signKey = ecdsa.keyFromPrivate(key, 'hex');
var sig = ecdsa.sign(new Buffer(this._hashFunction(msg), 'hex'), signKey);
Expand All @@ -154,7 +222,7 @@ var ECDSA_SHA = api.CryptoSuite.extend({
// debug('cert:\n', JSON.stringify(cert, null, 4));

var ab = new Uint8Array(cert.subjectPublicKey.value_block.value_hex);
var ecdsaChainKey = this.ecdsaKeyFromPublic(ab, 'hex');
var ecdsaChainKey = this.getKeyPairForEncryption(ab, 'hex');

return ecdsaChainKey
},
Expand Down Expand Up @@ -234,67 +302,6 @@ var ECDSA_SHA = api.CryptoSuite.extend({
return this.eciesEncryptECDSA(ecdsa.keyFromPublic(recipientPublicKey.pubKeyHex, 'hex'), msg)
},

eciesDecrypt: function(recipientPrivateKey, cipherText) {
var self = this;
// debug("recipientPrivateKey=%s", util.inspect(recipientPrivateKey));//XXX
var level = recipientPrivateKey.ecparams.keylen;
var curveName = recipientPrivateKey.curveName;
// debug("=============> %d", level);
if (this._securityLevel != level) {
throw Error("Invalid key. It's security does not match the current security level " + this._securityLevel + " " + level);
}
//cipherText = ephemeralPubKeyBytes + encryptedTokBytes + macBytes
//ephemeralPubKeyBytes = first ((384+7)/8)*2 + 1 bytes = first 97 bytes
//hmac is sha3_384 = 48 bytes or sha3_256 = 32 bytes
var Rb_len = Math.floor((level + 7) / 8) * 2 + 1;
var D_len = level >> 3;
var ct_len = cipherText.length;

if (ct_len <= Rb_len + D_len)
throw new Error("Illegal cipherText length: " + ct_len + " must be > " + (Rb_len + D_len));

var Rb = cipherText.slice(0, Rb_len); // ephemeral public key bytes
var EM = cipherText.slice(Rb_len, ct_len - D_len); // encrypted content bytes
var D = cipherText.slice(ct_len - D_len);

// debug("Rb :\n", new Buffer(Rb).toString('hex'));
// debug("EM :\n", new Buffer(EM).toString('hex'));
// debug("D :\n", new Buffer(D).toString('hex'));

var EC = elliptic.ec;
//var curve = elliptic.curves['p'+level];
var ecdsa = new EC('p' + level);

//convert bytes to usable key object
var ephPubKey = ecdsa.keyFromPublic(new Buffer(Rb, 'hex'), 'hex');
//var encPrivKey = ecdsa.keyFromPrivate(ecKeypair2.prvKeyObj.prvKeyHex, 'hex');
var privKey = ecdsa.keyFromPrivate(recipientPrivateKey.prvKeyHex, 'hex');
// debug('computing Z...', privKey, ephPubKey);

var Z = privKey.derive(ephPubKey.pub);
// debug('Z computed', Z);
// debug('secret: ', new Buffer(Z.toArray(), 'hex'));
var kdfOutput = self._hkdf(Z.toArray(), ECIESKDFOutput, null, null);
var aesKey = kdfOutput.slice(0, AESKeyLength);
var hmacKey = kdfOutput.slice(AESKeyLength, AESKeyLength + HMACKeyLength);
// debug('secret: ', new Buffer(Z.toArray(), 'hex'));
// debug('aesKey: ', new Buffer(aesKey, 'hex'));
// debug('hmacKey: ', new Buffer(hmacKey, 'hex'));

var recoveredD = self.hmac(hmacKey, EM);
debug('recoveredD: ', new Buffer(recoveredD).toString('hex'));

if (D.compare(new Buffer(recoveredD)) != 0) {
// debug("D="+D.toString('hex')+" vs "+new Buffer(recoveredD).toString('hex'));
throw new Error("HMAC verify failed");
}
var iv = EM.slice(0, IVLength);
var cipher = crypto.createDecipheriv('aes-256-cfb', new Buffer(aesKey), iv);
var decryptedBytes = cipher.update(EM.slice(IVLength));
// debug("decryptedBytes: ",new Buffer(decryptedBytes).toString('hex'));
return decryptedBytes;
},

aesKeyGen: function() {
return crypto.randomBytes(AESKeyLength);
},
Expand Down
24 changes: 12 additions & 12 deletions lib/MemberServices.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@ var MemberServices = api.MemberServices.extend({

// Sign the registration request
var buf = protoReq.toBuffer();
var signKey = self.cryptoPrimitives.ecdsaKeyFromPrivate(registrar.getEnrollment().key, 'hex');
var sig = self.cryptoPrimitives.ecdsaSign(signKey, buf);
var signKey = self.cryptoPrimitives.getKeyPairForSigning(registrar.getEnrollment().key, 'hex');
var sig = self.cryptoPrimitives.sign(signKey, buf);
protoReq.setSig( new _caProto.Signature(
{
type: _caProto.CryptoType.ECDSA,
type: _caProto.CryptoType[self.cryptoPrimitives.getPublicKeyAlgorithm()],
r: new Buffer(sig.r.toString()),
s: new Buffer(sig.s.toString())
}
Expand Down Expand Up @@ -172,12 +172,12 @@ var MemberServices = api.MemberServices.extend({
return;
}

// generate ECDSA keys: signing and encryption keys
// generate key pairs for signing and encryption
// 1) signing key
var signingKeyPair = self.cryptoPrimitives.ecdsaKeyGen();
var signingKeyPair = self.cryptoPrimitives.generateKeyPair();
var spki = new asn1.x509.SubjectPublicKeyInfo(signingKeyPair.pubKeyObj);
// 2) encryption key
var encryptionKeyPair = self.cryptoPrimitives.ecdsaKeyGen();
var encryptionKeyPair = self.cryptoPrimitives.generateKeyPair();
var spki2 = new asn1.x509.SubjectPublicKeyInfo(encryptionKeyPair.pubKeyObj);

// create the proto message
Expand All @@ -190,15 +190,15 @@ var MemberServices = api.MemberServices.extend({
// public signing key (ecdsa)
var signPubKey = new _caProto.PublicKey(
{
type: _caProto.CryptoType.ECDSA,
type: _caProto.CryptoType[self.cryptoPrimitives.getPublicKeyAlgorithm()],
key: new Buffer(spki.getASN1Object().getEncodedHex(), 'hex')
});
eCertCreateRequest.setSign(signPubKey);

// public encryption key (ecdsa)
var encPubKey = new _caProto.PublicKey(
{
type: _caProto.CryptoType.ECDSA,
type: _caProto.CryptoType[self.cryptoPrimitives.getPublicKeyAlgorithm()],
key: new Buffer(spki2.getASN1Object().getEncodedHex(), 'hex')
});
eCertCreateRequest.setEnc(encPubKey);
Expand All @@ -210,7 +210,7 @@ var MemberServices = api.MemberServices.extend({
}

var cipherText = eCertCreateResp.tok.tok;
var decryptedTokBytes = self.cryptoPrimitives.eciesDecrypt(encryptionKeyPair.prvKeyObj, cipherText);
var decryptedTokBytes = self.cryptoPrimitives.asymmetricDecrypt(encryptionKeyPair.prvKeyObj, cipherText);

//debug(decryptedTokBytes);
// debug(decryptedTokBytes.toString());
Expand All @@ -220,13 +220,13 @@ var MemberServices = api.MemberServices.extend({

var buf = eCertCreateRequest.toBuffer();

var signKey = self.cryptoPrimitives.ecdsaKeyFromPrivate(signingKeyPair.prvKeyObj.prvKeyHex, 'hex');
var signKey = self.cryptoPrimitives.getKeyPairForSigning(signingKeyPair.prvKeyObj.prvKeyHex, 'hex');
//debug(new Buffer(sha3_384(buf),'hex'));
var sig = self.cryptoPrimitives.ecdsaSign(signKey, buf);
var sig = self.cryptoPrimitives.sign(signKey, buf);

eCertCreateRequest.setSig(new _caProto.Signature(
{
type: _caProto.CryptoType.ECDSA,
type: _caProto.CryptoType[self.cryptoPrimitives.getPublicKeyAlgorithm()],
r: new Buffer(sig.r.toString()),
s: new Buffer(sig.s.toString())
}
Expand Down
71 changes: 55 additions & 16 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ module.exports.CryptoSuite = Base.extend(/** @lends module:api.CryptoSuite.proto
*/
TCertEncTCertIndex: "1.2.3.4.5.6.7",

/**
* Returns the supported asymmetric key algorithms that this suite represents
*
* @returns {string} The algorithm name such as "ECDSA", "RSA" etc.
*/
getPublicKeyAlgorithm: function() {},

/**
* Get the security level of the asymmetric keys algorithm, which corresponds to the
* length of the key
Expand Down Expand Up @@ -106,34 +113,66 @@ module.exports.CryptoSuite = Base.extend(/** @lends module:api.CryptoSuite.proto
*/
generateNonce: function() {},

ecdsaKeyFromPrivate: function(key, encoding) {},

ecdsaKeyFromPublic: function(key, encoding) {},
/**
* Generate asymmetric key pair
*
* @returns {Object} an associative array which has the following parameters:
* prvKeyObj - RSAKey or ECDSA object of private key
* pubKeyObj - RSAKey or ECDSA object of public key
*/
generateKeyPair: function() {},

ecdsaPrivateKeyToASN1: function(prvKeyHex /*string*/ ) {},
/**
* Decrypt, using the private key of an asymmetric key pair, a cipher that was
* encrypted with the public key of the key pair
*
* @returns {byte[]} Decripted bytes
*/
asymmetricDecrypt: function(recipientPrivateKey, cipherText) {},

ecdsaSign: function(key /*Buffer*/ , msg /*Buffer*/ ) {},
/**
* Packages the private key into a key pair to use for signing
*
* @param {string} privateKey the private key
* @param {string} encoding "hex" or other encoding scheme
* @returns {Object} an associative array which has the following parameters:
* prvKeyObj - RSAKey or ECDSA object of private key
* pubKeyObj - null, as only the private key is needed for signing
*/
getKeyPairForSigning: function(privateKey, encoding) {},

ecdsaPEMToPublicKey: function(chainKey) {},
/**
* Packages the public key into a key pair to use for encryption
*
* @param {string} publicKey the public key
* @param {string} encoding "hex" or other encoding scheme
* @returns {Object} an associative array which has the following parameters:
* prvKeyObj - null, as only the public key is needed for encryption
* pubKeyObj - RSAKey or ECDSA object of public key
*/
getKeyPairForEncryption: function(publicKey, encoding) {},

eciesEncryptECDSA: function(ecdsaRecipientPublicKey, msg) {},
/**
* Signs the message with digital signature
*
* @param {Object} key The key pair object as an associative array with the following parameters:
* prvKeyObj - RSAKey or ECDSA object of private key
* pubKeyObj - null, as only the private key is needed for signing
* @param {Buffer} msg The message content to be signed
* @returns {Object} the signature
*/
sign: function(key /*Buffer*/ , msg /*Buffer*/ ) {},

ecdsaKeyGen: function() {},

ecdsaPrivateKeyToASN1: function(prvKeyHex /*string*/ ) {},
ecdsaPEMToPublicKey: function(chainKey) {},
eciesEncryptECDSA: function(ecdsaRecipientPublicKey, msg) {},
eciesKeyGen: function() {},

eciesEncrypt: function(recipientPublicKey, msg) {},

eciesDecrypt: function(recipientPrivateKey, cipherText) {},

hmac: function(key, bytes) {},

aesCBCPKCS7Decrypt: function(key, bytes) {},

aes256GCMDecrypt(key /*Buffer*/ , ct /*Buffer*/ ) {},

aesKeyGen: function() {},

hmacAESTruncated: function(key, bytes) {}

});
Expand Down

0 comments on commit 0b2d441

Please sign in to comment.