mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-06-12 09:06:50 +00:00
Replace indutny-elliptic lib with noble-curves
Unlike elliptic, noble-curves targets algorithmic constant time, and it relies on the native BigInts when available, resulting in a smaller bundle and improved performance. Also, expand testing of fallback elliptic implementation.
This commit is contained in:
parent
01df8ca889
commit
a9fae5ff12
97
package-lock.json
generated
97
package-lock.json
generated
@ -13,7 +13,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openpgp/asmcrypto.js": "^3.0.0",
|
||||
"@openpgp/elliptic": "^6.5.1",
|
||||
"@openpgp/jsdoc": "^3.6.11",
|
||||
"@openpgp/noble-curves": "^1.2.1-0",
|
||||
"@openpgp/noble-hashes": "^1.3.3-0",
|
||||
@ -572,21 +571,6 @@
|
||||
"integrity": "sha512-X/DPYy7uHe+dlY2Botb99uXwb2kXR6HTv0hQOnnI0TVEqOIMQyzCDWAzlX00AacsYryDAphuOndg6mk6wtJCNg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@openpgp/elliptic": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@openpgp/elliptic/-/elliptic-6.5.1.tgz",
|
||||
"integrity": "sha512-VR20QWndMXoZTAzCUqauDT4dLrHO4RTnyVV3szuRHllQSU/JZToLvWtFxpEQth4XWyqlxHPwq7tljE5V97+n1g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openpgp/jsdoc": {
|
||||
"version": "3.6.11",
|
||||
"resolved": "https://registry.npmjs.org/@openpgp/jsdoc/-/jsdoc-3.6.11.tgz",
|
||||
@ -1489,12 +1473,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/brorand": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/browser-stdout": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
|
||||
@ -3465,16 +3443,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hash.js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
|
||||
"integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hasha": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz",
|
||||
@ -3496,17 +3464,6 @@
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hash.js": "^1.0.3",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
@ -4975,12 +4932,6 @@
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
|
||||
"integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M="
|
||||
},
|
||||
"node_modules/minimalistic-crypto-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@ -7993,21 +7944,6 @@
|
||||
"integrity": "sha512-X/DPYy7uHe+dlY2Botb99uXwb2kXR6HTv0hQOnnI0TVEqOIMQyzCDWAzlX00AacsYryDAphuOndg6mk6wtJCNg==",
|
||||
"dev": true
|
||||
},
|
||||
"@openpgp/elliptic": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@openpgp/elliptic/-/elliptic-6.5.1.tgz",
|
||||
"integrity": "sha512-VR20QWndMXoZTAzCUqauDT4dLrHO4RTnyVV3szuRHllQSU/JZToLvWtFxpEQth4XWyqlxHPwq7tljE5V97+n1g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@openpgp/jsdoc": {
|
||||
"version": "3.6.11",
|
||||
"resolved": "https://registry.npmjs.org/@openpgp/jsdoc/-/jsdoc-3.6.11.tgz",
|
||||
@ -8699,12 +8635,6 @@
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"brorand": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
||||
"dev": true
|
||||
},
|
||||
"browser-stdout": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
|
||||
@ -10238,16 +10168,6 @@
|
||||
"has-symbols": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"hash.js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
|
||||
"integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hasha": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz",
|
||||
@ -10263,17 +10183,6 @@
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true
|
||||
},
|
||||
"hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"hash.js": "^1.0.3",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
@ -11425,12 +11334,6 @@
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
|
||||
"integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M="
|
||||
},
|
||||
"minimalistic-crypto-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
@ -64,7 +64,6 @@
|
||||
"devDependencies": {
|
||||
"@openpgp/asmcrypto.js": "^3.0.0",
|
||||
"@openpgp/noble-curves": "^1.2.1-0",
|
||||
"@openpgp/elliptic": "^6.5.1",
|
||||
"@openpgp/jsdoc": "^3.6.11",
|
||||
"@openpgp/noble-hashes": "^1.3.3-0",
|
||||
"@openpgp/seek-bzip": "^1.0.5-git",
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
import nacl from '@openpgp/tweetnacl/nacl-fast-light';
|
||||
import { CurveWithOID, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams } from './oid_curves';
|
||||
import { CurveWithOID, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams, getNobleCurve } from './oid_curves';
|
||||
import * as aesKW from '../../aes_kw';
|
||||
import { getRandomBytes } from '../../random';
|
||||
import hash from '../../hash';
|
||||
@ -29,7 +29,6 @@ import enums from '../../../enums';
|
||||
import util from '../../../util';
|
||||
import { b64ToUint8Array } from '../../../encoding/base64';
|
||||
import * as pkcs5 from '../../pkcs5';
|
||||
import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
|
||||
import getCipher from '../../cipher/getCipher';
|
||||
|
||||
const webCrypto = util.getWebCrypto();
|
||||
@ -105,13 +104,16 @@ async function genPublicEphemeralKey(curve, Q) {
|
||||
return await webPublicEphemeralKey(curve, Q);
|
||||
} catch (err) {
|
||||
util.printDebugError(err);
|
||||
return jsPublicEphemeralKey(curve, Q);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'node':
|
||||
return nodePublicEphemeralKey(curve, Q);
|
||||
default: {
|
||||
return jsPublicEphemeralKey(curve, Q);
|
||||
}
|
||||
}
|
||||
return ellipticPublicEphemeralKey(curve, Q);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -165,13 +167,16 @@ async function genPrivateEphemeralKey(curve, V, Q, d) {
|
||||
return await webPrivateEphemeralKey(curve, V, Q, d);
|
||||
} catch (err) {
|
||||
util.printDebugError(err);
|
||||
return jsPrivateEphemeralKey(curve, V, d);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'node':
|
||||
return nodePrivateEphemeralKey(curve, V, d);
|
||||
default: {
|
||||
return jsPrivateEphemeralKey(curve, V, d);
|
||||
}
|
||||
}
|
||||
return ellipticPrivateEphemeralKey(curve, V, d);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,6 +210,24 @@ export async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
function jsPrivateEphemeralKey(curve, V, d) {
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
// The output includes parity byte
|
||||
const sharedSecretWithParity = nobleCurve.getSharedSecret(d, V);
|
||||
const sharedKey = sharedSecretWithParity.subarray(1);
|
||||
return { secretKey: d, sharedKey };
|
||||
}
|
||||
|
||||
async function jsPublicEphemeralKey(curve, Q) {
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
const { publicKey: V, privateKey: v } = await curve.genKeyPair();
|
||||
|
||||
// The output includes parity byte
|
||||
const sharedSecretWithParity = nobleCurve.getSharedSecret(v, Q);
|
||||
const sharedKey = sharedSecretWithParity.subarray(1);
|
||||
return { publicKey: V, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ECDHE secret from private key and public part of ephemeral key using webCrypto
|
||||
*
|
||||
@ -306,46 +329,6 @@ async function webPublicEphemeralKey(curve, Q) {
|
||||
return { publicKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ECDHE secret from private key and public part of ephemeral key using indutny/elliptic
|
||||
*
|
||||
* @param {CurveWithOID} curve - Elliptic curve object
|
||||
* @param {Uint8Array} V - Public part of ephemeral key
|
||||
* @param {Uint8Array} d - Recipient private key
|
||||
* @returns {Promise<{secretKey: Uint8Array, sharedKey: Uint8Array}>}
|
||||
* @async
|
||||
*/
|
||||
async function ellipticPrivateEphemeralKey(curve, V, d) {
|
||||
const indutnyCurve = await getIndutnyCurve(curve.name);
|
||||
V = keyFromPublic(indutnyCurve, V);
|
||||
d = keyFromPrivate(indutnyCurve, d);
|
||||
const secretKey = new Uint8Array(d.getPrivate());
|
||||
const S = d.derive(V.getPublic());
|
||||
const len = indutnyCurve.curve.p.byteLength();
|
||||
const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
|
||||
return { secretKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ECDHE ephemeral key and secret from public key using indutny/elliptic
|
||||
*
|
||||
* @param {CurveWithOID} curve - Elliptic curve object
|
||||
* @param {Uint8Array} Q - Recipient public key
|
||||
* @returns {Promise<{publicKey: Uint8Array, sharedKey: Uint8Array}>}
|
||||
* @async
|
||||
*/
|
||||
async function ellipticPublicEphemeralKey(curve, Q) {
|
||||
const indutnyCurve = await getIndutnyCurve(curve.name);
|
||||
const v = await curve.genKeyPair();
|
||||
Q = keyFromPublic(indutnyCurve, Q);
|
||||
const V = keyFromPrivate(indutnyCurve, v.privateKey);
|
||||
const publicKey = v.publicKey;
|
||||
const S = V.derive(Q.getPublic());
|
||||
const len = indutnyCurve.curve.p.byteLength();
|
||||
const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
|
||||
return { publicKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ECDHE secret from private key and public part of ephemeral key using nodeCrypto
|
||||
*
|
||||
|
@ -24,8 +24,7 @@ import enums from '../../../enums';
|
||||
import util from '../../../util';
|
||||
import { getRandomBytes } from '../../random';
|
||||
import hash from '../../hash';
|
||||
import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams } from './oid_curves';
|
||||
import { getIndutnyCurve, keyFromPrivate, keyFromPublic } from './indutnyKey';
|
||||
import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams, getNobleCurve } from './oid_curves';
|
||||
|
||||
const webCrypto = util.getWebCrypto();
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
@ -74,7 +73,14 @@ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed
|
||||
}
|
||||
}
|
||||
}
|
||||
return ellipticSign(curve, hashed, privateKey);
|
||||
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
// lowS: non-canonical sig: https://stackoverflow.com/questions/74338846/ecdsa-signature-verification-mismatch
|
||||
const signature = nobleCurve.sign(hashed, privateKey, { lowS: false });
|
||||
return {
|
||||
r: signature.r.toUint8Array('be', curve.payloadSize),
|
||||
s: signature.s.toUint8Array('be', curve.payloadSize)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,8 +117,10 @@ export async function verify(oid, hashAlgo, signature, message, publicKey, hashe
|
||||
return nodeVerify(curve, hashAlgo, signature, message, publicKey);
|
||||
}
|
||||
}
|
||||
const digest = (typeof hashAlgo === 'undefined') ? message : hashed;
|
||||
return ellipticVerify(curve, signature, digest, publicKey);
|
||||
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
// lowS: non-canonical sig: https://stackoverflow.com/questions/74338846/ecdsa-signature-verification-mismatch
|
||||
return nobleCurve.verify(util.concatUint8Array([signature.r, signature.s]), hashed, publicKey, { lowS: false });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,23 +164,6 @@ export async function validateParams(oid, Q, d) {
|
||||
// Helper functions //
|
||||
// //
|
||||
//////////////////////////
|
||||
|
||||
async function ellipticSign(curve, hashed, privateKey) {
|
||||
const indutnyCurve = await getIndutnyCurve(curve.name);
|
||||
const key = keyFromPrivate(indutnyCurve, privateKey);
|
||||
const signature = key.sign(hashed);
|
||||
return {
|
||||
r: signature.r.toArrayLike(Uint8Array),
|
||||
s: signature.s.toArrayLike(Uint8Array)
|
||||
};
|
||||
}
|
||||
|
||||
async function ellipticVerify(curve, signature, digest, publicKey) {
|
||||
const indutnyCurve = await getIndutnyCurve(curve.name);
|
||||
const key = keyFromPublic(indutnyCurve, publicKey);
|
||||
return key.verify(digest, signature);
|
||||
}
|
||||
|
||||
async function webSign(curve, hashAlgo, message, keyPair) {
|
||||
const len = curve.payloadSize;
|
||||
const jwk = privateToJWK(curve.payloadSize, webCurves[curve.name], keyPair.publicKey, keyPair.privateKey);
|
||||
|
@ -1,44 +0,0 @@
|
||||
// OpenPGP.js - An OpenPGP implementation in javascript
|
||||
// Copyright (C) 2015-2016 Decentral
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 3.0 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
/**
|
||||
* @fileoverview Wrapper for a KeyPair of an curve from indutny/elliptic library
|
||||
* @module crypto/public_key/elliptic/indutnyKey
|
||||
*/
|
||||
|
||||
import config from '../../../config';
|
||||
|
||||
export function keyFromPrivate(indutnyCurve, priv) {
|
||||
const keyPair = indutnyCurve.keyPair({ priv: priv });
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
export function keyFromPublic(indutnyCurve, pub) {
|
||||
const keyPair = indutnyCurve.keyPair({ pub: pub });
|
||||
if (keyPair.validate().result !== true) {
|
||||
throw new Error('Invalid elliptic public key');
|
||||
}
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
export async function getIndutnyCurve(name) {
|
||||
if (!config.useIndutnyElliptic) {
|
||||
throw new Error('This curve is only supported in the full build of OpenPGP.js');
|
||||
}
|
||||
const { default: elliptic } = await import('@openpgp/elliptic');
|
||||
return new elliptic.ec(name);
|
||||
}
|
@ -19,37 +19,58 @@
|
||||
* @fileoverview Wrapper of an instance of an Elliptic Curve
|
||||
* @module crypto/public_key/elliptic/curve
|
||||
*/
|
||||
import { BigInteger } from '@openpgp/noble-hashes/biginteger';
|
||||
import nacl from '@openpgp/tweetnacl/nacl-fast-light';
|
||||
import { p256 } from '@openpgp/noble-curves/p256';
|
||||
import { p384 } from '@openpgp/noble-curves/p384';
|
||||
import { p521 } from '@openpgp/noble-curves/p521';
|
||||
import { brainpoolP256r1 } from '@openpgp/noble-curves/brainpoolP256r1';
|
||||
import { brainpoolP384r1 } from '@openpgp/noble-curves/brainpoolP384r1';
|
||||
import { brainpoolP512r1 } from '@openpgp/noble-curves/brainpoolP512r1';
|
||||
import { secp256k1 } from '@openpgp/noble-curves/secp256k1';
|
||||
import { getRandomBytes } from '../../random';
|
||||
import enums from '../../../enums';
|
||||
import util from '../../../util';
|
||||
import { uint8ArrayToB64, b64ToUint8Array } from '../../../encoding/base64';
|
||||
import OID from '../../../type/oid';
|
||||
import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
|
||||
import { UnsupportedError } from '../../../packet/packet';
|
||||
|
||||
const webCrypto = util.getWebCrypto();
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
|
||||
const webCurves = {
|
||||
'p256': 'P-256',
|
||||
'p384': 'P-384',
|
||||
'p521': 'P-521'
|
||||
[enums.curve.p256]: 'P-256',
|
||||
[enums.curve.p384]: 'P-384',
|
||||
[enums.curve.p521]: 'P-521'
|
||||
};
|
||||
const knownCurves = nodeCrypto ? nodeCrypto.getCurves() : [];
|
||||
const nodeCurves = nodeCrypto ? {
|
||||
secp256k1: knownCurves.includes('secp256k1') ? 'secp256k1' : undefined,
|
||||
p256: knownCurves.includes('prime256v1') ? 'prime256v1' : undefined,
|
||||
p384: knownCurves.includes('secp384r1') ? 'secp384r1' : undefined,
|
||||
p521: knownCurves.includes('secp521r1') ? 'secp521r1' : undefined,
|
||||
ed25519: knownCurves.includes('ED25519') ? 'ED25519' : undefined,
|
||||
curve25519: knownCurves.includes('X25519') ? 'X25519' : undefined,
|
||||
brainpoolP256r1: knownCurves.includes('brainpoolP256r1') ? 'brainpoolP256r1' : undefined,
|
||||
brainpoolP384r1: knownCurves.includes('brainpoolP384r1') ? 'brainpoolP384r1' : undefined,
|
||||
brainpoolP512r1: knownCurves.includes('brainpoolP512r1') ? 'brainpoolP512r1' : undefined
|
||||
[enums.curve.secp256k1]: knownCurves.includes('secp256k1') ? 'secp256k1' : undefined,
|
||||
[enums.curve.p256]: knownCurves.includes('prime256v1') ? 'prime256v1' : undefined,
|
||||
[enums.curve.p384]: knownCurves.includes('secp384r1') ? 'secp384r1' : undefined,
|
||||
[enums.curve.p521]: knownCurves.includes('secp521r1') ? 'secp521r1' : undefined,
|
||||
[enums.curve.ed25519Legacy]: knownCurves.includes('ED25519') ? 'ED25519' : undefined,
|
||||
[enums.curve.curve25519Legacy]: knownCurves.includes('X25519') ? 'X25519' : undefined,
|
||||
[enums.curve.brainpoolP256r1]: knownCurves.includes('brainpoolP256r1') ? 'brainpoolP256r1' : undefined,
|
||||
[enums.curve.brainpoolP384r1]: knownCurves.includes('brainpoolP384r1') ? 'brainpoolP384r1' : undefined,
|
||||
[enums.curve.brainpoolP512r1]: knownCurves.includes('brainpoolP512r1') ? 'brainpoolP512r1' : undefined
|
||||
} : {};
|
||||
|
||||
const nobleCurvess = {
|
||||
[enums.curve.p256]: p256,
|
||||
[enums.curve.p384]: p384,
|
||||
[enums.curve.p521]: p521,
|
||||
[enums.curve.secp256k1]: secp256k1,
|
||||
[enums.curve.brainpoolP256r1]: brainpoolP256r1,
|
||||
[enums.curve.brainpoolP384r1]: brainpoolP384r1,
|
||||
[enums.curve.brainpoolP512r1]: brainpoolP512r1
|
||||
};
|
||||
export const getNobleCurve = curveName => {
|
||||
const curve = nobleCurvess[curveName];
|
||||
if (!curve) throw new Error('Unsupported curve');
|
||||
return curve;
|
||||
};
|
||||
|
||||
|
||||
const curves = {
|
||||
p256: {
|
||||
oid: [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
|
||||
@ -169,14 +190,13 @@ class CurveWithOID {
|
||||
}
|
||||
|
||||
async genKeyPair() {
|
||||
let keyPair;
|
||||
switch (this.type) {
|
||||
case 'web':
|
||||
try {
|
||||
return await webGenKeyPair(this.name);
|
||||
} catch (err) {
|
||||
util.printDebugError('Browser did not support generating ec key ' + err.message);
|
||||
break;
|
||||
return jsGenKeyPair(this.name);
|
||||
}
|
||||
case 'node':
|
||||
return nodeGenKeyPair(this.name);
|
||||
@ -185,8 +205,8 @@ class CurveWithOID {
|
||||
privateKey[0] = (privateKey[0] & 127) | 64;
|
||||
privateKey[31] &= 248;
|
||||
const secretKey = privateKey.slice().reverse();
|
||||
keyPair = nacl.box.keyPair.fromSecretKey(secretKey);
|
||||
const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]);
|
||||
const { publicKey: rawPublicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
|
||||
const publicKey = util.concatUint8Array([new Uint8Array([0x40]), rawPublicKey]);
|
||||
return { publicKey, privateKey };
|
||||
}
|
||||
case 'ed25519Legacy': {
|
||||
@ -195,26 +215,23 @@ class CurveWithOID {
|
||||
const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]);
|
||||
return { publicKey, privateKey };
|
||||
}
|
||||
default: {
|
||||
return jsGenKeyPair(this.name);
|
||||
}
|
||||
}
|
||||
const indutnyCurve = await getIndutnyCurve(this.name);
|
||||
keyPair = await indutnyCurve.genKeyPair({
|
||||
entropy: util.uint8ArrayToString(getRandomBytes(32))
|
||||
});
|
||||
return { publicKey: new Uint8Array(keyPair.getPublic('array', false)), privateKey: keyPair.getPrivate().toArrayLike(Uint8Array) };
|
||||
}
|
||||
}
|
||||
|
||||
async function generate(curve) {
|
||||
curve = new CurveWithOID(curve);
|
||||
async function generate(curveName) {
|
||||
const curve = new CurveWithOID(curveName);
|
||||
const { oid, hash, cipher } = curve;
|
||||
const keyPair = await curve.genKeyPair();
|
||||
const Q = BigInteger.new(keyPair.publicKey).toUint8Array();
|
||||
const secret = BigInteger.new(keyPair.privateKey).toUint8Array('be', curve.payloadSize);
|
||||
return {
|
||||
oid: curve.oid,
|
||||
Q,
|
||||
secret,
|
||||
hash: curve.hash,
|
||||
cipher: curve.cipher
|
||||
oid,
|
||||
Q: keyPair.publicKey,
|
||||
secret: util.leftPad(keyPair.privateKey, curve.payloadSize),
|
||||
hash,
|
||||
cipher
|
||||
};
|
||||
}
|
||||
|
||||
@ -269,20 +286,13 @@ async function validateStandardParams(algo, oid, Q, d) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const curve = await getIndutnyCurve(curveName);
|
||||
try {
|
||||
// Parse Q and check that it is on the curve but not at infinity
|
||||
Q = keyFromPublic(curve, Q).getPublic();
|
||||
} catch (validationErrors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
const nobleCurve = getNobleCurve(enums.write(enums.curve, oid.toHex()));
|
||||
/*
|
||||
* Re-derive public point Q' = dG from private key
|
||||
* Expect Q == Q'
|
||||
*/
|
||||
const dG = keyFromPrivate(curve, d).getPublic();
|
||||
if (!dG.eq(Q)) {
|
||||
const dG = nobleCurve.getPublicKey(d, false);
|
||||
if (!util.equalsUint8Array(dG, Q)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -298,7 +308,12 @@ export {
|
||||
// Helper functions //
|
||||
// //
|
||||
//////////////////////////
|
||||
|
||||
function jsGenKeyPair(name) {
|
||||
const nobleCurve = getNobleCurve(name);
|
||||
const privateKey = nobleCurve.utils.randomPrivateKey();
|
||||
const publicKey = nobleCurve.getPublicKey(privateKey, false);
|
||||
return { publicKey, privateKey };
|
||||
}
|
||||
|
||||
async function webGenKeyPair(name) {
|
||||
// Note: keys generated with ECDSA and ECDH are structurally equivalent
|
||||
|
@ -77,7 +77,7 @@ export default () => describe('ECDH key exchange @lightweight', function () {
|
||||
}
|
||||
expect(decrypt_message(
|
||||
'secp256k1', 2, 7, [], [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Private key is not valid for specified curve|Unknown point format/).notify(done);
|
||||
)).to.be.rejectedWith(Error, /Private key is not valid for specified curve|second arg must be public key/).notify(done);
|
||||
});
|
||||
it('Invalid elliptic public key', function (done) {
|
||||
if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) {
|
||||
@ -85,7 +85,7 @@ export default () => describe('ECDH key exchange @lightweight', function () {
|
||||
}
|
||||
expect(decrypt_message(
|
||||
'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_invalid_point, secp256k1_data, []
|
||||
)).to.be.rejectedWith(Error, /Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|Invalid elliptic public key/).notify(done);
|
||||
)).to.be.rejectedWith(/Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|bad point/).notify(done);
|
||||
});
|
||||
it('Invalid key data integrity', function (done) {
|
||||
if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) {
|
||||
@ -93,7 +93,7 @@ export default () => describe('ECDH key exchange @lightweight', function () {
|
||||
}
|
||||
expect(decrypt_message(
|
||||
'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_point, secp256k1_data, []
|
||||
)).to.be.rejectedWith(Error, /Key Data Integrity failed/).notify(done);
|
||||
)).to.be.rejectedWith(/Key Data Integrity failed/).notify(done);
|
||||
});
|
||||
|
||||
const Q1 = new Uint8Array([
|
||||
@ -143,9 +143,9 @@ export default () => describe('ECDH key exchange @lightweight', function () {
|
||||
const oid = new OID(curve.oid);
|
||||
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
|
||||
const data = util.stringToUint8Array('test');
|
||||
expect(
|
||||
ecdh.encrypt(oid, kdfParams, data, Q1, fingerprint1)
|
||||
).to.be.rejectedWith(Error, /Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|Unknown point format/);
|
||||
await expect(
|
||||
ecdh.encrypt(oid, kdfParams, data, Q1.subarray(1), fingerprint1)
|
||||
).to.be.rejectedWith(/Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|second arg must be public key/);
|
||||
});
|
||||
|
||||
it('Different keys', async function () {
|
||||
@ -199,8 +199,9 @@ export default () => describe('ECDH key exchange @lightweight', function () {
|
||||
expect(await ecdhX.decrypt(openpgp.enums.publicKey.x448, ephemeralPublicKey, wrappedKey, K_B, b)).to.deep.equal(data);
|
||||
});
|
||||
|
||||
['p256', 'p384', 'p521'].forEach(curveName => {
|
||||
it(`NIST ${curveName} - Successful exchange`, async function () {
|
||||
const allCurves = ['secp256k1', 'p256', 'p384', 'p521', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
|
||||
allCurves.forEach(curveName => {
|
||||
it(`${curveName} - Successful exchange`, async function () {
|
||||
const curve = new elliptic_curves.CurveWithOID(curveName);
|
||||
const oid = new OID(curve.oid);
|
||||
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
|
||||
@ -236,14 +237,16 @@ export default () => describe('ECDH key exchange @lightweight', function () {
|
||||
getNodeCryptoStub && getNodeCryptoStub.restore();
|
||||
};
|
||||
|
||||
['p256', 'p384', 'p521'].forEach(curveName => {
|
||||
it(`NIST ${curveName}`, async function () {
|
||||
allCurves.forEach(curveName => {
|
||||
it(`${curveName}`, async function () {
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
const webCrypto = util.getWebCrypto();
|
||||
if (!nodeCrypto && !webCrypto) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
const expectNativeWeb = new Set(['p256', 'p384']); // older versions of safari do not implement p521
|
||||
|
||||
const curve = new elliptic_curves.CurveWithOID(curveName);
|
||||
const oid = new OID(curve.oid);
|
||||
const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher });
|
||||
@ -254,9 +257,11 @@ export default () => describe('ECDH key exchange @lightweight', function () {
|
||||
|
||||
const nativeDecryptSpy = webCrypto ? sinonSandbox.spy(webCrypto, 'deriveBits') : sinonSandbox.spy(nodeCrypto, 'createECDH');
|
||||
expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data);
|
||||
const expectedNativeCallCount = nativeDecryptSpy.callCount;
|
||||
disableNative();
|
||||
expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data);
|
||||
if (curveName !== 'p521') { // safari does not implement p521 in webcrypto
|
||||
expect(nativeDecryptSpy.callCount).to.equal(expectedNativeCallCount); // assert that fallback implementation was called
|
||||
if (expectNativeWeb.has(curveName)) {
|
||||
expect(nativeDecryptSpy.calledOnce).to.be.true;
|
||||
}
|
||||
});
|
||||
|
@ -122,6 +122,21 @@ export default () => describe('Elliptic Curve Cryptography @lightweight', functi
|
||||
getNodeCryptoStub && getNodeCryptoStub.restore();
|
||||
};
|
||||
|
||||
const testNativeAndFallback = async fn => {
|
||||
const webCrypto = util.getWebCrypto();
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
const nativeSpy = webCrypto ? sinonSandbox.spy(webCrypto, 'importKey') : sinonSandbox.spy(nodeCrypto, 'createVerify'); // spy on function used on verification, since that's used by all tests calling `testNativeAndFallback`
|
||||
|
||||
// if native not available, fallback will be tested twice (not possible to automatically check native algo availability)
|
||||
enableNative();
|
||||
await fn();
|
||||
const expectedNativeCallCount = nativeSpy.callCount;
|
||||
disableNative();
|
||||
await fn();
|
||||
expect(nativeSpy.callCount).to.equal(expectedNativeCallCount);
|
||||
enableNative();
|
||||
};
|
||||
|
||||
const verify_signature = async function (oid, hash, r, s, message, pub) {
|
||||
if (util.isString(message)) {
|
||||
message = util.stringToUint8Array(message);
|
||||
@ -162,99 +177,81 @@ export default () => describe('Elliptic Curve Cryptography @lightweight', functi
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
]);
|
||||
it('Invalid curve oid', function () {
|
||||
return Promise.all([
|
||||
expect(verify_signature(
|
||||
'invalid oid', 8, [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Unknown curve/),
|
||||
expect(verify_signature(
|
||||
'\x00', 8, [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Unknown curve/)
|
||||
]);
|
||||
it('Invalid curve oid', async function () {
|
||||
await expect(verify_signature(
|
||||
'invalid oid', 8, [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Unknown curve/);
|
||||
await expect(verify_signature(
|
||||
'\x00', 8, [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Unknown curve/);
|
||||
});
|
||||
it('Invalid public key', async function () {
|
||||
it('secp256k1 - Invalid public key', async function () {
|
||||
if (!config.useIndutnyElliptic && !util.getNodeCrypto()) {
|
||||
this.skip(); // webcrypto does not implement secp256k1
|
||||
}
|
||||
if (util.getNodeCrypto()) {
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], []
|
||||
)).to.eventually.be.false;
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point_format
|
||||
)).to.eventually.be.false;
|
||||
}
|
||||
if (config.useIndutnyElliptic) {
|
||||
disableNative();
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Unknown point format/);
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point_format
|
||||
)).to.be.rejectedWith(Error, /Unknown point format/);
|
||||
this.skip(); // webcrypto does not implement secp256k1: JS fallback tested instead
|
||||
}
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], []
|
||||
)).to.eventually.be.false;
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point_format
|
||||
)).to.eventually.be.false;
|
||||
});
|
||||
it('Invalid point', async function () {
|
||||
it('secp256k1 - Invalid point', async function () {
|
||||
if (!config.useIndutnyElliptic && !util.getNodeCrypto()) {
|
||||
this.skip();
|
||||
}
|
||||
if (util.getNodeCrypto()) {
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
||||
)).to.eventually.be.false;
|
||||
}
|
||||
if (config.useIndutnyElliptic) {
|
||||
disableNative();
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
||||
)).to.be.rejectedWith(Error, /Invalid elliptic public key/);
|
||||
this.skip(); // webcrypto does not implement secp256k1: JS fallback tested instead
|
||||
}
|
||||
await expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
||||
)).to.eventually.be.false;
|
||||
});
|
||||
it('Invalid signature', function (done) {
|
||||
it('secp256k1 - Invalid signature', function (done) {
|
||||
if (!config.useIndutnyElliptic && !util.getNodeCrypto()) {
|
||||
this.skip();
|
||||
this.skip(); // webcrypto does not implement secp256k1: JS fallback tested instead
|
||||
}
|
||||
expect(verify_signature(
|
||||
'secp256k1', 8, [], [], [], secp256k1_point
|
||||
)).to.eventually.be.false.notify(done);
|
||||
});
|
||||
|
||||
const p384_message = new Uint8Array([
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
]);
|
||||
const p384_r = new Uint8Array([
|
||||
0x9D, 0x07, 0xCA, 0xA5, 0x9F, 0xBE, 0xB8, 0x76,
|
||||
0xA9, 0xB9, 0x66, 0x0F, 0xA0, 0x64, 0x70, 0x5D,
|
||||
0xE6, 0x37, 0x40, 0x43, 0xD0, 0x8E, 0x40, 0xA8,
|
||||
0x8B, 0x37, 0x83, 0xE7, 0xBC, 0x1C, 0x4C, 0x86,
|
||||
0xCB, 0x3C, 0xD5, 0x9B, 0x68, 0xF0, 0x65, 0xEB,
|
||||
0x3A, 0xB6, 0xD6, 0xA6, 0xCF, 0x85, 0x3D, 0xA9
|
||||
]);
|
||||
const p384_s = new Uint8Array([
|
||||
0x32, 0x85, 0x78, 0xCC, 0xEA, 0xC5, 0x22, 0x83,
|
||||
0x10, 0x73, 0x1C, 0xCF, 0x10, 0x8A, 0x52, 0x11,
|
||||
0x8E, 0x49, 0x9E, 0xCF, 0x7E, 0x17, 0x18, 0xC3,
|
||||
0x11, 0x11, 0xBC, 0x0F, 0x6D, 0x98, 0xE2, 0x16,
|
||||
0x68, 0x58, 0x23, 0x1D, 0x11, 0xEF, 0x3D, 0x21,
|
||||
0x30, 0x75, 0x24, 0x39, 0x48, 0x89, 0x03, 0xDC
|
||||
]);
|
||||
it('Valid signature', function (done) {
|
||||
expect(verify_signature('p384', 8, p384_r, p384_s, p384_message, key_data.p384.pub))
|
||||
.to.eventually.be.true.notify(done);
|
||||
it('P-384 - Valid signature', async function () {
|
||||
const p384_r = new Uint8Array([
|
||||
0x9D, 0x07, 0xCA, 0xA5, 0x9F, 0xBE, 0xB8, 0x76,
|
||||
0xA9, 0xB9, 0x66, 0x0F, 0xA0, 0x64, 0x70, 0x5D,
|
||||
0xE6, 0x37, 0x40, 0x43, 0xD0, 0x8E, 0x40, 0xA8,
|
||||
0x8B, 0x37, 0x83, 0xE7, 0xBC, 0x1C, 0x4C, 0x86,
|
||||
0xCB, 0x3C, 0xD5, 0x9B, 0x68, 0xF0, 0x65, 0xEB,
|
||||
0x3A, 0xB6, 0xD6, 0xA6, 0xCF, 0x85, 0x3D, 0xA9
|
||||
]);
|
||||
const p384_s = new Uint8Array([
|
||||
0x32, 0x85, 0x78, 0xCC, 0xEA, 0xC5, 0x22, 0x83,
|
||||
0x10, 0x73, 0x1C, 0xCF, 0x10, 0x8A, 0x52, 0x11,
|
||||
0x8E, 0x49, 0x9E, 0xCF, 0x7E, 0x17, 0x18, 0xC3,
|
||||
0x11, 0x11, 0xBC, 0x0F, 0x6D, 0x98, 0xE2, 0x16,
|
||||
0x68, 0x58, 0x23, 0x1D, 0x11, 0xEF, 0x3D, 0x21,
|
||||
0x30, 0x75, 0x24, 0x39, 0x48, 0x89, 0x03, 0xDC
|
||||
]);
|
||||
const p384_message = new Uint8Array([
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
]);
|
||||
|
||||
await testNativeAndFallback(
|
||||
() => expect(verify_signature('p384', 8, p384_r, p384_s, p384_message, key_data.p384.pub)).to.eventually.be.true
|
||||
);
|
||||
});
|
||||
it('Sign and verify message', function () {
|
||||
const curve = new elliptic_curves.CurveWithOID('p521');
|
||||
return curve.genKeyPair().then(async keyPair => {
|
||||
const keyPublic = new Uint8Array(keyPair.publicKey);
|
||||
const keyPrivate = new Uint8Array(keyPair.privateKey);
|
||||
const oid = curve.oid;
|
||||
const message = p384_message;
|
||||
return elliptic_curves.ecdsa.sign(oid, 10, message, keyPublic, keyPrivate, await hashMod.digest(10, message)).then(async signature => {
|
||||
await expect(elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic, await hashMod.digest(10, message)))
|
||||
.to.eventually.be.true;
|
||||
});
|
||||
const curves = ['secp256k1' , 'p256', 'p384', 'p521', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
|
||||
curves.forEach(curveName => it(`${curveName} - Sign and verify message`, async function () {
|
||||
const curve = new elliptic_curves.CurveWithOID(curveName);
|
||||
const { publicKey: keyPublic, privateKey: keyPrivate } = await curve.genKeyPair();
|
||||
const message = new Uint8Array([
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
]);
|
||||
const messageDigest = await hashMod.digest(openpgp.enums.hash.sha512, message);
|
||||
await testNativeAndFallback(async () => {
|
||||
const signature = await elliptic_curves.ecdsa.sign(curve.oid, openpgp.enums.hash.sha512, message, keyPublic, keyPrivate, messageDigest);
|
||||
await expect(elliptic_curves.ecdsa.verify(curve.oid, openpgp.enums.hash.sha512, signature, message, keyPublic, messageDigest)).to.eventually.be.true;
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import util from '../../src/util.js';
|
||||
|
||||
const elliptic_data = {
|
||||
key_data: {
|
||||
p256: {
|
||||
@ -95,6 +97,18 @@ const elliptic_data = {
|
||||
0xB8, 0xFD, 0x0B, 0xDF, 0x76, 0xCE, 0xBC, 0x95,
|
||||
0x4B, 0x92, 0x26, 0xFC, 0xAA, 0x7A, 0x7C, 0x3F
|
||||
])
|
||||
},
|
||||
brainpoolP256r1: {
|
||||
priv: util.hexToUint8Array('8b426897130e1e5e70a4d6320c4002bb1642a5e57ade066e060464137dfd5e05'),
|
||||
pub: util.hexToUint8Array('042a43d8cc20e5a3fbd75d3a5a9b17d867bba80f11334d0665f0c641d13460a52aa3373a4ccfaa7d76765a689bd9fe15a4fd107ef1ec9ac980234c31647170c81a')
|
||||
},
|
||||
brainpoolP384r1: {
|
||||
priv: util.hexToUint8Array('7ccc97acdf4b775606c5c994a37a8b28086167046ac0d55664ede4097d8de79dec56e69dfff5776d53fcbd2147bbae9f'),
|
||||
pub: util.hexToUint8Array('043809fa0c74ec9817cb73eba67db71e01663528fb9fbe6a123f8339346c37efc9ff7cd116074a80684448e44ee9204c795c88ad634ad272585c0b4e3093b11e6c99a6c0ca9c278f83ef57e2ed802502aee76f4529bcb873eef754bec894a5032f')
|
||||
},
|
||||
brainpoolP512r1: {
|
||||
priv: util.hexToUint8Array('0a32459d1ecf8815397a66f6cdb18692c6f79a3c6059b4c344d0162416c7603a82a9a938568edafb132c7433ffeeab4cf201d9542209eb28070bea56ab6b8938'),
|
||||
pub: util.hexToUint8Array('040f64473d9b3597752e3a87095c0b219dd85f56a79c3b2dc8fb2b0c95b60f4be45c41a8a7ea31d60e15fea6275eb7db93856bc2eb30cc8876513335d43812bd2c4e195e05679ac667a2f7fb05c5842779d18fa411500e43e2f291ea8348f061db15382d4db1cfcf106a29f46e1c00e7d63e635c51293f69c0dd4f6a61da589b2a')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -256,10 +256,6 @@ EJ4QcD/oQ6x1M/8X/iKQCtxZP8RnlrbH7ExkNON5s5g=
|
||||
expect(await result.signatures[0].verified).to.be.true;
|
||||
});
|
||||
it('Decrypt and verify message with leading zero in hash signed with old elliptic algorithm', async function () {
|
||||
//this test would not work with nodeCrypto, since message is signed with leading zero stripped from the hash
|
||||
if (util.getNodeCrypto()) {
|
||||
this.skip(); // eslint-disable-line no-invalid-this
|
||||
}
|
||||
const juliet = await load_priv_key('juliet');
|
||||
const romeo = await load_pub_key('romeo');
|
||||
const msg = await openpgp.readMessage({ armoredMessage: data.romeo.message_encrypted_with_leading_zero_in_hash_signed_by_elliptic_with_old_implementation });
|
||||
|
Loading…
x
Reference in New Issue
Block a user