mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-03-30 15:08:32 +00:00
Import noble-hashes, noble-curves and BN.js only on demand
This primarily affects the lightweight build, which will not include these (fairly large) libs in the main bundle file. This allows fetching their code only if required: - Noble-curves is only needed for curves other than curve25519. - Noble-hashes is needed for streamed hashing and e.g. SHA3 on web. - BN.js is used by the above libs, and it's also separately needed for platforms without native BigInt support.
This commit is contained in:
parent
5456211266
commit
9e1962f006
39
src/biginteger.js
Normal file
39
src/biginteger.js
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* This is a vanilla JS copy of @openpgp/noble-hashes/esm/biginteger/interface.ts .
|
||||
* We need to duplicate the file, instead of importing it, since in that case the BigIntegerInterface instance
|
||||
* would be shared with noble-hashes, which separately calls `setImplementation()` on load, causing it to throw due to
|
||||
* duplicate initialization.
|
||||
*/
|
||||
class BigInteger {
|
||||
static setImplementation(Implementation, replace = false) {
|
||||
if (BigInteger.Implementation && !replace) {
|
||||
throw new Error('Implementation already set');
|
||||
}
|
||||
BigInteger.Implementation = Implementation;
|
||||
}
|
||||
|
||||
static new(n) {
|
||||
return new BigInteger.Implementation(n);
|
||||
}
|
||||
}
|
||||
|
||||
const detectBigInt = () => typeof BigInt !== 'undefined';
|
||||
export async function getBigInteger() {
|
||||
if (BigInteger.Implementation) {
|
||||
return BigInteger;
|
||||
}
|
||||
|
||||
// TODOOOOO replace = true needed in case of concurrent class loading, how to fix without removing wrapper class?
|
||||
|
||||
if (detectBigInt()) {
|
||||
// NativeBigInteger is small, so it's imported in isolation (it could also be imported at the top level)
|
||||
const { default: NativeBigInteger } = await import('@openpgp/noble-hashes/esm/biginteger/native.interface');
|
||||
BigInteger.setImplementation(NativeBigInteger, true);
|
||||
} else {
|
||||
// FallbackBigInteger relies on large BN.js lib, which is also used by noble-hashes and noble-curves
|
||||
const { default: FallbackBigInteger } = await import('@openpgp/noble-hashes/esm/biginteger/bn.interface');
|
||||
BigInteger.setImplementation(FallbackBigInteger, true);
|
||||
}
|
||||
|
||||
return BigInteger;
|
||||
}
|
@ -5,11 +5,6 @@
|
||||
* @module crypto/hash
|
||||
*/
|
||||
|
||||
import { sha1 } from '@openpgp/noble-hashes/sha1';
|
||||
import { sha224, sha256 } from '@openpgp/noble-hashes/sha256';
|
||||
import { sha384, sha512 } from '@openpgp/noble-hashes/sha512';
|
||||
import { sha3_256, sha3_512 } from '@openpgp/noble-hashes/sha3';
|
||||
import { ripemd160 } from '@openpgp/noble-hashes/ripemd160';
|
||||
import * as stream from '@openpgp/web-stream-tools';
|
||||
import md5 from './md5';
|
||||
import util from '../../util';
|
||||
@ -31,48 +26,47 @@ function nodeHash(type) {
|
||||
};
|
||||
}
|
||||
|
||||
function nobleHash(hash, webCryptoHash) {
|
||||
function nobleHash(nobleHashName, webCryptoHashName) {
|
||||
const getNobleHash = async () => {
|
||||
const { nobleHashes } = await import('./noble_hashes');
|
||||
const hash = nobleHashes.get(nobleHashName);
|
||||
if (!hash) throw new Error('Unsupported hash');
|
||||
return hash;
|
||||
};
|
||||
|
||||
return async function(data) {
|
||||
if (stream.isArrayStream(data)) {
|
||||
data = await stream.readToEnd(data);
|
||||
}
|
||||
if (util.isStream(data)) {
|
||||
const hash = await getNobleHash();
|
||||
|
||||
const hashInstance = hash.create();
|
||||
return stream.transform(data, value => {
|
||||
hashInstance.update(value);
|
||||
}, () => hashInstance.digest());
|
||||
} else if (webCrypto && webCryptoHash) {
|
||||
return new Uint8Array(await webCrypto.digest(webCryptoHash, data));
|
||||
} else if (webCrypto && webCryptoHashName) {
|
||||
return new Uint8Array(await webCrypto.digest(webCryptoHashName, data));
|
||||
} else {
|
||||
const hash = await getNobleHash();
|
||||
|
||||
return hash(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const hashFunctions = {
|
||||
md5: nodeHash('md5') || md5,
|
||||
sha1: nodeHash('sha1') || nobleHash(sha1, 'SHA-1'),
|
||||
sha224: nodeHash('sha224') || nobleHash(sha224),
|
||||
sha256: nodeHash('sha256') || nobleHash(sha256, 'SHA-256'),
|
||||
sha384: nodeHash('sha384') || nobleHash(sha384, 'SHA-384'),
|
||||
sha512: nodeHash('sha512') || nobleHash(sha512, 'SHA-512'),
|
||||
ripemd: nodeHash('ripemd160') || nobleHash(ripemd160),
|
||||
sha3_256: nodeHash('sha3-256') || nobleHash(sha3_256),
|
||||
sha3_512: nodeHash('sha3-512') || nobleHash(sha3_512)
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
/** @see module:md5 */
|
||||
md5: hashFunctions.md5,
|
||||
sha1: hashFunctions.sha1,
|
||||
sha224: hashFunctions.sha224,
|
||||
sha256: hashFunctions.sha256,
|
||||
sha384: hashFunctions.sha384,
|
||||
sha512: hashFunctions.sha512,
|
||||
ripemd: hashFunctions.ripemd,
|
||||
sha3_256: hashFunctions.sha3_256,
|
||||
sha3_512: hashFunctions.sha3_512,
|
||||
md5: nodeHash('md5') || md5,
|
||||
sha1: nodeHash('sha1') || nobleHash('sha1', 'SHA-1'),
|
||||
sha224: nodeHash('sha224') || nobleHash('sha224'),
|
||||
sha256: nodeHash('sha256') || nobleHash('sha256', 'SHA-256'),
|
||||
sha384: nodeHash('sha384') || nobleHash('sha384', 'SHA-384'),
|
||||
sha512: nodeHash('sha512') || nobleHash('sha512', 'SHA-512'),
|
||||
ripemd: nodeHash('ripemd160') || nobleHash('ripemd160'),
|
||||
sha3_256: nodeHash('sha3-256') || nobleHash('sha3_256'),
|
||||
sha3_512: nodeHash('sha3-512') || nobleHash('sha3_512'),
|
||||
|
||||
/**
|
||||
* Create a hash on the specified data using the specified algorithm
|
||||
|
22
src/crypto/hash/noble_hashes.js
Normal file
22
src/crypto/hash/noble_hashes.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* This file is needed to dynamic import the noble-hashes.
|
||||
* Separate dynamic imports are not convenient as they result in too many chunks,
|
||||
* which share a lot of code anyway.
|
||||
*/
|
||||
|
||||
import { sha1 } from '@openpgp/noble-hashes/sha1';
|
||||
import { sha224, sha256 } from '@openpgp/noble-hashes/sha256';
|
||||
import { sha384, sha512 } from '@openpgp/noble-hashes/sha512';
|
||||
import { sha3_256, sha3_512 } from '@openpgp/noble-hashes/sha3';
|
||||
import { ripemd160 } from '@openpgp/noble-hashes/ripemd160';
|
||||
|
||||
export const nobleHashes = new Map(Object.entries({
|
||||
sha1,
|
||||
sha224,
|
||||
sha256,
|
||||
sha384,
|
||||
sha512,
|
||||
sha3_256,
|
||||
sha3_512,
|
||||
ripemd160
|
||||
}));
|
@ -19,7 +19,6 @@
|
||||
* @fileoverview A Digital signature algorithm implementation
|
||||
* @module crypto/public_key/dsa
|
||||
*/
|
||||
import { BigInteger } from '@openpgp/noble-hashes/biginteger';
|
||||
import { getRandomBigInteger } from '../random';
|
||||
import util from '../../util';
|
||||
import { isProbablePrime } from './prime';
|
||||
@ -42,6 +41,8 @@ import { isProbablePrime } from './prime';
|
||||
* @async
|
||||
*/
|
||||
export async function sign(hashAlgo, hashed, g, p, q, x) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
const one = BigInteger.new(1);
|
||||
p = BigInteger.new(p);
|
||||
q = BigInteger.new(q);
|
||||
@ -100,6 +101,8 @@ export async function sign(hashAlgo, hashed, g, p, q, x) {
|
||||
* @async
|
||||
*/
|
||||
export async function verify(hashAlgo, r, s, hashed, g, p, q, y) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
const zero = BigInteger.new(0);
|
||||
r = BigInteger.new(r);
|
||||
s = BigInteger.new(s);
|
||||
@ -142,6 +145,8 @@ export async function verify(hashAlgo, r, s, hashed, g, p, q, y) {
|
||||
* @async
|
||||
*/
|
||||
export async function validateParams(p, q, g, y, x) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
p = BigInteger.new(p);
|
||||
q = BigInteger.new(q);
|
||||
g = BigInteger.new(g);
|
||||
|
@ -19,9 +19,9 @@
|
||||
* @fileoverview ElGamal implementation
|
||||
* @module crypto/public_key/elgamal
|
||||
*/
|
||||
import { BigInteger } from '@openpgp/noble-hashes/biginteger';
|
||||
import { getRandomBigInteger } from '../random';
|
||||
import { emeEncode, emeDecode } from '../pkcs1';
|
||||
import util from '../../util';
|
||||
|
||||
/**
|
||||
* ElGamal Encryption function
|
||||
@ -34,6 +34,8 @@ import { emeEncode, emeDecode } from '../pkcs1';
|
||||
* @async
|
||||
*/
|
||||
export async function encrypt(data, p, g, y) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
p = BigInteger.new(p);
|
||||
g = BigInteger.new(g);
|
||||
y = BigInteger.new(y);
|
||||
@ -63,6 +65,8 @@ export async function encrypt(data, p, g, y) {
|
||||
* @async
|
||||
*/
|
||||
export async function decrypt(c1, c2, p, x, randomPayload) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
c1 = BigInteger.new(c1);
|
||||
c2 = BigInteger.new(c2);
|
||||
p = BigInteger.new(p);
|
||||
@ -82,6 +86,8 @@ export async function decrypt(c1, c2, p, x, randomPayload) {
|
||||
* @async
|
||||
*/
|
||||
export async function validateParams(p, g, y, x) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
p = BigInteger.new(p);
|
||||
g = BigInteger.new(g);
|
||||
y = BigInteger.new(y);
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
import nacl from '@openpgp/tweetnacl';
|
||||
import { CurveWithOID, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams, getNobleCurve } from './oid_curves';
|
||||
import { CurveWithOID, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams } from './oid_curves';
|
||||
import * as aesKW from '../../aes_kw';
|
||||
import { getRandomBytes } from '../../random';
|
||||
import hash from '../../hash';
|
||||
@ -210,8 +210,8 @@ export async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
function jsPrivateEphemeralKey(curve, V, d) {
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
async function jsPrivateEphemeralKey(curve, V, d) {
|
||||
const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdh, curve.name);
|
||||
// The output includes parity byte
|
||||
const sharedSecretWithParity = nobleCurve.getSharedSecret(d, V);
|
||||
const sharedKey = sharedSecretWithParity.subarray(1);
|
||||
@ -219,7 +219,7 @@ function jsPrivateEphemeralKey(curve, V, d) {
|
||||
}
|
||||
|
||||
async function jsPublicEphemeralKey(curve, Q) {
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdh, curve.name);
|
||||
const { publicKey: V, privateKey: v } = await curve.genKeyPair();
|
||||
|
||||
// The output includes parity byte
|
||||
|
@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import x25519 from '@openpgp/tweetnacl';
|
||||
import { x448 } from '@openpgp/noble-curves/ed448';
|
||||
import * as aesKW from '../../aes_kw';
|
||||
import { getRandomBytes } from '../../random';
|
||||
|
||||
@ -32,6 +31,7 @@ export async function generate(algo) {
|
||||
return { A, k };
|
||||
}
|
||||
case enums.publicKey.x448: {
|
||||
const x448 = await util.getNobleCurve(enums.publicKey.x448);
|
||||
const k = x448.utils.randomPrivateKey();
|
||||
const A = x448.getPublicKey(k);
|
||||
return { A, k };
|
||||
@ -60,6 +60,7 @@ export async function validateParams(algo, A, k) {
|
||||
return util.equalsUint8Array(A, publicKey);
|
||||
}
|
||||
case enums.publicKey.x448: {
|
||||
const x448 = await util.getNobleCurve(enums.publicKey.x448);
|
||||
/**
|
||||
* Derive public point A' from private key
|
||||
* and expect A == A'
|
||||
@ -102,6 +103,7 @@ export async function encrypt(algo, data, recipientA) {
|
||||
return { ephemeralPublicKey, wrappedKey };
|
||||
}
|
||||
case enums.publicKey.x448: {
|
||||
const x448 = await util.getNobleCurve(enums.publicKey.x448);
|
||||
const ephemeralSecretKey = x448.utils.randomPrivateKey();
|
||||
const sharedSecret = x448.getSharedSecret(ephemeralSecretKey, recipientA);
|
||||
const ephemeralPublicKey = x448.getPublicKey(ephemeralSecretKey);
|
||||
@ -146,6 +148,7 @@ export async function decrypt(algo, ephemeralPublicKey, wrappedKey, A, k) {
|
||||
return aesKW.unwrap(encryptionKey, wrappedKey);
|
||||
}
|
||||
case enums.publicKey.x448: {
|
||||
const x448 = await util.getNobleCurve(enums.publicKey.x448);
|
||||
const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey);
|
||||
const hkdfInput = util.concatUint8Array([
|
||||
ephemeralPublicKey,
|
||||
|
@ -24,7 +24,7 @@ import enums from '../../../enums';
|
||||
import util from '../../../util';
|
||||
import { getRandomBytes } from '../../random';
|
||||
import hash from '../../hash';
|
||||
import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams, getNobleCurve } from './oid_curves';
|
||||
import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams } from './oid_curves';
|
||||
|
||||
const webCrypto = util.getWebCrypto();
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
@ -74,7 +74,7 @@ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed
|
||||
}
|
||||
}
|
||||
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, curve.name);
|
||||
// lowS: non-canonical sig: https://stackoverflow.com/questions/74338846/ecdsa-signature-verification-mismatch
|
||||
const signature = nobleCurve.sign(hashed, privateKey, { lowS: false });
|
||||
return {
|
||||
@ -103,7 +103,7 @@ export async function verify(oid, hashAlgo, signature, message, publicKey, hashe
|
||||
// Similarly, secp256k1 should have been used rarely enough.
|
||||
// However, we implement the fix for all curves, since it's only needed in case of
|
||||
// verification failure, which is unexpected, hence a minor slowdown is acceptable.
|
||||
const tryFallbackVerificationForOldBug = () => (
|
||||
const tryFallbackVerificationForOldBug = async () => (
|
||||
hashed[0] === 0 ?
|
||||
jsVerify(curve, signature, hashed.subarray(1), publicKey) :
|
||||
false
|
||||
@ -133,7 +133,7 @@ export async function verify(oid, hashAlgo, signature, message, publicKey, hashe
|
||||
}
|
||||
}
|
||||
|
||||
const verified = jsVerify(curve, signature, hashed, publicKey);
|
||||
const verified = await jsVerify(curve, signature, hashed, publicKey);
|
||||
return verified || tryFallbackVerificationForOldBug();
|
||||
}
|
||||
|
||||
@ -183,8 +183,8 @@ export async function validateParams(oid, Q, d) {
|
||||
* Fallback javascript implementation of ECDSA verification.
|
||||
* To be used if no native implementation is available for the given curve/operation.
|
||||
*/
|
||||
function jsVerify(curve, signature, hashed, publicKey) {
|
||||
const nobleCurve = getNobleCurve(curve.name);
|
||||
async function jsVerify(curve, signature, hashed, publicKey) {
|
||||
const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, 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 });
|
||||
}
|
||||
|
@ -20,15 +20,12 @@
|
||||
* @module crypto/public_key/elliptic/eddsa
|
||||
*/
|
||||
|
||||
import { sha512 } from '@openpgp/noble-hashes/sha512';
|
||||
import ed25519 from '@openpgp/tweetnacl';
|
||||
import { ed448 } from '@openpgp/noble-curves/ed448';
|
||||
import util from '../../../util';
|
||||
import enums from '../../../enums';
|
||||
import hash from '../../hash';
|
||||
import { getRandomBytes } from '../../random';
|
||||
|
||||
ed25519.hash = bytes => sha512(bytes);
|
||||
|
||||
/**
|
||||
* Generate (non-legacy) EdDSA key
|
||||
@ -43,6 +40,7 @@ export async function generate(algo) {
|
||||
return { A, seed };
|
||||
}
|
||||
case enums.publicKey.ed448: {
|
||||
const ed448 = await util.getNobleCurve(enums.publicKey.ed448);
|
||||
const seed = ed448.utils.randomPrivateKey();
|
||||
const A = ed448.getPublicKey(seed);
|
||||
return { A, seed };
|
||||
@ -76,6 +74,7 @@ export async function sign(algo, hashAlgo, message, publicKey, privateKey, hashe
|
||||
return { RS: signature };
|
||||
}
|
||||
case enums.publicKey.ed448: {
|
||||
const ed448 = await util.getNobleCurve(enums.publicKey.ed448);
|
||||
const signature = ed448.sign(hashed, privateKey);
|
||||
return { RS: signature };
|
||||
}
|
||||
@ -104,8 +103,10 @@ export async function verify(algo, hashAlgo, { RS }, m, publicKey, hashed) {
|
||||
case enums.publicKey.ed25519: {
|
||||
return ed25519.sign.detached.verify(hashed, RS, publicKey);
|
||||
}
|
||||
case enums.publicKey.ed448:
|
||||
case enums.publicKey.ed448: {
|
||||
const ed448 = await util.getNobleCurve(enums.publicKey.ed448);
|
||||
return ed448.verify(RS, hashed, publicKey);
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported EdDSA algorithm');
|
||||
}
|
||||
@ -131,6 +132,8 @@ export async function validateParams(algo, A, seed) {
|
||||
}
|
||||
|
||||
case enums.publicKey.ed448: {
|
||||
const ed448 = await util.getNobleCurve(enums.publicKey.ed448);
|
||||
|
||||
const publicKey = ed448.getPublicKey(seed);
|
||||
return util.equalsUint8Array(A, publicKey);
|
||||
}
|
||||
|
27
src/crypto/public_key/elliptic/noble_curves.js
Normal file
27
src/crypto/public_key/elliptic/noble_curves.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* This file is needed to dynamic import the noble-curves.
|
||||
* Separate dynamic imports are not convenient as they result in too many chunks,
|
||||
* which share a lot of code anyway.
|
||||
*/
|
||||
|
||||
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 { x448, ed448 } from '@openpgp/noble-curves/ed448';
|
||||
import { secp256k1 } from '@openpgp/noble-curves/secp256k1';
|
||||
|
||||
export const nobleCurves = new Map(Object.entries({
|
||||
p256,
|
||||
p384,
|
||||
p521,
|
||||
brainpoolP256r1,
|
||||
brainpoolP384r1,
|
||||
brainpoolP512r1,
|
||||
secp256k1,
|
||||
x448,
|
||||
ed448
|
||||
}));
|
||||
|
@ -20,20 +20,12 @@
|
||||
* @module crypto/public_key/elliptic/curve
|
||||
*/
|
||||
import nacl from '@openpgp/tweetnacl';
|
||||
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 { UnsupportedError } from '../../../packet/packet';
|
||||
import defaultConfig from '../../../config';
|
||||
|
||||
const webCrypto = util.getWebCrypto();
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
@ -56,26 +48,6 @@ const nodeCurves = nodeCrypto ? {
|
||||
[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 => {
|
||||
if (!defaultConfig.useEllipticFallback) {
|
||||
// TODO make import dynamic
|
||||
throw new Error('This curve is only supported in the full build of OpenPGP.js');
|
||||
}
|
||||
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],
|
||||
@ -291,7 +263,7 @@ async function validateStandardParams(algo, oid, Q, d) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const nobleCurve = getNobleCurve(enums.write(enums.curve, oid.toHex()));
|
||||
const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, enums.write(enums.curve, oid.toHex())); // excluding curve25519Legacy, ecdh and ecdsa use the same curves
|
||||
/*
|
||||
* Re-derive public point Q' = dG from private key
|
||||
* Expect Q == Q'
|
||||
@ -313,8 +285,8 @@ export {
|
||||
// Helper functions //
|
||||
// //
|
||||
//////////////////////////
|
||||
function jsGenKeyPair(name) {
|
||||
const nobleCurve = getNobleCurve(name);
|
||||
async function jsGenKeyPair(name) {
|
||||
const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, name); // excluding curve25519Legacy, ecdh and ecdsa use the same curves
|
||||
const privateKey = nobleCurve.utils.randomPrivateKey();
|
||||
const publicKey = nobleCurve.getPublicKey(privateKey, false);
|
||||
return { publicKey, privateKey };
|
||||
|
@ -19,7 +19,7 @@
|
||||
* @fileoverview Algorithms for probabilistic random prime generation
|
||||
* @module crypto/public_key/prime
|
||||
*/
|
||||
import { BigInteger } from '@openpgp/noble-hashes/biginteger';
|
||||
import util from '../../util';
|
||||
import { getRandomBigInteger } from '../random';
|
||||
|
||||
/**
|
||||
@ -31,6 +31,8 @@ import { getRandomBigInteger } from '../random';
|
||||
* @async
|
||||
*/
|
||||
export async function randomProbablePrime(bits, e, k) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
const one = BigInteger.new(1);
|
||||
const min = one.leftShift(BigInteger.new(bits - 1));
|
||||
const thirty = BigInteger.new(30);
|
||||
@ -91,11 +93,15 @@ export async function isProbablePrime(n, e, k) {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export async function fermat(n, b) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
b = b || BigInteger.new(2);
|
||||
return b.modExp(n.dec(), n).isOne();
|
||||
}
|
||||
|
||||
export async function divisionTest(n) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
return smallPrimes.every(m => {
|
||||
return n.mod(BigInteger.new(m)) !== 0;
|
||||
});
|
||||
@ -224,6 +230,8 @@ const smallPrimes = [
|
||||
* @async
|
||||
*/
|
||||
export async function millerRabin(n, k, rand) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
const len = n.bitLength();
|
||||
|
||||
if (!k) {
|
||||
|
@ -19,7 +19,6 @@
|
||||
* @fileoverview RSA implementation
|
||||
* @module crypto/public_key/rsa
|
||||
*/
|
||||
import { BigInteger } from '@openpgp/noble-hashes/biginteger';
|
||||
import { randomProbablePrime } from './prime';
|
||||
import { getRandomBigInteger } from '../random';
|
||||
import util from '../../util';
|
||||
@ -159,6 +158,8 @@ export async function decrypt(data, n, e, d, p, q, u, randomPayload) {
|
||||
* @async
|
||||
*/
|
||||
export async function generate(bits, e) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
e = BigInteger.new(e);
|
||||
|
||||
// Native RSA keygen using Web Crypto
|
||||
@ -262,6 +263,8 @@ export async function generate(bits, e) {
|
||||
* @async
|
||||
*/
|
||||
export async function validateParams(n, e, d, p, q, u) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
n = BigInteger.new(n);
|
||||
p = BigInteger.new(p);
|
||||
q = BigInteger.new(q);
|
||||
@ -300,6 +303,8 @@ export async function validateParams(n, e, d, p, q, u) {
|
||||
}
|
||||
|
||||
async function bnSign(hashAlgo, n, d, hashed) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
n = BigInteger.new(n);
|
||||
const m = BigInteger.new(await emsaEncode(hashAlgo, hashed, n.byteLength()));
|
||||
d = BigInteger.new(d);
|
||||
@ -360,6 +365,8 @@ async function nodeSign(hashAlgo, data, n, e, d, p, q, u) {
|
||||
}
|
||||
|
||||
async function bnVerify(hashAlgo, s, n, e, hashed) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
n = BigInteger.new(n);
|
||||
s = BigInteger.new(s);
|
||||
e = BigInteger.new(e);
|
||||
@ -427,6 +434,8 @@ async function nodeEncrypt(data, n, e) {
|
||||
}
|
||||
|
||||
async function bnEncrypt(data, n, e) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
n = BigInteger.new(n);
|
||||
data = BigInteger.new(emeEncode(data, n.byteLength()));
|
||||
e = BigInteger.new(e);
|
||||
@ -478,6 +487,8 @@ async function nodeDecrypt(data, n, e, d, p, q, u, randomPayload) {
|
||||
}
|
||||
|
||||
async function bnDecrypt(data, n, e, d, p, q, u, randomPayload) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
data = BigInteger.new(data);
|
||||
n = BigInteger.new(n);
|
||||
e = BigInteger.new(e);
|
||||
@ -519,6 +530,8 @@ async function bnDecrypt(data, n, e, d, p, q, u, randomPayload) {
|
||||
* @param {Uint8Array} u
|
||||
*/
|
||||
async function privateToJWK(n, e, d, p, q, u) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
const pNum = BigInteger.new(p);
|
||||
const qNum = BigInteger.new(q);
|
||||
const dNum = BigInteger.new(d);
|
||||
|
@ -21,7 +21,6 @@
|
||||
* @fileoverview Provides tools for retrieving secure randomness from browsers or Node.js
|
||||
* @module crypto/random
|
||||
*/
|
||||
import { BigInteger } from '@openpgp/noble-hashes/biginteger';
|
||||
import util from '../util';
|
||||
|
||||
const nodeCrypto = util.getNodeCrypto();
|
||||
@ -52,6 +51,8 @@ export function getRandomBytes(length) {
|
||||
* @async
|
||||
*/
|
||||
export async function getRandomBigInteger(min, max) {
|
||||
const BigInteger = await util.getBigInteger();
|
||||
|
||||
if (max.lt(min)) {
|
||||
throw new Error('Illegal parameter value: max <= min');
|
||||
}
|
||||
|
33
src/util.js
33
src/util.js
@ -25,6 +25,8 @@
|
||||
import * as stream from '@openpgp/web-stream-tools';
|
||||
import { createRequire } from 'module'; // Must be stripped in browser built
|
||||
import enums from './enums';
|
||||
import defaultConfig from './config';
|
||||
import { getBigInteger } from './biginteger';
|
||||
|
||||
const debugMode = (() => {
|
||||
try {
|
||||
@ -48,6 +50,37 @@ const util = {
|
||||
|
||||
isStream: stream.isStream,
|
||||
|
||||
getBigInteger,
|
||||
|
||||
/**
|
||||
* Load noble-curves lib on demand and return the requested curve function
|
||||
* @param {enums.publicKey} publicKeyAlgo
|
||||
* @param {enums.curve} [curveName] - for algos supporting different curves (e.g. ECDSA)
|
||||
* @returns curve implementation
|
||||
* @throws on unrecognized curve, or curve not implemented by noble-curve
|
||||
*/
|
||||
getNobleCurve: async (publicKeyAlgo, curveName) => {
|
||||
if (!defaultConfig.useEllipticFallback) {
|
||||
throw new Error('This curve is only supported in the full build of OpenPGP.js');
|
||||
}
|
||||
|
||||
const { nobleCurves } = await import('./crypto/public_key/elliptic/noble_curves');
|
||||
switch (publicKeyAlgo) {
|
||||
case enums.publicKey.ecdh:
|
||||
case enums.publicKey.ecdsa: {
|
||||
const curve = nobleCurves.get(curveName);
|
||||
if (!curve) throw new Error('Unsupported curve');
|
||||
return curve;
|
||||
}
|
||||
case enums.publicKey.x448:
|
||||
return nobleCurves.get('x448');
|
||||
case enums.publicKey.ed448:
|
||||
return nobleCurves.get('ed448');
|
||||
default:
|
||||
throw new Error('Unsupported curve');
|
||||
}
|
||||
},
|
||||
|
||||
readNumber: function (bytes) {
|
||||
let n = 0;
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user