Skip to content

Commit 3c0aeff

Browse files
Merge pull request #8958 from BitGo/zahinmohammad/wcn-266-update-ofcisvalidpub-in-sdk-for-happy-path
feat(sdk-core): accept SPKI secp256k1 pubs in Ofc.isValidPub
2 parents d8c6406 + c7ab5b1 commit 3c0aeff

2 files changed

Lines changed: 51 additions & 5 deletions

File tree

  • modules

modules/bitgo/test/v2/unit/coins/ofc.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'should';
2+
import { generateKeyPairSync } from 'crypto';
23

34
import { TestBitGo } from '@bitgo/sdk-test';
45
import { BitGo } from '../../../../src/bitgo';
@@ -24,8 +25,39 @@ describe('OFC:', function () {
2425
ofcCoin.isValidMofNSetup({ m: 1, n: 1 }).should.be.true();
2526
});
2627

27-
it('should validate pub key', () => {
28-
const { pub } = ofcCoin.keychains().create();
29-
ofcCoin.isValidPub(pub).should.equal(true);
28+
describe('isValidPub', () => {
29+
it('accepts a BIP-32 xpub (user key)', () => {
30+
const { pub } = ofcCoin.keychains().create();
31+
ofcCoin.isValidPub(pub).should.equal(true);
32+
});
33+
34+
it('accepts a base64 SPKI secp256k1 public key (BitGo key)', () => {
35+
const { publicKey } = generateKeyPairSync('ec', { namedCurve: 'secp256k1' });
36+
const spkiBase64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
37+
ofcCoin.isValidPub(spkiBase64).should.equal(true);
38+
});
39+
40+
it('rejects a BIP-32 xprv (private key)', () => {
41+
const { prv } = ofcCoin.keychains().create();
42+
ofcCoin.isValidPub(prv).should.equal(false);
43+
});
44+
45+
it('rejects a SPKI public key on the wrong curve (P-256)', () => {
46+
const { publicKey } = generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
47+
const spkiBase64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
48+
ofcCoin.isValidPub(spkiBase64).should.equal(false);
49+
});
50+
51+
it('rejects garbage base64', () => {
52+
ofcCoin.isValidPub(Buffer.from('not a key').toString('base64')).should.equal(false);
53+
});
54+
55+
it('rejects an empty string', () => {
56+
ofcCoin.isValidPub('').should.equal(false);
57+
});
58+
59+
it('rejects a raw (non-SPKI) secp256k1 hex public key', () => {
60+
ofcCoin.isValidPub('02' + '11'.repeat(32)).should.equal(false);
61+
});
3062
});
3163
});

modules/sdk-core/src/coins/ofc.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @prettier
33
*/
4-
import { randomBytes } from 'crypto';
4+
import { createPublicKey, randomBytes } from 'crypto';
55
import { bip32 } from '@bitgo/utxo-lib';
66
import {
77
AuditDecryptedKeyParams,
@@ -70,12 +70,26 @@ export class Ofc extends BaseCoin {
7070
/**
7171
* Return boolean indicating whether input is valid public key for the coin.
7272
*
73+
* OFC wallets hold keys in two formats: the user key is a BIP-32 base58 xpub,
74+
* and the BitGo key (source: 'bitgo') is a base64-encoded SPKI (DER-wrapped)
75+
* secp256k1 public key. Both are valid.
76+
*
7377
* @param {String} pub the pub to be checked
7478
* @returns {Boolean} is it valid?
7579
*/
7680
isValidPub(pub: string): boolean {
81+
// BIP-32 base58 xpub (user key)
82+
try {
83+
if (bip32.fromBase58(pub).isNeutered()) {
84+
return true;
85+
}
86+
} catch (e) {
87+
// not a BIP-32 key; fall through to the SPKI check
88+
}
89+
// base64-encoded SPKI DER secp256k1 public key (BitGo key)
7790
try {
78-
return bip32.fromBase58(pub).isNeutered();
91+
const key = createPublicKey({ key: Buffer.from(pub, 'base64'), format: 'der', type: 'spki' });
92+
return key.asymmetricKeyType === 'ec' && key.asymmetricKeyDetails?.namedCurve === 'secp256k1';
7993
} catch (e) {
8094
return false;
8195
}

0 commit comments

Comments
 (0)