@@ -2,19 +2,20 @@ import { assert, bytesToHex } from '@metamask/utils';
22
33import type { BIP44CoinTypeNode } from './BIP44CoinTypeNode' ;
44import type { BIP44Node } from './BIP44Node' ;
5+ import { BYTES_KEY_LENGTH } from './constants' ;
56import type {
67 Network ,
78 RootedSLIP10PathTuple ,
89 SLIP10PathTuple ,
910} from './constants' ;
10- import { BYTES_KEY_LENGTH } from './constants' ;
1111import type { CryptographicFunctions } from './cryptography' ;
1212import type { SupportedCurve } from './curves' ;
1313import { getCurveByName } from './curves' ;
1414import { deriveKeyFromPath } from './derivation' ;
1515import { publicKeyToEthAddress } from './derivers/bip32' ;
1616import { getDerivationPathWithSeed } from './derivers/bip39' ;
1717import { decodeExtendedKey , encodeExtendedKey } from './extended-keys' ;
18+ import { PUBLIC_KEY_GUARD } from './guard' ;
1819import {
1920 getBytes ,
2021 getBytesUnsafe ,
@@ -99,18 +100,32 @@ export type SLIP10NodeInterface = JsonSLIP10Node & {
99100 toJSON ( ) : JsonSLIP10Node ;
100101} ;
101102
102- export type SLIP10NodeConstructorOptions = {
103+ type BaseSLIP10NodeConstructorOptions = {
103104 readonly depth : number ;
104105 readonly masterFingerprint ?: number | undefined ;
105106 readonly parentFingerprint : number ;
106107 readonly index : number ;
107108 readonly network ?: Network | undefined ;
108109 readonly chainCode : Uint8Array ;
109- readonly privateKey ?: Uint8Array | undefined ;
110- readonly publicKey : Uint8Array ;
111110 readonly curve : SupportedCurve ;
112111} ;
113112
113+ type SLIP10NodePrivateKeyConstructorOptions =
114+ BaseSLIP10NodeConstructorOptions & {
115+ readonly privateKey : Uint8Array ;
116+ readonly publicKey ?: Uint8Array | undefined ;
117+ } ;
118+
119+ type SLIP10NodePublicKeyConstructorOptions =
120+ BaseSLIP10NodeConstructorOptions & {
121+ readonly privateKey ?: Uint8Array | undefined ;
122+ readonly publicKey : Uint8Array ;
123+ } ;
124+
125+ export type SLIP10NodeConstructorOptions =
126+ | SLIP10NodePrivateKeyConstructorOptions
127+ | SLIP10NodePublicKeyConstructorOptions ;
128+
114129export type SLIP10ExtendedKeyOptions = {
115130 readonly depth : number ;
116131 readonly masterFingerprint ?: number | undefined ;
@@ -121,6 +136,12 @@ export type SLIP10ExtendedKeyOptions = {
121136 readonly privateKey ?: string | Uint8Array | undefined ;
122137 readonly publicKey ?: string | Uint8Array | undefined ;
123138 readonly curve : SupportedCurve ;
139+
140+ /**
141+ * For internal use only. This is used to ensure the public key provided to
142+ * the constructor is trusted.
143+ */
144+ readonly guard ?: typeof PUBLIC_KEY_GUARD ;
124145} ;
125146
126147export type SLIP10DerivationPathOptions = {
@@ -276,6 +297,7 @@ export class SLIP10Node implements SLIP10NodeInterface {
276297 publicKey,
277298 chainCode,
278299 curve,
300+ guard,
279301 } = options ;
280302
281303 const chainCodeBytes = getBytes ( chainCode , BYTES_KEY_LENGTH ) ;
@@ -299,11 +321,20 @@ export class SLIP10Node implements SLIP10NodeInterface {
299321 privateKey ,
300322 curveObject . privateKeyLength ,
301323 ) ;
324+
302325 assert (
303326 curveObject . isValidPrivateKey ( privateKeyBytes ) ,
304327 `Invalid private key: Value is not a valid ${ curve } private key.` ,
305328 ) ;
306329
330+ const trustedPublicKey =
331+ guard === PUBLIC_KEY_GUARD && publicKey
332+ ? // `publicKey` is typed as `string | Uint8Array`, but we know it's
333+ // a `Uint8Array` because of the guard. We use `getBytes` to ensure
334+ // the type is correct.
335+ getBytes ( publicKey , curveObject . publicKeyLength )
336+ : undefined ;
337+
307338 return new SLIP10Node (
308339 {
309340 depth,
@@ -313,7 +344,7 @@ export class SLIP10Node implements SLIP10NodeInterface {
313344 network,
314345 chainCode : chainCodeBytes ,
315346 privateKey : privateKeyBytes ,
316- publicKey : await curveObject . getPublicKey ( privateKeyBytes ) ,
347+ publicKey : trustedPublicKey ,
317348 curve,
318349 } ,
319350 cryptographicFunctions ,
@@ -478,7 +509,7 @@ export class SLIP10Node implements SLIP10NodeInterface {
478509
479510 public readonly privateKeyBytes ?: Uint8Array | undefined ;
480511
481- public readonly publicKeyBytes : Uint8Array ;
512+ # publicKeyBytes: Uint8Array | undefined ;
482513
483514 readonly #cryptographicFunctions: CryptographicFunctions ;
484515
@@ -503,15 +534,20 @@ export class SLIP10Node implements SLIP10NodeInterface {
503534 'SLIP10Node can only be constructed using `SLIP10Node.fromJSON`, `SLIP10Node.fromExtendedKey`, `SLIP10Node.fromDerivationPath`, or `SLIP10Node.fromSeed`.' ,
504535 ) ;
505536
537+ assert (
538+ privateKey !== undefined || publicKey !== undefined ,
539+ 'SLIP10Node requires either a private key or a public key to be set.' ,
540+ ) ;
541+
506542 this . depth = depth ;
507543 this . masterFingerprint = masterFingerprint ;
508544 this . parentFingerprint = parentFingerprint ;
509545 this . index = index ;
510546 this . network = network ;
511547 this . chainCodeBytes = chainCode ;
512548 this . privateKeyBytes = privateKey ;
513- this . publicKeyBytes = publicKey ;
514549 this . curve = curve ;
550+ this . #publicKeyBytes = publicKey ;
515551 this . #cryptographicFunctions = cryptographicFunctions ;
516552
517553 Object . freeze ( this ) ;
@@ -533,6 +569,31 @@ export class SLIP10Node implements SLIP10NodeInterface {
533569 return bytesToHex ( this . publicKeyBytes ) ;
534570 }
535571
572+ /**
573+ * Get the public key bytes. This will lazily derive the public key from the
574+ * private key if it is not already set.
575+ *
576+ * @returns The public key bytes.
577+ */
578+ public get publicKeyBytes ( ) : Uint8Array {
579+ if ( this . #publicKeyBytes !== undefined ) {
580+ return this . #publicKeyBytes;
581+ }
582+
583+ // This assertion is mainly for type safety, as `SLIP10Node` requires either
584+ // a private key or a public key to always be set.
585+ assert (
586+ this . privateKeyBytes ,
587+ 'Either a private key or public key is required.' ,
588+ ) ;
589+
590+ this . #publicKeyBytes = getCurveByName ( this . curve ) . getPublicKey (
591+ this . privateKeyBytes ,
592+ ) ;
593+
594+ return this . #publicKeyBytes;
595+ }
596+
536597 public get compressedPublicKeyBytes ( ) : Uint8Array {
537598 return getCurveByName ( this . curve ) . compressPublicKey ( this . publicKeyBytes ) ;
538599 }
0 commit comments