@@ -9,8 +9,7 @@ import CryptoSwift
9
9
10
10
extension UInt32 {
11
11
public func serialize32( ) -> Data {
12
- let uint32 = UInt32 ( self )
13
- var bigEndian = uint32. bigEndian
12
+ var bigEndian = self . bigEndian
14
13
let count = MemoryLayout< UInt32> . size
15
14
let bytePtr = withUnsafePointer ( to: & bigEndian) {
16
15
$0. withMemoryRebound ( to: UInt8 . self, capacity: count) {
@@ -24,11 +23,11 @@ extension UInt32 {
24
23
25
24
public class HDNode {
26
25
public struct HDversion {
26
+ // swiftlint:disable force_unwrapping
27
27
public var privatePrefix : Data = Data . fromHex ( " 0x0488ADE4 " ) !
28
28
public var publicPrefix : Data = Data . fromHex ( " 0x0488B21E " ) !
29
- public init ( ) {
30
-
31
- }
29
+ // swiftlint:enable force_unwrapping
30
+ public init ( ) { }
32
31
}
33
32
public var path : String ? = " m "
34
33
public var privateKey : Data ?
@@ -38,23 +37,17 @@ public class HDNode {
38
37
public var parentFingerprint : Data = Data ( repeating: 0 , count: 4 )
39
38
public var childNumber : UInt32 = UInt32 ( 0 )
40
39
public var isHardened : Bool {
41
- get {
42
- return self . childNumber >= ( UInt32 ( 1 ) << 31 )
43
- }
40
+ childNumber >= ( UInt32 ( 1 ) << 31 )
44
41
}
45
42
public var index : UInt32 {
46
- get {
47
43
if self . isHardened {
48
- return self . childNumber - ( UInt32 ( 1 ) << 31 )
44
+ return childNumber - ( UInt32 ( 1 ) << 31 )
49
45
} else {
50
- return self . childNumber
46
+ return childNumber
51
47
}
52
- }
53
48
}
54
49
public var hasPrivate : Bool {
55
- get {
56
- return privateKey != nil
57
- }
50
+ privateKey != nil
58
51
}
59
52
60
53
init ( ) {
@@ -81,8 +74,11 @@ public class HDNode {
81
74
chaincode = data [ 13 ..< 45 ]
82
75
if serializePrivate {
83
76
privateKey = data [ 46 ..< 78 ]
84
- guard let pubKey = Utilities . privateToPublic ( privateKey!, compressed: true ) else { return nil }
85
- if pubKey [ 0 ] != 0x02 && pubKey [ 0 ] != 0x03 { return nil }
77
+ guard
78
+ let privateKey = privateKey,
79
+ let pubKey = Utilities . privateToPublic ( privateKey, compressed: true ) ,
80
+ ( pubKey [ 0 ] == 0x02 || pubKey [ 0 ] == 0x03 )
81
+ else { return nil }
86
82
publicKey = pubKey
87
83
} else {
88
84
publicKey = data [ 45 ..< 78 ]
@@ -94,10 +90,10 @@ public class HDNode {
94
90
95
91
public init ? ( seed: Data ) {
96
92
guard seed. count >= 16 else { return nil }
93
+ // swiftlint:disable force_unwrapping
97
94
let hmacKey = " Bitcoin seed " . data ( using: . ascii) !
98
- let hmac : Authenticator = HMAC ( key: hmacKey. bytes, variant: HMAC . Variant. sha2 ( . sha512) )
99
- guard let entropy = try ? hmac. authenticate ( seed. bytes) else { return nil }
100
- guard entropy. count == 64 else { return nil }
95
+ let hmac = HMAC ( key: hmacKey. bytes, variant: HMAC . Variant. sha2 ( . sha512) )
96
+ guard let entropy = try ? hmac. authenticate ( seed. bytes) , entropy. count == 64 else { return nil }
101
97
let I_L = entropy [ 0 ..< 32 ]
102
98
let I_R = entropy [ 32 ..< 64 ]
103
99
chaincode = Data ( I_R)
@@ -112,6 +108,7 @@ public class HDNode {
112
108
}
113
109
114
110
private static var curveOrder = BigUInt ( " FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 " , radix: 16 ) !
111
+ // swiftlint:enable force_unwrapping
115
112
public static var defaultPath : String = " m/44'/60'/0'/0 "
116
113
public static var defaultPathPrefix : String = " m/44'/60'/0' "
117
114
public static var defaultPathMetamask : String = " m/44'/60'/0'/0/0 "
@@ -120,130 +117,15 @@ public class HDNode {
120
117
}
121
118
122
119
extension HDNode {
123
- public func derive ( index: UInt32 , derivePrivateKey: Bool , hardened: Bool = false ) -> HDNode ? {
120
+ public func derive( index: UInt32 , derivePrivateKey: Bool , hardened: Bool = false ) -> HDNode ? {
124
121
if derivePrivateKey {
125
- if self . hasPrivate { // derive private key when is itself extended private key
126
- var entropy : [ UInt8 ]
127
- var trueIndex : UInt32
128
- if index >= ( UInt32 ( 1 ) << 31 ) || hardened {
129
- trueIndex = index
130
- if trueIndex < ( UInt32 ( 1 ) << 31 ) {
131
- trueIndex = trueIndex + ( UInt32 ( 1 ) << 31 )
132
- }
133
- let hmac : Authenticator = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
134
- var inputForHMAC = Data ( )
135
- inputForHMAC. append ( Data ( [ UInt8 ( 0x00 ) ] ) )
136
- inputForHMAC. append ( self . privateKey!)
137
- inputForHMAC. append ( trueIndex. serialize32 ( ) )
138
- guard let ent = try ? hmac. authenticate ( inputForHMAC. bytes) else { return nil }
139
- guard ent. count == 64 else { return nil }
140
- entropy = ent
141
- } else {
142
- trueIndex = index
143
- let hmac : Authenticator = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
144
- var inputForHMAC = Data ( )
145
- inputForHMAC. append ( self . publicKey)
146
- inputForHMAC. append ( trueIndex. serialize32 ( ) )
147
- guard let ent = try ? hmac. authenticate ( inputForHMAC. bytes) else { return nil }
148
- guard ent. count == 64 else { return nil }
149
- entropy = ent
150
- }
151
- let I_L = entropy [ 0 ..< 32 ]
152
- let I_R = entropy [ 32 ..< 64 ]
153
- let cc = Data ( I_R)
154
- let bn = BigUInt ( Data ( I_L) )
155
- if bn > HDNode . curveOrder {
156
- if trueIndex < UInt32 . max {
157
- return self . derive ( index: index+ 1 , derivePrivateKey: derivePrivateKey, hardened: hardened)
158
- }
159
- return nil
160
- }
161
- let newPK = ( bn + BigUInt( self . privateKey!) ) % HDNode. curveOrder
162
- if newPK == BigUInt ( 0 ) {
163
- if trueIndex < UInt32 . max {
164
- return self . derive ( index: index+ 1 , derivePrivateKey: derivePrivateKey, hardened: hardened)
165
- }
166
- return nil
167
- }
168
- guard let privKeyCandidate = newPK. serialize ( ) . setLengthLeft ( 32 ) else { return nil }
169
- guard SECP256K1 . verifyPrivateKey ( privateKey: privKeyCandidate) else { return nil }
170
- guard let pubKeyCandidate = SECP256K1 . privateToPublic ( privateKey: privKeyCandidate, compressed: true ) else { return nil }
171
- guard pubKeyCandidate. bytes [ 0 ] == 0x02 || pubKeyCandidate. bytes [ 0 ] == 0x03 else { return nil }
172
- guard self . depth < UInt8 . max else { return nil }
173
- let newNode = HDNode ( )
174
- newNode. chaincode = cc
175
- newNode. depth = self . depth + 1
176
- newNode. publicKey = pubKeyCandidate
177
- newNode. privateKey = privKeyCandidate
178
- newNode. childNumber = trueIndex
179
- guard let fprint = try ? RIPEMD160 . hash ( message: self . publicKey. sha256 ( ) ) [ 0 ..< 4 ] else {
180
- return nil
181
- }
182
- newNode. parentFingerprint = fprint
183
- var newPath = String ( )
184
- if newNode. isHardened {
185
- newPath = self . path! + " / "
186
- newPath += String ( newNode. index % HDNode. hardenedIndexPrefix) + " ' "
187
- } else {
188
- newPath = self . path! + " / " + String( newNode. index)
189
- }
190
- newNode. path = newPath
191
- return newNode
192
- } else {
193
- return nil // derive private key when is itself extended public key (impossible)
194
- }
195
- } else { // deriving only the public key
196
- var entropy : [ UInt8 ] // derive public key when is itself public key
197
- if index >= ( UInt32 ( 1 ) << 31 ) || hardened {
198
- return nil // no derivation of hardened public key from extended public key
199
- } else {
200
- let hmac : Authenticator = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
201
- var inputForHMAC = Data ( )
202
- inputForHMAC. append ( self . publicKey)
203
- inputForHMAC. append ( index. serialize32 ( ) )
204
- guard let ent = try ? hmac. authenticate ( inputForHMAC. bytes) else { return nil }
205
- guard ent. count == 64 else { return nil }
206
- entropy = ent
207
- }
208
- let I_L = entropy [ 0 ..< 32 ]
209
- let I_R = entropy [ 32 ..< 64 ]
210
- let cc = Data ( I_R)
211
- let bn = BigUInt ( Data ( I_L) )
212
- if bn > HDNode . curveOrder {
213
- if index < UInt32 . max {
214
- return self . derive ( index: index+ 1 , derivePrivateKey: derivePrivateKey, hardened: hardened)
215
- }
216
- return nil
217
- }
218
- guard let tempKey = bn. serialize ( ) . setLengthLeft ( 32 ) else { return nil }
219
- guard SECP256K1 . verifyPrivateKey ( privateKey: tempKey) else { return nil }
220
- guard let pubKeyCandidate = SECP256K1 . privateToPublic ( privateKey: tempKey, compressed: true ) else { return nil }
221
- guard pubKeyCandidate. bytes [ 0 ] == 0x02 || pubKeyCandidate. bytes [ 0 ] == 0x03 else { return nil }
222
- guard let newPublicKey = SECP256K1 . combineSerializedPublicKeys ( keys: [ self . publicKey, pubKeyCandidate] , outputCompressed: true ) else { return nil }
223
- guard newPublicKey. bytes [ 0 ] == 0x02 || newPublicKey. bytes [ 0 ] == 0x03 else { return nil }
224
- guard self . depth < UInt8 . max else { return nil }
225
- let newNode = HDNode ( )
226
- newNode. chaincode = cc
227
- newNode. depth = self . depth + 1
228
- newNode. publicKey = newPublicKey
229
- newNode. childNumber = index
230
- guard let fprint = try ? RIPEMD160 . hash ( message: self . publicKey. sha256 ( ) ) [ 0 ..< 4 ] else {
231
- return nil
232
- }
233
- newNode. parentFingerprint = fprint
234
- var newPath = String ( )
235
- if newNode. isHardened {
236
- newPath = self . path! + " / "
237
- newPath += String ( newNode. index % HDNode. hardenedIndexPrefix) + " ' "
238
- } else {
239
- newPath = self . path! + " / " + String( newNode. index)
240
- }
241
- newNode. path = newPath
242
- return newNode
122
+ return self . derivePrivateKey ( index: index, hardened: hardened)
123
+ } else {
124
+ return derivePublicKey ( index: index, hardened: hardened)
243
125
}
244
126
}
245
127
246
- public func derive ( path: String , derivePrivateKey: Bool = true ) -> HDNode ? {
128
+ public func derive( path: String , derivePrivateKey: Bool = true ) -> HDNode ? {
247
129
let components = path. components ( separatedBy: " / " )
248
130
var currentNode : HDNode = self
249
131
var firstComponent = 0
@@ -262,30 +144,158 @@ extension HDNode {
262
144
return currentNode
263
145
}
264
146
147
+ /// Derive public key when is itself private key.
148
+ /// Derivation of private key when is itself extended public key is impossible and will return `nil`.
149
+ private func derivePrivateKey( index: UInt32 , hardened: Bool ) -> HDNode ? {
150
+ guard let privateKey = privateKey else {
151
+ // derive private key when is itself extended public key (impossible)
152
+ return nil
153
+ }
154
+
155
+ var trueIndex = index
156
+ if trueIndex < ( UInt32 ( 1 ) << 31 ) && hardened {
157
+ trueIndex += ( UInt32 ( 1 ) << 31 )
158
+ }
159
+
160
+ guard let entropy = calculateEntropy ( index: trueIndex, privateKey: privateKey, hardened: hardened) else { return nil }
161
+
162
+ let I_L = entropy [ 0 ..< 32 ]
163
+ let I_R = entropy [ 32 ..< 64 ]
164
+ let chainCode = Data ( I_R)
165
+ let bn = BigUInt ( Data ( I_L) )
166
+ if bn > HDNode . curveOrder {
167
+ if trueIndex < UInt32 . max {
168
+ return self . derive ( index: index + 1 , derivePrivateKey: true , hardened: hardened)
169
+ }
170
+ return nil
171
+ }
172
+ let newPK = ( bn + BigUInt( privateKey) ) % HDNode. curveOrder
173
+ if newPK == BigUInt ( 0 ) {
174
+ if trueIndex < UInt32 . max {
175
+ return self . derive ( index: index + 1 , derivePrivateKey: true , hardened: hardened)
176
+ }
177
+ return nil
178
+ }
179
+
180
+ guard
181
+ let newPrivateKey = newPK. serialize ( ) . setLengthLeft ( 32 ) ,
182
+ SECP256K1 . verifyPrivateKey ( privateKey: newPrivateKey) ,
183
+ let newPublicKey = SECP256K1 . privateToPublic ( privateKey: newPrivateKey, compressed: true ) ,
184
+ ( newPublicKey. bytes [ 0 ] == 0x02 || newPublicKey. bytes [ 0 ] == 0x03 ) ,
185
+ self . depth < UInt8 . max
186
+ else { return nil }
187
+ return createNode ( chainCode: chainCode, depth: depth + 1 , publicKey: newPublicKey, privateKey: newPrivateKey, childNumber: trueIndex)
188
+ }
189
+
190
+ /// Derive public key when is itself public key.
191
+ /// No derivation of hardened public key from extended public key is allowed.
192
+ private func derivePublicKey( index: UInt32 , hardened: Bool ) -> HDNode ? {
193
+ if index >= ( UInt32 ( 1 ) << 31 ) || hardened {
194
+ // no derivation of hardened public key from extended public key
195
+ return nil
196
+ }
197
+
198
+ guard let entropy = calculateEntropy ( index: index, hardened: hardened) else { return nil }
199
+
200
+ let I_L = entropy [ 0 ..< 32 ]
201
+ let I_R = entropy [ 32 ..< 64 ]
202
+ let chainCode = Data ( I_R)
203
+ let bn = BigUInt ( Data ( I_L) )
204
+ if bn > HDNode . curveOrder {
205
+ if index < UInt32 . max {
206
+ return self . derive ( index: index+ 1 , derivePrivateKey: false , hardened: hardened)
207
+ }
208
+ return nil
209
+ }
210
+
211
+ guard
212
+ let tempKey = bn. serialize ( ) . setLengthLeft ( 32 ) ,
213
+ SECP256K1 . verifyPrivateKey ( privateKey: tempKey) ,
214
+ let pubKeyCandidate = SECP256K1 . privateToPublic ( privateKey: tempKey, compressed: true ) ,
215
+ ( pubKeyCandidate. bytes [ 0 ] == 0x02 || pubKeyCandidate. bytes [ 0 ] == 0x03 ) ,
216
+ let newPublicKey = SECP256K1 . combineSerializedPublicKeys ( keys: [ self . publicKey, pubKeyCandidate] , outputCompressed: true ) ,
217
+ ( newPublicKey. bytes [ 0 ] == 0x02 || newPublicKey. bytes [ 0 ] == 0x03 ) ,
218
+ self . depth < UInt8 . max
219
+ else { return nil }
220
+
221
+ return createNode ( chainCode: chainCode, depth: depth + 1 , publicKey: newPublicKey, childNumber: index)
222
+ }
223
+
224
+ private func createNode( chainCode: Data , depth: UInt8 , publicKey: Data , privateKey: Data ? = nil , childNumber: UInt32 ) -> HDNode ? {
225
+ let newNode = HDNode ( )
226
+ newNode. chaincode = chainCode
227
+ newNode. depth = depth
228
+ newNode. publicKey = publicKey
229
+ newNode. privateKey = privateKey
230
+ newNode. childNumber = childNumber
231
+ guard
232
+ let fprint = try ? RIPEMD160 . hash ( message: self . publicKey. sha256 ( ) ) [ 0 ..< 4 ] ,
233
+ let path = path
234
+ else { return nil }
235
+ newNode. parentFingerprint = fprint
236
+ var newPath = String ( )
237
+ if newNode. isHardened {
238
+ newPath = path + " / "
239
+ newPath += String ( newNode. index % HDNode. hardenedIndexPrefix) + " ' "
240
+ } else {
241
+ newPath = path + " / " + String( newNode. index)
242
+ }
243
+ newNode. path = newPath
244
+ return newNode
245
+ }
246
+
247
+ private func calculateHMACInput( _ index: UInt32 , privateKey: Data ? = nil , hardened: Bool ) -> Data {
248
+ var inputForHMAC = Data ( )
249
+
250
+ if let privateKey = privateKey, ( index >= ( UInt32 ( 1 ) << 31 ) || hardened) {
251
+ inputForHMAC. append ( Data ( [ UInt8 ( 0x00 ) ] ) )
252
+ inputForHMAC. append ( privateKey)
253
+ } else {
254
+ inputForHMAC. append ( self . publicKey)
255
+ }
256
+
257
+ inputForHMAC. append ( index. serialize32 ( ) )
258
+ return inputForHMAC
259
+ }
260
+
261
+ /// Calculates entropy used for private or public key derivation.
262
+ /// - Parameters:
263
+ /// - index: index
264
+ /// - privateKey: private key data or `nil` if entropy is calculated for a public key;
265
+ /// - hardened: is hardened key
266
+ /// - Returns: 64 bytes entropy or `nil`.
267
+ private func calculateEntropy( index: UInt32 , privateKey: Data ? = nil , hardened: Bool ) -> [ UInt8 ] ? {
268
+ let inputForHMAC = calculateHMACInput ( index, privateKey: privateKey, hardened: hardened)
269
+ let hmac = HMAC ( key: self . chaincode. bytes, variant: . sha2( . sha512) )
270
+ guard let entropy = try ? hmac. authenticate ( inputForHMAC. bytes) , entropy. count == 64 else { return nil }
271
+ return entropy
272
+ }
273
+
265
274
public func serializeToString( serializePublic: Bool = true , version: HDversion = HDversion ( ) ) -> String ? {
266
275
guard let data = self . serialize ( serializePublic: serializePublic, version: version) else { return nil }
267
- let encoded = Base58 . base58FromBytes ( data. bytes)
268
- return encoded
276
+ return Base58 . base58FromBytes ( data. bytes)
269
277
}
270
278
271
279
public func serialize( serializePublic: Bool = true , version: HDversion = HDversion ( ) ) -> Data ? {
272
280
var data = Data ( )
273
- if !serializePublic && !self . hasPrivate { return nil }
281
+ /// Public or private key
282
+ let keyData : Data
274
283
if serializePublic {
284
+ keyData = publicKey
275
285
data. append ( version. publicPrefix)
276
286
} else {
287
+ guard let privateKey = privateKey else { return nil }
288
+ keyData = privateKey
277
289
data. append ( version. privatePrefix)
278
290
}
279
- data. append ( contentsOf: [ self . depth] )
280
- data. append ( self . parentFingerprint)
281
- data. append ( self . childNumber. serialize32 ( ) )
282
- data. append ( self . chaincode)
283
- if serializePublic {
284
- data. append ( self . publicKey)
285
- } else {
291
+ data. append ( contentsOf: [ depth] )
292
+ data. append ( parentFingerprint)
293
+ data. append ( childNumber. serialize32 ( ) )
294
+ data. append ( chaincode)
295
+ if !serializePublic {
286
296
data. append ( contentsOf: [ 0x00 ] )
287
- data. append ( self . privateKey!)
288
297
}
298
+ data. append ( keyData)
289
299
let hashedData = data. sha256 ( ) . sha256 ( )
290
300
let checksum = hashedData [ 0 ..< 4 ]
291
301
data. append ( checksum)
0 commit comments