From a5d9e6d09e18b43af36766400efb5eb0f5120f3a Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:25:47 -0300 Subject: [PATCH] Generation of keys for elliptic curves --- src/crypto/crypto.js | 26 +++++++++++++++++++++++++- src/key.js | 16 ++++++++++++---- src/openpgp.js | 8 ++++++-- src/packet/secret_key.js | 4 ++-- test/general/ecc.js | 15 +++++++++++++++ 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 88849808..73568598 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -22,7 +22,9 @@ * @requires crypto/public_key * @requires crypto/random * @requires type/ecdh_symkey + * @requires type/kdf_params * @requires type/mpi + * @requires type/oid * @module crypto/crypto */ @@ -32,7 +34,9 @@ import random from './random.js'; import cipher from './cipher'; import publicKey from './public_key'; import type_ecdh_symkey from '../type/ecdh_symkey.js'; +import type_kdf_params from '../type/kdf_params.js'; import type_mpi from '../type/mpi.js'; +import type_oid from '../type/oid.js'; function BigInteger2mpi(bn) { var mpi = new type_mpi(); @@ -224,7 +228,7 @@ export default { } }, - generateMpi: function(algo, bits) { + generateMpi: function(algo, bits, curve) { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': @@ -241,6 +245,26 @@ export default { output.push(keyObject.u); return mapResult(output); }); + + case 'ecdsa': + return publicKey.elliptic.generate(curve).then(function (key) { + return [ + new type_oid(key.oid), + BigInteger2mpi(key.R), + BigInteger2mpi(key.r) + ]; + }); + + case 'ecdh': + return publicKey.elliptic.generate(curve).then(function (key) { + return [ + new type_oid(key.oid), + BigInteger2mpi(key.R), + new type_kdf_params(key.hash, key.cipher), + BigInteger2mpi(key.r) + ]; + }); + default: throw new Error('Unsupported algorithm for key generation.'); } diff --git a/src/key.js b/src/key.js index 75fcf772..1a6ae9b9 100644 --- a/src/key.js +++ b/src/key.js @@ -1115,8 +1115,12 @@ export function readArmored(armoredText) { export function generate(options) { var secretKeyPacket, secretSubkeyPacket; return Promise.resolve().then(() => { + if (options.curve) { + options.keyType = enums.publicKey.ecdsa; + } options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; - if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated + if (options.keyType !== enums.publicKey.rsa_encrypt_sign && + options.keyType !== enums.publicKey.ecdsa) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated throw new Error('Only RSA Encrypt or Sign supported'); } @@ -1135,13 +1139,17 @@ export function generate(options) { function generateSecretKey() { secretKeyPacket = new packet.SecretKey(); secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - return secretKeyPacket.generate(options.numBits); + return secretKeyPacket.generate(options.numBits, options.curve); } function generateSecretSubkey() { secretSubkeyPacket = new packet.SecretSubkey(); - secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - return secretSubkeyPacket.generate(options.numBits); + var subkeyType = options.keyType; + if (subkeyType === enums.publicKey.ecdsa) { + subkeyType = enums.publicKey.ecdh; + } + secretSubkeyPacket.algorithm = enums.read(enums.publicKey, subkeyType); + return secretSubkeyPacket.generate(options.numBits, options.curve); } } diff --git a/src/openpgp.js b/src/openpgp.js index ba4d27af..88074dfd 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -97,8 +97,9 @@ export function destroyWorker() { * { key:Key, privateKeyArmored:String, publicKeyArmored:String } * @static */ -export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0 } = {}) { - const options = formatUserIds({ userIds, passphrase, numBits, unlocked, keyExpirationTime }); + +export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, curve=null } = {}) { + const options = formatUserIds({ userIds, passphrase, numBits, unlocked, curve }); if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported return asyncProxy.delegate('generateKey', options); @@ -455,6 +456,9 @@ function checkCleartextOrMessage(message) { * Format user ids for internal use. */ function formatUserIds(options) { + if (!options.curve) { + delete options.curve; + } if (!options.userIds) { return options; } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index e4f88e41..8e525e41 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -273,10 +273,10 @@ SecretKey.prototype.decrypt = function (passphrase) { return true; }; -SecretKey.prototype.generate = function (bits) { +SecretKey.prototype.generate = function (bits, curve) { var self = this; - return crypto.generateMpi(self.algorithm, bits).then(function(mpi) { + return crypto.generateMpi(self.algorithm, bits, curve).then(function(mpi) { self.mpi = mpi; self.isDecrypted = true; }); diff --git a/test/general/ecc.js b/test/general/ecc.js index 61bef4b6..b4522604 100644 --- a/test/general/ecc.js +++ b/test/general/ecc.js @@ -211,4 +211,19 @@ describe('Elliptic Curve Cryptography', function () { }); }); }); + it('Generate key', function (done) { + var options = { + userIds: {name: "Hamlet (secp256k1)", email: "hamlet@example.net"}, + curve: "secp256k1", + passphrase: "ophelia" + }; + openpgp.generateKey(options).then(function (key) { + expect(key).to.exist; + expect(key.key).to.exist; + expect(key.key.primaryKey).to.exist; + expect(key.privateKeyArmored).to.exist; + expect(key.publicKeyArmored).to.exist; + done(); + }); + }); });