diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 73af1a7..0f47e35 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -9,12 +9,12 @@ on: jobs: build: - runs-on: macos-12 + runs-on: macos-13 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.0' + xcode-version: '15.0' - uses: actions/checkout@v2 - name: Build run: xcodebuild -scheme LibAuk build -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13' diff --git a/Package.swift b/Package.swift index 3c2d89a..ce0a045 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,11 @@ -// swift-tools-version:5.5 +// swift-tools-version:5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "LibAuk", - platforms: [.iOS("15.0")], + platforms: [.iOS("16.0")], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( @@ -13,11 +13,12 @@ let package = Package( targets: ["LibAuk"]), ], dependencies: [ - .package(name: "Web3.swift", url: "https://github.com/bitmark-inc/Web3.swift.git", .branch("master")), - .package(url: "https://github.com/BlockchainCommons/URKit.git", .exact("7.5.0")), + .package(name: "Web3.swift", url: "https://github.com/bitmark-inc/Web3.swift.git", branch: "migrate_secp256"), + .package(url: "https://github.com/BlockchainCommons/URKit.git", from: "14.0.2"), .package(url: "https://github.com/keefertaylor/Base58Swift.git", from: "2.1.0"), - .package(name: "KukaiCoreSwift", url: "https://github.com/autonomy-system/kukai-core-swift.git", .branch("main")), - .package(name: "TweetNacl", url: "https://github.com/bitmark-inc/tweetnacl-swiftwrap", branch: "master") + .package(name: "KukaiCoreSwift", url: "https://github.com/autonomy-system/kukai-core-swift.git", branch: "migrate_secp256"), + .package(name: "TweetNacl", url: "https://github.com/bitmark-inc/tweetnacl-swiftwrap", branch: "master"), + .package(url: "https://github.com/BlockchainCommons/BCSwiftFoundation.git", branch: "master") // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), ], @@ -27,19 +28,16 @@ let package = Package( .target( name: "LibAuk", dependencies: [ - .target(name: "LibWally"), .product(name: "Web3", package: "Web3.swift"), .product(name: "URKit", package: "URKit"), - .product(name: "KukaiCoreSwift", package: "KukaiCoreSwift"), .product(name: "Base58Swift", package: "Base58Swift"), + .product(name: "KukaiCoreSwift", package: "KukaiCoreSwift"), .product(name: "TweetNacl", package: "TweetNacl"), + .product(name: "BCFoundation", package: "BCSwiftFoundation") ]), .testTarget( name: "LibAukTests", - dependencies: ["LibAuk"]), - .binaryTarget( - name: "LibWally", - path: "Frameworks/LibWally.xcframework") + dependencies: ["LibAuk"]) ], swiftLanguageVersions: [.v5] ) diff --git a/Sources/LibAuk/LibAuk.swift b/Sources/LibAuk/LibAuk.swift index 5595524..29d823e 100644 --- a/Sources/LibAuk/LibAuk.swift +++ b/Sources/LibAuk/LibAuk.swift @@ -7,7 +7,7 @@ import Foundation import Combine -import LibWally +import BCFoundation public class LibAuk { @@ -33,15 +33,15 @@ public class LibAuk { } public func calculateEthFirstAddress(words: [String], passphrase: String) -> AnyPublisher { - Future { promise in + Future { promise in guard let entropy = Keys.entropy(words), - let mnemonic = Keys.mnemonic(entropy) else { + let bip39 = Keys.mnemonic(entropy) else { promise(.failure(LibAukError.invalidMnemonicError)) return } - promise(.success(mnemonic)) - }.tryMap({ mnemonic in - let ethPrivateKey = try Keys.ethereumPrivateKey(mnemonic: mnemonic, passphrase: passphrase) + promise(.success(bip39)) + }.tryMap({ bip39 in + let ethPrivateKey = try Keys.ethereumPrivateKey(bip39: bip39, passphrase: passphrase) return ethPrivateKey.address.hex(eip55: true) }) .eraseToAnyPublisher() diff --git a/Sources/LibAuk/Model/Seed.swift b/Sources/LibAuk/Model/Seed.swift index e95c49e..119ad81 100644 --- a/Sources/LibAuk/Model/Seed.swift +++ b/Sources/LibAuk/Model/Seed.swift @@ -7,6 +7,7 @@ import Foundation import URKit +import OrderedCollections public class Seed: Codable { public let data: Data @@ -20,6 +21,7 @@ public class Seed: Codable { self.creationDate = creationDate self.passphrase = passphrase } + /* func cbor(nameLimit: Int? = nil, noteLimit: Int? = nil) -> CBOR { var a: [OrderedMap.Entry] = [ @@ -82,13 +84,13 @@ public class Seed: Codable { while let element = iterator.next() { let (indexElement, valueElement) = element - guard case let CBOR.unsignedInt(index) = indexElement else { + guard case let CBOR.unsigned(index) = indexElement else { throw LibAukError.other(reason: "ur:crypto-seed: CBOR contains invalid keys.") } switch index { case 1: - guard case let CBOR.data(data) = valueElement else { + guard case let CBOR.bytes(data) = valueElement else { throw LibAukError.other(reason: "ur:crypto-seed: CBOR doesn't contain data field.") } seedData = data @@ -98,12 +100,12 @@ public class Seed: Codable { } creationDate = d case 3: - guard case let CBOR.utf8String(s) = valueElement else { + guard case let CBOR.text(s) = valueElement else { throw LibAukError.other(reason: "ur:crypto-seed: Name field doesn't contain a string.") } name = s case 4: - guard case let CBOR.utf8String(s) = valueElement else { + guard case let CBOR.text(s) = valueElement else { throw LibAukError.other(reason: "ur:crypto-seed: Passphrase field doesn't contain a string.") } passphrase = s @@ -115,4 +117,5 @@ public class Seed: Codable { self.init(data: seedData!, name: name, creationDate: creationDate, passphrase: passphrase) } + */ } diff --git a/Sources/LibAuk/Storage/SecureStorage.swift b/Sources/LibAuk/Storage/SecureStorage.swift index e8e46a6..eda7c46 100644 --- a/Sources/LibAuk/Storage/SecureStorage.swift +++ b/Sources/LibAuk/Storage/SecureStorage.swift @@ -4,10 +4,9 @@ // // Created by Ho Hien on 8/9/21. // - import Foundation import Combine -import LibWally +import BCFoundation import Web3 import KukaiCoreSwift import Base58Swift @@ -54,13 +53,13 @@ class SecureStorage: SecureStorageProtocol { self.keychain = keychain } - private func generateEthAddresses(mnemonic: BIP39Mnemonic, passphrase: String?, start: Int = 0, end: Int = 100) -> [Int: String] { + private func generateEthAddresses(bip39: BIP39, passphrase: String?, start: Int = 0, end: Int = 100) -> [Int: String] { var ethAddresses: [Int: String] = [:] for index in start...end { do { // Generate Ethereum private key for the current index - let privateKey = try Keys.ethereumPrivateKeyWithIndex(mnemonic: mnemonic, passphrase: passphrase, index: index) + let privateKey = try Keys.ethereumPrivateKeyWithIndex(bip39: bip39, passphrase: passphrase, index: index) // Derive Ethereum address from the private key let address = privateKey.address.hex(eip55: true) @@ -76,12 +75,12 @@ class SecureStorage: SecureStorageProtocol { return ethAddresses } - private func generateTezosPublicKeys(mnemonic: BIP39Mnemonic, passphrase: String?, start: Int, end: Int) -> [Int: String] { + private func generateTezosPublicKeys(bip39: BIP39, passphrase: String?, start: Int, end: Int) -> [Int: String] { var tezosPublicKeys: [Int: String] = [:] for index in start...end { // Generate Tezos wallet for the current index - let tezosWallet = Keys.tezosWalletWithIndex(mnemonic: mnemonic, passphrase: passphrase, index: index) + let tezosWallet = Keys.tezosWalletWithIndex(bip39: bip39, passphrase: passphrase, index: index) // Get the public key for the Tezos wallet let publicKey = tezosWallet?.publicKeyBase58encoded() @@ -93,8 +92,8 @@ class SecureStorage: SecureStorageProtocol { return tezosPublicKeys } - private func generateEthAddress(mnemonic: BIP39Mnemonic, passphrase: String?) throws -> String { - let ethPrivateKey = try Keys.ethereumPrivateKey(mnemonic: mnemonic, passphrase: passphrase) + private func generateEthAddress(bip39: BIP39, passphrase: String?) throws -> String { + let ethPrivateKey = try Keys.ethereumPrivateKey(bip39: bip39, passphrase: passphrase) let ethAddress = ethPrivateKey.address.hex(eip55: true) return ethAddress } @@ -106,9 +105,9 @@ class SecureStorage: SecureStorageProtocol { let name = seed.name /* accountDidKey */ - let mnemonic = Keys.mnemonic(seed.data)! + let bip39 = Keys.mnemonic(seed.data)! let passphrase = seed.passphrase - let privateKey = try Keys.accountDIDPrivateKey(mnemonic: mnemonic) + let privateKey = try Keys.accountDIDPrivateKey(bip39: bip39) // Multicodec encoded with prefix 0xe7 var bytes: [UInt8] = [231, 1] @@ -116,18 +115,18 @@ class SecureStorage: SecureStorageProtocol { let did = "did:key:z\(Base58.base58Encode(bytes))" /* pre-generate 100 eth addresses */ - let ethAddresses = self.generateEthAddresses(mnemonic: mnemonic, passphrase: passphrase, start: 0, end: self.preGenerateAddressLimit) + let ethAddresses = self.generateEthAddresses(bip39: bip39, passphrase: passphrase, start: 0, end: self.preGenerateAddressLimit) /* encryptionPrivateKey */ - let encryptionPrivateKey = try Keys.encryptionPrivateKey(mnemonic: mnemonic, passphrase: passphrase) + let encryptionPrivateKey = try Keys.encryptionPrivateKey(bip39: bip39, passphrase: passphrase) /* accountDIDPrivateKey */ - let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(mnemonic: mnemonic, passphrase: passphrase) + let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(bip39: bip39, passphrase: passphrase) /* tezos public key */ - let tezosPublicKeys = self.generateTezosPublicKeys(mnemonic: mnemonic, passphrase: passphrase, start: 0, end: self.preGenerateAddressLimit) + let tezosPublicKeys = self.generateTezosPublicKeys(bip39: bip39, passphrase: passphrase, start: 0, end: self.preGenerateAddressLimit) - let ethAddress = try self.generateEthAddress(mnemonic: mnemonic, passphrase: passphrase) + let ethAddress = try self.generateEthAddress(bip39: bip39, passphrase: passphrase) var seedPublicData = SeedPublicData(ethAddress: ethAddress, creationDate: Date(), @@ -164,15 +163,16 @@ class SecureStorage: SecureStorageProtocol { return } + guard let entropy = KeyCreator.createEntropy() else { promise(.failure(LibAukError.keyCreationError)) return } let seed = Seed(data: entropy, name: name, creationDate: Date(), passphrase: passphrase) - let seedData = seed.urString.utf8 + //let seedData = seed.urString.utf8 - self.keychain.set(seedData, forKey: Constant.KeychainKey.seed, isSync: true, isPrivate: isPrivate) + self.keychain.set(seed.data, forKey: Constant.KeychainKey.seed, isSync: true, isPrivate: isPrivate) promise(.success(seed)) } .flatMap { seed in @@ -195,9 +195,9 @@ class SecureStorage: SecureStorageProtocol { if let entropy = Keys.entropy(words) { let seed = Seed(data: entropy, name: name, creationDate: creationDate ?? Date(), passphrase: passphrase) - let seedData = seed.urString.utf8 + //let seedData = seed.urString.utf8 - self.keychain.set(seedData, forKey: Constant.KeychainKey.seed, isSync: true, isPrivate: isPrivate) + self.keychain.set(seed.data, forKey: Constant.KeychainKey.seed, isSync: true, isPrivate: isPrivate) promise(.success(seed)) } else { promise(.failure(LibAukError.invalidMnemonicError)) @@ -217,7 +217,7 @@ class SecureStorage: SecureStorageProtocol { func setSeed(seed: Seed, isPrivate: Bool) -> AnyPublisher { Future { promise in - guard self.keychain.set(seed.urString.utf8, forKey: Constant.KeychainKey.seed, isSync: true, isPrivate: isPrivate) else { + guard self.keychain.set(seed.data, forKey: Constant.KeychainKey.seed, isSync: true, isPrivate: isPrivate) else { promise(.success(false)) return } @@ -311,8 +311,8 @@ class SecureStorage: SecureStorageProtocol { } .catch({ error in Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -320,13 +320,13 @@ class SecureStorage: SecureStorageProtocol { promise(.success(seed)) } .compactMap { seed in - guard let mnemonic = Keys.mnemonic(seed.data) else { + guard let bip39 = Keys.mnemonic(seed.data) else { return nil } - return (mnemonic, seed.passphrase) + return (bip39, seed.passphrase) } - .tryMap { (mnemonic: BIP39Mnemonic, passphrase: String?) in - let ethPrivateKey = try Keys.ethereumPrivateKeyWithIndex(mnemonic: mnemonic, passphrase: passphrase, index: index) + .tryMap { (bip39: BIP39, passphrase: String?) in + let ethPrivateKey = try Keys.ethereumPrivateKeyWithIndex(bip39: bip39, passphrase: passphrase, index: index) return ethPrivateKey.address.hex(eip55: true) } .eraseToAnyPublisher() @@ -336,8 +336,8 @@ class SecureStorage: SecureStorageProtocol { private func getSeed() -> AnyPublisher { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -349,8 +349,8 @@ class SecureStorage: SecureStorageProtocol { func ethSign(message: Bytes) -> AnyPublisher<(v: UInt, r: Bytes, s: Bytes), Error> { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -358,13 +358,13 @@ class SecureStorage: SecureStorageProtocol { promise(.success(seed)) } .compactMap { seed in - guard let mnemonic = Keys.mnemonic(seed.data) else { + guard let bip39 = Keys.mnemonic(seed.data) else { return nil } - return (mnemonic, seed.passphrase) + return (bip39, seed.passphrase) } - .tryMap { (mnemonic: BIP39Mnemonic, passphrase: String?) in - let ethPrivateKey = try Keys.ethereumPrivateKey(mnemonic: mnemonic, passphrase: passphrase) + .tryMap { (bip39: BIP39, passphrase: String?) in + let ethPrivateKey = try Keys.ethereumPrivateKey(bip39: bip39, passphrase: passphrase) return try ethPrivateKey.sign(message: message) } .eraseToAnyPublisher() @@ -372,8 +372,8 @@ class SecureStorage: SecureStorageProtocol { func ethSignWithIndex(message: Bytes, index: Int) -> AnyPublisher<(v: UInt, r: Bytes, s: Bytes), Error> { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -381,13 +381,13 @@ class SecureStorage: SecureStorageProtocol { promise(.success(seed)) } .compactMap { seed in - guard let mnemonic = Keys.mnemonic(seed.data) else { + guard let bip39 = Keys.mnemonic(seed.data) else { return nil } - return (mnemonic, seed.passphrase) + return (bip39, seed.passphrase) } - .tryMap { (mnemonic: BIP39Mnemonic, passphrase: String?) in - let ethPrivateKey = try Keys.ethereumPrivateKeyWithIndex(mnemonic: mnemonic, passphrase: passphrase, index: index) + .tryMap { (bip39: BIP39, passphrase: String?) in + let ethPrivateKey = try Keys.ethereumPrivateKeyWithIndex(bip39: bip39, passphrase: passphrase, index: index) return try ethPrivateKey.sign(message: message) } .eraseToAnyPublisher() @@ -395,8 +395,8 @@ class SecureStorage: SecureStorageProtocol { func ethSignTransaction(transaction: EthereumTransaction, chainId: EthereumQuantity) -> AnyPublisher { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -404,13 +404,13 @@ class SecureStorage: SecureStorageProtocol { promise(.success(seed)) } .compactMap { seed in - guard let mnemonic = Keys.mnemonic(seed.data) else { + guard let bip39 = Keys.mnemonic(seed.data) else { return nil } - return (mnemonic, seed.passphrase) + return (bip39, seed.passphrase) } - .tryMap { (mnemonic: BIP39Mnemonic, passphrase: String?) in - let ethPrivateKey = try Keys.ethereumPrivateKey(mnemonic: mnemonic, passphrase: passphrase) + .tryMap { (bip39: BIP39, passphrase: String?) in + let ethPrivateKey = try Keys.ethereumPrivateKey(bip39: bip39, passphrase: passphrase) return try transaction.sign(with: ethPrivateKey, chainId: chainId) } @@ -419,8 +419,8 @@ class SecureStorage: SecureStorageProtocol { func ethSignTransactionWithIndex(transaction: EthereumTransaction, chainId: EthereumQuantity, index: Int) -> AnyPublisher { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -428,20 +428,20 @@ class SecureStorage: SecureStorageProtocol { promise(.success(seed)) } .compactMap { seed in - guard let mnemonic = Keys.mnemonic(seed.data) else { + guard let bip39 = Keys.mnemonic(seed.data) else { return nil } - return (mnemonic, seed.passphrase) + return (bip39, seed.passphrase) } - .tryMap { (mnemonic: BIP39Mnemonic, passphrase: String?) in - let ethPrivateKey = try Keys.ethereumPrivateKeyWithIndex(mnemonic: mnemonic, passphrase: passphrase, index: index) + .tryMap { (bip39: BIP39, passphrase: String?) in + let ethPrivateKey = try Keys.ethereumPrivateKeyWithIndex(bip39: bip39, passphrase: passphrase, index: index) return try transaction.sign(with: ethPrivateKey, chainId: chainId) } .eraseToAnyPublisher() } - private func getEncryptKey(usingLegacy: Bool = false) -> AnyPublisher { + private func getEncryptKey(usingLegacy: Bool = false) -> AnyPublisher { return Future { promise in guard let seedPublicData = self.getSeedPublicData() else { promise(.failure(LibAukError.emptyKey)) @@ -454,7 +454,7 @@ class SecureStorage: SecureStorageProtocol { if (usingLegacy) { return SymmetricKey(data: privateKey.rawRepresentation) } else { - let encryptionKey = HKDF.deriveKey(inputKeyMaterial: SymmetricKey(data: privateKey.rawRepresentation), salt: Data(), outputByteCount: 32) + let encryptionKey = HKDF.deriveKey(inputKeyMaterial: SymmetricKey(data: privateKey.rawRepresentation), salt: Data(), outputByteCount: 32) return encryptionKey } }) @@ -551,8 +551,8 @@ class SecureStorage: SecureStorageProtocol { func exportSeed() -> AnyPublisher { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -602,8 +602,8 @@ class SecureStorage: SecureStorageProtocol { internal func getTezosWallet() -> AnyPublisher { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -616,16 +616,16 @@ class SecureStorage: SecureStorageProtocol { } return (mnemonic, seed.passphrase) } - .compactMap { (mnemonic: BIP39Mnemonic, passphrase: String?) in - Keys.tezosWallet(mnemonic: mnemonic, passphrase: passphrase) + .compactMap { (bip39: BIP39, passphrase: String?) in + Keys.tezosWallet(bip39: bip39, passphrase: passphrase) } .eraseToAnyPublisher() } internal func getTezosWalletWithIndex(index: Int) -> AnyPublisher { Future { promise in - guard let seedUR = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), - let seed = try? Seed(urString: seedUR.utf8) else { + guard let data = self.keychain.getData(Constant.KeychainKey.seed, isSync: true), + let seed = try? Seed(data: data, name: self.getName() ?? "") else { promise(.failure(LibAukError.emptyKey)) return } @@ -638,8 +638,8 @@ class SecureStorage: SecureStorageProtocol { } return (mnemonic, seed.passphrase) } - .compactMap { (mnemonic: BIP39Mnemonic, passphrase: String?) in - Keys.tezosWalletWithIndex(mnemonic: mnemonic, passphrase: passphrase, index: index) + .compactMap { (bip39: BIP39, passphrase: String?) in + Keys.tezosWalletWithIndex(bip39: bip39, passphrase: passphrase, index: index) } .eraseToAnyPublisher() } diff --git a/Sources/LibAuk/Utils/KeyCreator.swift b/Sources/LibAuk/Utils/KeyCreator.swift index 2181253..e400fb5 100644 --- a/Sources/LibAuk/Utils/KeyCreator.swift +++ b/Sources/LibAuk/Utils/KeyCreator.swift @@ -6,13 +6,13 @@ // import Foundation -import LibWally +import BCFoundation class KeyCreator { static func createEntropy() -> Data? { guard let hex = Data.secureRandom(16)?.hexString, - let entropy = try? BIP39Mnemonic.Entropy(hex: hex) else { + let entropy = try? BIP39(hex: hex) else { return nil } diff --git a/Sources/LibAuk/Utils/Keys.swift b/Sources/LibAuk/Utils/Keys.swift index 5aa6fca..7d80f5d 100644 --- a/Sources/LibAuk/Utils/Keys.swift +++ b/Sources/LibAuk/Utils/Keys.swift @@ -6,105 +6,121 @@ // import Foundation -import LibWally import Web3 +import BCFoundation import KukaiCoreSwift import CryptoKit import TweetNacl class Keys { - static func fingerprint(mnemonic: BIP39Mnemonic, passphrase: String? = "") -> String? { - guard let hdMasterKey = try? HDKey(seed: mnemonic.seedHex(passphrase: passphrase ?? "")) else { return nil } + static func fingerprint(bip39: BIP39, passphrase: String? = "") -> String? { - return hdMasterKey.fingerprint.hexString + guard let hdMasterKey = try? HDKey(bip39Seed: BIP39.Seed(bip39: bip39, passphrase: passphrase ?? "")) else { return nil } + + return hdMasterKey.keyFingerprint.hex } static func validMnemonicArray(_ words: [String]) -> Bool { - guard (try? BIP39Mnemonic(words: words)) != nil else { return false } - - return true + return BIP39(words: words)?.data != nil } static func validMnemonicString(_ words: String) -> Bool { - guard (try? BIP39Mnemonic(words: words)) != nil else { return false } - - return true + return BIP39(mnemonic: words)?.data != nil } static func entropy(_ words: [String]) -> Data? { - guard let mnemonic = try? BIP39Mnemonic(words: words) else { return nil } - - return mnemonic.entropy.data + return BIP39(words: words)?.data } static func entropy(_ words: String) -> Data? { - guard let mnemonic = try? BIP39Mnemonic(words: words) else { return nil } - - return mnemonic.entropy.data + return BIP39(mnemonic: words)?.data } - static func mnemonic(_ entropy: Data) -> BIP39Mnemonic? { - let bip39entropy = BIP39Mnemonic.Entropy(entropy) + static func mnemonic(_ entropy: Data) -> BIP39? { + let bip39entropy = BIP39(data: entropy) - return try? BIP39Mnemonic(entropy: bip39entropy) + return bip39entropy } - static func accountDIDPrivateKey(mnemonic: BIP39Mnemonic, passphrase: String? = "") throws -> Secp256k1.Signing.PrivateKey { - let masterKey = try HDKey(seed: mnemonic.seedHex(passphrase: passphrase ?? "")) - let derivationPath = try BIP32Path(string: Constant.accountDerivationPath) - let account = try masterKey.derive(using: derivationPath) - - guard let privateKey = account.privKey?.data.bytes else { + static func accountDIDPrivateKey(bip39: BIP39, passphrase: String? = "") throws -> Secp256k1.Signing.PrivateKey { + guard let masterKey = try? HDKey(bip39Seed: BIP39.Seed(bip39: bip39, passphrase: passphrase ?? "")) else { + throw LibAukError.keyCreationError + } + guard let derivationPath = DerivationPath(string: Constant.accountDerivationPath) else { throw LibAukError.keyDerivationError } + guard let account = try? HDKey(parent: masterKey, childDerivationPath: derivationPath) else { + throw LibAukError.keyCreationError + } + + guard let privateKey = account.base58PrivateKey?.bytes else { + throw LibAukError.keyCreationError + } return try Secp256k1.Signing.PrivateKey(rawRepresentation: privateKey) } - static func encryptionPrivateKey(mnemonic: BIP39Mnemonic, passphrase: String? = "") throws -> Secp256k1.Signing.PrivateKey { - let masterKey = try HDKey(seed: mnemonic.seedHex(passphrase: passphrase ?? "")) - let derivationPath = try BIP32Path(string: Constant.encryptionKeyDerivationPath) - let keyPair = try masterKey.derive(using: derivationPath) - - guard let privateKey = keyPair.privKey?.data.bytes else { + static func encryptionPrivateKey(bip39: BIP39, passphrase: String? = "") throws -> Secp256k1.Signing.PrivateKey { + guard let masterKey = try? HDKey(bip39Seed: BIP39.Seed(bip39: bip39, passphrase: passphrase ?? "")) else { + throw LibAukError.keyCreationError + } + guard let derivationPath = DerivationPath(string: Constant.encryptionKeyDerivationPath) else { throw LibAukError.keyDerivationError } + guard let keyPair = try? HDKey(parent: masterKey, childDerivationPath: derivationPath) else { + throw LibAukError.keyCreationError + } + guard let privateKey = keyPair.base58PrivateKey?.bytes else { + throw LibAukError.keyCreationError + } return try Secp256k1.Signing.PrivateKey(rawRepresentation: privateKey) } - static func ethereumPrivateKey(mnemonic: BIP39Mnemonic, passphrase: String? = "") throws -> EthereumPrivateKey { - let masterKey = try HDKey(seed: mnemonic.seedHex(passphrase: passphrase ?? "")) - let derivationPath = try BIP32Path(string: Constant.ethDerivationPath) - let account = try masterKey.derive(using: derivationPath) - - guard let privateKey = account.privKey?.data.bytes else { + static func ethereumPrivateKey(bip39: BIP39, passphrase: String? = "") throws -> EthereumPrivateKey { + guard let masterKey = try? HDKey(bip39Seed: BIP39.Seed(bip39: bip39, passphrase: passphrase ?? "")) else { + throw LibAukError.keyCreationError + } + guard let derivationPath = DerivationPath(string: Constant.ethDerivationPath) else { throw LibAukError.keyDerivationError } + guard let account = try? HDKey(parent: masterKey, childDerivationPath: derivationPath) else { + throw LibAukError.keyCreationError + } + + guard let privateKey = account.base58PrivateKey?.bytes else { + throw LibAukError.keyCreationError + } return try EthereumPrivateKey(privateKey) } - static func ethereumPrivateKeyWithIndex(mnemonic: BIP39Mnemonic, passphrase: String? = "", index: Int) throws -> EthereumPrivateKey { - let masterKey = try HDKey(seed: mnemonic.seedHex(passphrase: passphrase ?? "")) + static func ethereumPrivateKeyWithIndex(bip39: BIP39, passphrase: String? = "", index: Int) throws -> EthereumPrivateKey { + guard let masterKey = try? HDKey(bip39Seed: BIP39.Seed(bip39: bip39, passphrase: passphrase ?? "")) else { + throw LibAukError.keyCreationError + } let path = "m/44'/60'/0'/0/\(index)" - let derivationPath = try BIP32Path(string: path) - let account = try masterKey.derive(using: derivationPath) - - guard let privateKey = account.privKey?.data.bytes else { + guard let derivationPath = DerivationPath(string: path) else { throw LibAukError.keyDerivationError } + guard let account = try? HDKey(parent: masterKey, childDerivationPath: derivationPath) else { + throw LibAukError.keyCreationError + } + + guard let privateKey = account.base58PublicKey?.bytes else { + throw LibAukError.keyCreationError + } return try EthereumPrivateKey(privateKey) } - static func tezosWallet(mnemonic: BIP39Mnemonic, passphrase: String? = "") -> HDWallet? { - HDWallet.create(withMnemonic: mnemonic.words.joined(separator: " "), passphrase: passphrase ?? "") + static func tezosWallet(bip39: BIP39, passphrase: String? = "") -> HDWallet? { + HDWallet.create(withMnemonic: bip39.mnemonic, passphrase: passphrase ?? "") } - static func tezosWalletWithIndex(mnemonic: BIP39Mnemonic, passphrase: String? = "", index: Int) -> HDWallet? { + static func tezosWalletWithIndex(bip39: BIP39, passphrase: String? = "", index: Int) -> HDWallet? { let path = "m/44'/1729'/\(index)'/0'" - return HDWallet.create(withMnemonic: mnemonic.words.joined(separator: " "), passphrase: passphrase ?? "", derivationPath: path) + return HDWallet.create(withMnemonic: bip39.mnemonic, passphrase: passphrase ?? "", derivationPath: path) } } diff --git a/Tests/LibAukTests/Storage/SecureStorage_Tests.swift b/Tests/LibAukTests/Storage/SecureStorage_Tests.swift index ac0709a..06e48c9 100644 --- a/Tests/LibAukTests/Storage/SecureStorage_Tests.swift +++ b/Tests/LibAukTests/Storage/SecureStorage_Tests.swift @@ -7,8 +7,8 @@ import Foundation import XCTest -import LibWally import Combine +import BCFoundation import Web3 import URKit @testable import LibAuk @@ -82,11 +82,11 @@ class SecureStorage_Tests: XCTestCase { func testIsWalletCreatedSuccessfully() throws { let words = "daring mix cradle palm crowd sea observe whisper rubber either uncle oak" let seed = Seed(data: Keys.entropy(words)!, name: "", creationDate: Date(), passphrase: "") - let mnemonic = Keys.mnemonic(seed.data)! + let bip39 = Keys.mnemonic(seed.data)! var seedPublicData = SeedPublicData(ethAddress: "0xA00cbE6a45102135A210F231901faA6c05D51465", creationDate: Date(), name: "", did: "did:key:zQ3shUnBWE7Dkskaozsnzsb78kVcgQFbtXf7zdCCDN3qepBGL", preGenerateEthAddress: [:], tezosPublicKeys: [:]) - let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(mnemonic: mnemonic, passphrase: "") - let encryptionPrivateKey = try Keys.encryptionPrivateKey(mnemonic: mnemonic, passphrase: "") + let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(bip39: bip39, passphrase: "") + let encryptionPrivateKey = try Keys.encryptionPrivateKey(bip39: bip39, passphrase: "") seedPublicData.encryptionPrivateKey = encryptionPrivateKey seedPublicData.accountDIDPrivateKey = accountDIDPrivateKey let seedPublicDataRaw = try JSONEncoder().encode(seedPublicData) @@ -113,11 +113,11 @@ class SecureStorage_Tests: XCTestCase { func testGetETHAddressSuccessfully() throws { let words = "daring mix cradle palm crowd sea observe whisper rubber either uncle oak" let seed = Seed(data: Keys.entropy(words)!, name: "", creationDate: Date(), passphrase: "") - let mnemonic = Keys.mnemonic(seed.data)! + let bip39 = Keys.mnemonic(seed.data)! var seedPublicData = SeedPublicData(ethAddress: "0xA00cbE6a45102135A210F231901faA6c05D51465", creationDate: Date(), name: "", did: "did:key:zQ3shUnBWE7Dkskaozsnzsb78kVcgQFbtXf7zdCCDN3qepBGL", preGenerateEthAddress: [:], tezosPublicKeys: [:]) - let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(mnemonic: mnemonic, passphrase: "") - let encryptionPrivateKey = try Keys.encryptionPrivateKey(mnemonic: mnemonic, passphrase: "") + let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(bip39: bip39, passphrase: "") + let encryptionPrivateKey = try Keys.encryptionPrivateKey(bip39: bip39, passphrase: "") seedPublicData.encryptionPrivateKey = encryptionPrivateKey seedPublicData.accountDIDPrivateKey = accountDIDPrivateKey let seedPublicDataRaw = try JSONEncoder().encode(seedPublicData) @@ -129,11 +129,11 @@ class SecureStorage_Tests: XCTestCase { func testGetAccountDIDSuccessfully() throws { let words = "daring mix cradle palm crowd sea observe whisper rubber either uncle oak" let seed = Seed(data: Keys.entropy(words)!, name: "", creationDate: Date(), passphrase: "") - let mnemonic = Keys.mnemonic(seed.data)! + let bip39 = Keys.mnemonic(seed.data)! var seedPublicData = SeedPublicData(ethAddress: "0xA00cbE6a45102135A210F231901faA6c05D51465", creationDate: Date(), name: "", did: "did:key:zQ3shUnBWE7Dkskaozsnzsb78kVcgQFbtXf7zdCCDN3qepBGL", preGenerateEthAddress: [:], tezosPublicKeys: [:]) - let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(mnemonic: mnemonic, passphrase: "") - let encryptionPrivateKey = try Keys.encryptionPrivateKey(mnemonic: mnemonic, passphrase: "") + let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(bip39: bip39, passphrase: "") + let encryptionPrivateKey = try Keys.encryptionPrivateKey(bip39: bip39, passphrase: "") seedPublicData.encryptionPrivateKey = encryptionPrivateKey seedPublicData.accountDIDPrivateKey = accountDIDPrivateKey let seedPublicDataRaw = try JSONEncoder().encode(seedPublicData) @@ -160,11 +160,11 @@ class SecureStorage_Tests: XCTestCase { func testGetAccountDIDSignatureSuccessfully() throws { let words = "daring mix cradle palm crowd sea observe whisper rubber either uncle oak" let seed = Seed(data: Keys.entropy(words)!, name: "", creationDate: Date(), passphrase: "") - let mnemonic = Keys.mnemonic(seed.data)! + let bip39 = Keys.mnemonic(seed.data)! var seedPublicData = SeedPublicData(ethAddress: "0xA00cbE6a45102135A210F231901faA6c05D51465", creationDate: Date(), name: "", did: "did:key:zQ3shUnBWE7Dkskaozsnzsb78kVcgQFbtXf7zdCCDN3qepBGL", preGenerateEthAddress: [:], tezosPublicKeys: [:]) - let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(mnemonic: mnemonic, passphrase: "") - let encryptionPrivateKey = try Keys.encryptionPrivateKey(mnemonic: mnemonic, passphrase: "") + let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(bip39: bip39, passphrase: "") + let encryptionPrivateKey = try Keys.encryptionPrivateKey(bip39: bip39, passphrase: "") seedPublicData.encryptionPrivateKey = encryptionPrivateKey seedPublicData.accountDIDPrivateKey = accountDIDPrivateKey let seedPublicDataRaw = try JSONEncoder().encode(seedPublicData) @@ -409,11 +409,11 @@ class SecureStorage_Tests: XCTestCase { func testRemoveKeysSuccessfully() throws { let words = "daring mix cradle palm crowd sea observe whisper rubber either uncle oak" let seed = Seed(data: Keys.entropy(words)!, name: "", creationDate: Date(), passphrase: "") - let mnemonic = Keys.mnemonic(seed.data)! + let bip39 = Keys.mnemonic(seed.data)! var seedPublicData = SeedPublicData(ethAddress: "0xA00cbE6a45102135A210F231901faA6c05D51465", creationDate: Date(), name: "", did: "did:key:zQ3shUnBWE7Dkskaozsnzsb78kVcgQFbtXf7zdCCDN3qepBGL", preGenerateEthAddress: [:], tezosPublicKeys: [:]) - let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(mnemonic: mnemonic, passphrase: "") - let encryptionPrivateKey = try Keys.encryptionPrivateKey(mnemonic: mnemonic, passphrase: "") + let accountDIDPrivateKey = try Keys.accountDIDPrivateKey(bip39: bip39, passphrase: "") + let encryptionPrivateKey = try Keys.encryptionPrivateKey(bip39: bip39, passphrase: "") seedPublicData.encryptionPrivateKey = encryptionPrivateKey seedPublicData.accountDIDPrivateKey = accountDIDPrivateKey let seedPublicDataRaw = try JSONEncoder().encode(seedPublicData)