Compare commits

...

2 Commits

Author SHA1 Message Date
overtorment
e5e2aa3024 carved out curves from create-ecdh 2025-07-06 22:16:38 +01:00
overtorment
bcfa524270 removed createSign/createVerify 2025-07-06 22:04:10 +01:00
7 changed files with 30 additions and 388 deletions

View File

@ -2,6 +2,8 @@
* `randomBytes` is removed and will throw an exception. Bring your own CSPRNG for your runtime
* `randomfill`
* `createSign()` & `createVerify()` are carved out (could not get noble to work with it)
* for `createECDH`, curves `secp224r1` & `prime192v1` are carved out (not supported in noble)
# crypto-browserify <sup>[![Version Badge][npm-version-svg]][package-url]</sup>

View File

@ -50,12 +50,21 @@ exports.getDiffieHellman = dh.getDiffieHellman;
exports.createDiffieHellman = dh.createDiffieHellman;
exports.DiffieHellman = dh.DiffieHellman;
var sign = require('./noble-sign-wrapper');
exports.createSign = function () {
throw new Error('Not implemented');
};
exports.createSign = sign.createSign;
exports.Sign = sign.Sign;
exports.createVerify = sign.createVerify;
exports.Verify = sign.Verify;
exports.Sign = function () {
throw new Error('Not implemented');
};
exports.createVerify = function () {
throw new Error('Not implemented');
};
exports.Verify = function () {
throw new Error('Not implemented');
};
exports.createECDH = require('./noble-ecdh-wrapper');

View File

