From 3370eaa2aa27ea3cca31da6cc4731412f43ef13f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 2 Feb 2018 05:42:54 -0800 Subject: [PATCH] Expands truncated little-endian parameters in EdDSA --- src/crypto/public_key/elliptic/curves.js | 3 ++- src/crypto/public_key/elliptic/ecdh.js | 20 +++++++-------- src/crypto/public_key/elliptic/ecdsa.js | 4 +-- src/crypto/public_key/elliptic/eddsa.js | 8 +++--- src/crypto/public_key/elliptic/key.js | 14 +++++------ src/crypto/public_key/jsbn.js | 4 +-- test/general/openpgp.js | 1 + test/general/x25519.js | 32 +++++++++++++++++++++++- 8 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index f835171a..9b7cf974 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -95,7 +95,8 @@ const curves = { ed25519: { oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]), keyType: enums.publicKey.eddsa, - hash: enums.hash.sha512 + hash: enums.hash.sha512, + payloadSize: 32 }, curve25519: { oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index c65dc26d..ff5d3f4e 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -18,11 +18,11 @@ // Key encryption and decryption for RFC 6637 ECDH /** - * @requires crypto/hash - * @requires crypto/cipher - * @requires crypto/aes_kw * @requires crypto/public_key/elliptic/curves * @requires crypto/public_key/jsbn + * @requires crypto/cipher + * @requires crypto/hash + * @requires crypto/aes_kw * @requires type/oid * @requires type/kdf_params * @requires enums @@ -32,15 +32,15 @@ 'use strict'; -import BigInteger from '../jsbn.js'; -import curves from './curves.js'; +import curves from './curves'; +import BigInteger from '../jsbn'; import cipher from '../../cipher'; import hash from '../../hash'; -import aes_kw from '../../aes_kw.js'; -import enums from '../../../enums.js'; -import util from '../../../util.js'; -import type_kdf_params from '../../../type/kdf_params.js'; -import type_oid from '../../../type/oid.js'; +import aes_kw from '../../aes_kw'; +import type_kdf_params from '../../../type/kdf_params'; +import type_oid from '../../../type/oid'; +import enums from '../../../enums'; +import util from '../../../util'; // Build Param for ECDH algorithm (RFC 6637) diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 4b73166e..0e2dd6e3 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -27,8 +27,8 @@ 'use strict'; import hash from '../../hash'; -import curves from './curves.js'; -import BigInteger from '../jsbn.js'; +import curves from './curves'; +import BigInteger from '../jsbn'; /** * Sign a message using the provided key diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 857f5ecd..0b62a1f8 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -27,8 +27,8 @@ 'use strict'; import hash from '../../hash'; -import curves from './curves.js'; -import BigInteger from '../jsbn.js'; +import curves from './curves'; +import BigInteger from '../jsbn'; /** * Sign a message using the provided key @@ -60,8 +60,10 @@ async function sign(oid, hash_algo, m, d) { async function verify(oid, hash_algo, signature, m, Q) { const curve = curves.get(oid); const key = curve.keyFromPublic(Q.toByteArray()); + const R = signature.r.toByteArray(), S = signature.s.toByteArray(); return key.verify( - m, { R: signature.r.toByteArray(), S: signature.s.toByteArray() }, hash_algo + m, { R: Array(curve.payloadSize - R.length).fill(0).concat(R), + S: Array(curve.payloadSize - S.length).fill(0).concat(S) }, hash_algo ); } diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 417b34ec..cc9d7404 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -18,21 +18,22 @@ // Wrapper for a KeyPair of an Elliptic Curve /** - * @requires bn.js - * @requires asn1.js - * @requires jwk-to-pem * @requires crypto/public_key/elliptic/curves + * @requires crypto/public_key/jsbn * @requires crypto/hash * @requires util * @requires enums * @requires config * @requires encoding/base64 + * @requires jwk-to-pem + * @requires asn1.js * @module crypto/public_key/elliptic/key */ 'use strict'; import curves from './curves'; +import BigInteger from '../jsbn'; import hash from '../../hash'; import util from '../../../util'; import enums from '../../../enums'; @@ -44,7 +45,6 @@ const webCurves = curves.webCurves; const nodeCrypto = util.getNodeCrypto(); const nodeCurves = curves.nodeCurves; -const BN = nodeCrypto ? require('bn.js') : undefined; const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined; const ECDSASignature = nodeCrypto ? require('asn1.js').define('ECDSASignature', function() { @@ -167,8 +167,8 @@ async function webSign(curve, hash_algo, message, keyPair) { async function webVerify(curve, hash_algo, {r, s}, message, publicKey) { var l = curve.payloadSize; - r = (r.length === l) ? r : [0].concat(r); - s = (s.length === l) ? s : [0].concat(s); + r = Array(l - r.length).fill(0).concat(r); + s = Array(l - s.length).fill(0).concat(s); var signature = new Uint8Array(r.concat(s)).buffer; const key = await webCrypto.importKey( "jwk", @@ -227,7 +227,7 @@ async function nodeSign(curve, hash_algo, message, keyPair) { } async function nodeVerify(curve, hash_algo, {r, s}, message, publicKey) { - var signature = ECDSASignature.encode({ r: new BN(r), s: new BN(s) }, 'der'); + var signature = ECDSASignature.encode({ r: new BigInteger(r), s: new BigInteger(s) }, 'der'); const key = jwkToPem( { "kty": "EC", diff --git a/src/crypto/public_key/jsbn.js b/src/crypto/public_key/jsbn.js index 908a6be7..bcfe7705 100644 --- a/src/crypto/public_key/jsbn.js +++ b/src/crypto/public_key/jsbn.js @@ -37,7 +37,7 @@ * @module crypto/public_key/jsbn */ -import util from '../../util.js'; +import util from '../../util'; // Basic JavaScript BN library - subset useful for RSA encryption. @@ -53,7 +53,7 @@ var j_lm = ((canary & 0xffffff) == 0xefcafe); export default function BigInteger(a, b, c) { if (a != null) if ("number" == typeof a) this.fromNumber(a, b, c); - else if (b == null && "string" != typeof a) this.fromString(a, 256); + else if (b == null && !util.isString(a)) this.fromString(a, 256); else this.fromString(a, b); } diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 9f874e21..4991b0e2 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -1224,6 +1224,7 @@ describe('OpenPGP.js public api tests', function() { }); }); + // FIXME this test sporadically fails it('should encrypt and decrypt with two passwords', function() { var encOpt = { data: plaintext, diff --git a/test/general/x25519.js b/test/general/x25519.js index 3ba34002..1cd5e88f 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -107,6 +107,7 @@ describe('X25519 Cryptography', function () { '-----END PGP MESSAGE-----'].join('\n') } }; + function load_pub_key(name) { if (data[name].pub_key) { return data[name].pub_key; @@ -269,7 +270,6 @@ describe('X25519 Cryptography', function () { var msg = openpgp.cleartext.readArmored(signed.data); // Verifying signed message return Promise.all([ - // FIXME this test sporadically fails openpgp.verify( { message: msg, publicKeys: hi.toPublic() } ).then(output => expect(output.signatures[0].valid).to.be.true), @@ -302,4 +302,34 @@ describe('X25519 Cryptography', function () { }); }); }); + + it('Should handle little-endian parameters in EdDSA', function () { + var pubKey = [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + 'Version: OpenPGP.js VERSION', + 'Comment: https://openpgpjs.org', + '', + 'xjMEWnRgnxYJKwYBBAHaRw8BAQdAZ8gxxCdUxIv4tBwhfUMW2uoEb1KvOfP8', + 'D+0ObBtsLnfNDkhpIDxoaUBoZWwubG8+wnYEEBYKACkFAlp0YJ8GCwkHCAMC', + 'CRDAYsFlymHCFQQVCAoCAxYCAQIZAQIbAwIeAQAAswsA/3qNZnwBn/ef4twv', + 'uvmFicYK//DDX1jIkpDiQ+/okLUEAPdAr3J/Z2WA7OD0d36trHNB06WLXJUu', + 'aCVm1TwoJHcNzjgEWnRgnxIKKwYBBAGXVQEFAQEHQPBVH+skap0NHMBw2HMe', + 'xWYUQ67I9Did3KoJuuEJ/ctQAwEIB8JhBBgWCAATBQJadGCfCRDAYsFlymHC', + 'FQIbDAAAhNQBAKmy4gPorjbwTwy5usylHttP28XnTdaGkZ1E7Rc3G9luAQCs', + 'Gbm1oe83ZB+0aSp5m34YkpHQNb80y8PGFy7nIexiAA==', + '=xeG/', + '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); + var hi = openpgp.key.readArmored(pubKey).keys[0]; + return hi.verifyPrimaryUser().then(() => { + var results = hi.getPrimaryUser(); + expect(results.user).to.exist; + var user = results.user; + expect(user.selfCertifications[0].verify( + hi.primaryKey, {userid: user.userId, key: hi.primaryKey} + )).to.eventually.be.true; + expect(user.verifyCertificate( + hi.primaryKey, user.selfCertifications[0], [hi] + )).to.eventually.equal(openpgp.enums.keyStatus.valid); + }); + }); });