@@ -52,22 +52,48 @@ function mergeToV3ParamsWithDefaults(params?: Partial<V3Params>): V3Params {
52
52
}
53
53
}
54
54
55
- interface KDFParams {
56
- c : number
57
- prf : string
55
+ // KDF params
56
+
57
+ interface ScryptKDFParams {
58
58
dklen : number
59
59
n : number
60
- r : number
61
60
p : number
61
+ r : number
62
+ salt : string
63
+ }
64
+
65
+ interface PBKDFParams {
66
+ c : number
67
+ dklen : number
68
+ prf : string
62
69
salt : string
63
70
}
64
71
72
+ // union of both the PBKDF2 and Scrypt KDF parameters representing all possible
73
+ // parameters the user could supply
74
+ type AllKDFParams = ScryptKDFParams & PBKDFParams
75
+
76
+ type KDFParams = ScryptKDFParams | PBKDFParams
77
+
78
+ function kdfParamsForPBKDF ( params : AllKDFParams ) : PBKDFParams {
79
+ delete params . n
80
+ delete params . p
81
+ delete params . r
82
+ return params
83
+ }
84
+
85
+ function kdfParamsForScrypt ( params : AllKDFParams ) : ScryptKDFParams {
86
+ delete params . c
87
+ delete params . prf
88
+ return params
89
+ }
90
+
65
91
/**
66
92
* Based on the parameter list passed to the Wallet.prototype.toV3() method this
67
93
* returns a list of parameters for running the key derivation function.
68
94
* @param params params passed into the .toV3() method
69
95
*/
70
- function mergeKDFParamsWithDefaults ( params : V3Params ) : KDFParams {
96
+ function mergeKDFParamsWithDefaults ( params : V3Params ) : AllKDFParams {
71
97
const kdfDefaults = {
72
98
c : 262144 ,
73
99
prf : 'hmac-sha256' ,
@@ -84,24 +110,79 @@ function mergeKDFParamsWithDefaults(params: V3Params): KDFParams {
84
110
prf : kdfDefaults . prf ,
85
111
n : params . n || kdfDefaults . n ,
86
112
r : params . r || kdfDefaults . r ,
87
- p : params . p || kdfDefaults . c ,
113
+ p : params . p || kdfDefaults . p ,
88
114
}
89
115
}
90
116
91
- function stripUnusedKDFParamsForPBKDF2 ( params : KDFParams ) : Partial < KDFParams > {
92
- delete params . n
93
- delete params . r
94
- delete params . p
95
- return params
117
+ // JSON keystore types
118
+
119
+ // https://github.com/ethereum/homestead-guide/blob/master/old-docs-for-reference/go-ethereum-wiki.rst/Passphrase-protected-key-store-spec.rst
120
+ interface V1Keystore {
121
+ Address : string
122
+ Crypto : {
123
+ CipherText : string
124
+ IV : string
125
+ KeyHeader : {
126
+ Kdf : string
127
+ KdfParams : {
128
+ DkLen : number
129
+ N : number
130
+ P : number
131
+ R : number
132
+ SaltLen : number
133
+ }
134
+ Version : string
135
+ }
136
+ MAC : string
137
+ Salt : string
138
+ }
139
+ Id : string
140
+ Version : string
96
141
}
97
142
98
- function stripUnusedKDFParamsForScrypt ( params : KDFParams ) : Partial < KDFParams > {
99
- delete params . c
100
- delete params . prf
101
- return params
143
+ // https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
144
+ interface V3Keystore {
145
+ crypto : {
146
+ cipher : string
147
+ cipherparams : {
148
+ iv : string
149
+ }
150
+ ciphertext : string
151
+ kdf : string
152
+ kdfparams : KDFParams
153
+ mac : string
154
+ }
155
+ id : string
156
+ version : number
157
+ }
158
+
159
+ interface EthSaleKeystore {
160
+ encseed : string
161
+ ethaddr : string
162
+ btcaddr : string
163
+ email : string
102
164
}
103
165
104
- class Wallet {
166
+ // wallet implementation
167
+
168
+ export default class Wallet {
169
+ constructor (
170
+ private readonly privateKey ?: Buffer | undefined ,
171
+ private publicKey : Buffer | undefined = undefined ,
172
+ ) {
173
+ if ( privateKey && publicKey ) {
174
+ throw new Error ( 'Cannot supply both a private and a public key to the constructor' )
175
+ }
176
+
177
+ if ( privateKey && ! ethUtil . isValidPrivate ( privateKey ) ) {
178
+ throw new Error ( 'Private key does not satisfy the curve requirements (ie. it is invalid)' )
179
+ }
180
+
181
+ if ( publicKey && ! ethUtil . isValidPublic ( publicKey ) ) {
182
+ throw new Error ( 'Invalid public key' )
183
+ }
184
+ }
185
+
105
186
// static methods
106
187
107
188
public static generate ( icapDirect : boolean = false ) : Wallet {
@@ -164,10 +245,8 @@ class Wallet {
164
245
return Wallet . fromPrivateKey ( tmp . slice ( 46 ) )
165
246
}
166
247
167
- // https://github.com/ethereum/go-ethereum/wiki/Passphrase-protected-key-store-spec
168
- public static fromV1 ( input : string | Object , password : string ) : Wallet {
169
- const json = typeof input === 'object' ? input : JSON . parse ( input )
170
-
248
+ public static fromV1 ( input : string | V1Keystore , password : string ) : Wallet {
249
+ const json : V1Keystore = typeof input === 'object' ? input : JSON . parse ( input )
171
250
if ( json . Version !== '1' ) {
172
251
throw new Error ( 'Not a V1 Wallet' )
173
252
}
@@ -187,7 +266,6 @@ class Wallet {
187
266
188
267
const ciphertext = Buffer . from ( json . Crypto . CipherText , 'hex' )
189
268
const mac = ethUtil . keccak256 ( Buffer . concat ( [ derivedKey . slice ( 16 , 32 ) , ciphertext ] ) )
190
-
191
269
if ( mac . toString ( 'hex' ) !== json . Crypto . MAC ) {
192
270
throw new Error ( 'Key derivation failed - possibly wrong passphrase' )
193
271
}
@@ -198,16 +276,15 @@ class Wallet {
198
276
Buffer . from ( json . Crypto . IV , 'hex' ) ,
199
277
)
200
278
const seed = runCipherBuffer ( decipher , ciphertext )
201
-
202
279
return new Wallet ( seed )
203
280
}
204
281
205
282
public static fromV3 (
206
- input : string | Object ,
283
+ input : string | V3Keystore ,
207
284
password : string ,
208
285
nonStrict : boolean = false ,
209
286
) : Wallet {
210
- const json =
287
+ const json : V3Keystore =
211
288
typeof input === 'object' ? input : JSON . parse ( nonStrict ? input . toLowerCase ( ) : input )
212
289
213
290
if ( json . version !== 3 ) {
@@ -264,8 +341,8 @@ class Wallet {
264
341
* Based on https://github.com/ethereum/pyethsaletool/blob/master/pyethsaletool.py
265
342
* JSON fields: encseed, ethaddr, btcaddr, email
266
343
*/
267
- public static fromEthSale ( input : string | Object , password : string ) : Wallet {
268
- const json = typeof input === 'object' ? input : JSON . parse ( input )
344
+ public static fromEthSale ( input : string | EthSaleKeystore , password : string ) : Wallet {
345
+ const json : EthSaleKeystore = typeof input === 'object' ? input : JSON . parse ( input )
269
346
270
347
const encseed = Buffer . from ( json . encseed , 'hex' )
271
348
@@ -301,26 +378,6 @@ class Wallet {
301
378
return this . privateKey
302
379
}
303
380
304
- constructor (
305
- private readonly privateKey ?: Buffer | undefined ,
306
- private publicKey : Buffer | undefined = undefined ,
307
- ) {
308
- if ( privateKey && publicKey ) {
309
- throw new Error ( 'Cannot supply both a private and a public key to the constructor' )
310
- }
311
-
312
- if ( ! privateKey && ! publicKey ) {
313
- }
314
-
315
- if ( privateKey && ! ethUtil . isValidPrivate ( privateKey ) ) {
316
- throw new Error ( 'Private key does not satisfy the curve requirements (ie. it is invalid)' )
317
- }
318
-
319
- if ( publicKey && ! ethUtil . isValidPublic ( publicKey ) ) {
320
- throw new Error ( 'Invalid public key' )
321
- }
322
- }
323
-
324
381
// public instance methods
325
382
326
383
public getPrivateKey ( ) : Buffer {
@@ -351,15 +408,17 @@ class Wallet {
351
408
return ethUtil . toChecksumAddress ( this . getAddressString ( ) )
352
409
}
353
410
354
- public toV3 ( password : string , opts ?: Partial < V3Params > ) {
411
+ public toV3 ( password : string , opts ?: Partial < V3Params > ) : V3Keystore {
355
412
if ( ! keyExists ( this . privateKey ) ) {
356
413
throw new Error ( 'This is a public key only wallet' )
357
414
}
358
415
359
416
const params = mergeToV3ParamsWithDefaults ( opts )
360
417
const kdfParams = mergeKDFParamsWithDefaults ( params )
361
418
362
- let derivedKey : Buffer , finalKDFParams : Partial < KDFParams >
419
+ let derivedKey : Buffer
420
+ let finalKDFParams : KDFParams
421
+
363
422
if ( params . kdf === 'pbkdf2' ) {
364
423
derivedKey = crypto . pbkdf2Sync (
365
424
Buffer . from ( password ) ,
@@ -368,7 +427,7 @@ class Wallet {
368
427
kdfParams . dklen ,
369
428
'sha256' ,
370
429
)
371
- finalKDFParams = stripUnusedKDFParamsForPBKDF2 ( kdfParams )
430
+ finalKDFParams = kdfParamsForPBKDF ( kdfParams )
372
431
} else if ( params . kdf === 'scrypt' ) {
373
432
// FIXME: support progress reporting callback
374
433
derivedKey = scryptsy (
@@ -379,7 +438,7 @@ class Wallet {
379
438
kdfParams . p ,
380
439
kdfParams . dklen ,
381
440
)
382
- finalKDFParams = stripUnusedKDFParamsForScrypt ( kdfParams )
441
+ finalKDFParams = kdfParamsForScrypt ( kdfParams )
383
442
} else {
384
443
throw new Error ( 'Unsupported kdf' )
385
444
}
@@ -401,6 +460,7 @@ class Wallet {
401
460
return {
402
461
version : 3 ,
403
462
id : uuidv4 ( { random : params . uuid } ) ,
463
+ // @ts -ignore FIXME: official V3 keystore spec omits the address key
404
464
address : this . getAddress ( ) . toString ( 'hex' ) ,
405
465
crypto : {
406
466
ciphertext : ciphertext . toString ( 'hex' ) ,
@@ -442,8 +502,6 @@ function runCipherBuffer(cipher: crypto.Cipher | crypto.Decipher, data: Buffer):
442
502
return Buffer . concat ( [ cipher . update ( data ) , cipher . final ( ) ] )
443
503
}
444
504
445
- function keyExists ( k : Buffer | undefined ) : k is Buffer {
446
- return k !== undefined
505
+ function keyExists ( k : Buffer | undefined | null ) : k is Buffer {
506
+ return k !== undefined && k !== null
447
507
}
448
-
449
- export = Wallet
0 commit comments