@@ -54,6 +54,20 @@ export interface KeyOptions {
5454 language ?: string ;
5555 algo ?: string ; // eddsa or ecdsa (Bitcoin) by default
5656} ;
57+ interface AddKeyOptions {
58+ passphrase ?: string ;
59+ password ?: string ;
60+ sjclOpts ?: any ;
61+ algo ?: string ;
62+ existingAlgo ?: string ;
63+ }
64+
65+ interface SetFromMnemonicOptions {
66+ passphrase ?: string ;
67+ algo ?: string ;
68+ password ?: string ;
69+ sjclOpts ?: any ;
70+ }
5771
5872export class Key {
5973 // ecdsa
@@ -210,10 +224,7 @@ export class Key {
210224 return a . id == b . id || a . fingerPrint == b . fingerPrint || a . fingerPrintEDDSA == b . fingerPrintEDDSA ;
211225 }
212226
213- private setFromMnemonic (
214- m ,
215- opts : { passphrase ?: string ; password ?: string ; sjclOpts ?: any , algo ?: string }
216- ) {
227+ private setFromMnemonic ( m , opts : SetFromMnemonicOptions ) {
217228 const algos = opts . algo ? [ opts . algo ] : SUPPORTED_ALGOS ;
218229 for ( const algo of algos ) {
219230 const xpriv = m . toHDPrivateKey ( opts . passphrase , NETWORK , ALGO_TO_KEY_TYPE [ algo ] ) ;
@@ -275,42 +286,73 @@ export class Key {
275286 this . #mnemonicHasPassphrase = null ;
276287 }
277288
278- // Adds an additonal supported key to the object
279- // By default it creates the new key based on the existing bitcoin key (ECDSA)
280- addKeyByAlgorithm ( algo , opts : { passphrase ?: string ; password ?: string ; sjclOpts ?: any , algo ?: string , existingAlgo ?: string } ) {
289+ /**
290+ * Adds an additional supported key to the object
291+ * By default it creates the new key based on the existing bitcoin key (ECDSA)
292+ */
293+ addKeyByAlgorithm ( algo : string , opts : AddKeyOptions = { } ) {
281294 const existingAlgo = opts . existingAlgo || 'ECDSA' ;
295+
282296 if ( this . #mnemonic) {
283- if ( this . #mnemonicHasPassphrase) {
284- if ( ! opts . passphrase ) {
285- throw new Error ( 'Missing Passphrase' )
286- }
287- this . setFromMnemonic ( this . #mnemonic, { passphrase : opts . passphrase , algo } )
288- } else {
289- this . setFromMnemonic ( this . #mnemonic, { algo } ) ;
290- }
291- } else if ( this . #mnemonicEncrypted) {
292- if ( ! opts . password ) {
293- throw new Error ( 'Missing Password' )
294- }
295- if ( this . #mnemonicHasPassphrase) {
296- if ( ! opts . passphrase ) {
297- throw new Error ( 'Missing Passphrase' )
298- }
299- this . setFromMnemonic ( this . #mnemonic, { passphrase : opts . passphrase , algo, password : opts . password } )
300- } else {
301- this . setFromMnemonic ( this . #mnemonic, { algo, password : opts . password } ) ;
302- }
303- } else if ( this . #getPrivKeyEncrypted( { algo : existingAlgo } ) ) {
304- if ( ! opts . password ) {
305- throw new Error ( 'Missing Password' )
306- }
307- const xPriv = sjcl . decrypt ( opts . password , this . #getPrivKeyEncrypted( { algo : existingAlgo } ) ) ;
297+ this . #addKeyFromMnemonic( algo , this . #mnemonic, opts ) ;
298+ return ;
299+ }
300+ if ( this . #mnemonicEncrypted) {
301+ this . #validatePassword( opts . password ) ;
302+ this . #addKeyFromMnemonic( algo , this . #mnemonicEncrypted, opts ) ;
303+ return ;
304+ }
305+ if ( this . #hasExistingPrivateKey( existingAlgo ) ) {
306+ this . #addKeyFromExistingPrivateKey( algo , existingAlgo , opts ) ;
307+ return ;
308+ }
309+
310+ throw new Error ( `No key source available. Missing private key for algorithm: ${ existingAlgo } ` ) ;
311+ }
312+
313+ /**
314+ * Creates key from plain mnemonic
315+ */
316+ #addKeyFromMnemonic( algo : string , mnemonic : string , opts : AddKeyOptions ) {
317+ const mnemonicOpts : SetFromMnemonicOptions = { algo } ;
318+
319+ if ( this . #mnemonicHasPassphrase) {
320+ this . #validatePassphrase( opts . passphrase ) ;
321+ mnemonicOpts . passphrase = opts . passphrase ;
322+ }
323+
324+ this . setFromMnemonic ( this . #mnemonic, mnemonicOpts ) ;
325+ }
326+
327+ /**
328+ * Creates key from existing private key (encrypted or plain)
329+ */
330+ #addKeyFromExistingPrivateKey( algo : string , existingAlgo : string , opts : AddKeyOptions ) {
331+ const encryptedPrivKey = this . #getPrivKeyEncrypted( { algo : existingAlgo } ) ;
332+
333+ if ( encryptedPrivKey ) {
334+ this . #validatePassword( opts . password ) ;
335+ const xPriv = sjcl . decrypt ( opts . password ! , encryptedPrivKey ) ;
308336 this . setFromExtendedPrivateKey ( xPriv , { algo, password : opts . password } ) ;
309- } else if ( this . #getPrivKey( { algo : existingAlgo } ) ) {
310- const xPriv = this . #getPrivKey( { algo : existingAlgo } ) ;
311- this . setFromExtendedPrivateKey ( xPriv , { algo } )
312337 } else {
313- throw new Error ( `Missing Priv Key ${ existingAlgo } ` ) ;
338+ const xPriv = this . #getPrivKey( { algo : existingAlgo } ) ;
339+ this . setFromExtendedPrivateKey ( xPriv , { algo } ) ;
340+ }
341+ }
342+
343+ #hasExistingPrivateKey( existingAlgo : string ) : boolean {
344+ return ! ! ( this . #getPrivKeyEncrypted( { algo : existingAlgo } ) || this . #getPrivKey( { algo : existingAlgo } ) ) ;
345+ }
346+
347+ #validatePassword( password ?: string ) {
348+ if ( ! password ) {
349+ throw new Error ( 'Password is required for encrypted content' ) ;
350+ }
351+ }
352+
353+ #validatePassphrase( passphrase ?: string ) {
354+ if ( ! passphrase ) {
355+ throw new Error ( 'Passphrase is required for mnemonic with passphrase' ) ;
314356 }
315357 }
316358
0 commit comments