From 7525de037ae3d3a0ce8dca19348f6992fb64b9e0 Mon Sep 17 00:00:00 2001 From: Russel Date: Thu, 29 Oct 2020 23:02:29 +0300 Subject: [PATCH 01/10] no need to override host app build system --- FearlessUtils.podspec | 1 - 1 file changed, 1 deletion(-) diff --git a/FearlessUtils.podspec b/FearlessUtils.podspec index 81afb07..4f5ec31 100644 --- a/FearlessUtils.podspec +++ b/FearlessUtils.podspec @@ -19,7 +19,6 @@ Pod::Spec.new do |s| s.ios.deployment_target = '11.0' - s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64'} s.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } s.source_files = 'FearlessUtils/Classes/**/*' From 07fd4c799f63a5e9f400f8cfeb94b7a002eaf1b5 Mon Sep 17 00:00:00 2001 From: Russel Date: Thu, 29 Oct 2020 23:15:17 +0300 Subject: [PATCH 02/10] temp disable podspec for ci --- FearlessUtils.podspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/FearlessUtils.podspec b/FearlessUtils.podspec index 4f5ec31..1c1b649 100644 --- a/FearlessUtils.podspec +++ b/FearlessUtils.podspec @@ -19,8 +19,6 @@ Pod::Spec.new do |s| s.ios.deployment_target = '11.0' - s.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } - s.source_files = 'FearlessUtils/Classes/**/*' s.dependency 'IrohaCrypto/sr25519', '~> 0.7.0' s.dependency 'IrohaCrypto/ed25519', '~> 0.7.0' From 3b9c002b1d3cfdbcbf9bdaf97a03facb254f6498 Mon Sep 17 00:00:00 2001 From: Russel Date: Sat, 31 Oct 2020 00:25:11 +0300 Subject: [PATCH 03/10] add keystore encoding --- .../FearlessUtils.xcodeproj/project.pbxproj | 4 + Example/Podfile.lock | 2 +- .../Classes/Common/Data+Random.swift | 20 +++++ .../Classes/Keystore/KeystoreBuilder.swift | 75 +++++++++++++++++ .../Classes/Keystore/KeystoreCommon.swift | 48 +++++++++++ .../Classes/Keystore/KeystoreData.swift | 2 +- .../Classes/Keystore/KeystoreDefinition.swift | 8 +- .../Classes/Keystore/KeystoreExtractor.swift | 61 ++++---------- .../Classes/Keystore/ScryptParameters.swift | 46 ++++++++++- Tests/Keystore/KeystoreBuilderTests.swift | 81 +++++++++++++++++++ 10 files changed, 293 insertions(+), 54 deletions(-) create mode 100644 FearlessUtils/Classes/Common/Data+Random.swift create mode 100644 FearlessUtils/Classes/Keystore/KeystoreBuilder.swift create mode 100644 FearlessUtils/Classes/Keystore/KeystoreCommon.swift create mode 100644 Tests/Keystore/KeystoreBuilderTests.swift diff --git a/Example/FearlessUtils.xcodeproj/project.pbxproj b/Example/FearlessUtils.xcodeproj/project.pbxproj index 685ffb6..e8fcdaa 100644 --- a/Example/FearlessUtils.xcodeproj/project.pbxproj +++ b/Example/FearlessUtils.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 84DA3B4524C8CE5A00B5E27F /* ScaleUInt8Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA3B3B24C8CE5900B5E27F /* ScaleUInt8Tests.swift */; }; 84ED7256254A001A006102DF /* SubstrateQREncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ED7255254A001A006102DF /* SubstrateQREncoderTests.swift */; }; 84ED725C254A0233006102DF /* SubstrateQRDecoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ED725B254A0233006102DF /* SubstrateQRDecoderTests.swift */; }; + 84F4A847254CAD73000CF0A3 /* KeystoreBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F4A846254CAD73000CF0A3 /* KeystoreBuilderTests.swift */; }; A0FDCFB5B61F738FEE34A5E6 /* Pods_FearlessUtils_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16D2E77D7010BEBF43DFE08E /* Pods_FearlessUtils_Tests.framework */; }; B2BF1BE136637C8404B79450 /* Pods_FearlessUtils_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9A63E44DBCDCE738F798358 /* Pods_FearlessUtils_Example.framework */; }; /* End PBXBuildFile section */ @@ -93,6 +94,7 @@ 84DA3B4824C8CEEF00B5E27F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info.plist; path = Tests/Info.plist; sourceTree = SOURCE_ROOT; }; 84ED7255254A001A006102DF /* SubstrateQREncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubstrateQREncoderTests.swift; sourceTree = ""; }; 84ED725B254A0233006102DF /* SubstrateQRDecoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubstrateQRDecoderTests.swift; sourceTree = ""; }; + 84F4A846254CAD73000CF0A3 /* KeystoreBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeystoreBuilderTests.swift; sourceTree = ""; }; 93F24F5EEC204E4B0F918C39 /* Pods-FearlessUtils_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FearlessUtils_Example.debug.xcconfig"; path = "Target Support Files/Pods-FearlessUtils_Example/Pods-FearlessUtils_Example.debug.xcconfig"; sourceTree = ""; }; B82F3EE26AAEE44C6E7F9962 /* Pods-FearlessUtils_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FearlessUtils_Tests.release.xcconfig"; path = "Target Support Files/Pods-FearlessUtils_Tests/Pods-FearlessUtils_Tests.release.xcconfig"; sourceTree = ""; }; B9A63E44DBCDCE738F798358 /* Pods_FearlessUtils_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FearlessUtils_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -233,6 +235,7 @@ isa = PBXGroup; children = ( 84CB472324DBE82C00837E11 /* KeystoreExtractorTests.swift */, + 84F4A846254CAD73000CF0A3 /* KeystoreBuilderTests.swift */, ); path = Keystore; sourceTree = ""; @@ -517,6 +520,7 @@ 84ED7256254A001A006102DF /* SubstrateQREncoderTests.swift in Sources */, 842D1E7324CFF4EC00C30A7A /* KeypairDeriviationTests.swift in Sources */, 84CB472424DBE82C00837E11 /* KeystoreExtractorTests.swift in Sources */, + 84F4A847254CAD73000CF0A3 /* KeystoreBuilderTests.swift in Sources */, 842D1E7124CFF31800C30A7A /* KeypairDeriviation.swift in Sources */, 842D1E2524C9AC5D00C30A7A /* ScaleBoolOptionTests.swift in Sources */, 845D80ED24C8D91A00EC2540 /* SeedFactoryTests.swift in Sources */, diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 2e4ab2d..355c5b4 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -44,7 +44,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: BigInt: 74b4d88367b0e819d9f77393549226d36faeb0d8 - FearlessUtils: d4e6e36e45ce2fb48c6981dba2192f1f6c7183eb + FearlessUtils: 979e4d4f7fce0432217588b6d693cb86166545e5 IrohaCrypto: bddf15865c4df189b9c5273ba9c675afae8b7f4c scrypt.c: b42ae06183251329d2b2c620c226fb541a4a3592 TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 diff --git a/FearlessUtils/Classes/Common/Data+Random.swift b/FearlessUtils/Classes/Common/Data+Random.swift new file mode 100644 index 0000000..16ea544 --- /dev/null +++ b/FearlessUtils/Classes/Common/Data+Random.swift @@ -0,0 +1,20 @@ +import Foundation + +enum RandomDataError: Error { + case generatorFailed +} + +extension Data { + static func gerateRandomBytes(of length: Int) throws -> Data { + var data = Data(count: length) + let result = data.withUnsafeMutableBytes { + SecRandomCopyBytes(kSecRandomDefault, length, $0.baseAddress!) + } + + guard result == errSecSuccess else { + throw RandomDataError.generatorFailed + } + + return data + } +} diff --git a/FearlessUtils/Classes/Keystore/KeystoreBuilder.swift b/FearlessUtils/Classes/Keystore/KeystoreBuilder.swift new file mode 100644 index 0000000..b010b39 --- /dev/null +++ b/FearlessUtils/Classes/Keystore/KeystoreBuilder.swift @@ -0,0 +1,75 @@ +import Foundation +import IrohaCrypto +import TweetNacl + +public class KeystoreBuilder { + private var name: String? + private var creationDate = Date() + + public init() {} +} + +extension KeystoreBuilder: KeystoreBuilding { + public func with(name: String) -> Self { + self.name = name + return self + } + + public func with(creationDate: Date) -> Self { + self.creationDate = creationDate + return self + } + + public func build(from data: KeystoreData, password: String?) throws -> KeystoreDefinition { + let scryptParameters = try ScryptParameters() + + let scryptData: Data + + if let password = password { + guard let passwordData = password.data(using: .utf8) else { + throw KeystoreExtractorError.invalidPasswordFormat + } + + scryptData = passwordData + } else { + scryptData = Data() + } + + let encryptionKey = try IRScryptKeyDeriviation() + .deriveKey(from: scryptData, + salt: scryptParameters.salt, + scryptN: UInt(scryptParameters.scryptN), + scryptP: UInt(scryptParameters.scryptP), + scryptR: UInt(scryptParameters.scryptR), + length: UInt(KeystoreConstants.encryptionKeyLength)) + + let nonce = try Data.gerateRandomBytes(of: KeystoreConstants.nonceLength) + + let secretKeyData: Data + switch data.cryptoType { + case .sr25519: + secretKeyData = try SNPrivateKey(rawData: data.secretKeyData).toEd25519Data() + case .ed25519: + secretKeyData = data.secretKeyData + case .ecdsa: + secretKeyData = data.secretKeyData + } + + let pcksData = KeystoreConstants.pkcs8Header + secretKeyData + + KeystoreConstants.pkcs8Divider + data.publicKeyData + let encrypted = try NaclSecretBox.secretBox(message: pcksData, nonce: nonce, key: encryptionKey) + let encoded = scryptParameters.encode() + nonce + encrypted + + let encodingType = [KeystoreEncodingType.scrypt.rawValue, KeystoreEncodingType.xsalsa.rawValue] + let encodingContent = [KeystoreEncodingContent.pkcs8.rawValue, data.cryptoType.rawValue] + let keystoreEncoding = KeystoreEncoding(content: encodingContent, + type: encodingType, + version: String(KeystoreConstants.version)) + + let meta = KeystoreMeta(name: name, createdAt: Int64(creationDate.timeIntervalSince1970)) + return KeystoreDefinition(address: data.address, + encoded: encoded.base64EncodedString(), + encoding: keystoreEncoding, + meta: meta) + } +} diff --git a/FearlessUtils/Classes/Keystore/KeystoreCommon.swift b/FearlessUtils/Classes/Keystore/KeystoreCommon.swift new file mode 100644 index 0000000..763dd01 --- /dev/null +++ b/FearlessUtils/Classes/Keystore/KeystoreCommon.swift @@ -0,0 +1,48 @@ +import Foundation + +public protocol KeystoreExtracting { + func extractFromDefinition(_ info: KeystoreDefinition, + password: String?) throws -> KeystoreData +} + +public protocol KeystoreBuilding { + func with(name: String) -> Self + func with(creationDate: Date) -> Self + + func build(from data: KeystoreData, password: String?) throws -> KeystoreDefinition +} + +public enum KeystoreExtractorError: Error { + case invalidBase64 + case missingScryptSalt + case missingScryptN + case missingScryptP + case missingScryptR + case unsupportedEncoding + case unsupportedContent + case unsupportedCryptoType + case invalidPasswordFormat + case missingPkcs8Header + case missingPkcs8Divider +} + +public enum KeystoreBuilderError: Error { + case invalidPasswordFormat +} + +enum KeystoreEncodingType: String { + case scrypt = "scrypt" + case xsalsa = "xsalsa20-poly1305" +} + +enum KeystoreEncodingContent: String { + case pkcs8 = "pkcs8" +} + +public struct KeystoreConstants { + public static let nonceLength = 24 + public static let encryptionKeyLength = 32 + public static let pkcs8Header = Data(bytes: [48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]) + public static let pkcs8Divider = Data(bytes: [161, 35, 3, 33, 0]) + public static let version = 3 +} diff --git a/FearlessUtils/Classes/Keystore/KeystoreData.swift b/FearlessUtils/Classes/Keystore/KeystoreData.swift index 458357c..4a2fcef 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreData.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreData.swift @@ -1,6 +1,6 @@ import Foundation -public struct KeystoreData { +public struct KeystoreData: Equatable { public let address: String public let secretKeyData: Data public let publicKeyData: Data diff --git a/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift b/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift index bb20c16..482392b 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift @@ -4,7 +4,7 @@ public struct KeystoreDefinition: Codable { public let address: String public let encoded: String public let encoding: KeystoreEncoding - public let meta: KeystoreMeta + public let meta: KeystoreMeta? public init(address: String, encoded: String, @@ -32,9 +32,9 @@ public struct KeystoreEncoding: Codable { public struct KeystoreMeta: Codable { enum CodingKeys: String, CodingKey { case name - case created = "whenCreated" + case createdAt = "whenCreated" } - public let name: String - public let created: Int64 + public let name: String? + public let createdAt: Int64? } diff --git a/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift b/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift index 34f3177..b2c67ff 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift @@ -2,39 +2,7 @@ import Foundation import IrohaCrypto import TweetNacl -public protocol KeystoreExtracting { - func extractFromDefinition(_ info: KeystoreDefinition, - password: String?) throws -> KeystoreData -} - -public enum KeystoreExtractorError: Error { - case invalidBase64 - case missingScryptSalt - case missingScryptN - case missingScryptP - case missingScryptR - case unsupportedEncoding - case unsupportedContent - case unsupportedCryptoType - case missingPkcs8Header - case missingPkcs8Divider -} - -private enum KeystoreEncodingType: String { - case scrypt = "scrypt" - case xsalsa = "xsalsa20-poly1305" -} - -private enum KeystoreEncodingContent: String { - case pkcs8 = "pkcs8" -} - -public struct KeystoreExtractor: KeystoreExtracting { - static let nonceLength = 24 - static let encryptionKeyLength = 32 - static let pkcs8Header = Data(bytes: [48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]) - static let pkcs8Divider = Data(bytes: [161, 35, 3, 33, 0]) - +public class KeystoreExtractor: KeystoreExtracting { public init() {} public func extractFromDefinition(_ info: KeystoreDefinition, @@ -57,21 +25,26 @@ public struct KeystoreExtractor: KeystoreExtracting { let scryptData: Data - if let passwordData = password?.data(using: .utf8) { + if let password = password { + guard let passwordData = password.data(using: .utf8) else { + throw KeystoreExtractorError.invalidPasswordFormat + } + scryptData = passwordData } else { scryptData = Data() } - let encryptionKey = try IRScryptKeyDeriviation().deriveKey(from: scryptData, - salt: scryptParameters.salt, - scryptN: UInt(scryptParameters.scryptN), - scryptP: UInt(scryptParameters.scryptP), - scryptR: UInt(scryptParameters.scryptR), - length: UInt(Self.encryptionKeyLength)) + let encryptionKey = try IRScryptKeyDeriviation() + .deriveKey(from: scryptData, + salt: scryptParameters.salt, + scryptN: UInt(scryptParameters.scryptN), + scryptP: UInt(scryptParameters.scryptP), + scryptR: UInt(scryptParameters.scryptR), + length: UInt(KeystoreConstants.encryptionKeyLength)) let nonceStart = ScryptParameters.encodedLength - let nonceEnd = ScryptParameters.encodedLength + Self.nonceLength + let nonceEnd = ScryptParameters.encodedLength + KeystoreConstants.nonceLength let nonce = Data(data[nonceStart..= Self.encodedLength else { throw ScryptParametersError.invalidDataLength } - self.salt = Data(data[0..<32]) + self.salt = Data(data[Self.saltRange]) - let valueN: UInt32 = data[32..<36].withUnsafeBytes { $0.pointee } + let valueN: UInt32 = data[Self.scryptNRange].withUnsafeBytes { $0.pointee } self.scryptN = valueN.littleEndian - let valueP: UInt32 = data[36..<40].withUnsafeBytes { $0.pointee } + let valueP: UInt32 = data[Self.scryptPRange].withUnsafeBytes { $0.pointee } self.scryptP = valueP.littleEndian - let valueR: UInt32 = data[40..<44].withUnsafeBytes { $0.pointee } + let valueR: UInt32 = data[Self.scryptRRange].withUnsafeBytes { $0.pointee } self.scryptR = valueR.littleEndian } + + func encode() -> Data { + var data = Data(repeating: 0, count: Self.encodedLength) + data.replaceSubrange(Self.saltRange, with: salt) + + var scryptN = self.scryptN + data.replaceSubrange(Self.scryptNRange, with: Data(bytes: &scryptN, count: MemoryLayout.size)) + + var scryptP = self.scryptP + data.replaceSubrange(Self.scryptPRange, with: Data(bytes: &scryptP, count: MemoryLayout.size)) + + var scryptR = self.scryptR + data.replaceSubrange(Self.scryptRRange, with: Data(bytes: &scryptR, count: MemoryLayout.size)) + + return data + } } diff --git a/Tests/Keystore/KeystoreBuilderTests.swift b/Tests/Keystore/KeystoreBuilderTests.swift new file mode 100644 index 0000000..ad7d253 --- /dev/null +++ b/Tests/Keystore/KeystoreBuilderTests.swift @@ -0,0 +1,81 @@ +import XCTest +import FearlessUtils +import IrohaCrypto + +class KeystoreBuilderTests: XCTestCase { + func testOnSr25519Json() { + guard let url = Bundle(for: KeystoreExtractorTests.self) + .url(forResource: "keystore-sr25519", withExtension: "json") else { + XCTFail("Can't find resource") + return + } + + do { + let testData = try Data(contentsOf: url) + performTestForData(testData, password: "test5") + } catch { + XCTFail("Unexpected error \(error)") + } + } + + func testOnEd25519Json() { + guard let url = Bundle(for: KeystoreExtractorTests.self) + .url(forResource: "keystore-ed25519", withExtension: "json") else { + XCTFail("Can't find resource") + return + } + + do { + let testData = try Data(contentsOf: url) + performTestForData(testData, password: "test2") + } catch { + XCTFail("Unexpected error \(error)") + } + } + + func testOnEcdsaJson() { + guard let url = Bundle(for: KeystoreExtractorTests.self) + .url(forResource: "keystore-ecdsa", withExtension: "json") else { + XCTFail("Can't find resource") + return + } + + do { + let testData = try Data(contentsOf: url) + performTestForData(testData, password: "test3") + } catch { + XCTFail("Unexpected error \(error)") + } + } + + // MARK: Private + + private func performTestForData(_ testData: Data, password: String) { + do { + let definition = try JSONDecoder().decode(KeystoreDefinition.self, from: testData) + + let extractor = KeystoreExtractor() + + let expectedKeystoreData = try extractor.extractFromDefinition(definition, + password: password) + + var builder = KeystoreBuilder() + + if let name = definition.meta?.name { + builder = builder.with(name: name) + } + + if let creationTimestamp = definition.meta?.createdAt { + builder = builder.with(creationDate: Date(timeIntervalSince1970: TimeInterval(creationTimestamp))) + } + + let resultDefinition = try builder.build(from: expectedKeystoreData, password: password) + + let resultKeystoreData = try extractor.extractFromDefinition(resultDefinition, password: password) + + XCTAssertEqual(expectedKeystoreData, resultKeystoreData) + } catch { + XCTFail("Unexpected error \(error)") + } + } +} From 56265efa33ee0e7f0186a5873801147f2a19659a Mon Sep 17 00:00:00 2001 From: Russel Date: Sat, 31 Oct 2020 00:49:56 +0300 Subject: [PATCH 04/10] storage key factory exttention add --- .../Classes/Common/StorageKeyFactory.swift | 36 ++++++++++++++++--- Tests/Common/StorageKeyFactoryTests.swift | 19 ++++++++-- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/FearlessUtils/Classes/Common/StorageKeyFactory.swift b/FearlessUtils/Classes/Common/StorageKeyFactory.swift index 147ccea..10c9321 100644 --- a/FearlessUtils/Classes/Common/StorageKeyFactory.swift +++ b/FearlessUtils/Classes/Common/StorageKeyFactory.swift @@ -1,15 +1,38 @@ import Foundation -public protocol StorageKeyFactoryProtocol { +public protocol StorageKeyFactoryProtocol: class { func createStorageKey(moduleName: String, serviceName: String) throws -> Data - func createStorageKey(moduleName: String, serviceName: String, identifier: Data) throws -> Data + func createStorageKey(moduleName: String, + serviceName: String, + identifier: Data, + hasher: StorageKeyHasher) throws -> Data } public enum StorageKeyFactoryError: Error { case badSerialization } -public struct StorageKeyFactory: StorageKeyFactoryProtocol { +public protocol StorageKeyHasher: class { + func hash(data: Data) throws -> Data +} + +public final class Blake128Concat: StorageKeyHasher { + public init() {} + + public func hash(data: Data) throws -> Data { + try data.blake128Concat() + } +} + +public final class Twox64Concat: StorageKeyHasher { + public init() {} + + public func hash(data: Data) throws -> Data { + data.twox64Concat() + } +} + +public final class StorageKeyFactory: StorageKeyFactoryProtocol { public init() {} public func createStorageKey(moduleName: String, serviceName: String) throws -> Data { @@ -24,10 +47,13 @@ public struct StorageKeyFactory: StorageKeyFactoryProtocol { return moduleKey.xxh128() + serviceKey.xxh128() } - public func createStorageKey(moduleName: String, serviceName: String, identifier: Data) throws -> Data { + public func createStorageKey(moduleName: String, + serviceName: String, + identifier: Data, + hasher: StorageKeyHasher) throws -> Data { let subkey = try createStorageKey(moduleName: moduleName, serviceName: serviceName) - let identifierHash = try identifier.blake128Concat() + let identifierHash: Data = try hasher.hash(data: identifier) return subkey + identifierHash } diff --git a/Tests/Common/StorageKeyFactoryTests.swift b/Tests/Common/StorageKeyFactoryTests.swift index ffd09dd..e7d99d6 100644 --- a/Tests/Common/StorageKeyFactoryTests.swift +++ b/Tests/Common/StorageKeyFactoryTests.swift @@ -3,7 +3,7 @@ import FearlessUtils class StorageKeyFactoryTests: XCTestCase { - func testKeyCreation() throws { + func testBlake128ConcatKeyCreation() throws { let factory = StorageKeyFactory() let identifier = try Data(hexString: "8ad2a3fba73321961cd5d1b8272aa95a21e75dd5b098fb36ed996961ac7b2931") @@ -12,9 +12,24 @@ class StorageKeyFactoryTests: XCTestCase { let key = try factory.createStorageKey(moduleName: "System", serviceName: "Account", - identifier: identifier) + identifier: identifier, + hasher: Blake128Concat()) XCTAssertEqual(key, expectedKey) } + func testTwox64ConcatKeyCreation() throws { + let factory = StorageKeyFactory() + + let identifier = try Data(hexString: "8ad2a3fba73321961cd5d1b8272aa95a21e75dd5b098fb36ed996961ac7b2931") + + let expectedKey = try Data(hexString: "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70c18acca30c9341bf8ad2a3fba73321961cd5d1b8272aa95a21e75dd5b098fb36ed996961ac7b2931") + + let key = try factory.createStorageKey(moduleName: "Staking", + serviceName: "Bonded", + identifier: identifier, + hasher: Twox64Concat()) + + XCTAssertEqual(key, expectedKey) + } } From 207be1936b428b6e1f6df8ae8f65171ba4847be9 Mon Sep 17 00:00:00 2001 From: Russel Date: Mon, 16 Nov 2020 12:02:10 +0300 Subject: [PATCH 05/10] add genesis hash --- FearlessUtils/Classes/Keystore/KeystoreBuilder.swift | 11 ++++++++++- FearlessUtils/Classes/Keystore/KeystoreCommon.swift | 1 + .../Classes/Keystore/KeystoreDefinition.swift | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/FearlessUtils/Classes/Keystore/KeystoreBuilder.swift b/FearlessUtils/Classes/Keystore/KeystoreBuilder.swift index b010b39..abc7c94 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreBuilder.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreBuilder.swift @@ -5,6 +5,7 @@ import TweetNacl public class KeystoreBuilder { private var name: String? private var creationDate = Date() + private var genesisHash: String? public init() {} } @@ -20,6 +21,11 @@ extension KeystoreBuilder: KeystoreBuilding { return self } + public func with(genesisHash: String) -> Self { + self.genesisHash = genesisHash + return self + } + public func build(from data: KeystoreData, password: String?) throws -> KeystoreDefinition { let scryptParameters = try ScryptParameters() @@ -66,7 +72,10 @@ extension KeystoreBuilder: KeystoreBuilding { type: encodingType, version: String(KeystoreConstants.version)) - let meta = KeystoreMeta(name: name, createdAt: Int64(creationDate.timeIntervalSince1970)) + let meta = KeystoreMeta(name: name, + createdAt: Int64(creationDate.timeIntervalSince1970), + genesisHash: genesisHash) + return KeystoreDefinition(address: data.address, encoded: encoded.base64EncodedString(), encoding: keystoreEncoding, diff --git a/FearlessUtils/Classes/Keystore/KeystoreCommon.swift b/FearlessUtils/Classes/Keystore/KeystoreCommon.swift index 763dd01..2864afb 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreCommon.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreCommon.swift @@ -8,6 +8,7 @@ public protocol KeystoreExtracting { public protocol KeystoreBuilding { func with(name: String) -> Self func with(creationDate: Date) -> Self + func with(genesisHash: String) -> Self func build(from data: KeystoreData, password: String?) throws -> KeystoreDefinition } diff --git a/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift b/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift index 482392b..c62e0ad 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift @@ -33,8 +33,10 @@ public struct KeystoreMeta: Codable { enum CodingKeys: String, CodingKey { case name case createdAt = "whenCreated" + case genesisHash } public let name: String? public let createdAt: Int64? + public let genesisHash: String? } From 55d367ac2416984e58c94f0424a88fcfe594a88b Mon Sep 17 00:00:00 2001 From: Russel Date: Mon, 16 Nov 2020 12:57:54 +0300 Subject: [PATCH 06/10] add keystor info factory --- .../Classes/Keystore/KeystoreExtractor.swift | 11 +++---- .../Classes/Keystore/KeystoreInfo.swift | 9 ++++++ .../Keystore/KeystoreInfoFactory.swift | 32 +++++++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 FearlessUtils/Classes/Keystore/KeystoreInfo.swift create mode 100644 FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift diff --git a/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift b/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift index b2c67ff..fb6a8eb 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreExtractor.swift @@ -57,17 +57,14 @@ public class KeystoreExtractor: KeystoreExtracting { } private func decodePkcs8(data: Data, definition: KeystoreDefinition) throws -> KeystoreData { + let info = try KeystoreInfoFactory().createInfo(from: definition) + let contentType = definition.encoding.content.count > 0 ? definition.encoding.content[0] : nil - let cryptoTypeValue = definition.encoding.content.count > 1 ? definition.encoding.content[1] : nil guard contentType == KeystoreEncodingContent.pkcs8.rawValue else { throw KeystoreExtractorError.unsupportedContent } - guard let value = cryptoTypeValue, let cryptoType = CryptoType(rawValue: value) else { - throw KeystoreExtractorError.unsupportedCryptoType - } - guard data.starts(with: KeystoreConstants.pkcs8Header) else { throw KeystoreExtractorError.missingPkcs8Header } @@ -82,7 +79,7 @@ public class KeystoreExtractor: KeystoreExtracting { let importedSecretData = Data(data[secretStart.. KeystoreInfo +} + +public enum KeystoreInfoFactoryError: Error { + case unsupportedCryptoType + case unsupportedAddressType +} + +public final class KeystoreInfoFactory: KeystoreInfoFactoryProtocol { + public func createInfo(from definition: KeystoreDefinition) throws -> KeystoreInfo { + let cryptoTypeValue = definition.encoding.content.count > 1 ? definition.encoding.content[1] : nil + + guard let value = cryptoTypeValue, let cryptoType = CryptoType(rawValue: value) else { + throw KeystoreInfoFactoryError.unsupportedCryptoType + } + + let addressTypeValue = try SS58AddressFactory().type(fromAddress: definition.address) + + guard let addressType = SNAddressType(rawValue: addressTypeValue.uint8Value) else { + throw KeystoreInfoFactoryError.unsupportedAddressType + } + + return KeystoreInfo(address: definition.address, + addressType: addressType, + cryptoType: cryptoType, + meta: definition.meta) + } +} From 7080eb6475729a18aec633dcf0f851a5a5805c0b Mon Sep 17 00:00:00 2001 From: Russel Date: Mon, 16 Nov 2020 13:22:52 +0300 Subject: [PATCH 07/10] fix keystore info factory interface --- FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift b/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift index 1c81257..83d0103 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift @@ -11,6 +11,8 @@ public enum KeystoreInfoFactoryError: Error { } public final class KeystoreInfoFactory: KeystoreInfoFactoryProtocol { + public init() {} + public func createInfo(from definition: KeystoreDefinition) throws -> KeystoreInfo { let cryptoTypeValue = definition.encoding.content.count > 1 ? definition.encoding.content[1] : nil From f66a91dedba84a597bdb1c9615f43ab24b0b15a3 Mon Sep 17 00:00:00 2001 From: Russel Date: Mon, 16 Nov 2020 13:30:12 +0300 Subject: [PATCH 08/10] make address type optoinal in info --- FearlessUtils/Classes/Keystore/KeystoreInfo.swift | 2 +- FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/FearlessUtils/Classes/Keystore/KeystoreInfo.swift b/FearlessUtils/Classes/Keystore/KeystoreInfo.swift index eea2b7c..e3368f2 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreInfo.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreInfo.swift @@ -3,7 +3,7 @@ import IrohaCrypto public struct KeystoreInfo { public let address: String - public let addressType: SNAddressType + public let addressType: SNAddressType? public let cryptoType: CryptoType public let meta: KeystoreMeta? } diff --git a/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift b/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift index 83d0103..ad4ef90 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift @@ -20,10 +20,12 @@ public final class KeystoreInfoFactory: KeystoreInfoFactoryProtocol { throw KeystoreInfoFactoryError.unsupportedCryptoType } - let addressTypeValue = try SS58AddressFactory().type(fromAddress: definition.address) + let addressType: SNAddressType? - guard let addressType = SNAddressType(rawValue: addressTypeValue.uint8Value) else { - throw KeystoreInfoFactoryError.unsupportedAddressType + if let addressTypeValue = try? SS58AddressFactory().type(fromAddress: definition.address) { + addressType = SNAddressType(rawValue: addressTypeValue.uint8Value) + } else { + addressType = nil } return KeystoreInfo(address: definition.address, From f821a9352d1592d60a43b7ced4fe977373d445a4 Mon Sep 17 00:00:00 2001 From: Russel Date: Thu, 19 Nov 2020 14:16:00 +0300 Subject: [PATCH 09/10] address in keystore must be optional --- FearlessUtils/Classes/Keystore/KeystoreData.swift | 4 ++-- FearlessUtils/Classes/Keystore/KeystoreDefinition.swift | 6 +++--- FearlessUtils/Classes/Keystore/KeystoreInfo.swift | 2 +- FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/FearlessUtils/Classes/Keystore/KeystoreData.swift b/FearlessUtils/Classes/Keystore/KeystoreData.swift index 4a2fcef..40dc931 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreData.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreData.swift @@ -1,12 +1,12 @@ import Foundation public struct KeystoreData: Equatable { - public let address: String + public let address: String? public let secretKeyData: Data public let publicKeyData: Data public let cryptoType: CryptoType - public init(address: String, secretKeyData: Data, publicKeyData: Data, cryptoType: CryptoType) { + public init(address: String?, secretKeyData: Data, publicKeyData: Data, cryptoType: CryptoType) { self.address = address self.secretKeyData = secretKeyData self.publicKeyData = publicKeyData diff --git a/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift b/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift index c62e0ad..d2b3928 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreDefinition.swift @@ -1,15 +1,15 @@ import Foundation public struct KeystoreDefinition: Codable { - public let address: String + public let address: String? public let encoded: String public let encoding: KeystoreEncoding public let meta: KeystoreMeta? - public init(address: String, + public init(address: String?, encoded: String, encoding: KeystoreEncoding, - meta: KeystoreMeta) { + meta: KeystoreMeta?) { self.address = address self.encoded = encoded self.encoding = encoding diff --git a/FearlessUtils/Classes/Keystore/KeystoreInfo.swift b/FearlessUtils/Classes/Keystore/KeystoreInfo.swift index e3368f2..4a48543 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreInfo.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreInfo.swift @@ -2,7 +2,7 @@ import Foundation import IrohaCrypto public struct KeystoreInfo { - public let address: String + public let address: String? public let addressType: SNAddressType? public let cryptoType: CryptoType public let meta: KeystoreMeta? diff --git a/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift b/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift index ad4ef90..d2cb631 100644 --- a/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift +++ b/FearlessUtils/Classes/Keystore/KeystoreInfoFactory.swift @@ -22,7 +22,8 @@ public final class KeystoreInfoFactory: KeystoreInfoFactoryProtocol { let addressType: SNAddressType? - if let addressTypeValue = try? SS58AddressFactory().type(fromAddress: definition.address) { + if let address = definition.address, + let addressTypeValue = try? SS58AddressFactory().type(fromAddress: address) { addressType = SNAddressType(rawValue: addressTypeValue.uint8Value) } else { addressType = nil From d36fec452d130372da3f1fa99a35f7ae48758343 Mon Sep 17 00:00:00 2001 From: Russel Date: Sat, 21 Nov 2020 23:49:29 +0300 Subject: [PATCH 10/10] fix arm64 simulators issues --- FearlessUtils.podspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FearlessUtils.podspec b/FearlessUtils.podspec index 1c1b649..68d135c 100644 --- a/FearlessUtils.podspec +++ b/FearlessUtils.podspec @@ -19,6 +19,8 @@ Pod::Spec.new do |s| s.ios.deployment_target = '11.0' + s.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64', 'VALID_ARCHS' => 'x86_64 armv7 arm64' } + s.source_files = 'FearlessUtils/Classes/**/*' s.dependency 'IrohaCrypto/sr25519', '~> 0.7.0' s.dependency 'IrohaCrypto/ed25519', '~> 0.7.0'