Expands truncated little-endian parameters in EdDSA

This commit is contained in:
Mahrud Sayrafi 2018-02-02 05:42:54 -08:00
parent 9cbfbf453b
commit 3370eaa2aa
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
8 changed files with 60 additions and 26 deletions

View File

@ -95,7 +95,8 @@ const curves = {
ed25519: { ed25519: {
oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]), oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]),
keyType: enums.publicKey.eddsa, keyType: enums.publicKey.eddsa,
hash: enums.hash.sha512 hash: enums.hash.sha512,
payloadSize: 32
}, },
curve25519: { curve25519: {
oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]),

View File

@ -18,11 +18,11 @@
// Key encryption and decryption for RFC 6637 ECDH // 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/elliptic/curves
* @requires crypto/public_key/jsbn * @requires crypto/public_key/jsbn
* @requires crypto/cipher
* @requires crypto/hash
* @requires crypto/aes_kw
* @requires type/oid * @requires type/oid
* @requires type/kdf_params * @requires type/kdf_params
* @requires enums * @requires enums
@ -32,15 +32,15 @@
'use strict'; 'use strict';
import BigInteger from '../jsbn.js'; import curves from './curves';
import curves from './curves.js'; import BigInteger from '../jsbn';
import cipher from '../../cipher'; import cipher from '../../cipher';
import hash from '../../hash'; import hash from '../../hash';
import aes_kw from '../../aes_kw.js'; import aes_kw from '../../aes_kw';
import enums from '../../../enums.js'; import type_kdf_params from '../../../type/kdf_params';
import util from '../../../util.js'; import type_oid from '../../../type/oid';
import type_kdf_params from '../../../type/kdf_params.js'; import enums from '../../../enums';
import type_oid from '../../../type/oid.js'; import util from '../../../util';
// Build Param for ECDH algorithm (RFC 6637) // Build Param for ECDH algorithm (RFC 6637)

View File

@ -27,8 +27,8 @@
'use strict'; 'use strict';
import hash from '../../hash'; import hash from '../../hash';
import curves from './curves.js'; import curves from './curves';
import BigInteger from '../jsbn.js'; import BigInteger from '../jsbn';
/** /**
* Sign a message using the provided key * Sign a message using the provided key

View File

@ -27,8 +27,8 @@
'use strict'; 'use strict';
import hash from '../../hash'; import hash from '../../hash';
import curves from './curves.js'; import curves from './curves';
import BigInteger from '../jsbn.js'; import BigInteger from '../jsbn';
/** /**
* Sign a message using the provided key * 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) { async function verify(oid, hash_algo, signature, m, Q) {
const curve = curves.get(oid); const curve = curves.get(oid);
const key = curve.keyFromPublic(Q.toByteArray()); const key = curve.keyFromPublic(Q.toByteArray());
const R = signature.r.toByteArray(), S = signature.s.toByteArray();
return key.verify( 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
); );
} }

View File

@ -18,21 +18,22 @@
// Wrapper for a KeyPair of an Elliptic Curve // 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/elliptic/curves
* @requires crypto/public_key/jsbn
* @requires crypto/hash * @requires crypto/hash
* @requires util * @requires util
* @requires enums * @requires enums
* @requires config * @requires config
* @requires encoding/base64 * @requires encoding/base64
* @requires jwk-to-pem
* @requires asn1.js
* @module crypto/public_key/elliptic/key * @module crypto/public_key/elliptic/key
*/ */
'use strict'; 'use strict';
import curves from './curves'; import curves from './curves';
import BigInteger from '../jsbn';
import hash from '../../hash'; import hash from '../../hash';
import util from '../../../util'; import util from '../../../util';
import enums from '../../../enums'; import enums from '../../../enums';
@ -44,7 +45,6 @@ const webCurves = curves.webCurves;
const nodeCrypto = util.getNodeCrypto(); const nodeCrypto = util.getNodeCrypto();
const nodeCurves = curves.nodeCurves; const nodeCurves = curves.nodeCurves;
const BN = nodeCrypto ? require('bn.js') : undefined;
const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined; const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined;
const ECDSASignature = nodeCrypto ? const ECDSASignature = nodeCrypto ?
require('asn1.js').define('ECDSASignature', function() { 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) { async function webVerify(curve, hash_algo, {r, s}, message, publicKey) {
var l = curve.payloadSize; var l = curve.payloadSize;
r = (r.length === l) ? r : [0].concat(r); r = Array(l - r.length).fill(0).concat(r);
s = (s.length === l) ? s : [0].concat(s); s = Array(l - s.length).fill(0).concat(s);
var signature = new Uint8Array(r.concat(s)).buffer; var signature = new Uint8Array(r.concat(s)).buffer;
const key = await webCrypto.importKey( const key = await webCrypto.importKey(
"jwk", "jwk",
@ -227,7 +227,7 @@ async function nodeSign(curve, hash_algo, message, keyPair) {
} }
async function nodeVerify(curve, hash_algo, {r, s}, message, publicKey) { 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( const key = jwkToPem(
{ {
"kty": "EC", "kty": "EC",

View File

@ -37,7 +37,7 @@
* @module crypto/public_key/jsbn * @module crypto/public_key/jsbn
*/ */
import util from '../../util.js'; import util from '../../util';
// Basic JavaScript BN library - subset useful for RSA encryption. // 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) { export default function BigInteger(a, b, c) {
if (a != null) if (a != null)
if ("number" == typeof a) this.fromNumber(a, b, c); 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); else this.fromString(a, b);
} }

View File

@ -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() { it('should encrypt and decrypt with two passwords', function() {
var encOpt = { var encOpt = {
data: plaintext, data: plaintext,

View File

@ -107,6 +107,7 @@ describe('X25519 Cryptography', function () {
'-----END PGP MESSAGE-----'].join('\n') '-----END PGP MESSAGE-----'].join('\n')
} }
}; };
function load_pub_key(name) { function load_pub_key(name) {
if (data[name].pub_key) { if (data[name].pub_key) {
return data[name].pub_key; return data[name].pub_key;
@ -269,7 +270,6 @@ describe('X25519 Cryptography', function () {
var msg = openpgp.cleartext.readArmored(signed.data); var msg = openpgp.cleartext.readArmored(signed.data);
// Verifying signed message // Verifying signed message
return Promise.all([ return Promise.all([
// FIXME this test sporadically fails
openpgp.verify( openpgp.verify(
{ message: msg, publicKeys: hi.toPublic() } { message: msg, publicKeys: hi.toPublic() }
).then(output => expect(output.signatures[0].valid).to.be.true), ).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);
});
});
}); });