Skip to content

Commit f1efd88

Browse files
committed
fix: mitigate CVE-2024-48930 by validating secp256k1 curve points, enforce secp256k1 v5.0.1
TICKET: DX-1569
1 parent 6196331 commit f1efd88

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed

modules/bitgo/test/ecdh.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'should';
55
import { bip32 } from '@bitgo/utxo-lib';
66
import * as crypto from 'crypto';
77
import * as utxolib from '@bitgo/utxo-lib';
8+
import * as secp256k1 from 'secp256k1';
89

910
import { getSharedSecret, signMessageWithDerivedEcdhKey, verifyEcdhSignature } from '@bitgo/sdk-core';
1011
import { TestBitGo } from '@bitgo/sdk-test';
@@ -82,4 +83,26 @@ describe('ECDH utils', () => {
8283
isVerify = verifyEcdhSignature(JSON.stringify(message), signedMessage.toString('hex'), derivedPubKey);
8384
isVerify.should.be.false();
8485
});
86+
87+
it('getSharedSecret rejects invalid public keys for CVE-2024-48930 mitigation', function () {
88+
const validPrivateKey = Buffer.alloc(32, 1); // Create a dummy private key
89+
const validPublicKey = Buffer.from(secp256k1.publicKeyCreate(validPrivateKey));
90+
91+
// Valid key should work
92+
const sharedSecret = getSharedSecret(validPrivateKey, validPublicKey);
93+
assert.strictEqual(sharedSecret.length, 32);
94+
95+
// Test with invalid public key
96+
const invalidPublicKey = Buffer.from('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 'hex');
97+
// Modify last byte to make it invalid but keep the format valid
98+
invalidPublicKey[32] = invalidPublicKey[32] ^ 0xff;
99+
100+
try {
101+
getSharedSecret(validPrivateKey, invalidPublicKey);
102+
assert.fail('Should have thrown error for invalid public key');
103+
} catch (e) {
104+
// The test passes as long as an error is thrown
105+
assert.ok(true, 'Error was thrown as expected');
106+
}
107+
});
85108
});

modules/sdk-core/src/bitgo/ecdh.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@ import * as assert from 'assert';
1616
import * as secp256k1 from 'secp256k1';
1717
import { ECPairInterface, BIP32Interface, bip32, ECPair } from '@bitgo/utxo-lib';
1818

19+
/**
20+
* Validate that a public key is on the secp256k1 curve
21+
* This is a defense-in-depth check to prevent invalid curve attacks
22+
* @param publicKey Buffer containing the public key to validate
23+
* @returns boolean true if the key is valid, false otherwise
24+
*/
25+
function validatePublicKey(publicKey: Buffer): boolean {
26+
try {
27+
// Validates that the public key is a point on the secp256k1 curve
28+
return secp256k1.publicKeyVerify(publicKey);
29+
} catch (e) {
30+
return false;
31+
}
32+
}
33+
1934
/**
2035
* Calculate the Elliptic Curve Diffie Hellman
2136
* @param privateKey HDNode of private key
@@ -55,8 +70,15 @@ export function getSharedSecret(
5570
assert.strictEqual(privateKey.length, 32);
5671
assert.strictEqual(publicKey.length, 33);
5772

73+
// Extra validation to ensure the public key is on the curve
74+
// This provides defense-in-depth against CVE-2024-48930 (GHSA-584q-6j8j-r5pm)
75+
if (!validatePublicKey(publicKey)) {
76+
throw new Error(`invalid public key: not on secp256k1 curve`);
77+
}
78+
5879
// FIXME(BG-34386): we should use `secp256k1.ecdh()` in the future
5980
// see discussion here https://github.com/bitcoin-core/secp256k1/issues/352
81+
// Additional validation has been added above to protect against CVE-2024-48930
6082
const buffer = Buffer.from(secp256k1.publicKeyTweakMul(publicKey, privateKey))
6183
// remove leading parity bit
6284
.slice(1);

package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,5 +143,21 @@
143143
"terser": "^5.14.2",
144144
"tmp": "^0.2.3"
145145
},
146+
"overrides": {
147+
"ethereum-cryptography": {
148+
"secp256k1": "5.0.1"
149+
},
150+
"hdkey": {
151+
"secp256k1": "5.0.1"
152+
},
153+
"eccrypto": {
154+
"secp256k1": "5.0.1"
155+
},
156+
"bip322-js": {
157+
"bitcoinjs-message": {
158+
"secp256k1": "5.0.1"
159+
}
160+
}
161+
},
146162
"packageManager": "[email protected]"
147163
}

scripts/check-package-dependencies.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const options = {
3434
'sass-loader',
3535
'style-loader',
3636
'ts-loader',
37+
'secp256k1',
3738
],
3839
specials: [
3940
depcheck.special.babel,

0 commit comments

Comments
 (0)