@ -4,23 +4,16 @@ var secp256k1 = require('@noble/curves/secp256k1');
var nist = require('@noble/curves/nist');
var Buffer = require('safe-buffer').Buffer;
// Fallback to create-ecdh/browser for unsupported curves and hybrid format
var createEcdhBrowser = require('create-ecdh/browser');
// Curve name mapping
var curveMap = {
secp256k1: secp256k1.secp256k1,
secp224r1: null, // Will fallback to create-ecdh
secp224r1: null, // Not available in noble
prime256v1: nist.p256,
prime192v1: null // Not available in noble
};
function ECDH(curveName) {
// Fallback for secp224r1 and prime192v1
if (curveName === 'secp224r1' || curveName === 'prime192v1') {
// Use create-ecdh/browser fallback for secp224r1 and prime192v1
return createEcdhBrowser(curveName);
}
if (!curveMap[curveName]) {
throw new Error('Unsupported curve: ' + curveName);
}
@ -51,11 +44,9 @@ ECDH.prototype.getPublicKey = function (encoding, format) {
throw new Error('Public key not set');
}
// Fallback for hybrid format
if (format === 'hybrid') {
// Use create-ecdh/browser fallback for hybrid format
// This is required because noble-curves does not support hybrid format
return createEcdhBrowser(this.curveName).setPrivateKey(this.getPrivateKey()).getPublicKey(encoding, format);
// noble-curves does not support hybrid format
throw new Error('Unsupported format: ' + format);
}
var key;

View File

@ -1,358 +0,0 @@
'use strict';
/* global Uint8Array */
var secp256k1 = require('@noble/curves/secp256k1');
var p256 = require('@noble/curves/p256');
var p384 = require('@noble/curves/p384');
var p521 = require('@noble/curves/p521');
var ed25519 = require('@noble/curves/ed25519');
var ed448 = require('@noble/curves/ed448');
var sha2 = require('@noble/hashes/sha2');
var legacy = require('@noble/hashes/legacy');
// For RSA algorithms, fall back to browserify-sign (legacy, not noble)
var browserifySign = require('browserify-sign');
// Map algorithm names to noble curve functions
var curveFunctions = {
secp256k1: secp256k1,
p256: p256,
p384: p384,
p521: p521,
ed25519: ed25519,
ed448: ed448
};
// Map hash algorithms to noble hash functions
var hashFunctions = {
sha1: legacy.sha1,
sha224: sha2.sha224,
sha256: sha2.sha256,
sha384: sha2.sha384,
sha512: sha2.sha512,
md5: legacy.md5,
rmd160: legacy.ripemd160
};
// Helper function to convert data to Uint8Array
function toUint8Array(data) {
if (data instanceof Uint8Array) {
return data;
}
if (typeof data === 'string') {
return new Uint8Array(Buffer.from(data, 'utf8'));
}
if (Buffer.isBuffer(data)) {
return new Uint8Array(data);
}
throw new Error('Unsupported data type');
}
// Helper function to create browserify-sign fallback
function createSignFallback(algorithm) {
var signInstance = browserifySign.createSign(algorithm);
return {
update: signInstance.update.bind(signInstance),
sign: signInstance.sign.bind(signInstance)
};
}
// Helper function to create browserify-sign verify fallback
function createVerifyFallback(algorithm) {
var verifyInstance = browserifySign.createVerify(algorithm);
return {
update: verifyInstance.update.bind(verifyInstance),
verify: verifyInstance.verify.bind(verifyInstance)
};
}
function parseSingleHash(parts) {
var singleHashName = parts[0];
var singleHash = hashFunctions[singleHashName];
if (singleHash) {
return {
useNoble: true,
curve: curveFunctions.p256,
hash: singleHash,
reason: 'legacy-hash-only'
};
}
return {
useNoble: false,
reason: 'invalid-format'
};
}
function parseMultiPart(parts) {
var curveName = parts[0];
var hashName = parts[1];
if (curveName === 'rsa') {
return {
useNoble: false,
reason: 'rsa'
};
}
var curve = curveFunctions[curveName];
var hash = hashFunctions[hashName];
if (!curve) {
return {
useNoble: false,
reason: 'unsupported-curve'
};
}
if (!hash) {
return {
useNoble: false,
reason: 'unsupported-hash'
};
}
return {
useNoble: true,
curve: curve,
hash: hash
};
}
// Helper function to parse algorithm and determine if it should use noble
function parseAlgorithm(algorithm) {
var parts = algorithm.toLowerCase().split('-');
if (parts.length === 1) {
return parseSingleHash(parts);
}
if (parts.length < 2) {
return { useNoble: false, reason: 'invalid-format' };
}
return parseMultiPart(parts);
}
// Helper function to try noble curve signing
function tryNobleSign(curve, hashedData, privateKeyBytes) {
if (curve.sign) {
return curve.sign(hashedData, privateKeyBytes);
}
if (curve.Signature && curve.Signature.sign) {
return curve.Signature.sign(hashedData, privateKeyBytes);
}
if (curve.secp256k1 && curve.secp256k1.sign) {
return curve.secp256k1.sign(hashedData, privateKeyBytes);
}
return null;
}
// Helper function to try noble curve verification
function tryNobleVerify(curve, signatureBytes, hashedData, publicKeyBytes) {
if (curve.verify) {
return curve.verify(signatureBytes, hashedData, publicKeyBytes);
}
if (curve.Signature && curve.Signature.verify) {
return curve.Signature.verify(signatureBytes, hashedData, publicKeyBytes);
}
if (curve.secp256k1 && curve.secp256k1.verify) {
return curve.secp256k1.verify(signatureBytes, hashedData, publicKeyBytes);
}
return null;
}
// Helper function to create fallback instances
function createFallbackInst(algorithm, data, type) {
var inst = type === 'sign' ? createSignFallback(algorithm) : createVerifyFallback(algorithm);
inst.data = data;
return inst;
}
function Sign(algorithm) {
this.algorithm = algorithm;
this.curve = null;
this.hashFunction = null;
this.privateKey = null;
this.data = [];
this.finalized = false;
var parsed = parseAlgorithm(algorithm);
if (!parsed.useNoble) {
var fallback = createSignFallback(algorithm);
this.update = fallback.update;
this.sign = fallback.sign;
return;
}
this.curve = parsed.curve;
this.hashFunction = parsed.hash;
}
Sign.prototype.update = function (data, encoding) {
if (this.finalized) {
throw new Error('Sign already finalized');
}
var uint8Data;
if (encoding) {
if (encoding === 'hex') {
uint8Data = new Uint8Array(Buffer.from(data, 'hex'));
} else if (encoding === 'base64') {
uint8Data = new Uint8Array(Buffer.from(data, 'base64'));
} else {
throw new Error('Unsupported encoding: ' + encoding);
}
} else {
uint8Data = toUint8Array(data);
}
this.data.push(uint8Data);
return this;
};
Sign.prototype.sign = function (privateKey, encoding) {
if (this.finalized) {
throw new Error('Sign already finalized');
}
if (!privateKey) {
throw new Error('Private key is required');
}
// Concatenate all data
var allData = new Uint8Array(this.data.reduce(function (total, chunk) {
return total + chunk.length;
}, 0));
var offset = 0;
for (var i = 0; i < this.data.length; i++) {
allData.set(this.data[i], offset);
offset += this.data[i].length;
}
// Hash the data
var hashedData = this.hashFunction(allData);
// Convert private key to Uint8Array if needed
var privateKeyBytes = toUint8Array(privateKey);
// Try to sign using noble curve
var signature;
try {
signature = tryNobleSign(this.curve, hashedData, privateKeyBytes);
if (signature === null) {
var signFallback = createFallbackInst(this.algorithm, this.data, 'sign');
return signFallback.sign(privateKey, encoding);
}
} catch (error) {
var signFallbackCatch = createFallbackInst(this.algorithm, this.data, 'sign');
return signFallbackCatch.sign(privateKey, encoding);
}
this.finalized = true;
// Return signature based on encoding
if (encoding === 'hex') {
return signature.toHex ? signature.toHex() : Buffer.from(signature).toString('hex');
}
if (encoding === 'base64') {
return signature.toBase64 ? signature.toBase64() : Buffer.from(signature).toString('base64');
}
return signature.toBytes ? signature.toBytes() : Buffer.from(signature);
};
function Verify(algorithm) {
this.algorithm = algorithm;
this.curve = null;
this.hashFunction = null;
this.publicKey = null;
this.data = [];
this.finalized = false;
var parsed = parseAlgorithm(algorithm);
if (!parsed.useNoble) {
var fallback = createVerifyFallback(algorithm);
this.update = fallback.update;
this.verify = fallback.verify;
return;
}
this.curve = parsed.curve;
this.hashFunction = parsed.hash;
}
Verify.prototype.update = function (data, encoding) {
if (this.finalized) {
throw new Error('Verify already finalized');
}
var uint8Data;
if (encoding) {
if (encoding === 'hex') {
uint8Data = new Uint8Array(Buffer.from(data, 'hex'));
} else if (encoding === 'base64') {
uint8Data = new Uint8Array(Buffer.from(data, 'base64'));
} else {
throw new Error('Unsupported encoding: ' + encoding);
}
} else {
uint8Data = toUint8Array(data);
}
this.data.push(uint8Data);
return this;
};
Verify.prototype.verify = function (publicKey, signature, encoding) {
if (this.finalized) {
throw new Error('Verify already finalized');
}
if (!publicKey) {
throw new Error('Public key is required');
}
if (!signature) {
throw new Error('Signature is required');
}
// Concatenate all data
var allData = new Uint8Array(this.data.reduce(function (total, chunk) {
return total + chunk.length;
}, 0));
var offset = 0;
for (var i = 0; i < this.data.length; i++) {
allData.set(this.data[i], offset);
offset += this.data[i].length;
}
// Hash the data
var hashedData = this.hashFunction(allData);
// Parse signature based on encoding
var signatureBytes;
if (encoding === 'hex') {
signatureBytes = new Uint8Array(Buffer.from(signature, 'hex'));
} else if (encoding === 'base64') {
signatureBytes = new Uint8Array(Buffer.from(signature, 'base64'));
} else {
signatureBytes = toUint8Array(signature);
}
// Convert public key to Uint8Array if needed
var publicKeyBytes = toUint8Array(publicKey);
// Verify the signature using noble curve
var isValid;
try {
isValid = tryNobleVerify(this.curve, signatureBytes, hashedData, publicKeyBytes);
if (isValid === null) {
var verifyFallback = createFallbackInst(this.algorithm, this.data, 'verify');
return verifyFallback.verify(publicKey, signature, encoding);
}
} catch (error) {
var verifyFallbackCatch = createFallbackInst(this.algorithm, this.data, 'verify');
return verifyFallbackCatch.verify(publicKey, signature, encoding);
}
this.finalized = true;
return isValid;
};
// Export the functions
exports.createSign = function (algorithm) {
return new Sign(algorithm);
};
exports.Sign = Sign;
exports.createVerify = function (algorithm) {
return new Verify(algorithm);
};
exports.Verify = Verify;

View File

@ -23,11 +23,8 @@
"@noble/ciphers": "^1.3.0",
"@noble/curves": "^1.9.2",
"@noble/hashes": "^1.8.0",
"browserify-sign": "^4.2.3",
"create-ecdh": "^4.0.4",
"diffie-hellman": "^5.0.3",
"public-encrypt": "^4.0.3",
"randomfill": "^1.0.4"
"public-encrypt": "^4.0.3"
},
"devDependencies": {
"@ljharb/eslint-config": "^21.1.1",

View File

@ -2,9 +2,9 @@
var mods = [
'secp256k1',
'secp224r1',
'prime256v1',
'prime192v1'
// 'secp224r1', // not supported in noble
'prime256v1'
// 'prime192v1' // not supported in noble
];
var test = require('tape');
var createECDH1 = require('../noble-ecdh-wrapper');
@ -40,7 +40,7 @@ mods.forEach(function (mod) {
});
test('createECDH: ' + mod + ' set stuff', function (t) {
t.plan(5);
t.plan(4);
var dh1 = createECDH1(mod);
var dh2 = createECDH2(mod);
dh1.generateKeys();
@ -54,7 +54,8 @@ mods.forEach(function (mod) {
var pubk2 = dh2.getPublicKey();
t.equals(pubk1.toString('hex'), pubk2.toString('hex'), 'same public keys, uncompressed');
t.equals(dh1.getPublicKey('hex', 'compressed'), dh2.getPublicKey('hex', 'compressed'), 'same public keys compressed');
t.equals(dh1.getPublicKey('hex', 'hybrid'), dh2.getPublicKey('hex', 'hybrid'), 'same public keys hybrid');
// not supported in noble
// t.equals(dh1.getPublicKey('hex', 'hybrid'), dh2.getPublicKey('hex', 'hybrid'), 'same public keys hybrid');
var pub1 = dh1.computeSecret(pubk2).toString('hex');
var pub2 = dh2.computeSecret(pubk1).toString('hex');
t.equals(pub1, pub2, 'equal secrets');

View File

@ -1,8 +1,8 @@
'use strict';
var test = require('tape');
var nodeCrypto = require('../');
var ourCrypto = require('../noble-sign-wrapper');
var nodeCrypto = require('crypto');
var ourCrypto = require('../');
var rsa = {
'private': '2d2d2d2d2d424547494e205253412050524956415445204b45592d2d2d2d2d0a4d4949456a77494241414b422f6779376d6a615767506546645659445a5752434139424e69763370506230657332372b464b593068737a4c614f7734374578430a744157704473483438545841667948425977424c67756179666b344c4749757078622b43474d62526f337845703043626659314a62793236543976476a5243310a666f484444554a4738347561526279487161663469367a74346756522b786c4145496a6b614641414b38634f6f58415431435671474c4c6c6a554363684c38500a6a61486a2f7972695a2f53377264776c49334c6e41427877776d4c726d522f7637315774706d4f2f614e47384e2b31706f2b5177616768546b79513539452f5a0a7641754f6b4657486f6b32712f523650594161326a645a397a696d3046714f502b6e6b5161454452624246426d4271547635664647666b32577341664b662f520a47302f5646642b5a654d353235315465547658483639356e6c53476175566c3941674d42414145436766344c725748592f6c35346f7554685a577676627275670a70667a36734a583267396c3779586d576c455773504543566f2f375355627059467074364f5a7939397a53672b494b624771574b6664686f4b725477495674430a4c30595a304e6c6d646e414e53497a30726f785147375a786b4c352b764853772f506d443978345577662b437a38684154436d4e42763171633630646b7975570a34434c71653732716154695657526f4f316961675167684e634c6f6f36765379363545784c614344545068613779753276773468465a705769456a57346478660a7246644c696978353242433836596c416c784d452f724c6738494a5676696c62796f39615764586d784f6155544c527636506b4644312f6756647738563951720a534c4e39466c4b326b6b6a695830647a6f6962765a7733744d6e74337979644178305838372b734d5256616843316270336b56507a3448793045575834514a2f0a504d33317647697549546b324e43643531445874314c746e324f503546614a536d4361456a6830586b5534716f7559796a585774384275364254436c327675610a466730556a6939432b496b504c6d61554d624d494f7761546b386357714c74685378734c6537304a354f6b477267664b554d2f772b4248483150742f506a7a6a0a432b2b6c306b6946614f5644566141563947704c504c43426f4b2f50433952622f72784d4d6f43434e774a2f4e5a756564496e793277334c4d69693737682f540a7a53766572674e47686a5936526e7661386c4c584a36646c726b6350417970733367577778716a344e5230542b474d3062445550564c62374d303758563753580a7637564a476d35324a625247774d3173732b72385854544e656d65476b2b5752784737546774734d715947584c66423851786b2f66352f4d63633030546c38750a7758464e7366784a786d7436416273547233673336774a2f49684f6e69627a3941642b6e63686c426e4e3351655733434b48717a61523138766f717674566d320a6b4a66484b31357072482f7353476d786d6945476772434a545a78744462614e434f372f56426a6e4b756455554968434177734c747571302f7a7562397641640a384731736366497076357161534e7a6d4b6f5838624f77417276725336775037794b726354737557496c484438724a5649374945446e516f5470354738664b310a68774a2f4d4968384d35763072356455594576366f494a5747636c65364148314a6d73503557496166677137325a32323838704863434648774e59384467394a0a3736517377564c6e556850546c6d6d33454f4f50474574616d32694144357230416679746c62346c624e6f51736a32737a65584f4e4458422b366f7565616a680a564e454c55723848635350356c677a525a6a4a57366146497a6a394c44526d516e55414f6a475358564f517445774a2f4d43515a374e2f763464494b654452410a3864385545785a332b674748756d7a697a7447524a30745172795a483250616b50354937562b316c377145556e4a3263336d462b65317634314570394c4376680a627a72504b773964786831386734622b37624d707357506e7372614b6836697078633761614f615a5630447867657a347a635a753050316f6c4f30634e334b4d0a6e784a305064733352386241684e43446453324a5a61527035513d3d0a2d2d2d2d2d454e44205253412050524956415445204b45592d2d2d2d2d0a',
@ -22,7 +22,7 @@ ec['public'] = new Buffer(ec['public'], 'hex');
function testit(keys, message, scheme) {
var pub = keys['public'];
var priv = keys['private'];
test(message.toString(), function (t) {
test(message.toString(), { skip: true }, function (t) {
t.test('js sign and verify', function (st) {
st.plan(1);
var mySign = ourCrypto.createSign(scheme);