Skip to content

Commit 3f934fe

Browse files
committed
improve ed25519 bip32 derivation
1 parent beed95e commit 3f934fe

File tree

5 files changed

+38
-27
lines changed

5 files changed

+38
-27
lines changed

SubstrateSdk/Classes/Crypto/BIP32ExtendedKeypair.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@ import NovaCrypto
22

33
public class BIP32ExtendedKeypair {
44
let keypair: IRCryptoKeypairProtocol
5-
let nextSeed: Data
65
let chaincode: Data
76

87
init(
98
keypair: IRCryptoKeypairProtocol,
10-
nextSeed: Data,
119
chaincode: Data
1210
) {
1311
self.keypair = keypair
14-
self.nextSeed = nextSeed
1512
self.chaincode = chaincode
1613
}
1714
}

SubstrateSdk/Classes/Crypto/KeypairFactory/BIP32Ed25519KeyFactory.swift

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ public final class BIP32Ed25519KeyFactory: BIP32KeypairFactory {
1818
let privateKeySeed = hmacResult[...31]
1919
let chainCode = hmacResult[32...]
2020

21-
let keypair = try internalFactory.derive(fromSeed: privateKeySeed)
21+
// we are returning seed as private key as both further derivation and signature requires it
22+
let publicKey = try internalFactory.derive(fromSeed: privateKeySeed).publicKey()
23+
let secretKey = try EDPrivateKey(rawData: privateKeySeed)
24+
25+
let keypair = IRCryptoKeypair(publicKey: publicKey, privateKey: secretKey)
2226

23-
return BIP32ExtendedKeypair(
24-
keypair: keypair,
25-
nextSeed: privateKeySeed,
26-
chaincode: chainCode
27-
)
27+
return BIP32ExtendedKeypair(keypair: keypair, chaincode: chainCode)
2828
}
2929

3030
override func createKeypairFrom(
@@ -35,8 +35,9 @@ public final class BIP32Ed25519KeyFactory: BIP32KeypairFactory {
3535
switch chaincode.type {
3636
case .hard:
3737
let padding = Data(repeating: 0, count: 1)
38+
let privateKeyData = parentKeypair.privateKey().rawData()
3839

39-
return padding + parentKeypair.nextSeed + chaincode.data
40+
return padding + privateKeyData + chaincode.data
4041

4142
case .soft:
4243
throw BIP32KeypairFactoryError.unsupportedSoftDerivation
@@ -50,12 +51,13 @@ public final class BIP32Ed25519KeyFactory: BIP32KeypairFactory {
5051

5152
let childPrivateKeySeed = hmacResult[...31]
5253
let childChaincode = hmacResult[32...]
53-
let keypair = try internalFactory.derive(fromSeed: childPrivateKeySeed)
54-
55-
return BIP32ExtendedKeypair(
56-
keypair: keypair,
57-
nextSeed: childPrivateKeySeed,
58-
chaincode: childChaincode
59-
)
54+
55+
// we are returning seed as private key as both further derivation and signature requires it
56+
let publicKey = try internalFactory.derive(fromSeed: childPrivateKeySeed).publicKey()
57+
let secretKey = try EDPrivateKey(rawData: childPrivateKeySeed)
58+
59+
let keypair = IRCryptoKeypair(publicKey: publicKey, privateKey: secretKey)
60+
61+
return BIP32ExtendedKeypair(keypair: keypair, chaincode: childChaincode)
6062
}
6163
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import NovaCrypto
22

33
public enum BIP32KeypairFactoryError: Error {
4+
case invalidMasterKey
45
case invalidChildKey
56
case unsupportedSoftDerivation
67
}

SubstrateSdk/Classes/Crypto/KeypairFactory/BIP32Secp256KeypairFactory.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ public final class BIP32Secp256KeypairFactory: BIP32KeypairFactory {
1616
)
1717

1818
let privateKeyData = hmacResult[...31]
19+
20+
let privateKeyInt = BigUInt(privateKeyData)
21+
22+
guard privateKeyInt < .secp256k1CurveOrder, privateKeyInt > 0 else {
23+
throw BIP32KeypairFactoryError.invalidMasterKey
24+
}
25+
1926
let privateKey = try SECPrivateKey(rawData: privateKeyData)
2027
let chainCode = hmacResult[32...]
2128

2229
let keypair = try internalFactory.derive(fromPrivateKey: privateKey)
2330

24-
return BIP32ExtendedKeypair(
25-
keypair: keypair,
26-
nextSeed: privateKeyData,
27-
chaincode: chainCode
28-
)
31+
return BIP32ExtendedKeypair(keypair: keypair, chaincode: chainCode)
2932
}
3033

3134
override func createKeypairFrom(
@@ -36,8 +39,9 @@ public final class BIP32Secp256KeypairFactory: BIP32KeypairFactory {
3639
switch chaincode.type {
3740
case .hard:
3841
let padding = Data(repeating: 0, count: 1)
39-
40-
return padding + parentKeypair.nextSeed + chaincode.data
42+
let privateKeyData = parentKeypair.privateKey().rawData()
43+
44+
return padding + privateKeyData + chaincode.data
4145

4246
case .soft:
4347
return parentKeypair.publicKey().rawData() + chaincode.data
@@ -58,7 +62,7 @@ public final class BIP32Secp256KeypairFactory: BIP32KeypairFactory {
5862
throw BIP32KeypairFactoryError.invalidChildKey
5963
}
6064

61-
privateKeyInt += BigUInt(parentKeypair.nextSeed)
65+
privateKeyInt += BigUInt(parentKeypair.privateKey().rawData())
6266
privateKeyInt %= .secp256k1CurveOrder
6367

6468
guard privateKeyInt > 0 else {
@@ -82,7 +86,6 @@ public final class BIP32Secp256KeypairFactory: BIP32KeypairFactory {
8286

8387
return BIP32ExtendedKeypair(
8488
keypair: keypair,
85-
nextSeed: privateKeyData,
8689
chaincode: childChaincode
8790
)
8891
}

Tests/Crypto/BIP32/BIP32Ed25519KeypairDerivationTests.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,18 @@ final class BIP32Ed25519KeypairDerivationTests: XCTestCase {
7979

8080
let keypair = try keypairFactory.createKeypairFromSeed(seed, chaincodeList: result.chaincodes)
8181

82+
let expectedPrivateKey = try Data(hexString: item.privateKey)
8283
let expectedPublicKey = try item.getPublicKeyWithoutPrefix()
83-
84+
85+
let actualPrivateKey = keypair.privateKey().rawData()
8486
let actualPublicKey = keypair.publicKey().rawData()
8587

88+
XCTAssertEqual(
89+
expectedPrivateKey,
90+
actualPrivateKey,
91+
"Expected private key \(expectedPrivateKey.toHex()) but received \(actualPrivateKey.toHex())"
92+
)
93+
8694
XCTAssertEqual(
8795
expectedPublicKey,
8896
actualPublicKey,

0 commit comments

Comments
 (0)