From 5cf30eff88c08d5993a81e5f43bbf56d6b682bb2 Mon Sep 17 00:00:00 2001 From: goncalo-frade-iohk Date: Thu, 20 Jun 2024 14:27:08 +0100 Subject: [PATCH] feat(agent): add new agent derivation path --- .../Sources/ApolloImpl+KeyRestoration.swift | 38 +++++++-- .../Apollo/Sources/ApolloImpl+Public.swift | 4 +- .../Apollo/Sources/Model/Ed25519Key.swift | 14 +++- .../Apollo/Sources/Model/LinkSecret.swift | 1 + .../Apollo/Sources/Model/Secp256k1Key.swift | 2 + .../Apollo/Sources/Model/X25519Key.swift | 14 +++- .../CreateEd25519KeyPairOperation.swift | 14 +++- .../CreateX25519KeyPairOperation.swift | 14 +++- .../Castor/Tests/PrismDIDPublicKeyTests.swift | 2 +- .../Models/KeyManagement/DerivableKey.swift | 66 ++++++++++++++- .../Models/KeyManagement/StorableKey.swift | 4 + .../EdgeAgent/Sources/EdgeAgent+Backup.swift | 5 +- .../EdgeAgent+DIDHigherFucntions.swift | 5 +- .../Helpers/EdgeAgentDerivationPath.swift | 37 +++++++++ EdgeAgentSDK/EdgeAgent/Tests/CheckTest.swift | 2 +- .../EdgeAgent/Tests/DerivationPathTests.swift | 46 +++++++++++ .../Sources/Domain/StorableKeyModel.swift | 1 + .../PersistentStorage/DAO/CDKeyDAO.swift | 6 +- .../Models/CDKey+CoreDataProperties.swift | 1 + .../CDKeychainKey+CoreDataProperties.swift | 1 - .../PrismPluto.xcdatamodeld/.xccurrentversion | 2 +- .../PrismPlutoV2.xcdatamodel/contents | 81 +++++++++++++++++++ .../Pluto/Tests/Helper/PrivateKey+Test.swift | 1 + 23 files changed, 326 insertions(+), 35 deletions(-) create mode 100644 EdgeAgentSDK/EdgeAgent/Sources/Helpers/EdgeAgentDerivationPath.swift create mode 100644 EdgeAgentSDK/EdgeAgent/Tests/DerivationPathTests.swift create mode 100644 EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPlutoV2.xcdatamodel/contents diff --git a/EdgeAgentSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift b/EdgeAgentSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift index 519e4173..09f93e92 100644 --- a/EdgeAgentSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift +++ b/EdgeAgentSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift @@ -15,26 +15,37 @@ extension ApolloImpl: KeyRestoration { } public func restorePrivateKey(_ key: StorableKey) throws -> PrivateKey { + let derivationPath: DerivationPath? + if let derivationStr = key.queryDerivationPath { + derivationPath = try DerivationPath(string: derivationStr) + } else if let index = key.index { + derivationPath = DerivationPath(index: index) + } else { + derivationPath = nil + } switch key.restorationIdentifier { case "secp256k1+priv": - guard let index = key.index else { + guard let derivationPath else { throw ApolloError.restoratonFailedNoIdentifierOrInvalid } return Secp256k1PrivateKey( identifier: key.identifier, - internalKey: .init(raw: key.storableData.toKotlinByteArray()), derivationPath: DerivationPath(index: index) + internalKey: .init(raw: key.storableData.toKotlinByteArray()), + derivationPath: derivationPath ) case "x25519+priv": return try CreateX25519KeyPairOperation(logger: Self.logger) .compute( identifier: key.identifier, - fromPrivateKey: key.storableData + fromPrivateKey: key.storableData, + derivationPath: derivationPath ) case "ed25519+priv": return try CreateEd25519KeyPairOperation(logger: Self.logger) .compute( identifier: key.identifier, - fromPrivateKey: key.storableData + fromPrivateKey: key.storableData, + derivationPath: derivationPath ) default: throw ApolloError.restoratonFailedNoIdentifierOrInvalid @@ -65,26 +76,37 @@ extension ApolloImpl: KeyRestoration { } public func restoreKey(_ key: StorableKey) async throws -> Key { + let derivationPath: DerivationPath? + if let derivationStr = key.queryDerivationPath { + derivationPath = try DerivationPath(string: derivationStr) + } else if let index = key.index { + derivationPath = DerivationPath(index: index) + } else { + derivationPath = nil + } switch key.restorationIdentifier { case "secp256k1+priv": - guard let index = key.index else { + guard let derivationPath else { throw ApolloError.restoratonFailedNoIdentifierOrInvalid } return Secp256k1PrivateKey( identifier: key.identifier, - internalKey: .init(raw: key.storableData.toKotlinByteArray()), derivationPath: DerivationPath(index: index) + internalKey: .init(raw: key.storableData.toKotlinByteArray()), + derivationPath: derivationPath ) case "x25519+priv": return try CreateX25519KeyPairOperation(logger: Self.logger) .compute( identifier: key.identifier, - fromPrivateKey: key.storableData + fromPrivateKey: key.storableData, + derivationPath: derivationPath ) case "ed25519+priv": return try CreateEd25519KeyPairOperation(logger: Self.logger) .compute( identifier: key.identifier, - fromPrivateKey: key.storableData + fromPrivateKey: key.storableData, + derivationPath: derivationPath ) case "secp256k1+pub": return Secp256k1PublicKey( diff --git a/EdgeAgentSDK/Apollo/Sources/ApolloImpl+Public.swift b/EdgeAgentSDK/Apollo/Sources/ApolloImpl+Public.swift index fcb9937b..99446add 100644 --- a/EdgeAgentSDK/Apollo/Sources/ApolloImpl+Public.swift +++ b/EdgeAgentSDK/Apollo/Sources/ApolloImpl+Public.swift @@ -60,7 +60,9 @@ extension ApolloImpl: Apollo { if let keyData = parameters[KeyProperties.rawKey.rawValue].flatMap({ Data(base64Encoded: $0) }) { - let derivationPath = try parameters[KeyProperties.derivationPath.rawValue].map { try DerivationPath(string: $0) } ?? DerivationPath(index:0) + let derivationPath = try parameters[KeyProperties.derivationPath.rawValue].map { + try DerivationPath(string: $0) + } ?? DerivationPath() return Secp256k1PrivateKey(raw: keyData, derivationPath: derivationPath) } else { guard diff --git a/EdgeAgentSDK/Apollo/Sources/Model/Ed25519Key.swift b/EdgeAgentSDK/Apollo/Sources/Model/Ed25519Key.swift index 290a0316..1af814ff 100644 --- a/EdgeAgentSDK/Apollo/Sources/Model/Ed25519Key.swift +++ b/EdgeAgentSDK/Apollo/Sources/Model/Ed25519Key.swift @@ -6,19 +6,23 @@ import Foundation struct Ed25519PrivateKey: PrivateKey { private let internalKey: ApolloLibrary.KMMEdPrivateKey let keyType: String = "EC" - let keySpecifications: [String : String] = [ - "curve" : "Ed25519" - ] + let keySpecifications: [String : String] + let derivationPath: Domain.DerivationPath? var identifier: String var size: Int { raw.count } var raw: Data { internalKey.raw.toData() } init( identifier: String = UUID().uuidString, - internalKey: ApolloLibrary.KMMEdPrivateKey + internalKey: ApolloLibrary.KMMEdPrivateKey, + derivationPath: Domain.DerivationPath? = nil ) { self.identifier = identifier self.internalKey = internalKey + var keySpecifications = ["curve" : "Ed25519"] + derivationPath.map { keySpecifications[KeyProperties.derivationPath.rawValue] = $0.keyPathString() } + self.keySpecifications = keySpecifications + self.derivationPath = derivationPath } func publicKey() -> PublicKey { @@ -46,6 +50,7 @@ extension Ed25519PrivateKey: KeychainStorableKey { var restorationIdentifier: String { "ed25519+priv" } var storableData: Data { raw } var index: Int? { nil } + var queryDerivationPath: String? { derivationPath?.keyPathString() } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } @@ -83,6 +88,7 @@ extension Ed25519PublicKey: KeychainStorableKey { var restorationIdentifier: String { "ed25519+pub" } var storableData: Data { raw } var index: Int? { nil } + var queryDerivationPath: String? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } diff --git a/EdgeAgentSDK/Apollo/Sources/Model/LinkSecret.swift b/EdgeAgentSDK/Apollo/Sources/Model/LinkSecret.swift index fa91d108..d7dcd504 100644 --- a/EdgeAgentSDK/Apollo/Sources/Model/LinkSecret.swift +++ b/EdgeAgentSDK/Apollo/Sources/Model/LinkSecret.swift @@ -41,6 +41,7 @@ extension LinkSecret: KeychainStorableKey { var restorationIdentifier: String { "linkSecret+key" } var storableData: Data { raw } var index: Int? { nil } + var queryDerivationPath: String? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } diff --git a/EdgeAgentSDK/Apollo/Sources/Model/Secp256k1Key.swift b/EdgeAgentSDK/Apollo/Sources/Model/Secp256k1Key.swift index d70a64c5..813fe148 100644 --- a/EdgeAgentSDK/Apollo/Sources/Model/Secp256k1Key.swift +++ b/EdgeAgentSDK/Apollo/Sources/Model/Secp256k1Key.swift @@ -57,6 +57,7 @@ extension Secp256k1PrivateKey: KeychainStorableKey { var restorationIdentifier: String { "secp256k1+priv" } var storableData: Data { raw } var index: Int? { derivationPath.index } + var queryDerivationPath: String? { derivationPath.keyPathString() } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } @@ -112,6 +113,7 @@ extension Secp256k1PublicKey: KeychainStorableKey { var restorationIdentifier: String { "secp256k1+pub" } var storableData: Data { raw } var index: Int? { nil } + var queryDerivationPath: String? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } diff --git a/EdgeAgentSDK/Apollo/Sources/Model/X25519Key.swift b/EdgeAgentSDK/Apollo/Sources/Model/X25519Key.swift index 7595525d..26ad6a35 100644 --- a/EdgeAgentSDK/Apollo/Sources/Model/X25519Key.swift +++ b/EdgeAgentSDK/Apollo/Sources/Model/X25519Key.swift @@ -5,19 +5,23 @@ import Foundation struct X25519PrivateKey: PrivateKey { private let internalKey: ApolloLibrary.KMMX25519PrivateKey let keyType: String = "EC" - let keySpecifications: [String : String] = [ - "curve" : "x25519" - ] + let keySpecifications: [String : String] + let derivationPath: Domain.DerivationPath? var identifier:String var size: Int { raw.count } var raw: Data { internalKey.raw.toData() } init( identifier: String = UUID().uuidString, - internalKey: ApolloLibrary.KMMX25519PrivateKey + internalKey: ApolloLibrary.KMMX25519PrivateKey, + derivationPath: Domain.DerivationPath? = nil ) { self.identifier = identifier self.internalKey = internalKey + var keySpecifications = ["curve" : "x25519"] + derivationPath.map { keySpecifications[KeyProperties.derivationPath.rawValue] = $0.keyPathString() } + self.keySpecifications = keySpecifications + self.derivationPath = derivationPath } func publicKey() -> PublicKey { @@ -34,6 +38,7 @@ extension X25519PrivateKey: KeychainStorableKey { var restorationIdentifier: String { "x25519+priv" } var storableData: Data { raw } var index: Int? { nil } + var queryDerivationPath: String? { derivationPath?.keyPathString() } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } @@ -71,6 +76,7 @@ extension X25519PublicKey: KeychainStorableKey { var restorationIdentifier: String { "x25519+pub" } var storableData: Data { raw } var index: Int? { nil } + var queryDerivationPath: String? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } diff --git a/EdgeAgentSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift b/EdgeAgentSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift index 79fe560c..b8ca1b47 100644 --- a/EdgeAgentSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift +++ b/EdgeAgentSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift @@ -12,10 +12,15 @@ struct CreateEd25519KeyPairOperation { } - func compute(identifier: String = UUID().uuidString, fromPrivateKey: Data) throws -> PrivateKey { + func compute( + identifier: String = UUID().uuidString, + fromPrivateKey: Data, + derivationPath: Domain.DerivationPath? = nil + ) throws -> PrivateKey { return Ed25519PrivateKey( identifier: identifier, - internalKey: KMMEdPrivateKey(raw: fromPrivateKey.toKotlinByteArray()) + internalKey: KMMEdPrivateKey(raw: fromPrivateKey.toKotlinByteArray()), + derivationPath: derivationPath ) } @@ -27,6 +32,9 @@ struct CreateEd25519KeyPairOperation { .derive(path: keyPath.keyPathString() ) - return Ed25519PrivateKey(internalKey: .init(raw: derivedHdKey.privateKey)) + return Ed25519PrivateKey( + internalKey: .init(raw: derivedHdKey.privateKey), + derivationPath: keyPath + ) } } diff --git a/EdgeAgentSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift b/EdgeAgentSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift index 48de1609..4ca9a1a9 100644 --- a/EdgeAgentSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift +++ b/EdgeAgentSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift @@ -13,11 +13,16 @@ struct CreateX25519KeyPairOperation { } - func compute(identifier: String = UUID().uuidString, fromPrivateKey: Data) throws -> PrivateKey { + func compute( + identifier: String = UUID().uuidString, + fromPrivateKey: Data, + derivationPath: Domain.DerivationPath? = nil + ) throws -> PrivateKey { let privateKey = KMMX25519PrivateKey(raw: fromPrivateKey.toKotlinByteArray()) return X25519PrivateKey( identifier: identifier, - internalKey: privateKey + internalKey: privateKey, + derivationPath: derivationPath ) } @@ -29,6 +34,9 @@ struct CreateX25519KeyPairOperation { .derive(path: keyPath.keyPathString() ) - return X25519PrivateKey(internalKey: KMMEdPrivateKey(raw: derivedHdKey.privateKey).x25519PrivateKey()) + return X25519PrivateKey( + internalKey: KMMEdPrivateKey(raw: derivedHdKey.privateKey).x25519PrivateKey(), + derivationPath: keyPath + ) } } diff --git a/EdgeAgentSDK/Castor/Tests/PrismDIDPublicKeyTests.swift b/EdgeAgentSDK/Castor/Tests/PrismDIDPublicKeyTests.swift index a72b0b71..31d78b72 100644 --- a/EdgeAgentSDK/Castor/Tests/PrismDIDPublicKeyTests.swift +++ b/EdgeAgentSDK/Castor/Tests/PrismDIDPublicKeyTests.swift @@ -16,7 +16,7 @@ final class PrismDIDPublicKeyTests: XCTestCase { KeyProperties.type.rawValue: "EC", KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, KeyProperties.seed.rawValue: seed.value.base64Encoded(), - KeyProperties.derivationPath.rawValue: DerivationPath(index: 0).keyPathString() + KeyProperties.derivationPath.rawValue: DerivationPath().keyPathString() ]) } diff --git a/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/DerivableKey.swift b/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/DerivableKey.swift index 74f44668..f368c94c 100644 --- a/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/DerivableKey.swift +++ b/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/DerivableKey.swift @@ -2,13 +2,60 @@ import Foundation /// `DerivationPath` is a structure that represents the path for key derivation in hierarchical deterministic (HD) wallets. public struct DerivationPath { + + public enum Axis: RawRepresentable { + case normal(Int) + case hardened(Int) + + public init?(rawValue: Int) { + if rawValue < 0 { + return nil + } + if rawValue >= 0 && rawValue < (1 << 31) { + self = .normal(rawValue) + } else if rawValue >= (1 << 31) && rawValue < (1 << 32) { + self = .hardened(rawValue - (1 << 31)) + } else { + return nil + } + } + + public var rawValue: Int { + switch self { + case .normal(let int): + return int + case .hardened(let int): + return int + } + } + + public var string: String { + switch self { + case .normal(let int): + return "\(int)" + case .hardened(let int): + return "\(int)'" + } + } + } + /// The index of the key in the path. + @available(*, deprecated, renamed: "axis", message: "Use axis instead this property will be removed on a future version") public let index: Int + public let axis: [Axis] + /// Creates a new `DerivationPath` instance from a given index. /// - Parameter index: The index of the key in the path. + @available(*, deprecated, renamed: "init(axis:)", message: "Use init(axis:) instead this method will be removed on a future version") public init(index: Int) { self.index = index + self.axis = [.hardened(index), .hardened(0), .hardened(0)] + } + + public init(axis: [Axis] = [.hardened(0), .hardened(0), .hardened(0)]) { + self.axis = axis + self.index = axis.first?.rawValue ?? 0 } /// Creates a new `DerivationPath` instance from a path string. @@ -26,16 +73,29 @@ public struct DerivationPath { let parsingRegex = try NSRegularExpression(pattern: "\\d+", options: []) let matches = parsingRegex.matches(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count)) - guard let firstMatch = matches.first else { throw CommonError.invalidRegex(regex: pattern, invalid: string) } + guard let firstMatch = matches.first else { + throw CommonError.invalidRegex(regex: pattern, invalid: string) + } let range = Range(firstMatch.range, in: string)! - guard let index = Int(String(string[range])) else { throw CommonError.invalidRegex(regex: pattern, invalid: string) } + guard let index = Int(String(string[range])) else { + throw CommonError.invalidRegex(regex: pattern, invalid: string) + } self.index = index + self.axis = try matches.map { + let range = Range($0.range, in: string)! + guard + let index = Int(String(string[range])) + else { + throw CommonError.invalidRegex(regex: pattern, invalid: string) + } + return Axis.hardened(index) + } } /// Returns a string representation of this `DerivationPath`. /// - Returns: A string in the format of "m/'/0'/0'". public func keyPathString() -> String { - return "m/\(index)'/0'/0'" + return (["m"] + axis.map(\.string)).joined(separator: "/") } } diff --git a/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift b/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift index e89456e0..fa804006 100644 --- a/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift +++ b/EdgeAgentSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift @@ -12,7 +12,11 @@ public protocol StorableKey { var storableData: Data { get } /// Indexation of the key is useful to keep track of a derivation index + @available(*, deprecated, renamed: "derivationPath", message: "Use derivationPath instead this property will be removed on a future version") var index: Int? { get } + + /// Derivation path used for this key is useful to keep track of a derivation index + var queryDerivationPath: String? { get } } /// Extension of the `Key` protocol to provide additional functionality related to storage. diff --git a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift index e7b5a4c1..fb0ba116 100644 --- a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift +++ b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift @@ -72,7 +72,7 @@ extension EdgeAgent { KeyProperties.type.rawValue: "EC", KeyProperties.curve.rawValue: KnownKeyCurves.x25519.rawValue, KeyProperties.seed.rawValue: seed.value.base64Encoded(), - KeyProperties.derivationPath.rawValue: DerivationPath(index: 0).keyPathString() + KeyProperties.derivationPath.rawValue: DerivationPath().keyPathString() ]) guard let exporting = masterKey.exporting else { @@ -109,7 +109,7 @@ extension EdgeAgent { KeyProperties.type.rawValue: "EC", KeyProperties.curve.rawValue: KnownKeyCurves.x25519.rawValue, KeyProperties.seed.rawValue: seed.value.base64Encoded(), - KeyProperties.derivationPath.rawValue: DerivationPath(index: 0).keyPathString() + KeyProperties.derivationPath.rawValue: DerivationPath().keyPathString() ]) guard let exporting = masterKey.exporting else { @@ -258,6 +258,7 @@ extension EdgeAgent { struct LinkSecretStorableKey: StorableKey { var identifier = "linkSecret" let index: Int? = nil + let queryDerivationPath: String? = nil let storableData: Data let restorationIdentifier = "linkSecret+key" } diff --git a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+DIDHigherFucntions.swift b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+DIDHigherFucntions.swift index 538f7e5c..2921a561 100644 --- a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+DIDHigherFucntions.swift +++ b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+DIDHigherFucntions.swift @@ -80,7 +80,10 @@ Could not find key in storage please use Castor instead and provide the private KeyProperties.type.rawValue: "EC", KeyProperties.seed.rawValue: seed.value.base64Encoded(), KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, - KeyProperties.derivationPath.rawValue: DerivationPath(index: index).keyPathString() + KeyProperties.derivationPath.rawValue: EdgeAgentDerivationPath( + keyPurpose: .master, + keyIndex: index + ).derivationPath.keyPathString() ]) let newDID = try castor.createPrismDID(masterPublicKey: privateKey.publicKey(), services: services) diff --git a/EdgeAgentSDK/EdgeAgent/Sources/Helpers/EdgeAgentDerivationPath.swift b/EdgeAgentSDK/EdgeAgent/Sources/Helpers/EdgeAgentDerivationPath.swift new file mode 100644 index 00000000..3dcd56ed --- /dev/null +++ b/EdgeAgentSDK/EdgeAgent/Sources/Helpers/EdgeAgentDerivationPath.swift @@ -0,0 +1,37 @@ +import Domain +import Foundation + +struct EdgeAgentDerivationPath { + enum KeyPurpose: Int { + case master = 1 + case issuing = 2 + case keyAgreement = 3 + case authentication = 4 + case revocation = 5 + case capabilityInvocation = 6 + case capabilityDelegation = 7 + } + let walletPurpose: Int + let didMethod: Int + let didIndex: Int + let keyPurpose: Int + let keyIndex: Int + + init(walletPurpose: Int = 29, didMethod: Int = 29, didIndex: Int = 0, keyPurpose: KeyPurpose, keyIndex: Int) { + self.walletPurpose = walletPurpose + self.didMethod = didMethod + self.didIndex = didIndex + self.keyPurpose = keyPurpose.rawValue + self.keyIndex = keyIndex + } + + var derivationPath: DerivationPath { + .init(axis: [ + .hardened(walletPurpose), + .hardened(didMethod), + .hardened(didIndex), + .hardened(keyPurpose), + .hardened(keyIndex), + ]) + } +} diff --git a/EdgeAgentSDK/EdgeAgent/Tests/CheckTest.swift b/EdgeAgentSDK/EdgeAgent/Tests/CheckTest.swift index ce764bf5..ad583a2b 100644 --- a/EdgeAgentSDK/EdgeAgent/Tests/CheckTest.swift +++ b/EdgeAgentSDK/EdgeAgent/Tests/CheckTest.swift @@ -17,7 +17,7 @@ final class CheckTests: XCTestCase { KeyProperties.type.rawValue: "EC", KeyProperties.seed.rawValue: seed.seed.value.base64Encoded(), KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, - KeyProperties.derivationPath.rawValue: DerivationPath(index: 0).keyPathString() + KeyProperties.derivationPath.rawValue: DerivationPath().keyPathString() ]) let payload = try "test".tryToData() diff --git a/EdgeAgentSDK/EdgeAgent/Tests/DerivationPathTests.swift b/EdgeAgentSDK/EdgeAgent/Tests/DerivationPathTests.swift new file mode 100644 index 00000000..5745927c --- /dev/null +++ b/EdgeAgentSDK/EdgeAgent/Tests/DerivationPathTests.swift @@ -0,0 +1,46 @@ +import Domain +@testable import EdgeAgent +import XCTest + +final class DerivationPathTests: XCTestCase { + func testDerivationPathInitString() throws { + let derivationStr = "m/29'/29'/0'/1'/2'" + let derivationPath = try DerivationPath(string: derivationStr) + + XCTAssertEqual(derivationPath.axis.count, 5) + XCTAssertEqual(derivationPath.axis.map(\.rawValue), [29, 29, 0, 1, 2]) + XCTAssertEqual(derivationPath.index, 29) + } + + func testDerivationPathInitAxis() throws { + let derivationAxis: [DerivationPath.Axis] = [ + .hardened(0x1D), + .hardened(0x1d), + .hardened(0), + .hardened(1), + .hardened(2) + ] + let derivationPath = DerivationPath(axis: derivationAxis) + + XCTAssertEqual(derivationPath.axis.count, 5) + XCTAssertEqual(derivationPath.axis.map(\.rawValue), [29, 29, 0, 1, 2]) + XCTAssertEqual(derivationPath.keyPathString(), "m/29'/29'/0'/1'/2'") + XCTAssertEqual(derivationPath.index, 29) + } + + func testDerivationPathSmaller() throws { + let derivationStr = "m/1'/0'/0'" + let derivationPath = try DerivationPath(string: derivationStr) + + XCTAssertEqual(derivationPath.axis.count, 3) + XCTAssertEqual(derivationPath.axis.map(\.rawValue), [1, 0, 0]) + XCTAssertEqual(derivationPath.index, 1) + } + + func testEdgeAgentDerivationPathMasterKey() throws { + let edgePath = EdgeAgentDerivationPath(keyPurpose: .master, keyIndex: 1) + + XCTAssertEqual(edgePath.derivationPath.axis.map(\.rawValue), [29, 29, 0, 1, 1]) + XCTAssertEqual(edgePath.derivationPath.keyPathString(), "m/29'/29'/0'/1'/1'") + } +} diff --git a/EdgeAgentSDK/Pluto/Sources/Domain/StorableKeyModel.swift b/EdgeAgentSDK/Pluto/Sources/Domain/StorableKeyModel.swift index 439d9079..16fb6a11 100644 --- a/EdgeAgentSDK/Pluto/Sources/Domain/StorableKeyModel.swift +++ b/EdgeAgentSDK/Pluto/Sources/Domain/StorableKeyModel.swift @@ -6,4 +6,5 @@ struct StorableKeyModel: StorableKey { let restorationIdentifier: String let storableData: Data let index: Int? + let queryDerivationPath: String? } diff --git a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift index 509e8aa1..c906cc63 100644 --- a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift +++ b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift @@ -54,14 +54,16 @@ extension CDKey { identifier: keychainKey.identifier, restorationIdentifier: keychainKey.restorationIdentifier, storableData: keyData, - index: keychainKey.index?.intValue + index: keychainKey.index?.intValue, + queryDerivationPath: keychainKey.derivationPath ) case let databaseKey as CDDatabaseKey: return StorableKeyModel( identifier: databaseKey.identifier, restorationIdentifier: databaseKey.restorationIdentifier, storableData: databaseKey.storableData, - index: databaseKey.index?.intValue + index: databaseKey.index?.intValue, + queryDerivationPath: databaseKey.derivationPath ) default: throw UnknownError.somethingWentWrongError( diff --git a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift index acdd203d..56fb57ed 100644 --- a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift +++ b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift @@ -9,6 +9,7 @@ extension CDKey { @NSManaged var identifier: String @NSManaged var restorationIdentifier: String @NSManaged var index: NSNumber? + @NSManaged var derivationPath: String? @NSManaged var did: CDDIDPrivateKey? } diff --git a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKeychainKey+CoreDataProperties.swift b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKeychainKey+CoreDataProperties.swift index cb9e8641..888ebb1c 100644 --- a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKeychainKey+CoreDataProperties.swift +++ b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDKeychainKey+CoreDataProperties.swift @@ -10,5 +10,4 @@ extension CDKeychainKey { @NSManaged var service: String @NSManaged var type: String @NSManaged var algorithm: String - } diff --git a/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/.xccurrentversion b/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/.xccurrentversion index 1939a3e2..390fb98c 100644 --- a/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/.xccurrentversion +++ b/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - PrismPluto.xcdatamodel + PrismPlutoV2.xcdatamodel diff --git a/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPlutoV2.xcdatamodel/contents b/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPlutoV2.xcdatamodel/contents new file mode 100644 index 00000000..2f7c595e --- /dev/null +++ b/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPlutoV2.xcdatamodel/contents @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EdgeAgentSDK/Pluto/Tests/Helper/PrivateKey+Test.swift b/EdgeAgentSDK/Pluto/Tests/Helper/PrivateKey+Test.swift index d98d28c8..e9f95ac6 100644 --- a/EdgeAgentSDK/Pluto/Tests/Helper/PrivateKey+Test.swift +++ b/EdgeAgentSDK/Pluto/Tests/Helper/PrivateKey+Test.swift @@ -8,6 +8,7 @@ struct MockPrivateKey: PrivateKey, StorableKey, Equatable { let raw: Data var identifier = "TestMockPrivKey" var index: Int? = 0 + var queryDerivationPath: String? = nil let restorationIdentifier = "MockPrivate" var storableData: Data { raw }