Skip to content

Commit 92db962

Browse files
Merge pull request #759 from JeneaVranceanu/chore/bip32hdnode/swift-lint-cleanup
chore: BIP32HDNode - SwiftLint clean up
2 parents 7f8230e + 892959f commit 92db962

File tree

1 file changed

+161
-151
lines changed

1 file changed

+161
-151
lines changed

Sources/Web3Core/KeystoreManager/BIP32HDNode.swift

100755100644
Lines changed: 161 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import CryptoSwift
99

1010
extension UInt32 {
1111
public func serialize32() -> Data {
12-
let uint32 = UInt32(self)
13-
var bigEndian = uint32.bigEndian
12+
var bigEndian = self.bigEndian
1413
let count = MemoryLayout<UInt32>.size
1514
let bytePtr = withUnsafePointer(to: &bigEndian) {
1615
$0.withMemoryRebound(to: UInt8.self, capacity: count) {
@@ -24,11 +23,11 @@ extension UInt32 {
2423

2524
public class HDNode {
2625
public struct HDversion {
26+
// swiftlint:disable force_unwrapping
2727
public var privatePrefix: Data = Data.fromHex("0x0488ADE4")!
2828
public var publicPrefix: Data = Data.fromHex("0x0488B21E")!
29-
public init() {
30-
31-
}
29+
// swiftlint:enable force_unwrapping
30+
public init() {}
3231
}
3332
public var path: String? = "m"
3433
public var privateKey: Data?
@@ -38,23 +37,17 @@ public class HDNode {
3837
public var parentFingerprint: Data = Data(repeating: 0, count: 4)
3938
public var childNumber: UInt32 = UInt32(0)
4039
public var isHardened: Bool {
41-
get {
42-
return self.childNumber >= (UInt32(1) << 31)
43-
}
40+
childNumber >= (UInt32(1) << 31)
4441
}
4542
public var index: UInt32 {
46-
get {
4743
if self.isHardened {
48-
return self.childNumber - (UInt32(1) << 31)
44+
return childNumber - (UInt32(1) << 31)
4945
} else {
50-
return self.childNumber
46+
return childNumber
5147
}
52-
}
5348
}
5449
public var hasPrivate: Bool {
55-
get {
56-
return privateKey != nil
57-
}
50+
privateKey != nil
5851
}
5952

6053
init() {
@@ -81,8 +74,11 @@ public class HDNode {
8174
chaincode = data[13..<45]
8275
if serializePrivate {
8376
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 }
8682
publicKey = pubKey
8783
} else {
8884
publicKey = data[45..<78]
@@ -94,10 +90,10 @@ public class HDNode {
9490

9591
public init?(seed: Data) {
9692
guard seed.count >= 16 else { return nil }
93+
// swiftlint:disable force_unwrapping
9794
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 }
10197
let I_L = entropy[0..<32]
10298
let I_R = entropy[32..<64]
10399
chaincode = Data(I_R)
@@ -112,6 +108,7 @@ public class HDNode {
112108
}
113109

114110
private static var curveOrder = BigUInt("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", radix: 16)!
111+
// swiftlint:enable force_unwrapping
115112
public static var defaultPath: String = "m/44'/60'/0'/0"
116113
public static var defaultPathPrefix: String = "m/44'/60'/0'"
117114
public static var defaultPathMetamask: String = "m/44'/60'/0'/0/0"
@@ -120,130 +117,15 @@ public class HDNode {
120117
}
121118

122119
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? {
124121
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)
243125
}
244126
}
245127

246-
public func derive (path: String, derivePrivateKey: Bool = true) -> HDNode? {
128+
public func derive(path: String, derivePrivateKey: Bool = true) -> HDNode? {
247129
let components = path.components(separatedBy: "/")
248130
var currentNode: HDNode = self
249131
var firstComponent = 0
@@ -262,30 +144,158 @@ extension HDNode {
262144
return currentNode
263145
}
264146

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+
265274
public func serializeToString(serializePublic: Bool = true, version: HDversion = HDversion()) -> String? {
266275
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)
269277
}
270278

271279
public func serialize(serializePublic: Bool = true, version: HDversion = HDversion()) -> Data? {
272280
var data = Data()
273-
if !serializePublic && !self.hasPrivate { return nil }
281+
/// Public or private key
282+
let keyData: Data
274283
if serializePublic {
284+
keyData = publicKey
275285
data.append(version.publicPrefix)
276286
} else {
287+
guard let privateKey = privateKey else { return nil }
288+
keyData = privateKey
277289
data.append(version.privatePrefix)
278290
}
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 {
286296
data.append(contentsOf: [0x00])
287-
data.append(self.privateKey!)
288297
}
298+
data.append(keyData)
289299
let hashedData = data.sha256().sha256()
290300
let checksum = hashedData[0..<4]
291301
data.append(checksum)

0 commit comments

Comments
 (0)