Skip to content

Commit 951ba0c

Browse files
committed
add support for array as phrase
1 parent c0506de commit 951ba0c

File tree

6 files changed

+267
-208
lines changed

6 files changed

+267
-208
lines changed

Sources/Web3Core/KeystoreManager/BIP32HDNode.swift

Lines changed: 145 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ extension UInt32 {
2222
}
2323

2424
public class HDNode {
25-
public struct HDversion {
26-
// swiftlint:disable force_unwrapping
27-
public var privatePrefix: Data = Data.fromHex("0x0488ADE4")!
28-
public var publicPrefix: Data = Data.fromHex("0x0488B21E")!
29-
// swiftlint:enable force_unwrapping
30-
public init() {}
25+
26+
private struct HDversion{
27+
public static var privatePrefix: Data? = Data.fromHex("0x0488ADE4")
28+
public static var publicPrefix: Data? = Data.fromHex("0x0488B21E")
29+
3130
}
3231
public var path: String? = "m"
3332
public var privateKey: Data?
@@ -40,11 +39,11 @@ public class HDNode {
4039
childNumber >= (UInt32(1) << 31)
4140
}
4241
public var index: UInt32 {
43-
if self.isHardened {
44-
return childNumber - (UInt32(1) << 31)
45-
} else {
46-
return childNumber
47-
}
42+
if self.isHardened {
43+
return childNumber - (UInt32(1) << 31)
44+
} else {
45+
return childNumber
46+
}
4847
}
4948
public var hasPrivate: Bool {
5049
privateKey != nil
@@ -65,7 +64,7 @@ public class HDNode {
6564
guard data.count == 82 else { return nil }
6665
let header = data[0..<4]
6766
var serializePrivate = false
68-
if header == HDNode.HDversion().privatePrefix {
67+
if header == HDversion.privatePrefix {
6968
serializePrivate = true
7069
}
7170
depth = data[4..<5].bytes[0]
@@ -90,25 +89,25 @@ public class HDNode {
9089

9190
public init?(seed: Data) {
9291
guard seed.count >= 16 else { return nil }
93-
// swiftlint:disable force_unwrapping
94-
let hmacKey = "Bitcoin seed".data(using: .ascii)!
95-
let hmac = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha2(.sha512))
92+
93+
guard let hmacKey = "Bitcoin seed".data(using: .ascii) else { return nil }
94+
let hmac:Authenticator = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha2(.sha512))
95+
9696
guard let entropy = try? hmac.authenticate(seed.bytes), entropy.count == 64 else { return nil }
9797
let I_L = entropy[0..<32]
9898
let I_R = entropy[32..<64]
9999
chaincode = Data(I_R)
100100
let privKeyCandidate = Data(I_L)
101101
guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else { return nil }
102102
guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else { return nil }
103-
guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else { return nil }
103+
guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else { return nil }
104104
publicKey = pubKeyCandidate
105105
privateKey = privKeyCandidate
106106
depth = 0x00
107107
childNumber = UInt32(0)
108108
}
109109

110110
private static var curveOrder = BigUInt("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", radix: 16)!
111-
// swiftlint:enable force_unwrapping
112111
public static var defaultPath: String = "m/44'/60'/0'/0"
113112
public static var defaultPathPrefix: String = "m/44'/60'/0'"
114113
public static var defaultPathMetamask: String = "m/44'/60'/0'/0/0"
@@ -119,183 +118,184 @@ public class HDNode {
119118
extension HDNode {
120119
public func derive(index: UInt32, derivePrivateKey: Bool, hardened: Bool = false) -> HDNode? {
121120
if derivePrivateKey {
122-
return self.derivePrivateKey(index: index, hardened: hardened)
123-
} else {
124-
return derivePublicKey(index: index, hardened: hardened)
121+
return deriveWithPrivateKey(index: index, hardened: hardened)
122+
} else { // deriving only the public key
123+
return deriveWithoutPrivateKey(index: index, hardened: hardened)
125124
}
126125
}
127126

128-
public func derive(path: String, derivePrivateKey: Bool = true) -> HDNode? {
129-
let components = path.components(separatedBy: "/")
130-
var currentNode: HDNode = self
131-
var firstComponent = 0
132-
if path.hasPrefix("m") {
133-
firstComponent = 1
127+
public func deriveWithoutPrivateKey(index: UInt32, hardened: Bool = false) -> HDNode? {
128+
var entropy: [UInt8] // derive public key when is itself public key
129+
if index >= (UInt32(1) << 31) || hardened {
130+
return nil // no derivation of hardened public key from extended public key
131+
} else {
132+
let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
133+
var inputForHMAC = Data()
134+
inputForHMAC.append(self.publicKey)
135+
inputForHMAC.append(index.serialize32())
136+
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
137+
guard ent.count == 64 else { return nil }
138+
entropy = ent
134139
}
135-
for component in components[firstComponent ..< components.count] {
136-
var hardened = false
137-
if component.hasSuffix("'") {
138-
hardened = true
140+
let I_L = entropy[0..<32]
141+
let I_R = entropy[32..<64]
142+
let cc = Data(I_R)
143+
let bn = BigUInt(Data(I_L))
144+
if bn > HDNode.curveOrder {
145+
if index < UInt32.max {
146+
return self.derive(index: index+1, derivePrivateKey: false, hardened: hardened)
139147
}
140-
guard let index = UInt32(component.trimmingCharacters(in: CharacterSet(charactersIn: "'"))) else { return nil }
141-
guard let newNode = currentNode.derive(index: index, derivePrivateKey: derivePrivateKey, hardened: hardened) else { return nil }
142-
currentNode = newNode
148+
return nil
143149
}
144-
return currentNode
150+
guard let tempKey = bn.serialize().setLengthLeft(32) else { return nil }
151+
guard SECP256K1.verifyPrivateKey(privateKey: tempKey) else {return nil }
152+
guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: tempKey, compressed: true) else { return nil }
153+
guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else { return nil }
154+
guard let newPublicKey = SECP256K1.combineSerializedPublicKeys(keys: [self.publicKey, pubKeyCandidate], outputCompressed: true) else { return nil }
155+
guard newPublicKey.bytes.first == 0x02 || newPublicKey.bytes.first == 0x03 else { return nil }
156+
guard self.depth < UInt8.max else { return nil }
157+
let newNode = HDNode()
158+
newNode.chaincode = cc
159+
newNode.depth = self.depth + 1
160+
newNode.publicKey = newPublicKey
161+
newNode.childNumber = index
162+
guard let fprint = try? RIPEMD160.hash(message: self.publicKey.sha256())[0..<4] else {
163+
return nil
164+
}
165+
newNode.parentFingerprint = fprint
166+
var newPath = String()
167+
if newNode.isHardened {
168+
newPath = (self.path ?? "") + "/"
169+
newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'"
170+
} else {
171+
newPath = (self.path ?? "") + "/" + String(newNode.index)
172+
}
173+
newNode.path = newPath
174+
return newNode
145175
}
176+
public func deriveWithPrivateKey(index: UInt32, hardened: Bool = false) -> HDNode? {
146177

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)
178+
guard let privateKey = self.privateKey else {
152179
return nil
153180
}
154-
155-
var trueIndex = index
156-
if trueIndex < (UInt32(1) << 31) && hardened {
157-
trueIndex += (UInt32(1) << 31)
181+
var entropy: [UInt8]
182+
var trueIndex: UInt32
183+
if index >= (UInt32(1) << 31) || hardened {
184+
trueIndex = index
185+
if trueIndex < (UInt32(1) << 31) {
186+
trueIndex = trueIndex + (UInt32(1) << 31)
187+
}
188+
let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
189+
var inputForHMAC = Data()
190+
inputForHMAC.append(Data([UInt8(0x00)]))
191+
inputForHMAC.append(privateKey)
192+
inputForHMAC.append(trueIndex.serialize32())
193+
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
194+
guard ent.count == 64 else { return nil }
195+
entropy = ent
196+
} else {
197+
trueIndex = index
198+
let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
199+
var inputForHMAC = Data()
200+
inputForHMAC.append(self.publicKey)
201+
inputForHMAC.append(trueIndex.serialize32())
202+
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
203+
guard ent.count == 64 else { return nil }
204+
entropy = ent
158205
}
159-
160-
guard let entropy = calculateEntropy(index: trueIndex, privateKey: privateKey, hardened: hardened) else { return nil }
161-
162206
let I_L = entropy[0..<32]
163207
let I_R = entropy[32..<64]
164-
let chainCode = Data(I_R)
208+
let cc = Data(I_R)
165209
let bn = BigUInt(Data(I_L))
166210
if bn > HDNode.curveOrder {
167211
if trueIndex < UInt32.max {
168-
return self.derive(index: index + 1, derivePrivateKey: true, hardened: hardened)
212+
return self.derive(index: index+1, derivePrivateKey: true, hardened: hardened)
169213
}
170214
return nil
171215
}
172-
let newPK = (bn + BigUInt(privateKey)) % HDNode.curveOrder
216+
let newPK = (bn + BigUInt(self.privateKey!)) % HDNode.curveOrder
173217
if newPK == BigUInt(0) {
174218
if trueIndex < UInt32.max {
175-
return self.derive(index: index + 1, derivePrivateKey: true, hardened: hardened)
219+
return self.derive(index: index+1, derivePrivateKey: true, hardened: hardened)
176220
}
177221
return nil
178222
}
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-
}
223+
guard let privKeyCandidate = newPK.serialize().setLengthLeft(32) else { return nil }
224+
guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else {return nil }
225+
guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else { return nil }
226+
guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else { return nil }
227+
guard self.depth < UInt8.max else { return nil }
228+
let newNode = HDNode()
229+
newNode.chaincode = cc
230+
newNode.depth = self.depth + 1
231+
newNode.publicKey = pubKeyCandidate
232+
newNode.privateKey = privKeyCandidate
233+
newNode.childNumber = trueIndex
234+
guard let fprint = try? RIPEMD160.hash(message: self.publicKey.sha256())[0..<4] else {
208235
return nil
209236
}
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 }
235237
newNode.parentFingerprint = fprint
236238
var newPath = String()
237239
if newNode.isHardened {
238-
newPath = path + "/"
240+
newPath = self.path! + "/"
239241
newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'"
240242
} else {
241-
newPath = path + "/" + String(newNode.index)
243+
newPath = self.path! + "/" + String(newNode.index)
242244
}
243245
newNode.path = newPath
244246
return newNode
247+
245248
}
246249

247-
private func calculateHMACInput(_ index: UInt32, privateKey: Data? = nil, hardened: Bool) -> Data {
248-
var inputForHMAC = Data()
250+
public func derive(path: String, derivePrivateKey: Bool = true) -> HDNode? {
249251

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)
252+
let components = path.components(separatedBy: "/")
253+
var currentNode: HDNode = self
254+
var firstComponent = 0
255+
if path.hasPrefix("m") {
256+
firstComponent = 1
255257
}
256-
257-
inputForHMAC.append(index.serialize32())
258-
return inputForHMAC
258+
for component in components[firstComponent ..< components.count] {
259+
var hardened = false
260+
if component.hasSuffix("'") {
261+
hardened = true
262+
}
263+
guard let index = UInt32(component.trimmingCharacters(in: CharacterSet(charactersIn: "'"))) else { return nil }
264+
guard let newNode = currentNode.derive(index: index, derivePrivateKey: derivePrivateKey, hardened: hardened) else { return nil }
265+
currentNode = newNode
266+
}
267+
return currentNode
259268
}
260269

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
270+
public func serializeToString(serializePublic: Bool = true) -> String? {
271+
guard let data = self.serialize(serializePublic: serializePublic) else { return nil }
272+
let encoded = Base58.base58FromBytes(data.bytes)
273+
return encoded
272274
}
273275

274-
public func serializeToString(serializePublic: Bool = true, version: HDversion = HDversion()) -> String? {
275-
guard let data = self.serialize(serializePublic: serializePublic, version: version) else { return nil }
276-
return Base58.base58FromBytes(data.bytes)
277-
}
276+
public func serialize(serializePublic: Bool = true) -> Data? {
278277

279-
public func serialize(serializePublic: Bool = true, version: HDversion = HDversion()) -> Data? {
280278
var data = Data()
281-
/// Public or private key
282-
let keyData: Data
279+
280+
guard serializePublic || privateKey != nil else {
281+
return nil
282+
}
283+
283284
if serializePublic {
284-
keyData = publicKey
285-
data.append(version.publicPrefix)
285+
data.append(HDversion.publicPrefix!)
286286
} else {
287-
guard let privateKey = privateKey else { return nil }
288-
keyData = privateKey
289-
data.append(version.privatePrefix)
287+
data.append(HDversion.privatePrefix!)
290288
}
291-
data.append(contentsOf: [depth])
292-
data.append(parentFingerprint)
293-
data.append(childNumber.serialize32())
294-
data.append(chaincode)
295-
if !serializePublic {
289+
data.append(contentsOf: [self.depth])
290+
data.append(self.parentFingerprint)
291+
data.append(self.childNumber.serialize32())
292+
data.append(self.chaincode)
293+
if serializePublic {
294+
data.append(self.publicKey)
295+
} else {
296296
data.append(contentsOf: [0x00])
297+
data.append(self.privateKey!)
297298
}
298-
data.append(keyData)
299299
let hashedData = data.sha256().sha256()
300300
let checksum = hashedData[0..<4]
301301
data.append(checksum)

0 commit comments

Comments
 (0)