@@ -128,121 +128,118 @@ public class HDNode {
128
128
extension HDNode {
129
129
public func derive( index: UInt32 , derivePrivateKey: Bool , hardened: Bool = false ) -> HDNode ? {
130
130
if derivePrivateKey {
131
- return deriveWithPrivateKey ( index: index, hardened: hardened)
132
- } else { // deriving only the public key
133
- return deriveWithoutPrivateKey ( index: index, hardened: hardened)
131
+ return self . derivePrivateKey ( index: index, hardened: hardened)
132
+ } else {
133
+ return derivePublicKey ( index: index, hardened: hardened)
134
134
}
135
135
}
136
136
137
- public func deriveWithoutPrivateKey( index: UInt32 , hardened: Bool = false ) -> HDNode ? {
138
- var entropy : [ UInt8 ] // derive public key when is itself public key
139
- if index >= Self . maxIterationIndex || hardened {
140
- return nil // no derivation of hardened public key from extended public key
141
- } else {
142
- let hmac : Authenticator = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
143
- var inputForHMAC = Data ( )
144
- inputForHMAC. append ( self . publicKey)
145
- inputForHMAC. append ( index. serialize32 ( ) )
146
- guard let ent = try ? hmac. authenticate ( inputForHMAC. bytes) else { return nil }
147
- guard ent. count == 64 else { return nil }
148
- entropy = ent
137
+ public func derive( path: String , derivePrivateKey: Bool = true ) -> HDNode ? {
138
+ let components = path. components ( separatedBy: " / " )
139
+ var currentNode : HDNode = self
140
+ var firstComponent = 0
141
+ if path. hasPrefix ( " m " ) {
142
+ firstComponent = 1
149
143
}
150
- let I_L = entropy [ 0 ..< 32 ]
151
- let I_R = entropy [ 32 ..< 64 ]
152
- let cc = Data ( I_R)
153
- let bn = BigUInt ( Data ( I_L) )
154
- if bn > HDNode . curveOrder {
155
- if index < UInt32 . max {
156
- return self . derive ( index: index+ 1 , derivePrivateKey: false , hardened: hardened)
144
+ for component in components [ firstComponent ..< components. count] {
145
+ var hardened = false
146
+ if component. hasSuffix ( " ' " ) {
147
+ hardened = true
157
148
}
158
- return nil
159
- }
160
- guard let tempKey = bn. serialize ( ) . setLengthLeft ( 32 ) else { return nil }
161
- guard SECP256K1 . verifyPrivateKey ( privateKey: tempKey) else { return nil }
162
- guard let pubKeyCandidate = SECP256K1 . privateToPublic ( privateKey: tempKey, compressed: true ) else { return nil }
163
- guard pubKeyCandidate. bytes. first == 0x02 || pubKeyCandidate. bytes. first == 0x03 else { return nil }
164
- guard let newPublicKey = SECP256K1 . combineSerializedPublicKeys ( keys: [ self . publicKey, pubKeyCandidate] , outputCompressed: true ) else { return nil }
165
- guard newPublicKey. bytes. first == 0x02 || newPublicKey. bytes. first == 0x03 else { return nil }
166
- guard self . depth < UInt8 . max else { return nil }
167
- let newNode = HDNode ( )
168
- newNode. chaincode = cc
169
- newNode. depth = self . depth + 1
170
- newNode. publicKey = newPublicKey
171
- newNode. childNumber = index
172
- guard let fprint = try ? RIPEMD160 . hash ( message: self . publicKey. sha256 ( ) ) [ 0 ..< 4 ] else {
173
- return nil
174
- }
175
- newNode. parentFingerprint = fprint
176
- var newPath = String ( )
177
- if newNode. isHardened {
178
- newPath = ( self . path ?? " " ) + " / "
179
- newPath += String ( newNode. index % HDNode. hardenedIndexPrefix) + " ' "
180
- } else {
181
- newPath = ( self . path ?? " " ) + " / " + String( newNode. index)
149
+ guard let index = UInt32 ( component. trimmingCharacters ( in: CharacterSet ( charactersIn: " ' " ) ) ) else { return nil }
150
+ guard let newNode = currentNode. derive ( index: index, derivePrivateKey: derivePrivateKey, hardened: hardened) else { return nil }
151
+ currentNode = newNode
182
152
}
183
- newNode. path = newPath
184
- return newNode
153
+ return currentNode
185
154
}
186
- public func deriveWithPrivateKey( index: UInt32 , hardened: Bool = false ) -> HDNode ? {
187
155
188
- guard let privateKey = self . privateKey else {
156
+ /// Derive public key when is itself private key.
157
+ /// Derivation of private key when is itself extended public key is impossible and will return `nil`.
158
+ private func derivePrivateKey( index: UInt32 , hardened: Bool ) -> HDNode ? {
159
+ guard let privateKey = privateKey else {
160
+ // derive private key when is itself extended public key (impossible)
189
161
return nil
190
162
}
191
- var entropy : [ UInt8 ]
192
- var trueIndex : UInt32
193
- if index >= Self . maxIterationIndex || hardened {
194
- trueIndex = index
195
- if trueIndex < Self . maxIterationIndex {
196
- trueIndex = trueIndex + Self. maxIterationIndex
197
- }
198
- let hmac : Authenticator = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
199
- var inputForHMAC = Data ( )
200
- inputForHMAC. append ( Data ( [ UInt8 ( 0x00 ) ] ) )
201
- inputForHMAC. append ( privateKey)
202
- inputForHMAC. append ( trueIndex. serialize32 ( ) )
203
- guard let ent = try ? hmac. authenticate ( inputForHMAC. bytes) else { return nil }
204
- guard ent. count == 64 else { return nil }
205
- entropy = ent
206
- } else {
207
- trueIndex = index
208
- let hmac : Authenticator = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
209
- var inputForHMAC = Data ( )
210
- inputForHMAC. append ( self . publicKey)
211
- inputForHMAC. append ( trueIndex. serialize32 ( ) )
212
- guard let ent = try ? hmac. authenticate ( inputForHMAC. bytes) else { return nil }
213
- guard ent. count == 64 else { return nil }
214
- entropy = ent
163
+
164
+ var trueIndex = index
165
+ if trueIndex < ( UInt32 ( 1 ) << 31 ) && hardened {
166
+ trueIndex += ( UInt32 ( 1 ) << 31 )
215
167
}
168
+
169
+ guard let entropy = calculateEntropy ( index: trueIndex, privateKey: privateKey, hardened: hardened) else { return nil }
170
+
216
171
let I_L = entropy [ 0 ..< 32 ]
217
172
let I_R = entropy [ 32 ..< 64 ]
218
- let cc = Data ( I_R)
173
+ let chainCode = Data ( I_R)
219
174
let bn = BigUInt ( Data ( I_L) )
220
175
if bn > HDNode . curveOrder {
221
176
if trueIndex < UInt32 . max {
222
- return self . derive ( index: index+ 1 , derivePrivateKey: true , hardened: hardened)
177
+ return self . derive ( index: index + 1 , derivePrivateKey: true , hardened: hardened)
223
178
}
224
179
return nil
225
180
}
226
- let newPK = ( bn + BigUInt( self . privateKey! ) ) % HDNode. curveOrder
181
+ let newPK = ( bn + BigUInt( privateKey) ) % HDNode. curveOrder
227
182
if newPK == BigUInt ( 0 ) {
228
183
if trueIndex < UInt32 . max {
229
- return self . derive ( index: index+ 1 , derivePrivateKey: true , hardened: hardened)
184
+ return self . derive ( index: index + 1 , derivePrivateKey: true , hardened: hardened)
230
185
}
231
186
return nil
232
187
}
233
- guard let privKeyCandidate = newPK. serialize ( ) . setLengthLeft ( 32 ) else { return nil }
234
- guard SECP256K1 . verifyPrivateKey ( privateKey: privKeyCandidate) else { return nil }
235
- guard let pubKeyCandidate = SECP256K1 . privateToPublic ( privateKey: privKeyCandidate, compressed: true ) else { return nil }
236
- guard pubKeyCandidate. bytes [ 0 ] == 0x02 || pubKeyCandidate. bytes [ 0 ] == 0x03 else { return nil }
237
- guard self . depth < UInt8 . max else { return nil }
188
+
189
+ guard
190
+ let newPrivateKey = newPK. serialize ( ) . setLengthLeft ( 32 ) ,
191
+ SECP256K1 . verifyPrivateKey ( privateKey: newPrivateKey) ,
192
+ let newPublicKey = SECP256K1 . privateToPublic ( privateKey: newPrivateKey, compressed: true ) ,
193
+ ( newPublicKey. bytes [ 0 ] == 0x02 || newPublicKey. bytes [ 0 ] == 0x03 ) ,
194
+ self . depth < UInt8 . max
195
+ else { return nil }
196
+ return createNode ( chainCode: chainCode, depth: depth + 1 , publicKey: newPublicKey, privateKey: newPrivateKey, childNumber: trueIndex)
197
+ }
198
+
199
+ /// Derive public key when is itself public key.
200
+ /// No derivation of hardened public key from extended public key is allowed.
201
+ private func derivePublicKey( index: UInt32 , hardened: Bool ) -> HDNode ? {
202
+ if index >= ( UInt32 ( 1 ) << 31 ) || hardened {
203
+ // no derivation of hardened public key from extended public key
204
+ return nil
205
+ }
206
+
207
+ guard let entropy = calculateEntropy ( index: index, hardened: hardened) else { return nil }
208
+
209
+ let I_L = entropy [ 0 ..< 32 ]
210
+ let I_R = entropy [ 32 ..< 64 ]
211
+ let chainCode = Data ( I_R)
212
+ let bn = BigUInt ( Data ( I_L) )
213
+ if bn > HDNode . curveOrder {
214
+ if index < UInt32 . max {
215
+ return self . derive ( index: index+ 1 , derivePrivateKey: false , hardened: hardened)
216
+ }
217
+ return nil
218
+ }
219
+
220
+ guard
221
+ let tempKey = bn. serialize ( ) . setLengthLeft ( 32 ) ,
222
+ SECP256K1 . verifyPrivateKey ( privateKey: tempKey) ,
223
+ let pubKeyCandidate = SECP256K1 . privateToPublic ( privateKey: tempKey, compressed: true ) ,
224
+ ( pubKeyCandidate. bytes [ 0 ] == 0x02 || pubKeyCandidate. bytes [ 0 ] == 0x03 ) ,
225
+ let newPublicKey = SECP256K1 . combineSerializedPublicKeys ( keys: [ self . publicKey, pubKeyCandidate] , outputCompressed: true ) ,
226
+ ( newPublicKey. bytes [ 0 ] == 0x02 || newPublicKey. bytes [ 0 ] == 0x03 ) ,
227
+ self . depth < UInt8 . max
228
+ else { return nil }
229
+
230
+ return createNode ( chainCode: chainCode, depth: depth + 1 , publicKey: newPublicKey, childNumber: index)
231
+ }
232
+
233
+ private func createNode( chainCode: Data , depth: UInt8 , publicKey: Data , privateKey: Data ? = nil , childNumber: UInt32 ) -> HDNode ? {
238
234
let newNode = HDNode ( )
239
- newNode. chaincode = cc
240
- newNode. depth = self . depth + 1
241
- newNode. publicKey = pubKeyCandidate
242
- newNode. privateKey = privKeyCandidate
243
- newNode. childNumber = trueIndex
244
- guard let fprint = try ? RIPEMD160 . hash ( message: self . publicKey. sha256 ( ) ) [ 0 ..< 4 ] ,
245
- let path = path
235
+ newNode. chaincode = chainCode
236
+ newNode. depth = depth
237
+ newNode. publicKey = publicKey
238
+ newNode. privateKey = privateKey
239
+ newNode. childNumber = childNumber
240
+ guard
241
+ let fprint = try ? RIPEMD160 . hash ( message: self . publicKey. sha256 ( ) ) [ 0 ..< 4 ] ,
242
+ let path = path
246
243
else { return nil }
247
244
newNode. parentFingerprint = fprint
248
245
var newPath = String ( )
@@ -254,58 +251,60 @@ extension HDNode {
254
251
}
255
252
newNode. path = newPath
256
253
return newNode
257
-
258
254
}
259
255
260
- public func derive( path: String , derivePrivateKey: Bool = true ) -> HDNode ? {
256
+ private func calculateHMACInput( _ index: UInt32 , privateKey: Data ? = nil , hardened: Bool ) -> Data {
257
+ var inputForHMAC = Data ( )
261
258
262
- let components = path. components ( separatedBy: " / " )
263
- var currentNode : HDNode = self
264
- var firstComponent = 0
265
- if path. hasPrefix ( " m " ) {
266
- firstComponent = 1
267
- }
268
- for component in components [ firstComponent ..< components. count] {
269
- var hardened = false
270
- if component. hasSuffix ( " ' " ) {
271
- hardened = true
272
- }
273
- guard let index = UInt32 ( component. trimmingCharacters ( in: CharacterSet ( charactersIn: " ' " ) ) ) else { return nil }
274
- guard let newNode = currentNode. derive ( index: index, derivePrivateKey: derivePrivateKey, hardened: hardened) else { return nil }
275
- currentNode = newNode
259
+ if let privateKey = privateKey, ( index >= ( UInt32 ( 1 ) << 31 ) || hardened) {
260
+ inputForHMAC. append ( Data ( [ UInt8 ( 0x00 ) ] ) )
261
+ inputForHMAC. append ( privateKey)
262
+ } else {
263
+ inputForHMAC. append ( self . publicKey)
276
264
}
277
- return currentNode
265
+
266
+ inputForHMAC. append ( index. serialize32 ( ) )
267
+ return inputForHMAC
278
268
}
279
269
280
- public func serializeToString( serializePublic: Bool = true ) -> String ? {
281
- guard let data = self . serialize ( serializePublic: serializePublic) else { return nil }
282
- let encoded = Base58 . base58FromBytes ( data. bytes)
283
- return encoded
270
+ /// Calculates entropy used for private or public key derivation.
271
+ /// - Parameters:
272
+ /// - index: index
273
+ /// - privateKey: private key data or `nil` if entropy is calculated for a public key;
274
+ /// - hardened: is hardened key
275
+ /// - Returns: 64 bytes entropy or `nil`.
276
+ private func calculateEntropy( index: UInt32 , privateKey: Data ? = nil , hardened: Bool ) -> [ UInt8 ] ? {
277
+ let inputForHMAC = calculateHMACInput ( index, privateKey: privateKey, hardened: hardened)
278
+ let hmac = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
279
+ guard let entropy = try ? hmac. authenticate ( inputForHMAC. bytes) , entropy. count == 64 else { return nil }
280
+ return entropy
284
281
}
285
282
286
- public func serialize( serializePublic: Bool = true ) -> Data ? {
283
+ public func serializeToString( serializePublic: Bool = true , version: HDversion = HDversion ( ) ) -> String ? {
284
+ guard let data = self . serialize ( serializePublic: serializePublic, version: version) else { return nil }
285
+ return Base58 . base58FromBytes ( data. bytes)
286
+ }
287
287
288
+ public func serialize( serializePublic: Bool = true , version: HDversion = HDversion ( ) ) -> Data ? {
288
289
var data = Data ( )
289
-
290
- guard serializePublic || privateKey != nil else {
291
- return nil
292
- }
293
-
290
+ /// Public or private key
291
+ let keyData : Data
294
292
if serializePublic {
295
- data. append ( HDversion . publicPrefix)
293
+ keyData = publicKey
294
+ data. append ( version. publicPrefix)
296
295
} else {
297
- data. append ( HDversion . privatePrefix)
296
+ guard let privateKey = privateKey else { return nil }
297
+ keyData = privateKey
298
+ data. append ( version. privatePrefix)
298
299
}
299
- data. append ( contentsOf: [ self . depth] )
300
- data. append ( self . parentFingerprint)
301
- data. append ( self . childNumber. serialize32 ( ) )
302
- data. append ( self . chaincode)
303
- if serializePublic {
304
- data. append ( self . publicKey)
305
- } else {
300
+ data. append ( contentsOf: [ depth] )
301
+ data. append ( parentFingerprint)
302
+ data. append ( childNumber. serialize32 ( ) )
303
+ data. append ( chaincode)
304
+ if !serializePublic {
306
305
data. append ( contentsOf: [ 0x00 ] )
307
- data. append ( self . privateKey!)
308
306
}
307
+ data. append ( keyData)
309
308
let hashedData = data. sha256 ( ) . sha256 ( )
310
309
let checksum = hashedData [ 0 ..< 4 ]
311
310
data. append ( checksum)
0 commit comments