Skip to content

Commit

Permalink
Merge pull request #15 from soramitsu/feature/keystore-encoding
Browse files Browse the repository at this point in the history
Add keystore encoding
  • Loading branch information
ERussel authored Nov 21, 2020
2 parents a61e770 + d36fec4 commit d90cd56
Show file tree
Hide file tree
Showing 15 changed files with 409 additions and 75 deletions.
4 changes: 4 additions & 0 deletions Example/FearlessUtils.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 = "<group>"; };
84ED725B254A0233006102DF /* SubstrateQRDecoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubstrateQRDecoderTests.swift; sourceTree = "<group>"; };
84F4A846254CAD73000CF0A3 /* KeystoreBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeystoreBuilderTests.swift; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
B9A63E44DBCDCE738F798358 /* Pods_FearlessUtils_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FearlessUtils_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -233,6 +235,7 @@
isa = PBXGroup;
children = (
84CB472324DBE82C00837E11 /* KeystoreExtractorTests.swift */,
84F4A846254CAD73000CF0A3 /* KeystoreBuilderTests.swift */,
);
path = Keystore;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
Expand Down
2 changes: 1 addition & 1 deletion Example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
BigInt: 74b4d88367b0e819d9f77393549226d36faeb0d8
FearlessUtils: d4e6e36e45ce2fb48c6981dba2192f1f6c7183eb
FearlessUtils: 979e4d4f7fce0432217588b6d693cb86166545e5
IrohaCrypto: bddf15865c4df189b9c5273ba9c675afae8b7f4c
scrypt.c: b42ae06183251329d2b2c620c226fb541a4a3592
TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6
Expand Down
3 changes: 1 addition & 2 deletions FearlessUtils.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ 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.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'
Expand Down
20 changes: 20 additions & 0 deletions FearlessUtils/Classes/Common/Data+Random.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
36 changes: 31 additions & 5 deletions FearlessUtils/Classes/Common/StorageKeyFactory.swift
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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
}
Expand Down
84 changes: 84 additions & 0 deletions FearlessUtils/Classes/Keystore/KeystoreBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import Foundation
import IrohaCrypto
import TweetNacl

public class KeystoreBuilder {
private var name: String?
private var creationDate = Date()
private var genesisHash: String?

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 with(genesisHash: String) -> Self {
self.genesisHash = genesisHash
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),
genesisHash: genesisHash)

return KeystoreDefinition(address: data.address,
encoded: encoded.base64EncodedString(),
encoding: keystoreEncoding,
meta: meta)
}
}
49 changes: 49 additions & 0 deletions FearlessUtils/Classes/Keystore/KeystoreCommon.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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 with(genesisHash: String) -> 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
}
6 changes: 3 additions & 3 deletions FearlessUtils/Classes/Keystore/KeystoreData.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Foundation

public struct KeystoreData {
public let address: String
public struct KeystoreData: Equatable {
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
Expand Down
16 changes: 9 additions & 7 deletions FearlessUtils/Classes/Keystore/KeystoreDefinition.swift
Original file line number Diff line number Diff line change
@@ -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 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
Expand All @@ -32,9 +32,11 @@ public struct KeystoreEncoding: Codable {
public struct KeystoreMeta: Codable {
enum CodingKeys: String, CodingKey {
case name
case created = "whenCreated"
case createdAt = "whenCreated"
case genesisHash
}

public let name: String
public let created: Int64
public let name: String?
public let createdAt: Int64?
public let genesisHash: String?
}
Loading

0 comments on commit d90cd56

Please sign in to comment.