From eebd96b2174bc43fbf008a9bde8bbc1ac3e41c7d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 26 Apr 2020 18:38:30 +0200 Subject: [PATCH 001/144] Added masterkey decryption methods --- CryptoLib.xcodeproj/project.pbxproj | 42 ++++-- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++ CryptoLib/Cryptor.swift | 39 ++++++ CryptoLib/Info.plist | 2 +- CryptoLib/Masterkey.swift | 128 ++++++++++++++++++ CryptoLibTests/CryptoLibTests.swift | 34 ----- CryptoLibTests/MasterkeyTests.swift | 53 ++++++++ Podfile | 2 +- Podfile.lock | 17 ++- README.md | 6 + 10 files changed, 282 insertions(+), 49 deletions(-) create mode 100644 CryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 CryptoLib/Cryptor.swift create mode 100644 CryptoLib/Masterkey.swift delete mode 100644 CryptoLibTests/CryptoLibTests.swift create mode 100644 CryptoLibTests/MasterkeyTests.swift create mode 100644 README.md diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index eb7432e..d538d3e 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -7,10 +7,13 @@ objects = { /* Begin PBXBuildFile section */ - 09D994438BAA505986957D1A /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D8F927A885B3AC17754363D1 /* libPods-CryptoLib.a */; }; 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; }; - 4A7C21412451F2AC00DE81E6 /* CryptoLibTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7C21402451F2AC00DE81E6 /* CryptoLibTests.swift */; }; 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 52D51418B3FFF0E212F47BAA /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A239569B6D7676AE64227515 /* libPods-CryptoLib.a */; }; + 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB811245412E900F9FF51 /* Cryptor.swift */; }; + 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB8132454708600F9FF51 /* Masterkey.swift */; }; + 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */; }; + 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -30,9 +33,12 @@ 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptoLib.h; sourceTree = ""; }; 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 4A7C21402451F2AC00DE81E6 /* CryptoLibTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoLibTests.swift; sourceTree = ""; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D8F927A885B3AC17754363D1 /* libPods-CryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9E9BB811245412E900F9FF51 /* Cryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = ""; }; + 9E9BB8132454708600F9FF51 /* Masterkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Masterkey.swift; sourceTree = ""; }; + 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyTests.swift; sourceTree = ""; }; + 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libCryptoSwift.a; sourceTree = BUILT_PRODUCTS_DIR; }; + A239569B6D7676AE64227515 /* libPods-CryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -40,7 +46,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 09D994438BAA505986957D1A /* libPods-CryptoLib.a in Frameworks */, + 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */, + 52D51418B3FFF0E212F47BAA /* libPods-CryptoLib.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -58,7 +65,8 @@ 33F96BB57BB41A059EFBA16D /* Frameworks */ = { isa = PBXGroup; children = ( - D8F927A885B3AC17754363D1 /* libPods-CryptoLib.a */, + 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */, + A239569B6D7676AE64227515 /* libPods-CryptoLib.a */, ); name = Frameworks; sourceTree = ""; @@ -88,6 +96,8 @@ children = ( 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */, 4A7C21362451F2AC00DE81E6 /* Info.plist */, + 9E9BB811245412E900F9FF51 /* Cryptor.swift */, + 9E9BB8132454708600F9FF51 /* Masterkey.swift */, ); path = CryptoLib; sourceTree = ""; @@ -95,7 +105,7 @@ 4A7C213F2451F2AC00DE81E6 /* CryptoLibTests */ = { isa = PBXGroup; children = ( - 4A7C21402451F2AC00DE81E6 /* CryptoLibTests.swift */, + 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, 4A7C21422451F2AD00DE81E6 /* Info.plist */, ); path = CryptoLibTests; @@ -107,7 +117,6 @@ 04D286DFEF49CAB9C0242FB8 /* Pods-CryptoLib.debug.xcconfig */, 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -174,6 +183,7 @@ TargetAttributes = { 4A7C21312451F2AC00DE81E6 = { CreatedOnToolsVersion = 11.4; + LastSwiftMigration = 1100; }; 4A7C213A2451F2AC00DE81E6 = { CreatedOnToolsVersion = 11.4; @@ -246,6 +256,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */, + 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -253,7 +265,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4A7C21412451F2AC00DE81E6 /* CryptoLibTests.swift in Sources */, + 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -319,7 +331,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -376,7 +388,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -392,6 +404,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 04D286DFEF49CAB9C0242FB8 /* Pods-CryptoLib.debug.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -405,9 +418,11 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.skymatic.CryptoLib; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -417,6 +432,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -430,6 +446,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.skymatic.CryptoLib; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -440,7 +457,9 @@ }; 4A7C214A2451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 04D286DFEF49CAB9C0242FB8 /* Pods-CryptoLib.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -458,6 +477,7 @@ 4A7C214B2451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/CryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/CryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift new file mode 100644 index 0000000..31c0a9f --- /dev/null +++ b/CryptoLib/Cryptor.swift @@ -0,0 +1,39 @@ +// +// Cryptor.swift +// CryptoLib +// +// Created by Sebastian Stenzel on 25.04.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import Foundation + +public class Cryptor { + + private let masterKey: Masterkey + + public init(masterKey: Masterkey) { + self.masterKey = masterKey; + } + + // MARK: Path Encryption and Decryption: + + public func encryptFileName(cleartextName: String, directoryId: [UInt8]) -> String { + return ""; // TODO + } + + public func decryptFileName(ciphertextName: String, directoryId: [UInt8]) -> String { + return ""; // TODO + } + + // MARK: File Content Encryption and Decryption + + func encryptSingleChunk(chunkNumber: UInt64, nonce: [UInt8], cleartext: [UInt8], fileKey: [UInt8]) -> [UInt8] { + return [UInt8](); // TODO + } + + func decryptSingleChunk(chunkNumber: UInt64, nonce: [UInt8], ciphertext: [UInt8], fileKey: [UInt8]) -> [UInt8] { + return [UInt8](); // TODO + } + +} diff --git a/CryptoLib/Info.plist b/CryptoLib/Info.plist index 9bcb244..c0701c6 100644 --- a/CryptoLib/Info.plist +++ b/CryptoLib/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift new file mode 100644 index 0000000..95b379c --- /dev/null +++ b/CryptoLib/Masterkey.swift @@ -0,0 +1,128 @@ +// +// Masterkey.swift +// CryptoLib +// +// Created by Sebastian Stenzel on 25.04.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import Foundation +import CryptoSwift +import CommonCrypto + +struct MasterkeyJson: Codable { + let scryptSalt: String + let scryptCostParam: Int + let scryptBlockSize: Int + let primaryMasterKey: String + let hmacMasterKey: String + let versionMac: String + let version: Int +} + +public class Masterkey { + + private(set) var aesMasterKey: [UInt8] + private(set) var macMasterKey: [UInt8] + + private init(aesMasterKey: [UInt8], macMasterKey: [UInt8]) { + self.aesMasterKey = aesMasterKey + self.macMasterKey = macMasterKey + } + + deinit { + for i in 0 ..< aesMasterKey.count { + aesMasterKey[i] = 0 + } + for i in 0 ..< macMasterKey.count { + macMasterKey[i] = 0 + } + } + + // MARK: - + // MARK: Masterkey Factory Methods + + public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) -> Masterkey? { + if let jsonData = try? Data(contentsOf: file) { + return createFromMasterkeyFile(jsonData: jsonData, password: password) + } else { + return nil + } + } + + public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) -> Masterkey? { + let jsonDecoder = JSONDecoder() + if let decoded = try? jsonDecoder.decode(MasterkeyJson.self, from: jsonData) { + return createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper); + } else { + return nil + } + } + + static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) -> Masterkey? { + let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) + let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!) + let saltAndPepper = salt + pepper + let kek = try? Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() + + if kek == nil { + debugPrint("scrypt failed") + return nil; + } + + let wrappedMasterKey = [UInt8](Data(base64Encoded: jsonData.primaryMasterKey)!) + let unwrappedMasterKey = unwrapMasterKey(wrappedKey: wrappedMasterKey, kek: kek!) + + let wrappedHmacKey = [UInt8](Data(base64Encoded: jsonData.hmacMasterKey)!) + let unwrappedHmacKey = unwrapMasterKey(wrappedKey: wrappedHmacKey, kek: kek!) + + if (unwrappedMasterKey != nil) && (unwrappedHmacKey != nil) { + return createFromRaw(aesMasterKey: unwrappedMasterKey!, macMasterKey: unwrappedHmacKey!) + } else { + return nil + } + } + + static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8]) -> Masterkey { + assert(aesMasterKey.count == kCCKeySizeAES256) + assert(macMasterKey.count == kCCKeySizeAES256) + return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) + } + + // MARK: - + // MARK: RFC 3394 Key Wrapping + + static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) -> [UInt8]? { + assert(kek.count == kCCKeySizeAES256) + var wrapepdKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) + var wrapepdKey = [UInt8](repeating: 0x00, count: wrapepdKeyLen); + let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrapepdKey, &wrapepdKeyLen) + if status == kCCSuccess { + return wrapepdKey + } else if status == kCCParamError { + // wrong password + return nil + } else { + debugPrint("unwrapping masterkey failed with status code ", status) + return nil + } + } + + static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) -> [UInt8]? { + assert(kek.count == kCCKeySizeAES256) + var unwrapepdKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) + var unwrapepdKey = [UInt8](repeating: 0x00, count: unwrapepdKeyLen); + let status = CCSymmetricKeyUnwrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, wrappedKey, wrappedKey.count, &unwrapepdKey, &unwrapepdKeyLen) + if status == kCCSuccess { + assert(unwrapepdKeyLen == kCCKeySizeAES256) + return unwrapepdKey + } else if status == kCCParamError { + // wrong password + return nil + } else { + debugPrint("unwrapping masterkey failed with status code ", status) + return nil + } + } + +} diff --git a/CryptoLibTests/CryptoLibTests.swift b/CryptoLibTests/CryptoLibTests.swift deleted file mode 100644 index 15d7fb7..0000000 --- a/CryptoLibTests/CryptoLibTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// CryptoLibTests.swift -// CryptoLibTests -// -// Created by Philipp Schmid on 23.04.20. -// Copyright © 2020 Skymatic GmbH. All rights reserved. -// - -import XCTest -@testable import CryptoLib - -class CryptoLibTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift new file mode 100644 index 0000000..c6b7d17 --- /dev/null +++ b/CryptoLibTests/MasterkeyTests.swift @@ -0,0 +1,53 @@ +// +// MasterkeyTests.swift +// CryptoLibTests +// +// Created by Sebastian Stenzel on 26.04.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import XCTest +@testable import CryptoLib + +class MasterkeyTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testWrapAndUnwrapKey() { + let rawKey = [UInt8](repeating: 0x77, count: 32) + let kek = [UInt8](repeating: 0x55, count: 32) + let wrapped = Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek) + XCTAssertNotNil(wrapped) + let unwrapped = Masterkey.unwrapMasterKey(wrappedKey: wrapped!, kek: kek) + XCTAssertNotNil(unwrapped) + XCTAssertEqual(rawKey, unwrapped) + } + + func testCreateFromMasterkeyFile() { + let expectedKeys = [UInt8](repeating: 0x00, count: 32) + let jsonData = """ + { + "version": 3, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + } + """.data(using: .utf8)! + + let masterKey = Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") + + XCTAssertNotNil(masterKey) + XCTAssertEqual(expectedKeys, masterKey?.aesMasterKey) + XCTAssertEqual(expectedKeys, masterKey?.macMasterKey) + } + +} diff --git a/Podfile b/Podfile index 100c397..31271f7 100644 --- a/Podfile +++ b/Podfile @@ -2,5 +2,5 @@ platform :ios, '8.0' inhibit_all_warnings! target 'CryptoLib' do - + pod 'CryptoSwift', '~> 1.3' end diff --git a/Podfile.lock b/Podfile.lock index 3d1598a..efe47cf 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,3 +1,16 @@ -PODFILE CHECKSUM: 5646dc38bd9a917cff701ec23e5ad0031bb81635 +PODS: + - CryptoSwift (1.3.1) -COCOAPODS: 1.9.1 +DEPENDENCIES: + - CryptoSwift (~> 1.3) + +SPEC REPOS: + trunk: + - CryptoSwift + +SPEC CHECKSUMS: + CryptoSwift: f12f037f6d0fcd6d48c96db0071b653de64e6c4d + +PODFILE CHECKSUM: 19268130c60a5ace46730a2344438b860f3a8a39 + +COCOAPODS: 1.8.4 diff --git a/README.md b/README.md new file mode 100644 index 0000000..73cca7e --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +High level wrapper for cryptographic operations used by Cryptomator iOS App + +In general, the following preference is used to choose the implementation of cryptographic primitives: +1. Apple Swift Crypto +2. Apple CommonCrypto +3. CryptoSwift From 87a7c1869df23cddde4d4fd1ee64c5cb9aa206f0 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 29 Apr 2020 15:32:03 +0200 Subject: [PATCH 002/144] Started implementation of SIV mode for file name encryption. Using CMAC primitives from CryptoSwift and CTR primitives from CommonCrypto --- CryptoLib.xcodeproj/project.pbxproj | 51 +++++++++++--- CryptoLib/AesSiv.swift | 101 ++++++++++++++++++++++++++++ CryptoLibTests/AesSivTests.swift | 49 ++++++++++++++ Podfile | 10 ++- Podfile.lock | 2 +- README.md | 6 +- 6 files changed, 206 insertions(+), 13 deletions(-) create mode 100644 CryptoLib/AesSiv.swift create mode 100644 CryptoLibTests/AesSivTests.swift diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index d538d3e..3e774c4 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -10,10 +10,13 @@ 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; 52D51418B3FFF0E212F47BAA /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A239569B6D7676AE64227515 /* libPods-CryptoLib.a */; }; + 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; + 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB811245412E900F9FF51 /* Cryptor.swift */; }; 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB8132454708600F9FF51 /* Masterkey.swift */; }; 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */; }; 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */; }; + A32DA6F6C54D8A5A6845B19E /* libPods-CryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DE9E50AD12277D0A1DC543FF /* libPods-CryptoLibTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -27,18 +30,23 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 04D286DFEF49CAB9C0242FB8 /* Pods-CryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.debug.xcconfig"; sourceTree = ""; }; + 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.debug.xcconfig"; sourceTree = ""; }; + 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.release.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.release.xcconfig"; sourceTree = ""; }; 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.release.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.release.xcconfig"; sourceTree = ""; }; 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CryptoLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptoLib.h; sourceTree = ""; }; 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.debug.xcconfig"; sourceTree = ""; }; + 9E44EEA524599C6900A37B01 /* AesSiv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSiv.swift; sourceTree = ""; }; + 9E44EEA724599C7800A37B01 /* AesSivTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSivTests.swift; sourceTree = ""; }; 9E9BB811245412E900F9FF51 /* Cryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = ""; }; 9E9BB8132454708600F9FF51 /* Masterkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Masterkey.swift; sourceTree = ""; }; 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyTests.swift; sourceTree = ""; }; 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libCryptoSwift.a; sourceTree = BUILT_PRODUCTS_DIR; }; A239569B6D7676AE64227515 /* libPods-CryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + DE9E50AD12277D0A1DC543FF /* libPods-CryptoLibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -67,6 +75,7 @@ children = ( 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */, A239569B6D7676AE64227515 /* libPods-CryptoLib.a */, + DE9E50AD12277D0A1DC543FF /* libPods-CryptoLibTests.a */, ); name = Frameworks; sourceTree = ""; @@ -98,6 +107,7 @@ 4A7C21362451F2AC00DE81E6 /* Info.plist */, 9E9BB811245412E900F9FF51 /* Cryptor.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, + 9E44EEA524599C6900A37B01 /* AesSiv.swift */, ); path = CryptoLib; sourceTree = ""; @@ -107,6 +117,7 @@ children = ( 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, 4A7C21422451F2AD00DE81E6 /* Info.plist */, + 9E44EEA724599C7800A37B01 /* AesSivTests.swift */, ); path = CryptoLibTests; sourceTree = ""; @@ -114,8 +125,10 @@ D7D76AD3F7F6155DB2723364 /* Pods */ = { isa = PBXGroup; children = ( - 04D286DFEF49CAB9C0242FB8 /* Pods-CryptoLib.debug.xcconfig */, 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */, + 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */, + 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */, + 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -157,6 +170,7 @@ isa = PBXNativeTarget; buildConfigurationList = 4A7C21492451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptoLibTests" */; buildPhases = ( + B0398845BD49B4481C76093F /* [CP] Check Pods Manifest.lock */, 4A7C21372451F2AC00DE81E6 /* Sources */, 4A7C21382451F2AC00DE81E6 /* Frameworks */, 4A7C21392451F2AC00DE81E6 /* Resources */, @@ -249,6 +263,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + B0398845BD49B4481C76093F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-CryptoLibTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -258,6 +294,7 @@ files = ( 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */, 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */, + 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -265,6 +302,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */, 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -402,7 +440,7 @@ }; 4A7C21472451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 04D286DFEF49CAB9C0242FB8 /* Pods-CryptoLib.debug.xcconfig */; + baseConfigurationReference = 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; @@ -412,7 +450,6 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CryptoLib/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -440,7 +477,6 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CryptoLib/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -457,9 +493,8 @@ }; 4A7C214A2451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 04D286DFEF49CAB9C0242FB8 /* Pods-CryptoLib.debug.xcconfig */; + baseConfigurationReference = 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -476,8 +511,8 @@ }; 4A7C214B2451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = CryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift new file mode 100644 index 0000000..6328400 --- /dev/null +++ b/CryptoLib/AesSiv.swift @@ -0,0 +1,101 @@ +// +// Siv.swift +// CryptoLib +// +// Created by Sebastian Stenzel on 29.04.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import Foundation +import CryptoSwift + +enum AesSivError: Error { + case invalidParameter(_ reason: String) +} + +public class AesSiv { + + static let zero = [UInt8](repeating: 0x00, count: 16) + static let dblConst : UInt8 = 0x87 + + internal static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + // Maximum permitted AD length is the block size in bits - 2 + if (ad.count > 126) { + throw AesSivError.invalidParameter("too many ad") + } + + guard let mac = try? CMAC.init(key: macKey) else { + throw AesSivError.invalidParameter("invalid macKey") + } + + // RFC 5297 defines a n == 0 case here. Where n is the length of the input vector: + // S1 = associatedData1, S2 = associatedData2, ... Sn = plaintext + // Since this method is invoked only by encrypt/decrypt, we always have a plaintext. + // Thus n > 0 + + var d = try mac.authenticate(zero) + for s in ad { + d = xor(dbl(d), try mac.authenticate(s)) + } + + let t: [UInt8] + if (plaintext.count >= 16) { + t = xorend(plaintext, d) + } else { + t = xor(dbl(d), pad(plaintext)) + } + + return try mac.authenticate(t) + } + + private static func shiftLeft(_ input: [UInt8]) -> [UInt8] { + var output = [UInt8](repeating: 0x00, count: input.count) + var bit: UInt8 = 0 + for i in (0..> 7) & 1 + } + return output + } + + private static func dbl(_ block: [UInt8]) -> [UInt8] { + var result = shiftLeft(block) + if (block[0] & 0x80 != 0x00) { + result[block.count - 1] ^= dblConst + } + return result + } + + private static func xor(_ data1: [UInt8], _ data2: [UInt8]) -> [UInt8] { + assert(data1.count <= data2.count, "Length of first input must be <= length of second input.") + var result = [UInt8](repeating: 0x00, count: data1.count) + for i in 0.. [UInt8] { + assert(data1.count >= data2.count, "Length of first input must be >= length of second input.") + var result = data1 + let diff = data1.count - data2.count + for i in 0.. [UInt8] { + var result = data + if (result.count < 16) { + result.append(0x80) + } + while (result.count < 16) { + result.append(0x00) + } + return result + } + +} diff --git a/CryptoLibTests/AesSivTests.swift b/CryptoLibTests/AesSivTests.swift new file mode 100644 index 0000000..90cac95 --- /dev/null +++ b/CryptoLibTests/AesSivTests.swift @@ -0,0 +1,49 @@ +// +// SivTests.swift +// CryptoLibTests +// +// Created by Sebastian Stenzel on 29.04.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import XCTest +@testable import CryptoLib + +class AesSivTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testS2v() { + let macKey: [UInt8] = [ + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 + ] + + let ad: [UInt8] = [ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + ] + + let plaintext: [UInt8] = [ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + ] + + let expected: [UInt8] = [ + 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, + 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93 + ] + + let result = try? AesSiv.s2v(macKey: macKey, plaintext: plaintext, ad: ad) + + XCTAssertEqual(expected, result) + } + +} diff --git a/Podfile b/Podfile index 31271f7..164d8e4 100644 --- a/Podfile +++ b/Podfile @@ -1,6 +1,14 @@ platform :ios, '8.0' inhibit_all_warnings! +def pods + pod 'CryptoSwift', '~> 1.3' +end + target 'CryptoLib' do - pod 'CryptoSwift', '~> 1.3' + pods +end + +target 'CryptoLibTests' do + pods end diff --git a/Podfile.lock b/Podfile.lock index efe47cf..612507b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -11,6 +11,6 @@ SPEC REPOS: SPEC CHECKSUMS: CryptoSwift: f12f037f6d0fcd6d48c96db0071b653de64e6c4d -PODFILE CHECKSUM: 19268130c60a5ace46730a2344438b860f3a8a39 +PODFILE CHECKSUM: fdfae3a473e25f9cfa2ad1476fc2fb79e55c2a10 COCOAPODS: 1.8.4 diff --git a/README.md b/README.md index 73cca7e..50b7953 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ High level wrapper for cryptographic operations used by Cryptomator iOS App In general, the following preference is used to choose the implementation of cryptographic primitives: -1. Apple Swift Crypto -2. Apple CommonCrypto -3. CryptoSwift +1. Apple Swift Crypto (HMAC) +2. Apple CommonCrypto (AES-CTR, RFC 3394 Key Derivation) +3. CryptoSwift (scrypt, CMAC-AES used in S2V for SIV) From 13a939405486c07657cd18e3646916a4979f97d5 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 29 Apr 2020 15:33:23 +0200 Subject: [PATCH 003/144] improved control flow in case of errors --- CryptoLib/Masterkey.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 95b379c..9bcf293 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -63,18 +63,16 @@ public class Masterkey { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!) let saltAndPepper = salt + pepper - let kek = try? Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() - - if kek == nil { + guard let kek = try? Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() else { debugPrint("scrypt failed") return nil; } let wrappedMasterKey = [UInt8](Data(base64Encoded: jsonData.primaryMasterKey)!) - let unwrappedMasterKey = unwrapMasterKey(wrappedKey: wrappedMasterKey, kek: kek!) + let unwrappedMasterKey = unwrapMasterKey(wrappedKey: wrappedMasterKey, kek: kek) let wrappedHmacKey = [UInt8](Data(base64Encoded: jsonData.hmacMasterKey)!) - let unwrappedHmacKey = unwrapMasterKey(wrappedKey: wrappedHmacKey, kek: kek!) + let unwrappedHmacKey = unwrapMasterKey(wrappedKey: wrappedHmacKey, kek: kek) if (unwrappedMasterKey != nil) && (unwrappedHmacKey != nil) { return createFromRaw(aesMasterKey: unwrappedMasterKey!, macMasterKey: unwrappedHmacKey!) From 9b0049bbae45ef832b0d4454af0e8a05ba3e2e57 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 29 Apr 2020 17:54:12 +0200 Subject: [PATCH 004/144] Implemented file name encryption and decryption, however this only works with 128 bit keys atm (due to limitations in the CMAC implementation) --- CryptoLib.xcodeproj/project.pbxproj | 5 ++ CryptoLib/AesSiv.swift | 79 +++++++++++++++++++++++-- CryptoLib/Cryptor.swift | 34 +++++++++-- CryptoLib/Masterkey.swift | 7 ++- CryptoLibTests/AesSivTests.swift | 90 ++++++++++++++++++++++++----- CryptoLibTests/CryptorTests.swift | 45 +++++++++++++++ 6 files changed, 232 insertions(+), 28 deletions(-) create mode 100644 CryptoLibTests/CryptorTests.swift diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index 3e774c4..10cc519 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; 52D51418B3FFF0E212F47BAA /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A239569B6D7676AE64227515 /* libPods-CryptoLib.a */; }; + 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB811245412E900F9FF51 /* Cryptor.swift */; }; @@ -39,6 +40,7 @@ 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.debug.xcconfig"; sourceTree = ""; }; + 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; 9E44EEA524599C6900A37B01 /* AesSiv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSiv.swift; sourceTree = ""; }; 9E44EEA724599C7800A37B01 /* AesSivTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSivTests.swift; sourceTree = ""; }; 9E9BB811245412E900F9FF51 /* Cryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = ""; }; @@ -64,6 +66,7 @@ buildActionMask = 2147483647; files = ( 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */, + A32DA6F6C54D8A5A6845B19E /* libPods-CryptoLibTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -116,6 +119,7 @@ isa = PBXGroup; children = ( 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, + 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */, 4A7C21422451F2AD00DE81E6 /* Info.plist */, 9E44EEA724599C7800A37B01 /* AesSivTests.swift */, ); @@ -304,6 +308,7 @@ files = ( 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */, 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */, + 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index 6328400..88bdd15 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -8,9 +8,12 @@ import Foundation import CryptoSwift +import CommonCrypto enum AesSivError: Error { case invalidParameter(_ reason: String) + case encryptionFailedWithStatus(_ status: CCCryptorStatus) + case unauthenticCiphertext } public class AesSiv { @@ -18,15 +21,79 @@ public class AesSiv { static let zero = [UInt8](repeating: 0x00, count: 16) static let dblConst : UInt8 = 0x87 - internal static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + public static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + if (plaintext.count > UInt32.max - 16) { + throw AesSivError.invalidParameter("ciphertext must be at least 16 bytes") + } + let iv = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) + let ciphertext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext) + return iv + ciphertext + } + + static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + if (ciphertext.count < 16) { + throw AesSivError.invalidParameter("ciphertext must be at least 16 bytes") + } + let iv = Array(ciphertext[..<16]) + let actualCiphertext = Array(ciphertext[16...]) + let plaintext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: actualCiphertext) + let control = try s2v(macKey: macKey, plaintext: plaintext, ad: ad); + + // time-constant comparison + assert(iv.count == control.count) + var diff: UInt8 = 0 + for i in 0.. [UInt8] { + assert(aesKey.count == kCCKeySizeAES256 || aesKey.count == kCCKeySizeAES128, "aesKey expected to be 128 or 256 bit") + + // clear out the 31st and 63rd bit (see https://tools.ietf.org/html/rfc5297#section-2.5) + var ctr = iv + ctr[8] &= 0x7F + ctr[12] &= 0x7F + + var cryptor: CCCryptorRef? + var status = CCCryptorCreateWithMode(CCOperation(kCCEncrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), ctr, aesKey, aesKey.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) + guard status == kCCSuccess, cryptor != nil else { + throw AesSivError.invalidParameter("failed to initialize cryptor") + } + defer { + CCCryptorRelease(cryptor) + } + + let outlen = CCCryptorGetOutputLength(cryptor, plaintext.count, true) + var ciphertext = [UInt8](repeating: 0x00, count: outlen) + + var numEncryptedBytes: Int = 0 + status = CCCryptorUpdate(cryptor, plaintext, plaintext.count, &ciphertext, ciphertext.count, &numEncryptedBytes) + guard status == kCCSuccess else { + throw AesSivError.encryptionFailedWithStatus(status) + } + + status = CCCryptorFinal(cryptor, &ciphertext, ciphertext.count, &numEncryptedBytes) + guard status == kCCSuccess else { + throw AesSivError.encryptionFailedWithStatus(status) + } + + return ciphertext + } + + internal static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [[UInt8]]) throws -> [UInt8] { // Maximum permitted AD length is the block size in bits - 2 if (ad.count > 126) { - throw AesSivError.invalidParameter("too many ad") + throw AesSivError.invalidParameter("too many ad") } - guard let mac = try? CMAC.init(key: macKey) else { - throw AesSivError.invalidParameter("invalid macKey") - } + let mac = try CMAC.init(key: macKey) // RFC 5297 defines a n == 0 case here. Where n is the length of the input vector: // S1 = associatedData1, S2 = associatedData2, ... Sn = plaintext @@ -86,7 +153,7 @@ public class AesSiv { return result } - // ISO7816d4: First bit 1, following bits 0. + // ISO/IEC 7816-4:2005 Padding: First bit 1, following bits 0 private static func pad(_ data: [UInt8]) -> [UInt8] { var result = data if (result.count < 16) { diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 31c0a9f..3427812 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -8,6 +8,18 @@ import Foundation +extension Data { + + public init?(base64UrlEncoded base64String: String, options: Data.Base64DecodingOptions = []) { + self.init(base64Encoded: base64String.replacingOccurrences(of: "_", with: "/").replacingOccurrences(of: "-", with: "+"), options: options) + } + + public func base64UrlEncodedString(options: Data.Base64EncodingOptions = []) -> String { + return self.base64EncodedString(options: options).replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_") + } + +} + public class Cryptor { private let masterKey: Masterkey @@ -18,12 +30,26 @@ public class Cryptor { // MARK: Path Encryption and Decryption: - public func encryptFileName(cleartextName: String, directoryId: [UInt8]) -> String { - return ""; // TODO + public func encryptFileName(cleartextName: String, directoryId: Data) -> String? { + let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) + if let ciphertext = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: directoryId.bytes) { + return Data(ciphertext).base64UrlEncodedString() + } else { + return nil + } } - public func decryptFileName(ciphertextName: String, directoryId: [UInt8]) -> String { - return ""; // TODO + public func decryptFileName(ciphertextName: String, directoryId: Data) -> String? { + guard let ciphertextData = Data(base64UrlEncoded: ciphertextName) else { + debugPrint("can not decode base64 string", ciphertextName) + return nil + } + + if let cleartext = try? AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: directoryId.bytes) { + return String(data: Data(cleartext), encoding: .utf8) + } else { + return nil + } } // MARK: File Content Encryption and Decryption diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 9bcf293..345f5b9 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -81,9 +81,10 @@ public class Masterkey { } } - static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8]) -> Masterkey { - assert(aesMasterKey.count == kCCKeySizeAES256) - assert(macMasterKey.count == kCCKeySizeAES256) + internal static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8]) -> Masterkey { + // TODO CMAC implementation doesn't support 256 bit keys yet -.- + // assert(aesMasterKey.count == kCCKeySizeAES256) + // assert(macMasterKey.count == kCCKeySizeAES256) return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) } diff --git a/CryptoLibTests/AesSivTests.swift b/CryptoLibTests/AesSivTests.swift index 90cac95..63340bd 100644 --- a/CryptoLibTests/AesSivTests.swift +++ b/CryptoLibTests/AesSivTests.swift @@ -10,27 +10,87 @@ import XCTest @testable import CryptoLib class AesSivTests: XCTestCase { + + let aesKey: [UInt8] = [ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + ] + + let macKey: [UInt8] = [ + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 + ] - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } + let ad: [UInt8] = [ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + ] + + func testEncrypt() { + let plaintext: [UInt8] = [ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + ] - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } + let expected: [UInt8] = [ + 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, + 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93, + 0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04, + 0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c + ] + + let result = try? AesSiv.encrypt(aesKey: aesKey, macKey: macKey, plaintext: plaintext, ad: ad) - func testS2v() { - let macKey: [UInt8] = [ - 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, - 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 + XCTAssertEqual(expected, result) + } + + func testDecrypt() { + let ciphertext: [UInt8] = [ + 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, + 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93, + 0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04, + 0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c ] - let ad: [UInt8] = [ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + let expected: [UInt8] = [ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee ] + let result = try? AesSiv.decrypt(aesKey: aesKey, macKey: macKey, ciphertext: ciphertext, ad: ad) + + XCTAssertEqual(expected, result) + } + + func testAesCtr() { + let aesKey: [UInt8] = [ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + ] + + let iv: [UInt8] = [ + 0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb, + 0x06, 0xf4, 0xd1, 0x4b, 0x7f, 0x2f, 0xbd, 0x0f + ] + + let plaintext = [UInt8](repeating: 0x00, count: 16 * 3) + + let expected: [UInt8] = [ + 0xbf, 0xf8, 0x66, 0x5c, 0xfd, 0xd7, 0x33, 0x63, + 0x55, 0x0f, 0x74, 0x00, 0xe8, 0xf9, 0xd3, 0x76, + 0xb2, 0xc9, 0x08, 0x8e, 0x71, 0x3b, 0x86, 0x17, + 0xd8, 0x83, 0x92, 0x26, 0xd9, 0xf8, 0x81, 0x59, + 0x9e, 0x44, 0xd8, 0x27, 0x23, 0x49, 0x49, 0xbc, + 0x1b, 0x12, 0x34, 0x8e, 0xbc, 0x19, 0x5e, 0xc7 + ] + + let result = try? AesSiv.aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext) + + XCTAssertEqual(expected, result) + } + + func testS2v() { let plaintext: [UInt8] = [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee @@ -41,7 +101,7 @@ class AesSivTests: XCTestCase { 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93 ] - let result = try? AesSiv.s2v(macKey: macKey, plaintext: plaintext, ad: ad) + let result = try? AesSiv.s2v(macKey: macKey, plaintext: plaintext, ad: [ad]) XCTAssertEqual(expected, result) } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift new file mode 100644 index 0000000..c5f7fb6 --- /dev/null +++ b/CryptoLibTests/CryptorTests.swift @@ -0,0 +1,45 @@ +// +// CryptorTests.swift +// CryptoLibTests +// +// Created by Sebastian Stenzel on 27.04.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import XCTest +@testable import CryptoLib + +class CryptorTests: XCTestCase { + + var masterkey: Masterkey! + + override func setUp() { + let aesKey: [UInt8] = Array(repeating: 0x55, count: 16) + let macKey: [UInt8] = Array(repeating: 0x77, count: 16) + masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) + + XCTAssertNotNil(masterkey) + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testEncryptAndDecryptName() { + continueAfterFailure = false + + let cryptor = Cryptor.init(masterKey: masterkey) + let dirId = "foo".data(using: .utf8)! + let originalName = "hello.txt" + + let ciphertextName = cryptor.encryptFileName(cleartextName: originalName, directoryId: dirId) + XCTAssertNotNil(ciphertextName) + + let cleartextName = cryptor.decryptFileName(ciphertextName: ciphertextName!, directoryId: dirId) + XCTAssertNotNil(cleartextName) + XCTAssertEqual(originalName, cleartextName!) + + } + + +} From 7dc8b56bac1b7db85ecb9ca7800f44be90e31b1e Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 3 May 2020 16:46:55 +0200 Subject: [PATCH 005/144] made file name encryption work with 256 bit keys --- CryptoLib/AesSiv.swift | 69 ++++++++++++++++++++++---- CryptoLibTests/AesSivTests.swift | 80 ++++++++++++++++++++++++++++++- CryptoLibTests/CryptorTests.swift | 4 +- 3 files changed, 141 insertions(+), 12 deletions(-) diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index 88bdd15..c711a92 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -7,7 +7,6 @@ // import Foundation -import CryptoSwift import CommonCrypto enum AesSivError: Error { @@ -53,8 +52,8 @@ public class AesSiv { return plaintext } - internal static func aesCtr(aesKey: [UInt8], iv: [UInt8], plaintext: [UInt8]) throws -> [UInt8] { - assert(aesKey.count == kCCKeySizeAES256 || aesKey.count == kCCKeySizeAES128, "aesKey expected to be 128 or 256 bit") + internal static func aesCtr(aesKey key: [UInt8], iv: [UInt8], plaintext: [UInt8]) throws -> [UInt8] { + assert(key.count == kCCKeySizeAES256 || key.count == kCCKeySizeAES128, "aesKey expected to be 128 or 256 bit") // clear out the 31st and 63rd bit (see https://tools.ietf.org/html/rfc5297#section-2.5) var ctr = iv @@ -62,7 +61,7 @@ public class AesSiv { ctr[12] &= 0x7F var cryptor: CCCryptorRef? - var status = CCCryptorCreateWithMode(CCOperation(kCCEncrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), ctr, aesKey, aesKey.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) + var status = CCCryptorCreateWithMode(CCOperation(kCCEncrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), ctr, key, key.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) guard status == kCCSuccess, cryptor != nil else { throw AesSivError.invalidParameter("failed to initialize cryptor") } @@ -93,16 +92,14 @@ public class AesSiv { throw AesSivError.invalidParameter("too many ad") } - let mac = try CMAC.init(key: macKey) - // RFC 5297 defines a n == 0 case here. Where n is the length of the input vector: // S1 = associatedData1, S2 = associatedData2, ... Sn = plaintext // Since this method is invoked only by encrypt/decrypt, we always have a plaintext. // Thus n > 0 - var d = try mac.authenticate(zero) + var d = try cmac(macKey: macKey, data: zero) for s in ad { - d = xor(dbl(d), try mac.authenticate(s)) + d = xor(dbl(d), try cmac(macKey: macKey, data: s)) } let t: [UInt8] @@ -112,7 +109,61 @@ public class AesSiv { t = xor(dbl(d), pad(plaintext)) } - return try mac.authenticate(t) + return try cmac(macKey: macKey, data: t) + } + + internal static func cmac(macKey key: [UInt8], data: [UInt8]) throws -> [UInt8] { + // subkey generation: + let l = try aes(key: key, plaintext: zero) + let k1 = l[0] & 0x80 == 0x00 ? shiftLeft(l) : dbl(l) + let k2 = k1[0] & 0x80 == 0x00 ? shiftLeft(k1) : dbl(k1) + + // determine number of blocks: + let n = (data.count + 15) / 16 + let lastBlockIdx: Int + let lastBlockComplete: Bool + if n == 0 { + lastBlockIdx = 0 + lastBlockComplete = false + } else { + lastBlockIdx = n - 1 + lastBlockComplete = data.count % 16 == 0 + } + + // blocks 0.. [UInt8] { + assert(key.count == kCCKeySizeAES128 || key.count == kCCKeySizeAES192 || key.count == kCCKeySizeAES256) + assert(plaintext.count == kCCBlockSizeAES128, "Attempt to run AES-ECB for plaintext != one single block") + + var ciphertext = [UInt8](repeating: 0x00, count: kCCBlockSizeAES128) + var ciphertextLen = 0 + let status = CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionECBMode), key, key.count, nil, plaintext, plaintext.count, &ciphertext, kCCBlockSizeAES128, &ciphertextLen) + + guard status == kCCSuccess else { + throw AesSivError.invalidParameter("AES failed") + } + + return ciphertext } private static func shiftLeft(_ input: [UInt8]) -> [UInt8] { diff --git a/CryptoLibTests/AesSivTests.swift b/CryptoLibTests/AesSivTests.swift index 63340bd..f559199 100644 --- a/CryptoLibTests/AesSivTests.swift +++ b/CryptoLibTests/AesSivTests.swift @@ -41,7 +41,7 @@ class AesSivTests: XCTestCase { ] let result = try? AesSiv.encrypt(aesKey: aesKey, macKey: macKey, plaintext: plaintext, ad: ad) - + XCTAssertEqual(expected, result) } @@ -105,5 +105,83 @@ class AesSivTests: XCTestCase { XCTAssertEqual(expected, result) } + + func testCmac1() { + let macKey: [UInt8] = [ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + ] + let message: [UInt8] = [] + let expected: [UInt8] = [ + 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 + ] + let result = try? AesSiv.cmac(macKey: macKey, data: message) + + XCTAssertEqual(expected, result) + } + + func testCmac2() { + let macKey: [UInt8] = [ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + ] + let message: [UInt8] = [ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a + ] + let expected: [UInt8] = [ + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c + ] + let result = try? AesSiv.cmac(macKey: macKey, data: message) + + XCTAssertEqual(expected, result) + } + + func testCmac3() { + let macKey: [UInt8] = [ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + ] + let message: [UInt8] = [ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 + ] + let expected: [UInt8] = [ + 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27, + ] + let result = try? AesSiv.cmac(macKey: macKey, data: message) + + XCTAssertEqual(expected, result) + } + + func testCmac4() { + let macKey: [UInt8] = [ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + ] + let message: [UInt8] = [ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + ] + let expected: [UInt8] = [ + 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe + ] + let result = try? AesSiv.cmac(macKey: macKey, data: message) + + XCTAssertEqual(expected, result) + } } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index c5f7fb6..1f6df27 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -14,8 +14,8 @@ class CryptorTests: XCTestCase { var masterkey: Masterkey! override func setUp() { - let aesKey: [UInt8] = Array(repeating: 0x55, count: 16) - let macKey: [UInt8] = Array(repeating: 0x77, count: 16) + let aesKey: [UInt8] = Array(repeating: 0x55, count: 32) + let macKey: [UInt8] = Array(repeating: 0x77, count: 32) masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) XCTAssertNotNil(masterkey) From d77c08d1c41f30c0e677f29adfb6ec1695f89b0e Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 3 May 2020 16:49:08 +0200 Subject: [PATCH 006/144] more accurate exception --- CryptoLib/AesSiv.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index c711a92..8ec8d28 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -160,7 +160,7 @@ public class AesSiv { let status = CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionECBMode), key, key.count, nil, plaintext, plaintext.count, &ciphertext, kCCBlockSizeAES128, &ciphertextLen) guard status == kCCSuccess else { - throw AesSivError.invalidParameter("AES failed") + throw AesSivError.encryptionFailedWithStatus(status) } return ciphertext From cea0fc59650dd1d9956885883a3407d8df918ac7 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 12:22:31 +0200 Subject: [PATCH 007/144] Renamed podspec, added org-prefix --- CryptoLib.podspec => CryptomatorCryptoLib.podspec | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename CryptoLib.podspec => CryptomatorCryptoLib.podspec (71%) diff --git a/CryptoLib.podspec b/CryptomatorCryptoLib.podspec similarity index 71% rename from CryptoLib.podspec rename to CryptomatorCryptoLib.podspec index c5b14cc..7588b3f 100644 --- a/CryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -1,12 +1,13 @@ Pod::Spec.new do |s| - s.name = 'CryptoLib' + s.name = 'CryptomatorCryptoLib' s.version = '0.1.0' - s.summary = 'CryptoLib is an iOS crypto library to access Cryptomator vaults.' + s.summary = 'CryptomatorCryptoLib is an iOS crypto library to access Cryptomator vaults.' s.homepage = 'https://github.com/cryptomator/cryptolib-swift' s.license = { :type => 'AGPLv3', :file => 'LICENSE.txt' } - s.author = { 'Philipp Schmid' => 'philipp.schmid@skymatic.de' } + s.author = { 'Philipp Schmid' => 'philipp.schmid@skymatic.de', + 'Sebastian Stenzel' => 'sebastian.stenzel@skymatic.de' } s.source = { :git => 'https://github.com/cryptomator/cryptolib-swift.git', :tag => s.version.to_s } s.social_media_url = 'https://twitter.com/Cryptomator' From 74274a2ddccfafe382fc8cdc804d0dfd42a3ecd9 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 12:53:38 +0200 Subject: [PATCH 008/144] Added missing dependency to satisfy `pod lib lint` --- CryptomatorCryptoLib.podspec | 1 + 1 file changed, 1 insertion(+) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 7588b3f..a0f2d6a 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -17,4 +17,5 @@ Pod::Spec.new do |s| s.source_files = 'CryptoLib/**/*{swift,h,m}' + s.dependency 'CryptoSwift', '~> 1.3' end From 5b0d06c51f70580324b99cc31610a16d89ffa606 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 12:59:05 +0200 Subject: [PATCH 009/144] Updated README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 50b7953..2b998d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ +[![Version](http://img.shields.io/cocoapods/v/CryptomatorCryptoLib.svg)](https://cocoapods.org/pods/CryptomatorCryptoLib) + High level wrapper for cryptographic operations used by Cryptomator iOS App In general, the following preference is used to choose the implementation of cryptographic primitives: 1. Apple Swift Crypto (HMAC) 2. Apple CommonCrypto (AES-CTR, RFC 3394 Key Derivation) -3. CryptoSwift (scrypt, CMAC-AES used in S2V for SIV) +3. CryptoSwift (scrypt) From 54ca5c38210826fe0dec81a280112706d0da26ca Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 13:45:23 +0200 Subject: [PATCH 010/144] API changes required for #2 --- CryptoLib/Cryptor.swift | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 3427812..e21c98b 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -20,6 +20,10 @@ extension Data { } +public enum FileNameEncoding { + case base64url +} + public class Cryptor { private let masterKey: Masterkey @@ -28,23 +32,34 @@ public class Cryptor { self.masterKey = masterKey; } + // MARK: - // MARK: Path Encryption and Decryption: - public func encryptFileName(cleartextName: String, directoryId: Data) -> String? { + public func encryptFileName(cleartextName: String, directoryId: Data, encoding: FileNameEncoding = .base64url) -> String? { + // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) - if let ciphertext = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: directoryId.bytes) { - return Data(ciphertext).base64UrlEncodedString() - } else { + guard let ciphertext = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: directoryId.bytes) else { return nil } + + // encode: + switch encoding { + case .base64url: return Data(ciphertext).base64UrlEncodedString() + } } - public func decryptFileName(ciphertextName: String, directoryId: Data) -> String? { - guard let ciphertextData = Data(base64UrlEncoded: ciphertextName) else { - debugPrint("can not decode base64 string", ciphertextName) + public func decryptFileName(ciphertextName: String, directoryId: Data, encoding: FileNameEncoding = .base64url) -> String? { + // decode: + let maybeCiphertextData : Data? = { + switch encoding { + case .base64url: return Data(base64UrlEncoded: ciphertextName) + } + }() + guard let ciphertextData = maybeCiphertextData else { return nil } + // decrypt: if let cleartext = try? AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: directoryId.bytes) { return String(data: Data(cleartext), encoding: .utf8) } else { @@ -52,6 +67,7 @@ public class Cryptor { } } + // MARK: - // MARK: File Content Encryption and Decryption func encryptSingleChunk(chunkNumber: UInt64, nonce: [UInt8], cleartext: [UInt8], fileKey: [UInt8]) -> [UInt8] { From 4ef003be6f896c48fb62e8cbdfab5ecbc282fc9e Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 14:15:49 +0200 Subject: [PATCH 011/144] simplified method signature and fixes #2 --- CryptoLib/Cryptor.swift | 12 ++++++++---- CryptoLibTests/CryptorTests.swift | 4 ++-- CryptomatorCryptoLib.podspec | 1 + Podfile | 3 ++- Podfile.lock | 8 ++++++-- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index e21c98b..3407977 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -7,6 +7,7 @@ // import Foundation +import SwiftBase32 extension Data { @@ -22,6 +23,7 @@ extension Data { public enum FileNameEncoding { case base64url + case base32 } public class Cryptor { @@ -35,24 +37,26 @@ public class Cryptor { // MARK: - // MARK: Path Encryption and Decryption: - public func encryptFileName(cleartextName: String, directoryId: Data, encoding: FileNameEncoding = .base64url) -> String? { + public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) -> String? { // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) - guard let ciphertext = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: directoryId.bytes) else { + guard let ciphertext = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: dirId.bytes) else { return nil } // encode: switch encoding { case .base64url: return Data(ciphertext).base64UrlEncodedString() + case .base32: return Data(ciphertext).base32EncodedString } } - public func decryptFileName(ciphertextName: String, directoryId: Data, encoding: FileNameEncoding = .base64url) -> String? { + public func decryptFileName(_ ciphertextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) -> String? { // decode: let maybeCiphertextData : Data? = { switch encoding { case .base64url: return Data(base64UrlEncoded: ciphertextName) + case .base32: return ciphertextName.base32DecodedData } }() guard let ciphertextData = maybeCiphertextData else { @@ -60,7 +64,7 @@ public class Cryptor { } // decrypt: - if let cleartext = try? AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: directoryId.bytes) { + if let cleartext = try? AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: dirId.bytes) { return String(data: Data(cleartext), encoding: .utf8) } else { return nil diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 1f6df27..b702164 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -32,10 +32,10 @@ class CryptorTests: XCTestCase { let dirId = "foo".data(using: .utf8)! let originalName = "hello.txt" - let ciphertextName = cryptor.encryptFileName(cleartextName: originalName, directoryId: dirId) + let ciphertextName = cryptor.encryptFileName(originalName, dirId: dirId) XCTAssertNotNil(ciphertextName) - let cleartextName = cryptor.decryptFileName(ciphertextName: ciphertextName!, directoryId: dirId) + let cleartextName = cryptor.decryptFileName(ciphertextName!, dirId: dirId) XCTAssertNotNil(cleartextName) XCTAssertEqual(originalName, cleartextName!) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index a0f2d6a..e2abf25 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -18,4 +18,5 @@ Pod::Spec.new do |s| s.source_files = 'CryptoLib/**/*{swift,h,m}' s.dependency 'CryptoSwift', '~> 1.3' + s.dependency 'SwiftBase32', '~> 0.8' end diff --git a/Podfile b/Podfile index 164d8e4..a1cdf77 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,8 @@ platform :ios, '8.0' inhibit_all_warnings! def pods - pod 'CryptoSwift', '~> 1.3' + pod 'CryptoSwift', '~> 1.3' + pod 'SwiftBase32', '~> 0.8' end target 'CryptoLib' do diff --git a/Podfile.lock b/Podfile.lock index 612507b..1897eb1 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,16 +1,20 @@ PODS: - CryptoSwift (1.3.1) + - SwiftBase32 (0.8.0) DEPENDENCIES: - CryptoSwift (~> 1.3) + - SwiftBase32 (~> 0.8) SPEC REPOS: trunk: - CryptoSwift + - SwiftBase32 SPEC CHECKSUMS: CryptoSwift: f12f037f6d0fcd6d48c96db0071b653de64e6c4d + SwiftBase32: 723fd05e2fce776f2b98f26fe423bd20fa80e411 -PODFILE CHECKSUM: fdfae3a473e25f9cfa2ad1476fc2fb79e55c2a10 +PODFILE CHECKSUM: f59f8af9b5d3a5bd2a764fde1a765ec1faccee0a -COCOAPODS: 1.8.4 +COCOAPODS: 1.9.1 From 58fb08ccff28dcbbb5dede880683b388f47fa946 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 14:28:22 +0200 Subject: [PATCH 012/144] fixes #3 --- CryptoLib/Cryptor.swift | 11 +++++++++++ CryptoLibTests/CryptorTests.swift | 12 +++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 3407977..9f08887 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -7,6 +7,7 @@ // import Foundation +import CommonCrypto import SwiftBase32 extension Data { @@ -37,6 +38,16 @@ public class Cryptor { // MARK: - // MARK: Path Encryption and Decryption: + public func encryptDirId(_ dirIdStr: String) -> String? { + let dirIdBytes = [UInt8](dirIdStr.utf8) + guard let encrypted = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: dirIdBytes) else { + return nil + } + var digest = [UInt8](repeating: 0x00, count: Int(CC_SHA1_DIGEST_LENGTH)) + CC_SHA1(encrypted, UInt32(encrypted.count) as CC_LONG, &digest) + return Data(digest).base32EncodedString + } + public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) -> String? { // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index b702164..0990192 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -20,10 +20,16 @@ class CryptorTests: XCTestCase { XCTAssertNotNil(masterkey) } + + func testEncryptDirId() { + let cryptor = Cryptor.init(masterKey: masterkey) + + let rootDir = cryptor.encryptDirId("") + XCTAssertEqual("VLWEHT553J5DR7OZLRJAYDIWFCXZABOD", rootDir) - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } + let testDir = cryptor.encryptDirId("918acfbd-a467-3f77-93f1-f4a44f9cfe9c") + XCTAssertEqual("7C3USOO3VU7IVQRKFMRFV3QE4VEZJECV", testDir) + } func testEncryptAndDecryptName() { continueAfterFailure = false From 135b6f37f2244a37bf0a3a3fb3e79eace8c7c525 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 16:21:01 +0200 Subject: [PATCH 013/144] Improved error handling --- CryptoLib/AesSiv.swift | 2 +- CryptoLib/Masterkey.swift | 74 +++++++++++++---------------- CryptoLibTests/MasterkeyTests.swift | 68 +++++++++++++++++++++++--- 3 files changed, 95 insertions(+), 49 deletions(-) diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index 8ec8d28..f5ffe69 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -22,7 +22,7 @@ public class AesSiv { public static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { if (plaintext.count > UInt32.max - 16) { - throw AesSivError.invalidParameter("ciphertext must be at least 16 bytes") + throw AesSivError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") } let iv = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) let ciphertext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext) diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 345f5b9..de84fb5 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -20,6 +20,13 @@ struct MasterkeyJson: Codable { let version: Int } +enum MasterkeyError: Error, Equatable { + case malformedMasterkeyFile(_ reason: String) + case invalidPassword + case unwrapFailed(_ status: CCCryptorStatus) + case wrapFailed(_ status: CCCryptorStatus) +} + public class Masterkey { private(set) var aesMasterKey: [UInt8] @@ -42,72 +49,59 @@ public class Masterkey { // MARK: - // MARK: Masterkey Factory Methods - public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) -> Masterkey? { - if let jsonData = try? Data(contentsOf: file) { - return createFromMasterkeyFile(jsonData: jsonData, password: password) - } else { - return nil - } + public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { + let jsonData = try Data(contentsOf: file) + return try createFromMasterkeyFile(jsonData: jsonData, password: password) + } - public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) -> Masterkey? { + public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { let jsonDecoder = JSONDecoder() - if let decoded = try? jsonDecoder.decode(MasterkeyJson.self, from: jsonData) { - return createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper); - } else { - return nil - } + let decoded = try jsonDecoder.decode(MasterkeyJson.self, from: jsonData) + return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper); } - static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) -> Masterkey? { + static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!) let saltAndPepper = salt + pepper - guard let kek = try? Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() else { - debugPrint("scrypt failed") - return nil; - } - - let wrappedMasterKey = [UInt8](Data(base64Encoded: jsonData.primaryMasterKey)!) - let unwrappedMasterKey = unwrapMasterKey(wrappedKey: wrappedMasterKey, kek: kek) + let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() - let wrappedHmacKey = [UInt8](Data(base64Encoded: jsonData.hmacMasterKey)!) - let unwrappedHmacKey = unwrapMasterKey(wrappedKey: wrappedHmacKey, kek: kek) + guard let wrappedMasterKey = Data(base64Encoded: jsonData.primaryMasterKey) else { + throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey") + } + let aesKey = try unwrapMasterKey(wrappedKey: wrappedMasterKey.bytes, kek: kek) - if (unwrappedMasterKey != nil) && (unwrappedHmacKey != nil) { - return createFromRaw(aesMasterKey: unwrappedMasterKey!, macMasterKey: unwrappedHmacKey!) - } else { - return nil + guard let wrappedHmacKey = Data(base64Encoded: jsonData.hmacMasterKey) else { + throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey") } + let macKey = try unwrapMasterKey(wrappedKey: wrappedHmacKey.bytes, kek: kek) + + return createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) } internal static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8]) -> Masterkey { - // TODO CMAC implementation doesn't support 256 bit keys yet -.- - // assert(aesMasterKey.count == kCCKeySizeAES256) - // assert(macMasterKey.count == kCCKeySizeAES256) + assert(aesMasterKey.count == kCCKeySizeAES256) + assert(macMasterKey.count == kCCKeySizeAES256) return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) } // MARK: - // MARK: RFC 3394 Key Wrapping - static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) -> [UInt8]? { + static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var wrapepdKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) var wrapepdKey = [UInt8](repeating: 0x00, count: wrapepdKeyLen); let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrapepdKey, &wrapepdKeyLen) if status == kCCSuccess { return wrapepdKey - } else if status == kCCParamError { - // wrong password - return nil } else { - debugPrint("unwrapping masterkey failed with status code ", status) - return nil + throw MasterkeyError.wrapFailed(status) } } - static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) -> [UInt8]? { + static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var unwrapepdKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) var unwrapepdKey = [UInt8](repeating: 0x00, count: unwrapepdKeyLen); @@ -115,12 +109,10 @@ public class Masterkey { if status == kCCSuccess { assert(unwrapepdKeyLen == kCCKeySizeAES256) return unwrapepdKey - } else if status == kCCParamError { - // wrong password - return nil + } else if status == kCCDecodeError { + throw MasterkeyError.invalidPassword } else { - debugPrint("unwrapping masterkey failed with status code ", status) - return nil + throw MasterkeyError.unwrapFailed(status) } } diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index c6b7d17..d453e36 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -19,17 +19,17 @@ class MasterkeyTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - func testWrapAndUnwrapKey() { + func testWrapAndUnwrapKey() throws { let rawKey = [UInt8](repeating: 0x77, count: 32) let kek = [UInt8](repeating: 0x55, count: 32) - let wrapped = Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek) + let wrapped = try Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek) XCTAssertNotNil(wrapped) - let unwrapped = Masterkey.unwrapMasterKey(wrappedKey: wrapped!, kek: kek) + let unwrapped = try Masterkey.unwrapMasterKey(wrappedKey: wrapped, kek: kek) XCTAssertNotNil(unwrapped) XCTAssertEqual(rawKey, unwrapped) } - func testCreateFromMasterkeyFile() { + func testCreateFromMasterkeyFile() throws { let expectedKeys = [UInt8](repeating: 0x00, count: 32) let jsonData = """ { @@ -43,11 +43,65 @@ class MasterkeyTests: XCTestCase { } """.data(using: .utf8)! - let masterKey = Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") + let masterKey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") XCTAssertNotNil(masterKey) - XCTAssertEqual(expectedKeys, masterKey?.aesMasterKey) - XCTAssertEqual(expectedKeys, masterKey?.macMasterKey) + XCTAssertEqual(expectedKeys, masterKey.aesMasterKey) + XCTAssertEqual(expectedKeys, masterKey.macMasterKey) + } + + func testCreateFromMasterkeyFileWithWrongPassword() throws { + let jsonData = """ + { + "version": 3, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + } + """.data(using: .utf8)! + + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password", { error in + XCTAssertEqual(error as! MasterkeyError, MasterkeyError.invalidPassword) + }) + } + + func testCreateFromMasterkeyFileWithMalformedJson1() throws { + let jsonData = """ + { + "version": 3, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + } + """.data(using: .utf8)! + + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in + XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey")) + }) + } + + func testCreateFromMasterkeyFileWithMalformedJson2() throws { + let jsonData = """ + { + "version": 3, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", + "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + } + """.data(using: .utf8)! + + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in + XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey")) + }) } } From f0d8635899f189572c2dc29a9b2be8eb593b3cec Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2020 16:21:23 +0200 Subject: [PATCH 014/144] fixes #1 --- CryptoLib/Masterkey.swift | 15 ++++++++++++ CryptoLibTests/MasterkeyTests.swift | 36 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index de84fb5..a1d0b73 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -77,6 +77,21 @@ public class Masterkey { } let macKey = try unwrapMasterKey(wrappedKey: wrappedHmacKey.bytes, kek: kek) + // time-constant version MAC check: + guard let storedVersionMac = Data(base64Encoded: jsonData.versionMac), storedVersionMac.count == CC_SHA256_DIGEST_LENGTH else { + throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac") + } + var calculatedVersionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + let versionBytes = withUnsafeBytes(of: UInt32(jsonData.version).bigEndian, Array.init) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macKey, macKey.count, versionBytes, versionBytes.count, &calculatedVersionMac) + var diff : UInt8 = 0x00 + for i in 0.. Date: Mon, 4 May 2020 16:22:20 +0200 Subject: [PATCH 015/144] bump version --- CryptomatorCryptoLib.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index e2abf25..cc71a3b 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'CryptomatorCryptoLib' - s.version = '0.1.0' + s.version = '0.2.0' s.summary = 'CryptomatorCryptoLib is an iOS crypto library to access Cryptomator vaults.' From 48ce10804e44d11e7bf1267f2406aa447df26f5b Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 5 May 2020 16:18:22 +0200 Subject: [PATCH 016/144] changed bundle identifier --- CryptoLib.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index 10cc519..23ddec2 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -461,7 +461,7 @@ "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.skymatic.CryptoLib; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLib; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -488,7 +488,7 @@ "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.skymatic.CryptoLib; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLib; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; @@ -507,7 +507,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.skymatic.CryptoLibTests; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLibTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -525,7 +525,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.skymatic.CryptoLibTests; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLibTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; From f690306e6d0c8b0c3e4e79123716b338759e8d71 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 5 May 2020 16:18:36 +0200 Subject: [PATCH 017/144] Use vault version 7 in unit tests --- CryptoLibTests/MasterkeyTests.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index bf4fb2f..a14c6b1 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -33,13 +33,13 @@ class MasterkeyTests: XCTestCase { let expectedKeys = [UInt8](repeating: 0x00, count: 32) let jsonData = """ { - "version": 3, + "version": 7, "scryptSalt": "AAAAAAAAAAA=", "scryptCostParam": 2, "scryptBlockSize": 8, "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" } """.data(using: .utf8)! @@ -53,13 +53,13 @@ class MasterkeyTests: XCTestCase { func testCreateFromMasterkeyFileWithWrongPassword() throws { let jsonData = """ { - "version": 3, + "version": 7, "scryptSalt": "AAAAAAAAAAA=", "scryptCostParam": 2, "scryptBlockSize": 8, "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" } """.data(using: .utf8)! @@ -71,13 +71,13 @@ class MasterkeyTests: XCTestCase { func testCreateFromMasterkeyFileWithInvalidVersionMac() throws { let jsonData = """ { - "version": 3, + "version": 7, "scryptSalt": "AAAAAAAAAAA=", "scryptCostParam": 2, "scryptBlockSize": 8, "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLa=" + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+G=" } """.data(using: .utf8)! @@ -89,13 +89,13 @@ class MasterkeyTests: XCTestCase { func testCreateFromMasterkeyFileWithMalformedJson1() throws { let jsonData = """ { - "version": 3, + "version": 7, "scryptSalt": "AAAAAAAAAAA=", "scryptCostParam": 2, "scryptBlockSize": 8, "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" } """.data(using: .utf8)! @@ -107,13 +107,13 @@ class MasterkeyTests: XCTestCase { func testCreateFromMasterkeyFileWithMalformedJson2() throws { let jsonData = """ { - "version": 3, + "version": 7, "scryptSalt": "AAAAAAAAAAA=", "scryptCostParam": 2, "scryptBlockSize": 8, "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", - "versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=" + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" } """.data(using: .utf8)! @@ -125,13 +125,13 @@ class MasterkeyTests: XCTestCase { func testCreateFromMasterkeyFileWithMalformedJson3() throws { let jsonData = """ { - "version": 3, + "version": 7, "scryptSalt": "AAAAAAAAAAA=", "scryptCostParam": 2, "scryptBlockSize": 8, "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "iUmRRHITuyJsJbVN" + "versionMac": "cn2sAK6l" } """.data(using: .utf8)! From c05488c587921b9fa5e3f454141b600c86bd1e58 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 7 May 2020 16:20:41 +0200 Subject: [PATCH 018/144] Added GitHub Actions workflow that builds and tests --- .github/workflows/build.yml | 15 ++++ .gitignore | 55 +------------ .../xcshareddata/xcschemes/CryptoLib.xcscheme | 77 +++++++++++++++++++ 3 files changed, 94 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..80303a6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,15 @@ +name: Build + +on: + [push] + +jobs: + build: + name: Build and test + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: pod install + - name: Build and test + run: xcodebuild -workspace 'CryptoLib.xcworkspace' -scheme 'CryptoLib' -destination 'OS=13.4,name=iPhone 11 Pro' clean test | xcpretty diff --git a/.gitignore b/.gitignore index 92d6acd..1446b5b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,11 @@ -#Mac +# macOS .DS_Store + # Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) -*.xcscmblueprint -*.xccheckout - -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) -build/ -DerivedData/ -*.moved-aside -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 - ## Obj-C/Swift specific *.hmap @@ -57,36 +39,3 @@ playground.xcworkspace # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # Pods/ -# -# Add this line if you want to avoid checking in source code from the Xcode workspace -# *.xcworkspace - -# Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. -# Carthage/Checkouts - -Carthage/Build/ - -# Accio dependency management -Dependencies/ -.accio/ - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. -# Instead, use fastlane to re-generate the screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/#source-control - -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots/**/*.png -fastlane/test_output - -# Code Injection -# -# After new code Injection tools there's a generated folder /iOSInjectionProject -# https://github.com/johnno1962/injectionforxcode - -iOSInjectionProject/ diff --git a/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme b/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme new file mode 100644 index 0000000..6d33408 --- /dev/null +++ b/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7119c466c95a874c938cfbad24d5ca1998f1a5dd Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 7 May 2020 16:29:11 +0200 Subject: [PATCH 019/144] Make action actually fail if xcodebuild fails, also set to unbuffered logging --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80303a6..45656b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,4 +12,4 @@ jobs: - name: Install dependencies run: pod install - name: Build and test - run: xcodebuild -workspace 'CryptoLib.xcworkspace' -scheme 'CryptoLib' -destination 'OS=13.4,name=iPhone 11 Pro' clean test | xcpretty + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace 'CryptoLib.xcworkspace' -scheme 'CryptoLib' -destination 'OS=13.4.1,name=iPhone 11 Pro' clean test | xcpretty From c6871a10cf5492fbec7c56ace3a4f16a8c25e2a0 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 7 May 2020 17:23:20 +0200 Subject: [PATCH 020/144] Added release action --- .github/workflows/build.yml | 27 +++++++++++++++++++++++++++ CryptomatorCryptoLib.podspec | 10 +++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 45656b4..bba86d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,3 +13,30 @@ jobs: run: pod install - name: Build and test run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace 'CryptoLib.xcworkspace' -scheme 'CryptoLib' -destination 'OS=13.4.1,name=iPhone 11 Pro' clean test | xcpretty + + release: + name: Deploy and draft a release + runs-on: macos-latest + needs: build + if: startsWith(github.ref, 'refs/tags/') + steps: + - uses: actions/checkout@v2 + - name: Deploy to CocoaPods + run: | + set -eo pipefail + export LIB_VERSION=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags' + pod lib lint + pod trunk push + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} + - name: Draft release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + body: | + :construction: Work in Progress + draft: true + prerelease: false diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index cc71a3b..cd339a7 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -1,22 +1,22 @@ Pod::Spec.new do |s| s.name = 'CryptomatorCryptoLib' - s.version = '0.2.0' + s.version = ENV['LIB_VERSION'] || 'SNAPSHOT' s.summary = 'CryptomatorCryptoLib is an iOS crypto library to access Cryptomator vaults.' - s.homepage = 'https://github.com/cryptomator/cryptolib-swift' s.license = { :type => 'AGPLv3', :file => 'LICENSE.txt' } s.author = { 'Philipp Schmid' => 'philipp.schmid@skymatic.de', - 'Sebastian Stenzel' => 'sebastian.stenzel@skymatic.de' } + 'Sebastian Stenzel' => 'sebastian.stenzel@skymatic.de', + 'Tobias Hagemann' => 'tobias.hagemann@skymatic.de' } s.source = { :git => 'https://github.com/cryptomator/cryptolib-swift.git', :tag => s.version.to_s } s.social_media_url = 'https://twitter.com/Cryptomator' s.public_header_files = 'CryptoLib/CryptoLib.h' s.ios.deployment_target = '8.0' s.swift_version = '5.0' - + s.source_files = 'CryptoLib/**/*{swift,h,m}' - + s.dependency 'CryptoSwift', '~> 1.3' s.dependency 'SwiftBase32', '~> 0.8' end From 72a798c52196d1537c762abe020f374816ca4ecb Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 7 May 2020 17:38:52 +0200 Subject: [PATCH 021/144] Fixed pod lib lint error --- CryptomatorCryptoLib.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index cd339a7..23f8bab 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'CryptomatorCryptoLib' - s.version = ENV['LIB_VERSION'] || 'SNAPSHOT' + s.version = ENV['LIB_VERSION'] || '0.0.1-snapshot' s.summary = 'CryptomatorCryptoLib is an iOS crypto library to access Cryptomator vaults.' s.homepage = 'https://github.com/cryptomator/cryptolib-swift' From 1d5e69fa2ff9a7c54e568cbd9ca0478114d07942 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 11 May 2020 12:37:32 +0200 Subject: [PATCH 022/144] Avoid dependency conflicts in test target --- Podfile | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Podfile b/Podfile index a1cdf77..0f9615f 100644 --- a/Podfile +++ b/Podfile @@ -1,15 +1,12 @@ platform :ios, '8.0' inhibit_all_warnings! +use_frameworks! :linkage => :static -def pods +target 'CryptoLib' do pod 'CryptoSwift', '~> 1.3' pod 'SwiftBase32', '~> 0.8' -end - -target 'CryptoLib' do - pods -end - -target 'CryptoLibTests' do - pods + + target 'CryptoLibTests' do + inherit! :search_paths + end end From 685bd829c1ddd77f0cf133b1f17d0540c3f78665 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 11 May 2020 12:38:10 +0200 Subject: [PATCH 023/144] Added public "version" field to masterkey --- CryptoLib/Masterkey.swift | 10 ++++++---- CryptoLibTests/CryptorTests.swift | 2 +- CryptoLibTests/MasterkeyTests.swift | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index a1d0b73..f311295 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -31,10 +31,12 @@ public class Masterkey { private(set) var aesMasterKey: [UInt8] private(set) var macMasterKey: [UInt8] + public let version: Int - private init(aesMasterKey: [UInt8], macMasterKey: [UInt8]) { + private init(aesMasterKey: [UInt8], macMasterKey: [UInt8], version: Int) { self.aesMasterKey = aesMasterKey self.macMasterKey = macMasterKey + self.version = version } deinit { @@ -92,13 +94,13 @@ public class Masterkey { throw MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac") } - return createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) + return createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey, version: jsonData.version) } - internal static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8]) -> Masterkey { + internal static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8], version: Int) -> Masterkey { assert(aesMasterKey.count == kCCKeySizeAES256) assert(macMasterKey.count == kCCKeySizeAES256) - return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) + return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: version) } // MARK: - diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 0990192..326fd7a 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -16,7 +16,7 @@ class CryptorTests: XCTestCase { override func setUp() { let aesKey: [UInt8] = Array(repeating: 0x55, count: 32) let macKey: [UInt8] = Array(repeating: 0x77, count: 32) - masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) + masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey, version: 7) XCTAssertNotNil(masterkey) } diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index a14c6b1..b903e05 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -46,6 +46,7 @@ class MasterkeyTests: XCTestCase { let masterKey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") XCTAssertNotNil(masterKey) + XCTAssertEqual(7, masterKey.version) XCTAssertEqual(expectedKeys, masterKey.aesMasterKey) XCTAssertEqual(expectedKeys, masterKey.macMasterKey) } From 3b938519ed5cffa7175eab8bbd7f253000afb63d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 11 May 2020 12:39:45 +0200 Subject: [PATCH 024/144] explicit error handling instead of optional results, don't encode/decode directory ids all the time --- CryptoLib/Cryptor.swift | 30 +++++++++++++++--------------- CryptoLibTests/CryptorTests.swift | 14 +++++++------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 9f08887..5516e2e 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -22,11 +22,15 @@ extension Data { } -public enum FileNameEncoding { +public enum FileNameEncoding : String { case base64url case base32 } +enum CryptorError: Error, Equatable { + case invalidCiphertext(_ reason: String? = nil) +} + public class Cryptor { private let masterKey: Masterkey @@ -38,22 +42,17 @@ public class Cryptor { // MARK: - // MARK: Path Encryption and Decryption: - public func encryptDirId(_ dirIdStr: String) -> String? { - let dirIdBytes = [UInt8](dirIdStr.utf8) - guard let encrypted = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: dirIdBytes) else { - return nil - } + public func encryptDirId(_ dirId: Data) throws -> String { + let encrypted = try AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: dirId.bytes) var digest = [UInt8](repeating: 0x00, count: Int(CC_SHA1_DIGEST_LENGTH)) CC_SHA1(encrypted, UInt32(encrypted.count) as CC_LONG, &digest) return Data(digest).base32EncodedString } - public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) -> String? { + public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) - guard let ciphertext = try? AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: dirId.bytes) else { - return nil - } + let ciphertext = try AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: dirId.bytes) // encode: switch encoding { @@ -62,7 +61,7 @@ public class Cryptor { } } - public func decryptFileName(_ ciphertextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) -> String? { + public func decryptFileName(_ ciphertextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // decode: let maybeCiphertextData : Data? = { switch encoding { @@ -71,14 +70,15 @@ public class Cryptor { } }() guard let ciphertextData = maybeCiphertextData else { - return nil + throw CryptorError.invalidCiphertext("Can't \(encoding.rawValue)-decode ciphertext name: \(ciphertextName)") } // decrypt: - if let cleartext = try? AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: dirId.bytes) { - return String(data: Data(cleartext), encoding: .utf8) + let cleartext = try AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: dirId.bytes) + if let str = String(data: Data(cleartext), encoding: .utf8) { + return str } else { - return nil + throw CryptorError.invalidCiphertext("Unable to decode cleartext using UTF-8.") } } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 326fd7a..446301b 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -21,29 +21,29 @@ class CryptorTests: XCTestCase { XCTAssertNotNil(masterkey) } - func testEncryptDirId() { + func testEncryptDirId() throws { let cryptor = Cryptor.init(masterKey: masterkey) - let rootDir = cryptor.encryptDirId("") + let rootDir = try cryptor.encryptDirId("".data(using: .utf8)!) XCTAssertEqual("VLWEHT553J5DR7OZLRJAYDIWFCXZABOD", rootDir) - let testDir = cryptor.encryptDirId("918acfbd-a467-3f77-93f1-f4a44f9cfe9c") + let testDir = try cryptor.encryptDirId("918acfbd-a467-3f77-93f1-f4a44f9cfe9c".data(using: .utf8)!) XCTAssertEqual("7C3USOO3VU7IVQRKFMRFV3QE4VEZJECV", testDir) } - func testEncryptAndDecryptName() { + func testEncryptAndDecryptName() throws { continueAfterFailure = false let cryptor = Cryptor.init(masterKey: masterkey) let dirId = "foo".data(using: .utf8)! let originalName = "hello.txt" - let ciphertextName = cryptor.encryptFileName(originalName, dirId: dirId) + let ciphertextName = try cryptor.encryptFileName(originalName, dirId: dirId) XCTAssertNotNil(ciphertextName) - let cleartextName = cryptor.decryptFileName(ciphertextName!, dirId: dirId) + let cleartextName = try cryptor.decryptFileName(ciphertextName, dirId: dirId) XCTAssertNotNil(cleartextName) - XCTAssertEqual(originalName, cleartextName!) + XCTAssertEqual(originalName, cleartextName) } From 1a53a80699df945a118337c6ecd22774c3348917 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 11 May 2020 12:52:37 +0200 Subject: [PATCH 025/144] partial undo of 1d5e69f --- CryptoLib.xcodeproj/project.pbxproj | 16 ++++++++-------- Podfile | 1 - Podfile.lock | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index 23ddec2..cb53ade 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 52D51418B3FFF0E212F47BAA /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A239569B6D7676AE64227515 /* libPods-CryptoLib.a */; }; + 7980E962F1389ACDDCEBB596 /* libPods-CryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */; }; 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; @@ -17,7 +17,7 @@ 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB8132454708600F9FF51 /* Masterkey.swift */; }; 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */; }; 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */; }; - A32DA6F6C54D8A5A6845B19E /* libPods-CryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DE9E50AD12277D0A1DC543FF /* libPods-CryptoLibTests.a */; }; + AAC7D038BEF14DA7788F6090 /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 085645A8AA97369573B917E8 /* libPods-CryptoLib.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -32,7 +32,9 @@ /* Begin PBXFileReference section */ 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.debug.xcconfig"; sourceTree = ""; }; + 085645A8AA97369573B917E8 /* libPods-CryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.release.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.release.xcconfig"; sourceTree = ""; }; + 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.release.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.release.xcconfig"; sourceTree = ""; }; 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CryptoLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptoLib.h; sourceTree = ""; }; @@ -47,8 +49,6 @@ 9E9BB8132454708600F9FF51 /* Masterkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Masterkey.swift; sourceTree = ""; }; 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyTests.swift; sourceTree = ""; }; 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libCryptoSwift.a; sourceTree = BUILT_PRODUCTS_DIR; }; - A239569B6D7676AE64227515 /* libPods-CryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - DE9E50AD12277D0A1DC543FF /* libPods-CryptoLibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -57,7 +57,7 @@ buildActionMask = 2147483647; files = ( 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */, - 52D51418B3FFF0E212F47BAA /* libPods-CryptoLib.a in Frameworks */, + AAC7D038BEF14DA7788F6090 /* libPods-CryptoLib.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -66,7 +66,7 @@ buildActionMask = 2147483647; files = ( 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */, - A32DA6F6C54D8A5A6845B19E /* libPods-CryptoLibTests.a in Frameworks */, + 7980E962F1389ACDDCEBB596 /* libPods-CryptoLibTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -77,8 +77,8 @@ isa = PBXGroup; children = ( 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */, - A239569B6D7676AE64227515 /* libPods-CryptoLib.a */, - DE9E50AD12277D0A1DC543FF /* libPods-CryptoLibTests.a */, + 085645A8AA97369573B917E8 /* libPods-CryptoLib.a */, + 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */, ); name = Frameworks; sourceTree = ""; diff --git a/Podfile b/Podfile index 0f9615f..5a4b462 100644 --- a/Podfile +++ b/Podfile @@ -1,6 +1,5 @@ platform :ios, '8.0' inhibit_all_warnings! -use_frameworks! :linkage => :static target 'CryptoLib' do pod 'CryptoSwift', '~> 1.3' diff --git a/Podfile.lock b/Podfile.lock index 1897eb1..92f02a9 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,6 +15,6 @@ SPEC CHECKSUMS: CryptoSwift: f12f037f6d0fcd6d48c96db0071b653de64e6c4d SwiftBase32: 723fd05e2fce776f2b98f26fe423bd20fa80e411 -PODFILE CHECKSUM: f59f8af9b5d3a5bd2a764fde1a765ec1faccee0a +PODFILE CHECKSUM: c66c63f4d05e7c2a8a790153994cdb7463c7b08a COCOAPODS: 1.9.1 From a807d5453ce15173715981cbf5cd72a95176dafa Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 11 May 2020 17:00:19 +0200 Subject: [PATCH 026/144] Added and applied SwiftFormat rules --- .swiftformat | 15 +++ CryptoLib.xcodeproj/project.pbxproj | 6 +- CryptoLib/AesSiv.swift | 96 ++++++++-------- CryptoLib/Cryptor.swift | 52 ++++----- CryptoLib/Masterkey.swift | 71 ++++++------ CryptoLibTests/AesSivTests.swift | 164 ++++++++++++++-------------- CryptoLibTests/CryptorTests.swift | 28 ++--- CryptoLibTests/MasterkeyTests.swift | 62 +++++------ 8 files changed, 244 insertions(+), 250 deletions(-) create mode 100644 .swiftformat diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..fc1fa30 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,15 @@ +# file options + +--exclude Pods + +# format options + +--commas inline +--importgrouping testable-bottom +--indent tab +--swiftversion 5.1 +--tabwidth 4 + +# rules + +--enable isEmpty diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index cb53ade..5c2bb46 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -108,9 +108,9 @@ children = ( 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */, 4A7C21362451F2AC00DE81E6 /* Info.plist */, + 9E44EEA524599C6900A37B01 /* AesSiv.swift */, 9E9BB811245412E900F9FF51 /* Cryptor.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, - 9E44EEA524599C6900A37B01 /* AesSiv.swift */, ); path = CryptoLib; sourceTree = ""; @@ -118,10 +118,10 @@ 4A7C213F2451F2AC00DE81E6 /* CryptoLibTests */ = { isa = PBXGroup; children = ( - 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, - 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */, 4A7C21422451F2AD00DE81E6 /* Info.plist */, 9E44EEA724599C7800A37B01 /* AesSivTests.swift */, + 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */, + 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, ); path = CryptoLibTests; sourceTree = ""; diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index f5ffe69..7863408 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -6,8 +6,8 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // -import Foundation import CommonCrypto +import Foundation enum AesSivError: Error { case invalidParameter(_ reason: String) @@ -16,50 +16,49 @@ enum AesSivError: Error { } public class AesSiv { - static let zero = [UInt8](repeating: 0x00, count: 16) - static let dblConst : UInt8 = 0x87 - + static let dblConst: UInt8 = 0x87 + public static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { - if (plaintext.count > UInt32.max - 16) { + if plaintext.count > UInt32.max - 16 { throw AesSivError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") } let iv = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) let ciphertext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext) return iv + ciphertext } - + static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { - if (ciphertext.count < 16) { + if ciphertext.count < 16 { throw AesSivError.invalidParameter("ciphertext must be at least 16 bytes") } let iv = Array(ciphertext[..<16]) let actualCiphertext = Array(ciphertext[16...]) let plaintext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: actualCiphertext) - let control = try s2v(macKey: macKey, plaintext: plaintext, ad: ad); - + let control = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) + // time-constant comparison assert(iv.count == control.count) var diff: UInt8 = 0 - for i in 0.. [UInt8] { assert(key.count == kCCKeySizeAES256 || key.count == kCCKeySizeAES128, "aesKey expected to be 128 or 256 bit") - + // clear out the 31st and 63rd bit (see https://tools.ietf.org/html/rfc5297#section-2.5) var ctr = iv ctr[8] &= 0x7F ctr[12] &= 0x7F - + var cryptor: CCCryptorRef? var status = CCCryptorCreateWithMode(CCOperation(kCCEncrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), ctr, key, key.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) guard status == kCCSuccess, cryptor != nil else { @@ -68,56 +67,56 @@ public class AesSiv { defer { CCCryptorRelease(cryptor) } - + let outlen = CCCryptorGetOutputLength(cryptor, plaintext.count, true) var ciphertext = [UInt8](repeating: 0x00, count: outlen) - + var numEncryptedBytes: Int = 0 status = CCCryptorUpdate(cryptor, plaintext, plaintext.count, &ciphertext, ciphertext.count, &numEncryptedBytes) guard status == kCCSuccess else { throw AesSivError.encryptionFailedWithStatus(status) } - + status = CCCryptorFinal(cryptor, &ciphertext, ciphertext.count, &numEncryptedBytes) guard status == kCCSuccess else { throw AesSivError.encryptionFailedWithStatus(status) } - + return ciphertext } - + internal static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [[UInt8]]) throws -> [UInt8] { // Maximum permitted AD length is the block size in bits - 2 - if (ad.count > 126) { + if ad.count > 126 { throw AesSivError.invalidParameter("too many ad") } - + // RFC 5297 defines a n == 0 case here. Where n is the length of the input vector: // S1 = associatedData1, S2 = associatedData2, ... Sn = plaintext // Since this method is invoked only by encrypt/decrypt, we always have a plaintext. // Thus n > 0 - + var d = try cmac(macKey: macKey, data: zero) for s in ad { d = xor(dbl(d), try cmac(macKey: macKey, data: s)) } - + let t: [UInt8] - if (plaintext.count >= 16) { + if plaintext.count >= 16 { t = xorend(plaintext, d) } else { t = xor(dbl(d), pad(plaintext)) } - + return try cmac(macKey: macKey, data: t) } - + internal static func cmac(macKey key: [UInt8], data: [UInt8]) throws -> [UInt8] { // subkey generation: let l = try aes(key: key, plaintext: zero) let k1 = l[0] & 0x80 == 0x00 ? shiftLeft(l) : dbl(l) let k2 = k1[0] & 0x80 == 0x00 ? shiftLeft(k1) : dbl(k1) - + // determine number of blocks: let n = (data.count + 15) / 16 let lastBlockIdx: Int @@ -129,17 +128,17 @@ public class AesSiv { lastBlockIdx = n - 1 lastBlockComplete = data.count % 16 == 0 } - + // blocks 0.. [UInt8] { assert(key.count == kCCKeySizeAES128 || key.count == kCCKeySizeAES192 || key.count == kCCKeySizeAES256) assert(plaintext.count == kCCBlockSizeAES128, "Attempt to run AES-ECB for plaintext != one single block") - + var ciphertext = [UInt8](repeating: 0x00, count: kCCBlockSizeAES128) var ciphertextLen = 0 let status = CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionECBMode), key, key.count, nil, plaintext, plaintext.count, &ciphertext, kCCBlockSizeAES128, &ciphertextLen) - + guard status == kCCSuccess else { throw AesSivError.encryptionFailedWithStatus(status) } - + return ciphertext } - + private static func shiftLeft(_ input: [UInt8]) -> [UInt8] { var output = [UInt8](repeating: 0x00, count: input.count) var bit: UInt8 = 0 - for i in (0..> 7) & 1 } return output } - + private static func dbl(_ block: [UInt8]) -> [UInt8] { var result = shiftLeft(block) - if (block[0] & 0x80 != 0x00) { + if block[0] & 0x80 != 0x00 { result[block.count - 1] ^= dblConst } return result } - + private static func xor(_ data1: [UInt8], _ data2: [UInt8]) -> [UInt8] { assert(data1.count <= data2.count, "Length of first input must be <= length of second input.") var result = [UInt8](repeating: 0x00, count: data1.count) - for i in 0.. [UInt8] { assert(data1.count >= data2.count, "Length of first input must be >= length of second input.") var result = data1 let diff = data1.count - data2.count - for i in 0.. [UInt8] { var result = data - if (result.count < 16) { + if result.count < 16 { result.append(0x80) } - while (result.count < 16) { + while result.count < 16 { result.append(0x00) } return result } - } diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 5516e2e..7223c16 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -6,23 +6,21 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // -import Foundation import CommonCrypto +import Foundation import SwiftBase32 extension Data { - public init?(base64UrlEncoded base64String: String, options: Data.Base64DecodingOptions = []) { - self.init(base64Encoded: base64String.replacingOccurrences(of: "_", with: "/").replacingOccurrences(of: "-", with: "+"), options: options) + self.init(base64Encoded: base64String.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/"), options: options) } - + public func base64UrlEncodedString(options: Data.Base64EncodingOptions = []) -> String { - return self.base64EncodedString(options: options).replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_") + base64EncodedString(options: options).replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_") } - } -public enum FileNameEncoding : String { +public enum FileNameEncoding: String { case base64url case base32 } @@ -32,38 +30,36 @@ enum CryptorError: Error, Equatable { } public class Cryptor { - private let masterKey: Masterkey - + public init(masterKey: Masterkey) { - self.masterKey = masterKey; + self.masterKey = masterKey } - - // MARK: - - // MARK: Path Encryption and Decryption: - + + // MARK: - Path Encryption and Decryption: + public func encryptDirId(_ dirId: Data) throws -> String { let encrypted = try AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: dirId.bytes) var digest = [UInt8](repeating: 0x00, count: Int(CC_SHA1_DIGEST_LENGTH)) CC_SHA1(encrypted, UInt32(encrypted.count) as CC_LONG, &digest) return Data(digest).base32EncodedString } - + public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) let ciphertext = try AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: dirId.bytes) - + // encode: switch encoding { case .base64url: return Data(ciphertext).base64UrlEncodedString() case .base32: return Data(ciphertext).base32EncodedString } } - + public func decryptFileName(_ ciphertextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // decode: - let maybeCiphertextData : Data? = { + let maybeCiphertextData: Data? = { switch encoding { case .base64url: return Data(base64UrlEncoded: ciphertextName) case .base32: return ciphertextName.base32DecodedData @@ -72,7 +68,7 @@ public class Cryptor { guard let ciphertextData = maybeCiphertextData else { throw CryptorError.invalidCiphertext("Can't \(encoding.rawValue)-decode ciphertext name: \(ciphertextName)") } - + // decrypt: let cleartext = try AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: dirId.bytes) if let str = String(data: Data(cleartext), encoding: .utf8) { @@ -81,16 +77,14 @@ public class Cryptor { throw CryptorError.invalidCiphertext("Unable to decode cleartext using UTF-8.") } } - - // MARK: - - // MARK: File Content Encryption and Decryption - - func encryptSingleChunk(chunkNumber: UInt64, nonce: [UInt8], cleartext: [UInt8], fileKey: [UInt8]) -> [UInt8] { - return [UInt8](); // TODO + + // MARK: - File Content Encryption and Decryption + + func encryptSingleChunk(chunkNumber _: UInt64, nonce _: [UInt8], cleartext _: [UInt8], fileKey _: [UInt8]) -> [UInt8] { + [UInt8]() // TODO: } - - func decryptSingleChunk(chunkNumber: UInt64, nonce: [UInt8], ciphertext: [UInt8], fileKey: [UInt8]) -> [UInt8] { - return [UInt8](); // TODO + + func decryptSingleChunk(chunkNumber _: UInt64, nonce _: [UInt8], ciphertext _: [UInt8], fileKey _: [UInt8]) -> [UInt8] { + [UInt8]() // TODO: } - } diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index f311295..0486b32 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -6,18 +6,18 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // -import Foundation -import CryptoSwift import CommonCrypto +import CryptoSwift +import Foundation struct MasterkeyJson: Codable { - let scryptSalt: String - let scryptCostParam: Int - let scryptBlockSize: Int - let primaryMasterKey: String - let hmacMasterKey: String - let versionMac: String - let version: Int + let scryptSalt: String + let scryptCostParam: Int + let scryptBlockSize: Int + let primaryMasterKey: String + let hmacMasterKey: String + let versionMac: String + let version: Int } enum MasterkeyError: Error, Equatable { @@ -28,7 +28,6 @@ enum MasterkeyError: Error, Equatable { } public class Masterkey { - private(set) var aesMasterKey: [UInt8] private(set) var macMasterKey: [UInt8] public let version: Int @@ -38,7 +37,7 @@ public class Masterkey { self.macMasterKey = macMasterKey self.version = version } - + deinit { for i in 0 ..< aesMasterKey.count { aesMasterKey[i] = 0 @@ -47,38 +46,36 @@ public class Masterkey { macMasterKey[i] = 0 } } - - // MARK: - - // MARK: Masterkey Factory Methods - - public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { - let jsonData = try Data(contentsOf: file) - return try createFromMasterkeyFile(jsonData: jsonData, password: password) + // MARK: - Masterkey Factory Methods + + public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { + let jsonData = try Data(contentsOf: file) + return try createFromMasterkeyFile(jsonData: jsonData, password: password, pepper: pepper) } - + public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { let jsonDecoder = JSONDecoder() let decoded = try jsonDecoder.decode(MasterkeyJson.self, from: jsonData) - return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper); + return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper) } - + static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!) let saltAndPepper = salt + pepper let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() - + guard let wrappedMasterKey = Data(base64Encoded: jsonData.primaryMasterKey) else { throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey") } let aesKey = try unwrapMasterKey(wrappedKey: wrappedMasterKey.bytes, kek: kek) - + guard let wrappedHmacKey = Data(base64Encoded: jsonData.hmacMasterKey) else { throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey") } let macKey = try unwrapMasterKey(wrappedKey: wrappedHmacKey.bytes, kek: kek) - + // time-constant version MAC check: guard let storedVersionMac = Data(base64Encoded: jsonData.versionMac), storedVersionMac.count == CC_SHA256_DIGEST_LENGTH else { throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac") @@ -86,30 +83,29 @@ public class Masterkey { var calculatedVersionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) let versionBytes = withUnsafeBytes(of: UInt32(jsonData.version).bigEndian, Array.init) CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macKey, macKey.count, versionBytes, versionBytes.count, &calculatedVersionMac) - var diff : UInt8 = 0x00 - for i in 0.. Masterkey { assert(aesMasterKey.count == kCCKeySizeAES256) assert(macMasterKey.count == kCCKeySizeAES256) return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: version) } - - // MARK: - - // MARK: RFC 3394 Key Wrapping - + + // MARK: - RFC 3394 Key Wrapping + static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var wrapepdKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) - var wrapepdKey = [UInt8](repeating: 0x00, count: wrapepdKeyLen); + var wrapepdKey = [UInt8](repeating: 0x00, count: wrapepdKeyLen) let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrapepdKey, &wrapepdKeyLen) if status == kCCSuccess { return wrapepdKey @@ -117,20 +113,19 @@ public class Masterkey { throw MasterkeyError.wrapFailed(status) } } - + static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var unwrapepdKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) - var unwrapepdKey = [UInt8](repeating: 0x00, count: unwrapepdKeyLen); + var unwrapepdKey = [UInt8](repeating: 0x00, count: unwrapepdKeyLen) let status = CCSymmetricKeyUnwrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, wrappedKey, wrappedKey.count, &unwrapepdKey, &unwrapepdKeyLen) if status == kCCSuccess { assert(unwrapepdKeyLen == kCCKeySizeAES256) return unwrapepdKey } else if status == kCCDecodeError { throw MasterkeyError.invalidPassword - } else { + } else { throw MasterkeyError.unwrapFailed(status) - } + } } - } diff --git a/CryptoLibTests/AesSivTests.swift b/CryptoLibTests/AesSivTests.swift index f559199..287ca5e 100644 --- a/CryptoLibTests/AesSivTests.swift +++ b/CryptoLibTests/AesSivTests.swift @@ -10,178 +10,176 @@ import XCTest @testable import CryptoLib class AesSivTests: XCTestCase { - let aesKey: [UInt8] = [ - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF ] - + let macKey: [UInt8] = [ - 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, - 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 + 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, + 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0 ] let ad: [UInt8] = [ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 ] - + func testEncrypt() { let plaintext: [UInt8] = [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, - 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE ] let expected: [UInt8] = [ - 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, - 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93, - 0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04, - 0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c + 0x85, 0x63, 0x2D, 0x07, 0xC6, 0xE8, 0xF3, 0x7F, + 0x95, 0x0A, 0xCD, 0x32, 0x0A, 0x2E, 0xCC, 0x93, + 0x40, 0xC0, 0x2B, 0x96, 0x90, 0xC4, 0xDC, 0x04, + 0xDA, 0xEF, 0x7F, 0x6A, 0xFE, 0x5C ] - + let result = try? AesSiv.encrypt(aesKey: aesKey, macKey: macKey, plaintext: plaintext, ad: ad) - + XCTAssertEqual(expected, result) } - + func testDecrypt() { let ciphertext: [UInt8] = [ - 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, - 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93, - 0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04, - 0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c + 0x85, 0x63, 0x2D, 0x07, 0xC6, 0xE8, 0xF3, 0x7F, + 0x95, 0x0A, 0xCD, 0x32, 0x0A, 0x2E, 0xCC, 0x93, + 0x40, 0xC0, 0x2B, 0x96, 0x90, 0xC4, 0xDC, 0x04, + 0xDA, 0xEF, 0x7F, 0x6A, 0xFE, 0x5C ] - + let expected: [UInt8] = [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, - 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE ] - + let result = try? AesSiv.decrypt(aesKey: aesKey, macKey: macKey, ciphertext: ciphertext, ad: ad) XCTAssertEqual(expected, result) } - + func testAesCtr() { let aesKey: [UInt8] = [ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F ] - + let iv: [UInt8] = [ - 0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb, - 0x06, 0xf4, 0xd1, 0x4b, 0x7f, 0x2f, 0xbd, 0x0f + 0x7B, 0xDB, 0x6E, 0x3B, 0x43, 0x26, 0x67, 0xEB, + 0x06, 0xF4, 0xD1, 0x4B, 0x7F, 0x2F, 0xBD, 0x0F ] let plaintext = [UInt8](repeating: 0x00, count: 16 * 3) let expected: [UInt8] = [ - 0xbf, 0xf8, 0x66, 0x5c, 0xfd, 0xd7, 0x33, 0x63, - 0x55, 0x0f, 0x74, 0x00, 0xe8, 0xf9, 0xd3, 0x76, - 0xb2, 0xc9, 0x08, 0x8e, 0x71, 0x3b, 0x86, 0x17, - 0xd8, 0x83, 0x92, 0x26, 0xd9, 0xf8, 0x81, 0x59, - 0x9e, 0x44, 0xd8, 0x27, 0x23, 0x49, 0x49, 0xbc, - 0x1b, 0x12, 0x34, 0x8e, 0xbc, 0x19, 0x5e, 0xc7 - ] - + 0xBF, 0xF8, 0x66, 0x5C, 0xFD, 0xD7, 0x33, 0x63, + 0x55, 0x0F, 0x74, 0x00, 0xE8, 0xF9, 0xD3, 0x76, + 0xB2, 0xC9, 0x08, 0x8E, 0x71, 0x3B, 0x86, 0x17, + 0xD8, 0x83, 0x92, 0x26, 0xD9, 0xF8, 0x81, 0x59, + 0x9E, 0x44, 0xD8, 0x27, 0x23, 0x49, 0x49, 0xBC, + 0x1B, 0x12, 0x34, 0x8E, 0xBC, 0x19, 0x5E, 0xC7 + ] + let result = try? AesSiv.aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext) XCTAssertEqual(expected, result) } - func testS2v() { + func testS2v() { let plaintext: [UInt8] = [ - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, - 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE ] - + let expected: [UInt8] = [ - 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, - 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93 + 0x85, 0x63, 0x2D, 0x07, 0xC6, 0xE8, 0xF3, 0x7F, + 0x95, 0x0A, 0xCD, 0x32, 0x0A, 0x2E, 0xCC, 0x93 ] - + let result = try? AesSiv.s2v(macKey: macKey, plaintext: plaintext, ad: [ad]) XCTAssertEqual(expected, result) } - + func testCmac1() { let macKey: [UInt8] = [ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C ] let message: [UInt8] = [] let expected: [UInt8] = [ - 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, - 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 + 0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59, 0x37, 0x28, + 0x7F, 0xA3, 0x7D, 0x12, 0x9B, 0x75, 0x67, 0x46 ] let result = try? AesSiv.cmac(macKey: macKey, data: message) - + XCTAssertEqual(expected, result) } - + func testCmac2() { let macKey: [UInt8] = [ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C ] let message: [UInt8] = [ - 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A ] let expected: [UInt8] = [ - 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, - 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c + 0x07, 0x0A, 0x16, 0xB4, 0x6B, 0x4D, 0x41, 0x44, + 0xF7, 0x9B, 0xDD, 0x9D, 0xD0, 0x4A, 0x28, 0x7C ] let result = try? AesSiv.cmac(macKey: macKey, data: message) - + XCTAssertEqual(expected, result) } - + func testCmac3() { let macKey: [UInt8] = [ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C ] let message: [UInt8] = [ - 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11 ] let expected: [UInt8] = [ - 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, - 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27, + 0xDF, 0xA6, 0x67, 0x47, 0xDE, 0x9A, 0xE6, 0x30, + 0x30, 0xCA, 0x32, 0x61, 0x14, 0x97, 0xC8, 0x27 ] let result = try? AesSiv.cmac(macKey: macKey, data: message) - + XCTAssertEqual(expected, result) } - + func testCmac4() { let macKey: [UInt8] = [ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C ] let message: [UInt8] = [ - 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, - 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, - 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, - 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 ] let expected: [UInt8] = [ - 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, - 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe + 0x51, 0xF0, 0xBE, 0xBF, 0x7E, 0x3B, 0x9D, 0x92, + 0xFC, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3C, 0xFE ] let result = try? AesSiv.cmac(macKey: macKey, data: message) - + XCTAssertEqual(expected, result) } - } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 446301b..fdaf826 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -10,20 +10,19 @@ import XCTest @testable import CryptoLib class CryptorTests: XCTestCase { - var masterkey: Masterkey! - override func setUp() { + override func setUp() { let aesKey: [UInt8] = Array(repeating: 0x55, count: 32) let macKey: [UInt8] = Array(repeating: 0x77, count: 32) masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey, version: 7) - + XCTAssertNotNil(masterkey) - } - + } + func testEncryptDirId() throws { - let cryptor = Cryptor.init(masterKey: masterkey) - + let cryptor = Cryptor(masterKey: masterkey) + let rootDir = try cryptor.encryptDirId("".data(using: .utf8)!) XCTAssertEqual("VLWEHT553J5DR7OZLRJAYDIWFCXZABOD", rootDir) @@ -31,21 +30,18 @@ class CryptorTests: XCTestCase { XCTAssertEqual("7C3USOO3VU7IVQRKFMRFV3QE4VEZJECV", testDir) } - func testEncryptAndDecryptName() throws { + func testEncryptAndDecryptName() throws { continueAfterFailure = false - - let cryptor = Cryptor.init(masterKey: masterkey) + + let cryptor = Cryptor(masterKey: masterkey) let dirId = "foo".data(using: .utf8)! let originalName = "hello.txt" - + let ciphertextName = try cryptor.encryptFileName(originalName, dirId: dirId) XCTAssertNotNil(ciphertextName) - + let cleartextName = try cryptor.decryptFileName(ciphertextName, dirId: dirId) XCTAssertNotNil(cleartextName) XCTAssertEqual(originalName, cleartextName) - - } - - + } } diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index b903e05..2cb27f6 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -10,15 +10,14 @@ import XCTest @testable import CryptoLib class MasterkeyTests: XCTestCase { + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - func testWrapAndUnwrapKey() throws { let rawKey = [UInt8](repeating: 0x77, count: 32) let kek = [UInt8](repeating: 0x55, count: 32) @@ -29,7 +28,7 @@ class MasterkeyTests: XCTestCase { XCTAssertEqual(rawKey, unwrapped) } - func testCreateFromMasterkeyFile() throws { + func testCreateFromMasterkeyFile() throws { let expectedKeys = [UInt8](repeating: 0x00, count: 32) let jsonData = """ { @@ -42,15 +41,15 @@ class MasterkeyTests: XCTestCase { "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" } """.data(using: .utf8)! - + let masterKey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") - + XCTAssertNotNil(masterKey) XCTAssertEqual(7, masterKey.version) XCTAssertEqual(expectedKeys, masterKey.aesMasterKey) XCTAssertEqual(expectedKeys, masterKey.macMasterKey) - } - + } + func testCreateFromMasterkeyFileWithWrongPassword() throws { let jsonData = """ { @@ -64,11 +63,11 @@ class MasterkeyTests: XCTestCase { } """.data(using: .utf8)! - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password", { error in + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password") { error in XCTAssertEqual(error as! MasterkeyError, MasterkeyError.invalidPassword) - }) - } - + } + } + func testCreateFromMasterkeyFileWithInvalidVersionMac() throws { let jsonData = """ { @@ -82,11 +81,11 @@ class MasterkeyTests: XCTestCase { } """.data(using: .utf8)! - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac")) - }) - } - + } + } + func testCreateFromMasterkeyFileWithMalformedJson1() throws { let jsonData = """ { @@ -100,11 +99,11 @@ class MasterkeyTests: XCTestCase { } """.data(using: .utf8)! - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey")) - }) - } - + } + } + func testCreateFromMasterkeyFileWithMalformedJson2() throws { let jsonData = """ { @@ -118,11 +117,11 @@ class MasterkeyTests: XCTestCase { } """.data(using: .utf8)! - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey")) - }) - } - + } + } + func testCreateFromMasterkeyFileWithMalformedJson3() throws { let jsonData = """ { @@ -136,9 +135,8 @@ class MasterkeyTests: XCTestCase { } """.data(using: .utf8)! - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in + XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac")) - }) - } - + } + } } From 5555838d81be0228875addd70956a5d0bd3a6fd6 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 11 May 2020 17:30:38 +0200 Subject: [PATCH 027/144] Added "Lint with SwiftFormat" build phase --- CryptoLib.xcodeproj/project.pbxproj | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index 5c2bb46..c578aa1 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -160,6 +160,7 @@ 4A7C212E2451F2AC00DE81E6 /* Sources */, 4A7C212F2451F2AC00DE81E6 /* Frameworks */, 4A7C21302451F2AC00DE81E6 /* Resources */, + 74862A712469A6B2003D81CB /* Lint With SwiftFormat */, ); buildRules = ( ); @@ -267,6 +268,24 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 74862A712469A6B2003D81CB /* Lint With SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Lint With SwiftFormat"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftformat >/dev/null; then\n swiftformat --lint .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; + }; B0398845BD49B4481C76093F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; From 2108e95dc23ea7ee325be989e10cc5ae19315575 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 11 May 2020 17:59:59 +0200 Subject: [PATCH 028/144] Updated SwiftFormat rules --- .swiftformat | 1 + 1 file changed, 1 insertion(+) diff --git a/.swiftformat b/.swiftformat index fc1fa30..1d63fe3 100644 --- a/.swiftformat +++ b/.swiftformat @@ -7,6 +7,7 @@ --commas inline --importgrouping testable-bottom --indent tab +--self init-only --swiftversion 5.1 --tabwidth 4 From f182c4e5a5e1b139e47bdf2d16f6222f9634dfbd Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 13 May 2020 19:23:53 +0200 Subject: [PATCH 029/144] Added lenient option to SwiftFormat lint --- CryptoLib.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index c578aa1..232acb5 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -284,7 +284,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftformat >/dev/null; then\n swiftformat --lint .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; + shellScript = "if which swiftformat >/dev/null; then\n swiftformat --lint --lenient .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; }; B0398845BD49B4481C76093F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; From 19c6449fc569a5428af11768fc5455695adc9626 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 18 May 2020 14:00:36 +0200 Subject: [PATCH 030/144] Disabled redundantReturn SwitFormat rule --- .swiftformat | 1 + CryptoLib/Cryptor.swift | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.swiftformat b/.swiftformat index 1d63fe3..21797ed 100644 --- a/.swiftformat +++ b/.swiftformat @@ -14,3 +14,4 @@ # rules --enable isEmpty +--disable redundantReturn diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 7223c16..d411ec4 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -16,7 +16,7 @@ extension Data { } public func base64UrlEncodedString(options: Data.Base64EncodingOptions = []) -> String { - base64EncodedString(options: options).replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_") + return base64EncodedString(options: options).replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_") } } @@ -81,10 +81,10 @@ public class Cryptor { // MARK: - File Content Encryption and Decryption func encryptSingleChunk(chunkNumber _: UInt64, nonce _: [UInt8], cleartext _: [UInt8], fileKey _: [UInt8]) -> [UInt8] { - [UInt8]() // TODO: + return [UInt8]() // TODO: } func decryptSingleChunk(chunkNumber _: UInt64, nonce _: [UInt8], ciphertext _: [UInt8], fileKey _: [UInt8]) -> [UInt8] { - [UInt8]() // TODO: + return [UInt8]() // TODO: } } From 36f797d2cc420305de07afbbd7eb90a171bd29b5 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 20 May 2020 17:41:27 +0200 Subject: [PATCH 031/144] Updated Podspec & Podfile --- CryptomatorCryptoLib.podspec | 7 +++---- Podfile | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 23f8bab..52bc1b6 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -12,11 +12,10 @@ Pod::Spec.new do |s| s.social_media_url = 'https://twitter.com/Cryptomator' s.public_header_files = 'CryptoLib/CryptoLib.h' + s.source_files = 'CryptoLib/**/*.{swift,h,m}' s.ios.deployment_target = '8.0' s.swift_version = '5.0' - s.source_files = 'CryptoLib/**/*{swift,h,m}' - - s.dependency 'CryptoSwift', '~> 1.3' - s.dependency 'SwiftBase32', '~> 0.8' + s.dependency 'CryptoSwift', '~> 1.3.0' + s.dependency 'SwiftBase32', '~> 0.8.0' end diff --git a/Podfile b/Podfile index 5a4b462..82772b5 100644 --- a/Podfile +++ b/Podfile @@ -2,9 +2,9 @@ platform :ios, '8.0' inhibit_all_warnings! target 'CryptoLib' do - pod 'CryptoSwift', '~> 1.3' - pod 'SwiftBase32', '~> 0.8' - + pod 'CryptoSwift', '~> 1.3.0' + pod 'SwiftBase32', '~> 0.8.0' + target 'CryptoLibTests' do inherit! :search_paths end From a8ece8b5f7647f8eb9767348ae89e7c96fbc285a Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 26 May 2020 16:48:48 +0200 Subject: [PATCH 032/144] Set unusedArguments SwiftFormat rule to closure-only --- .swiftformat | 1 + 1 file changed, 1 insertion(+) diff --git a/.swiftformat b/.swiftformat index 21797ed..8d62147 100644 --- a/.swiftformat +++ b/.swiftformat @@ -8,6 +8,7 @@ --importgrouping testable-bottom --indent tab --self init-only +--stripunusedargs closure-only --swiftversion 5.1 --tabwidth 4 From 48d3799d7b09411d4d97f723689120823a44c28b Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 26 May 2020 17:43:11 +0200 Subject: [PATCH 033/144] Disabled trailingClosures SwitFormat rule --- .swiftformat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index 8d62147..a9971bd 100644 --- a/.swiftformat +++ b/.swiftformat @@ -15,4 +15,4 @@ # rules --enable isEmpty ---disable redundantReturn +--disable redundantReturn,trailingClosures From 48dec1bbc55ccd8fb2a109c3807a898014ce1ca6 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 27 May 2020 15:05:03 +0200 Subject: [PATCH 034/144] Added "upload code coverage report" step in CI build --- .github/workflows/build.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bba86d3..c9f87bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,10 +9,24 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- - name: Install dependencies run: pod install - name: Build and test - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace 'CryptoLib.xcworkspace' -scheme 'CryptoLib' -destination 'OS=13.4.1,name=iPhone 11 Pro' clean test | xcpretty + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace 'CryptoLib.xcworkspace' -scheme 'CryptoLib' -destination 'name=iPhone 11 Pro' -enableCodeCoverage YES clean test | xcpretty + - name: Upload code coverage report + run: | + gem install slather + slather coverage -x --scheme CryptoLib --workspace CryptoLib.xcworkspace CryptoLib.xcodeproj + bash <(curl -Ls https://coverage.codacy.com/get.sh) + env: + CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} + continue-on-error: true release: name: Deploy and draft a release From e8d692f7d1d75d5526fd9ee1811bbaa298b2a64e Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 27 May 2020 15:23:01 +0200 Subject: [PATCH 035/144] =?UTF-8?q?=F0=9F=A6=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2b998d7..de7554d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ [![Version](http://img.shields.io/cocoapods/v/CryptomatorCryptoLib.svg)](https://cocoapods.org/pods/CryptomatorCryptoLib) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) +[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) High level wrapper for cryptographic operations used by Cryptomator iOS App From 0a37892862ee25e3da3d038e8bfa2f391105ac07 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 6 Jun 2020 09:39:13 +0200 Subject: [PATCH 036/144] first shot at chunk encryption (still missing appropriate unit tests) --- CryptoLib.xcodeproj/project.pbxproj | 8 ++++ CryptoLib/AesCtr.swift | 53 +++++++++++++++++++++++++++ CryptoLib/AesSiv.swift | 34 ++--------------- CryptoLib/Cryptor.swift | 56 ++++++++++++++++++++++++++-- CryptoLibTests/AesCtrTests.swift | 57 +++++++++++++++++++++++++++++ CryptoLibTests/AesSivTests.swift | 4 +- CryptoLibTests/CryptorTests.swift | 12 ++++++ Podfile.lock | 6 +-- 8 files changed, 191 insertions(+), 39 deletions(-) create mode 100644 CryptoLib/AesCtr.swift create mode 100644 CryptoLibTests/AesCtrTests.swift diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index 232acb5..c983425 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB8132454708600F9FF51 /* Masterkey.swift */; }; 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */; }; 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */; }; + 9EB822C1248AF82200879838 /* AesCtr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB822C0248AF82200879838 /* AesCtr.swift */; }; + 9EB822C3248AF9C500879838 /* AesCtrTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB822C2248AF9C500879838 /* AesCtrTests.swift */; }; AAC7D038BEF14DA7788F6090 /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 085645A8AA97369573B917E8 /* libPods-CryptoLib.a */; }; /* End PBXBuildFile section */ @@ -49,6 +51,8 @@ 9E9BB8132454708600F9FF51 /* Masterkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Masterkey.swift; sourceTree = ""; }; 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyTests.swift; sourceTree = ""; }; 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libCryptoSwift.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 9EB822C0248AF82200879838 /* AesCtr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesCtr.swift; sourceTree = ""; }; + 9EB822C2248AF9C500879838 /* AesCtrTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesCtrTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -108,6 +112,7 @@ children = ( 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */, 4A7C21362451F2AC00DE81E6 /* Info.plist */, + 9EB822C0248AF82200879838 /* AesCtr.swift */, 9E44EEA524599C6900A37B01 /* AesSiv.swift */, 9E9BB811245412E900F9FF51 /* Cryptor.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, @@ -119,6 +124,7 @@ isa = PBXGroup; children = ( 4A7C21422451F2AD00DE81E6 /* Info.plist */, + 9EB822C2248AF9C500879838 /* AesCtrTests.swift */, 9E44EEA724599C7800A37B01 /* AesSivTests.swift */, 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */, 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, @@ -316,6 +322,7 @@ buildActionMask = 2147483647; files = ( 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */, + 9EB822C1248AF82200879838 /* AesCtr.swift in Sources */, 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */, 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */, ); @@ -326,6 +333,7 @@ buildActionMask = 2147483647; files = ( 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */, + 9EB822C3248AF9C500879838 /* AesCtrTests.swift in Sources */, 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */, 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */, ); diff --git a/CryptoLib/AesCtr.swift b/CryptoLib/AesCtr.swift new file mode 100644 index 0000000..94d0e26 --- /dev/null +++ b/CryptoLib/AesCtr.swift @@ -0,0 +1,53 @@ +// +// AesCtr.swift +// CryptoLib +// +// Created by Sebastian Stenzel on 06.06.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// +import CommonCrypto +import Foundation + +enum AesCtrError: Error { + case invalidParameter(_ reason: String) + case encryptionFailedWithStatus(_ status: CCCryptorStatus) +} + +internal class AesCtr { + /** + High level AES-CTR wrapper around CommonCrypto primitives. Can be used for encryption and decryption (it is the same in CTR mode) + - Parameter key: 128 or 256 bit encryption key + - Parameter iv: 128 bit initialization vector (must not be reused!) + - Parameter data: data to be encrypted/decrypted + - Returns: encrypted/decrypted data + */ + public static func compute(key: [UInt8], iv: [UInt8], data: [UInt8]) throws -> [UInt8] { + assert(key.count == kCCKeySizeAES256 || key.count == kCCKeySizeAES128, "key expected to be 128 or 256 bit") + assert(iv.count == kCCBlockSizeAES128, "iv expected to be 128 bit") + + var cryptor: CCCryptorRef? + var status = CCCryptorCreateWithMode(CCOperation(kCCEncrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), iv, key, key.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) + guard status == kCCSuccess, cryptor != nil else { + throw AesCtrError.invalidParameter("failed to initialize cryptor") + } + defer { + CCCryptorRelease(cryptor) + } + + let outlen = CCCryptorGetOutputLength(cryptor, data.count, true) + var ciphertext = [UInt8](repeating: 0x00, count: outlen) + + var numEncryptedBytes: Int = 0 + status = CCCryptorUpdate(cryptor, data, data.count, &ciphertext, ciphertext.count, &numEncryptedBytes) + guard status == kCCSuccess else { + throw AesCtrError.encryptionFailedWithStatus(status) + } + + status = CCCryptorFinal(cryptor, &ciphertext, ciphertext.count, &numEncryptedBytes) + guard status == kCCSuccess else { + throw AesCtrError.encryptionFailedWithStatus(status) + } + + return ciphertext + } +} diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index 7863408..2fe5119 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -24,7 +24,7 @@ public class AesSiv { throw AesSivError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") } let iv = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) - let ciphertext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext) + let ciphertext = try ctr(aesKey: aesKey, iv: iv, plaintext: plaintext) return iv + ciphertext } @@ -34,7 +34,7 @@ public class AesSiv { } let iv = Array(ciphertext[..<16]) let actualCiphertext = Array(ciphertext[16...]) - let plaintext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: actualCiphertext) + let plaintext = try ctr(aesKey: aesKey, iv: iv, plaintext: actualCiphertext) let control = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) // time-constant comparison @@ -51,38 +51,12 @@ public class AesSiv { return plaintext } - internal static func aesCtr(aesKey key: [UInt8], iv: [UInt8], plaintext: [UInt8]) throws -> [UInt8] { - assert(key.count == kCCKeySizeAES256 || key.count == kCCKeySizeAES128, "aesKey expected to be 128 or 256 bit") - + internal static func ctr(aesKey key: [UInt8], iv: [UInt8], plaintext: [UInt8]) throws -> [UInt8] { // clear out the 31st and 63rd bit (see https://tools.ietf.org/html/rfc5297#section-2.5) var ctr = iv ctr[8] &= 0x7F ctr[12] &= 0x7F - - var cryptor: CCCryptorRef? - var status = CCCryptorCreateWithMode(CCOperation(kCCEncrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), ctr, key, key.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) - guard status == kCCSuccess, cryptor != nil else { - throw AesSivError.invalidParameter("failed to initialize cryptor") - } - defer { - CCCryptorRelease(cryptor) - } - - let outlen = CCCryptorGetOutputLength(cryptor, plaintext.count, true) - var ciphertext = [UInt8](repeating: 0x00, count: outlen) - - var numEncryptedBytes: Int = 0 - status = CCCryptorUpdate(cryptor, plaintext, plaintext.count, &ciphertext, ciphertext.count, &numEncryptedBytes) - guard status == kCCSuccess else { - throw AesSivError.encryptionFailedWithStatus(status) - } - - status = CCCryptorFinal(cryptor, &ciphertext, ciphertext.count, &numEncryptedBytes) - guard status == kCCSuccess else { - throw AesSivError.encryptionFailedWithStatus(status) - } - - return ciphertext + return try AesCtr.compute(key: key, iv: ctr, data: plaintext) } internal static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [[UInt8]]) throws -> [UInt8] { diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index d411ec4..c8c86de 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -20,6 +20,12 @@ extension Data { } } +extension FixedWidthInteger { + public func byteArray() -> [UInt8] { + return withUnsafeBytes(of: self, { [UInt8]($0) }) + } +} + public enum FileNameEncoding: String { case base64url case base32 @@ -27,6 +33,8 @@ public enum FileNameEncoding: String { enum CryptorError: Error, Equatable { case invalidCiphertext(_ reason: String? = nil) + case csprngError + case unauthenticCiphertext } public class Cryptor { @@ -80,11 +88,51 @@ public class Cryptor { // MARK: - File Content Encryption and Decryption - func encryptSingleChunk(chunkNumber _: UInt64, nonce _: [UInt8], cleartext _: [UInt8], fileKey _: [UInt8]) -> [UInt8] { - return [UInt8]() // TODO: + func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { + var chunkNonce = [UInt8](repeating: 0x00, count: kCCBlockSizeAES128) + guard SecRandomCopyBytes(kSecRandomDefault, chunkNonce.count, &chunkNonce) == errSecSuccess else { + throw CryptorError.csprngError + } + let ciphertext = try AesCtr.compute(key: fileKey, iv: chunkNonce, data: chunk) + let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext + var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + return chunkNonce + ciphertext + mac } - func decryptSingleChunk(chunkNumber _: UInt64, nonce _: [UInt8], ciphertext _: [UInt8], fileKey _: [UInt8]) -> [UInt8] { - return [UInt8]() // TODO: + func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { + assert(chunk.count >= kCCBlockSizeAES128 + Int(CC_SHA256_DIGEST_LENGTH), "ciphertext chunk must at least contain nonce + mac") + + // decompose chunk: + let beginOfMAC = chunk.count - Int(CC_SHA256_DIGEST_LENGTH) + let chunkNonce = [UInt8](chunk[0 ..< kCCBlockSizeAES128]) + let ciphertext = [UInt8](chunk[kCCBlockSizeAES128 ..< beginOfMAC]) + let expectedMAC = [UInt8](chunk[beginOfMAC...]) + + // check MAC: + let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext + var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + guard checkMAC(expected: expectedMAC, actual: mac) else { + throw CryptorError.unauthenticCiphertext + } + + // decrypt: + return try AesCtr.compute(key: fileKey, iv: chunkNonce, data: ciphertext) + } + + // time constant comparison: + private func checkMAC(expected: [UInt8], actual: [UInt8]) -> Bool { + assert(expected.count == actual.count, "MACs expected to be of same length") + + if #available(iOS 10.1, *) { + return timingsafe_bcmp(expected, actual, expected.count) == 0 + } else { + var diff: UInt8 = 0 + for i in 0 ..< expected.count { + diff |= expected[i] ^ actual[i] + } + return diff == 0 + } } } diff --git a/CryptoLibTests/AesCtrTests.swift b/CryptoLibTests/AesCtrTests.swift new file mode 100644 index 0000000..5aa0d80 --- /dev/null +++ b/CryptoLibTests/AesCtrTests.swift @@ -0,0 +1,57 @@ +// +// AesCtrTests.swift +// CryptoLibTests +// +// Created by Sebastian Stenzel on 06.06.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import XCTest +@testable import CryptoLib + +// test vectors F5.5 and F5.6 from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf +class AesCtrTests: XCTestCase { + let key: [UInt8] = [ + 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 + ] + + let iv: [UInt8] = [ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF + ] + + let plaintext: [UInt8] = [ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 + ] + + let ciphertext: [UInt8] = [ + 0x60, 0x1E, 0xC3, 0x13, 0x77, 0x57, 0x89, 0xA5, + 0xB7, 0xA7, 0xF5, 0x04, 0xBB, 0xF3, 0xD2, 0x28, + 0xF4, 0x43, 0xE3, 0xCA, 0x4D, 0x62, 0xB5, 0x9A, + 0xCA, 0x84, 0xE9, 0x90, 0xCA, 0xCA, 0xF5, 0xC5, + 0x2B, 0x09, 0x30, 0xDA, 0xA2, 0x3D, 0xE9, 0x4C, + 0xE8, 0x70, 0x17, 0xBA, 0x2D, 0x84, 0x98, 0x8D, + 0xDF, 0xC9, 0xC5, 0x8D, 0xB6, 0x7A, 0xAD, 0xA6, + 0x13, 0xC2, 0xDD, 0x08, 0x45, 0x79, 0x41, 0xA6 + ] + + func testEncrypt() { + let result = try? AesCtr.compute(key: key, iv: iv, data: plaintext) + XCTAssertEqual(ciphertext, result) + } + + func testDecrypt() { + let result = try? AesCtr.compute(key: key, iv: iv, data: ciphertext) + XCTAssertEqual(plaintext, result) + } +} diff --git a/CryptoLibTests/AesSivTests.swift b/CryptoLibTests/AesSivTests.swift index 287ca5e..439b54e 100644 --- a/CryptoLibTests/AesSivTests.swift +++ b/CryptoLibTests/AesSivTests.swift @@ -62,7 +62,7 @@ class AesSivTests: XCTestCase { XCTAssertEqual(expected, result) } - func testAesCtr() { + func testCtr() { let aesKey: [UInt8] = [ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F @@ -84,7 +84,7 @@ class AesSivTests: XCTestCase { 0x1B, 0x12, 0x34, 0x8E, 0xBC, 0x19, 0x5E, 0xC7 ] - let result = try? AesSiv.aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext) + let result = try? AesSiv.ctr(aesKey: aesKey, iv: iv, plaintext: plaintext) XCTAssertEqual(expected, result) } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index fdaf826..6dc87b8 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -44,4 +44,16 @@ class CryptorTests: XCTestCase { XCTAssertNotNil(cleartextName) XCTAssertEqual(originalName, cleartextName) } + + func testEncryptAndDecryptSingleChunk() throws { + let cryptor = Cryptor(masterKey: masterkey) + let nonce = [UInt8](repeating: 0x00, count: 16) + let filekey = [UInt8](repeating: 0x00, count: 32) + let cleartext = "hello world".data(using: .ascii)! + + let encrypted = try cryptor.encryptSingleChunk(cleartext.bytes, chunkNumber: 0, headerNonce: nonce, fileKey: filekey) + let decrypted = try cryptor.decryptSingleChunk(encrypted, chunkNumber: 0, headerNonce: nonce, fileKey: filekey) + + XCTAssertEqual(cleartext.bytes, decrypted) + } } diff --git a/Podfile.lock b/Podfile.lock index 92f02a9..6ae6ad3 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -3,8 +3,8 @@ PODS: - SwiftBase32 (0.8.0) DEPENDENCIES: - - CryptoSwift (~> 1.3) - - SwiftBase32 (~> 0.8) + - CryptoSwift (~> 1.3.0) + - SwiftBase32 (~> 0.8.0) SPEC REPOS: trunk: @@ -15,6 +15,6 @@ SPEC CHECKSUMS: CryptoSwift: f12f037f6d0fcd6d48c96db0071b653de64e6c4d SwiftBase32: 723fd05e2fce776f2b98f26fe423bd20fa80e411 -PODFILE CHECKSUM: c66c63f4d05e7c2a8a790153994cdb7463c7b08a +PODFILE CHECKSUM: 026266d3146c90650716bef1229c141bb12d87b4 COCOAPODS: 1.9.1 From 3b9cd8748b44bf0ba4cc63acac140a53624018d8 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 8 Jun 2020 20:32:37 +0200 Subject: [PATCH 037/144] Fixed funny typo --- CryptoLib/Masterkey.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 0486b32..6d846f1 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -104,11 +104,11 @@ public class Masterkey { static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) - var wrapepdKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) - var wrapepdKey = [UInt8](repeating: 0x00, count: wrapepdKeyLen) - let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrapepdKey, &wrapepdKeyLen) + var wrappedKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) + var wrappedKey = [UInt8](repeating: 0x00, count: wrappedKeyLen) + let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrappedKey, &wrappedKeyLen) if status == kCCSuccess { - return wrapepdKey + return wrappedKey } else { throw MasterkeyError.wrapFailed(status) } @@ -116,12 +116,12 @@ public class Masterkey { static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) - var unwrapepdKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) - var unwrapepdKey = [UInt8](repeating: 0x00, count: unwrapepdKeyLen) - let status = CCSymmetricKeyUnwrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, wrappedKey, wrappedKey.count, &unwrapepdKey, &unwrapepdKeyLen) + var unwrappedKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) + var unwrappedKey = [UInt8](repeating: 0x00, count: unwrappedKeyLen) + let status = CCSymmetricKeyUnwrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, wrappedKey, wrappedKey.count, &unwrappedKey, &unwrappedKeyLen) if status == kCCSuccess { - assert(unwrapepdKeyLen == kCCKeySizeAES256) - return unwrapepdKey + assert(unwrappedKeyLen == kCCKeySizeAES256) + return unwrappedKey } else if status == kCCDecodeError { throw MasterkeyError.invalidPassword } else { From 5accc9769a7aec1090d34916a9a9606207489c0b Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 9 Jun 2020 16:23:28 +0200 Subject: [PATCH 038/144] Added file header & file content encryption (WIP), unified errors --- CryptoLib.xcodeproj/project.pbxproj | 8 ++ CryptoLib/AesCtr.swift | 11 +- CryptoLib/AesSiv.swift | 16 +-- CryptoLib/CSPRNG.swift | 19 ++++ CryptoLib/CryptoError.swift | 18 ++++ CryptoLib/Cryptor.swift | 160 +++++++++++++++++++++++++--- CryptoLibTests/CryptorTests.swift | 57 ++++++++++ 7 files changed, 256 insertions(+), 33 deletions(-) create mode 100644 CryptoLib/CSPRNG.swift create mode 100644 CryptoLib/CryptoError.swift diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index c983425..a446137 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 74F0F752248FC7F200B4C26D /* CSPRNG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F751248FC7F200B4C26D /* CSPRNG.swift */; }; + 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F753248FC89B00B4C26D /* CryptoError.swift */; }; 7980E962F1389ACDDCEBB596 /* libPods-CryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */; }; 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; @@ -43,6 +45,8 @@ 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 74F0F751248FC7F200B4C26D /* CSPRNG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSPRNG.swift; sourceTree = ""; }; + 74F0F753248FC89B00B4C26D /* CryptoError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoError.swift; sourceTree = ""; }; 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.debug.xcconfig"; sourceTree = ""; }; 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; 9E44EEA524599C6900A37B01 /* AesSiv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSiv.swift; sourceTree = ""; }; @@ -114,7 +118,9 @@ 4A7C21362451F2AC00DE81E6 /* Info.plist */, 9EB822C0248AF82200879838 /* AesCtr.swift */, 9E44EEA524599C6900A37B01 /* AesSiv.swift */, + 74F0F753248FC89B00B4C26D /* CryptoError.swift */, 9E9BB811245412E900F9FF51 /* Cryptor.swift */, + 74F0F751248FC7F200B4C26D /* CSPRNG.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, ); path = CryptoLib; @@ -324,7 +330,9 @@ 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */, 9EB822C1248AF82200879838 /* AesCtr.swift in Sources */, 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */, + 74F0F752248FC7F200B4C26D /* CSPRNG.swift in Sources */, 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */, + 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/CryptoLib/AesCtr.swift b/CryptoLib/AesCtr.swift index 94d0e26..e0b0854 100644 --- a/CryptoLib/AesCtr.swift +++ b/CryptoLib/AesCtr.swift @@ -8,11 +8,6 @@ import CommonCrypto import Foundation -enum AesCtrError: Error { - case invalidParameter(_ reason: String) - case encryptionFailedWithStatus(_ status: CCCryptorStatus) -} - internal class AesCtr { /** High level AES-CTR wrapper around CommonCrypto primitives. Can be used for encryption and decryption (it is the same in CTR mode) @@ -28,7 +23,7 @@ internal class AesCtr { var cryptor: CCCryptorRef? var status = CCCryptorCreateWithMode(CCOperation(kCCEncrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), iv, key, key.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) guard status == kCCSuccess, cryptor != nil else { - throw AesCtrError.invalidParameter("failed to initialize cryptor") + throw CryptoError.invalidParameter("failed to initialize cryptor") } defer { CCCryptorRelease(cryptor) @@ -40,12 +35,12 @@ internal class AesCtr { var numEncryptedBytes: Int = 0 status = CCCryptorUpdate(cryptor, data, data.count, &ciphertext, ciphertext.count, &numEncryptedBytes) guard status == kCCSuccess else { - throw AesCtrError.encryptionFailedWithStatus(status) + throw CryptoError.ccCryptorError(status) } status = CCCryptorFinal(cryptor, &ciphertext, ciphertext.count, &numEncryptedBytes) guard status == kCCSuccess else { - throw AesCtrError.encryptionFailedWithStatus(status) + throw CryptoError.ccCryptorError(status) } return ciphertext diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index 2fe5119..aff3608 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -9,19 +9,13 @@ import CommonCrypto import Foundation -enum AesSivError: Error { - case invalidParameter(_ reason: String) - case encryptionFailedWithStatus(_ status: CCCryptorStatus) - case unauthenticCiphertext -} - public class AesSiv { static let zero = [UInt8](repeating: 0x00, count: 16) static let dblConst: UInt8 = 0x87 public static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { if plaintext.count > UInt32.max - 16 { - throw AesSivError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") + throw CryptoError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") } let iv = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) let ciphertext = try ctr(aesKey: aesKey, iv: iv, plaintext: plaintext) @@ -30,7 +24,7 @@ public class AesSiv { static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { if ciphertext.count < 16 { - throw AesSivError.invalidParameter("ciphertext must be at least 16 bytes") + throw CryptoError.invalidParameter("ciphertext must be at least 16 bytes") } let iv = Array(ciphertext[..<16]) let actualCiphertext = Array(ciphertext[16...]) @@ -45,7 +39,7 @@ public class AesSiv { } guard diff == 0 else { - throw AesSivError.unauthenticCiphertext + throw CryptoError.unauthenticCiphertext } return plaintext @@ -62,7 +56,7 @@ public class AesSiv { internal static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [[UInt8]]) throws -> [UInt8] { // Maximum permitted AD length is the block size in bits - 2 if ad.count > 126 { - throw AesSivError.invalidParameter("too many ad") + throw CryptoError.invalidParameter("too many ad") } // RFC 5297 defines a n == 0 case here. Where n is the length of the input vector: @@ -133,7 +127,7 @@ public class AesSiv { let status = CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionECBMode), key, key.count, nil, plaintext, plaintext.count, &ciphertext, kCCBlockSizeAES128, &ciphertextLen) guard status == kCCSuccess else { - throw AesSivError.encryptionFailedWithStatus(status) + throw CryptoError.ccCryptorError(status) } return ciphertext diff --git a/CryptoLib/CSPRNG.swift b/CryptoLib/CSPRNG.swift new file mode 100644 index 0000000..287faea --- /dev/null +++ b/CryptoLib/CSPRNG.swift @@ -0,0 +1,19 @@ +// +// CSPRNG.swift +// CryptoLib +// +// Created by Tobias Hagemann on 09.06.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import Foundation + +class CSPRNG { + func createRandomBytes(size: Int) throws -> [UInt8] { + var randomBytes = [UInt8](repeating: 0x00, count: size) + guard SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) == errSecSuccess else { + throw CryptoError.csprngError + } + return randomBytes + } +} diff --git a/CryptoLib/CryptoError.swift b/CryptoLib/CryptoError.swift new file mode 100644 index 0000000..d960c72 --- /dev/null +++ b/CryptoLib/CryptoError.swift @@ -0,0 +1,18 @@ +// +// CryptoError.swift +// CryptoLib +// +// Created by Tobias Hagemann on 09.06.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import CommonCrypto +import Foundation + +enum CryptoError: Error, Equatable { + case invalidParameter(_ reason: String) + case ccCryptorError(_ status: CCCryptorStatus) + case unauthenticCiphertext + case csprngError + case ioError +} diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index c8c86de..f6f7ae5 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -31,20 +31,25 @@ public enum FileNameEncoding: String { case base32 } -enum CryptorError: Error, Equatable { - case invalidCiphertext(_ reason: String? = nil) - case csprngError - case unauthenticCiphertext +struct FileHeader { + let nonce: [UInt8] + let contentKey: [UInt8] } public class Cryptor { private let masterKey: Masterkey + private let csprng: CSPRNG - public init(masterKey: Masterkey) { + public convenience init(masterKey: Masterkey) { + self.init(masterKey: masterKey, csprng: CSPRNG()) + } + + internal init(masterKey: Masterkey, csprng: CSPRNG) { self.masterKey = masterKey + self.csprng = csprng } - // MARK: - Path Encryption and Decryption: + // MARK: - Path Encryption and Decryption public func encryptDirId(_ dirId: Data) throws -> String { let encrypted = try AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: dirId.bytes) @@ -74,7 +79,7 @@ public class Cryptor { } }() guard let ciphertextData = maybeCiphertextData else { - throw CryptorError.invalidCiphertext("Can't \(encoding.rawValue)-decode ciphertext name: \(ciphertextName)") + throw CryptoError.invalidParameter("Can't \(encoding.rawValue)-decode ciphertext name: \(ciphertextName)") } // decrypt: @@ -82,17 +87,140 @@ public class Cryptor { if let str = String(data: Data(cleartext), encoding: .utf8) { return str } else { - throw CryptorError.invalidCiphertext("Unable to decode cleartext using UTF-8.") + throw CryptoError.invalidParameter("Unable to decode cleartext using UTF-8.") + } + } + + // MARK: - File Header Encryption and Decryption + + func createHeader() throws -> FileHeader { + let nonce = try csprng.createRandomBytes(size: kCCBlockSizeAES128) + let contentKey = try csprng.createRandomBytes(size: kCCKeySizeAES256) + return FileHeader(nonce: nonce, contentKey: contentKey) + } + + func encryptHeader(_ header: FileHeader) throws -> [UInt8] { + let cleartext = [UInt8](repeating: 0xFF, count: 8) + header.contentKey + let ciphertext = try AesCtr.compute(key: masterKey.aesMasterKey, iv: header.nonce, data: cleartext) + let toBeAuthenticated = header.nonce + ciphertext + var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + return header.nonce + ciphertext + mac + } + + func decryptHeader(_ header: [UInt8]) throws -> FileHeader { + // decompose header: + let beginOfMAC = header.count - Int(CC_SHA256_DIGEST_LENGTH) + let nonce = [UInt8](header[0 ..< kCCBlockSizeAES128]) + let ciphertext = [UInt8](header[kCCBlockSizeAES128 ..< beginOfMAC]) + let expectedMAC = [UInt8](header[beginOfMAC...]) + + // check MAC: + let toBeAuthenticated = nonce + ciphertext + var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + guard checkMAC(expected: expectedMAC, actual: mac) else { + throw CryptoError.unauthenticCiphertext } + + // decrypt: + let cleartext = try AesCtr.compute(key: masterKey.aesMasterKey, iv: nonce, data: ciphertext) + let contentKey = [UInt8](cleartext[8...]) + return FileHeader(nonce: nonce, contentKey: contentKey) } // MARK: - File Content Encryption and Decryption - func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { - var chunkNonce = [UInt8](repeating: 0x00, count: kCCBlockSizeAES128) - guard SecRandomCopyBytes(kSecRandomDefault, chunkNonce.count, &chunkNonce) == errSecSuccess else { - throw CryptorError.csprngError + // TODO: progress + func encryptContent(from cleartextURL: URL, to ciphertextURL: URL) throws { + // open cleartext input stream: + guard let cleartextStream = InputStream(url: cleartextURL) else { + throw CryptoError.ioError + } + cleartextStream.schedule(in: .current, forMode: .default) + cleartextStream.open() + defer { cleartextStream.close() } + + // open ciphertext output stream: + guard let ciphertextStream = OutputStream(url: ciphertextURL, append: false) else { + throw CryptoError.ioError } + ciphertextStream.schedule(in: .current, forMode: .default) + ciphertextStream.open() + defer { ciphertextStream.close() } + + // encrypt and write header: + let header = try createHeader() + let ciphertextHeader = try encryptHeader(header) + ciphertextStream.write(ciphertextHeader, maxLength: ciphertextHeader.count) + + // encrypt and write content: + var chunkNumber: UInt64 = 0 + while cleartextStream.hasBytesAvailable { + // read chunk: + var cleartextChunk = [UInt8](repeating: 0x00, count: 32 * 1024) + let length = cleartextStream.read(&cleartextChunk, maxLength: cleartextChunk.count) + guard length >= 0 else { + throw CryptoError.ioError + } + assert(length < cleartextChunk.count) + cleartextChunk.removeSubrange(length...) + + // encrypt and write chunk: + let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) + ciphertextStream.write(ciphertextChunk, maxLength: ciphertextChunk.count) + + // prepare next chunk: + chunkNumber += 1 + } + } + + // TODO: progress + func decryptContent(from ciphertextURL: URL, to cleartextURL: URL) throws { + // open ciphertext input stream: + guard let ciphertextStream = InputStream(url: ciphertextURL) else { + throw CryptoError.ioError + } + ciphertextStream.schedule(in: .current, forMode: .default) + ciphertextStream.open() + defer { ciphertextStream.close() } + + // open cleartext output stream: + guard let cleartextStream = OutputStream(url: cleartextURL, append: false) else { + throw CryptoError.ioError + } + cleartextStream.schedule(in: .current, forMode: .default) + cleartextStream.open() + defer { cleartextStream.close() } + + // read and decrypt file header: + var ciphertextHeader = [UInt8](repeating: 0x00, count: 88) + ciphertextStream.read(&ciphertextHeader, maxLength: ciphertextHeader.count) + let header = try decryptHeader(ciphertextHeader) + + // decrypt content: + var chunkNumber: UInt64 = 0 + while ciphertextStream.hasBytesAvailable { + // read chunk: + var ciphertextChunk = [UInt8](repeating: 0x00, count: 16 + 32 * 1024 + 32) + let length = ciphertextStream.read(&ciphertextChunk, maxLength: ciphertextChunk.count) + guard length >= 0 else { + throw CryptoError.ioError + } + assert(length < ciphertextChunk.count) + ciphertextChunk.removeSubrange(length...) + + // decrypt and write chunk: + let cleartextChunk = try decryptSingleChunk(ciphertextHeader, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) + cleartextStream.write(cleartextChunk, maxLength: cleartextChunk.count) + + // prepare next chunk: + chunkNumber += 1 + } + } + + func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { + let chunkNonce = try csprng.createRandomBytes(size: kCCBlockSizeAES128) let ciphertext = try AesCtr.compute(key: fileKey, iv: chunkNonce, data: chunk) let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) @@ -114,14 +242,18 @@ public class Cryptor { var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) guard checkMAC(expected: expectedMAC, actual: mac) else { - throw CryptorError.unauthenticCiphertext + throw CryptoError.unauthenticCiphertext } // decrypt: return try AesCtr.compute(key: fileKey, iv: chunkNonce, data: ciphertext) } - // time constant comparison: + // MARK: - Internal + + /** + Constant-time comparison + */ private func checkMAC(expected: [UInt8], actual: [UInt8]) -> Bool { assert(expected.count == actual.count, "MACs expected to be of same length") diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 6dc87b8..2abfad2 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -45,6 +45,57 @@ class CryptorTests: XCTestCase { XCTAssertEqual(originalName, cleartextName) } + func testCreateHeader() throws { + let cryptor = Cryptor(masterKey: masterkey, csprng: CSPRNGMock()) + let header = try cryptor.createHeader() + XCTAssertEqual([UInt8](repeating: 0xF0, count: 16), header.nonce) + XCTAssertEqual([UInt8](repeating: 0xF0, count: 32), header.contentKey) + } + + func testEncryptHeader() throws { + let cryptor = Cryptor(masterKey: masterkey, csprng: CSPRNGMock()) + let header = try cryptor.createHeader() + let encrypted = try cryptor.encryptHeader(header) + let expected: [UInt8] = [ + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0D, 0x91, 0xF2, 0x9C, 0xC6, 0x35, 0xD7, 0x5E, + 0x1E, 0x42, 0x23, 0x1E, 0xC7, 0x90, 0x57, 0xE3, + 0x8D, 0x98, 0xF3, 0x58, 0x07, 0x2C, 0x9F, 0x03, + 0xBC, 0xEA, 0x5A, 0x98, 0x3B, 0x68, 0x62, 0x89, + 0x3E, 0xBC, 0x5E, 0x5E, 0x27, 0x39, 0xCB, 0x8E, + 0xD4, 0x27, 0x61, 0x06, 0x8E, 0x7F, 0x3A, 0x4E, + 0xC7, 0x9F, 0x4D, 0x3E, 0x20, 0x57, 0xDC, 0xE4, + 0x65, 0xA5, 0xFF, 0x93, 0xC2, 0x7B, 0xD2, 0xB8, + 0x3F, 0xE3, 0xD0, 0x8C, 0xB3, 0x92, 0xED, 0x96 + ] + XCTAssertEqual(expected, encrypted) + } + + func testDecryptHeader() throws { + let cryptor = Cryptor(masterKey: masterkey, csprng: CSPRNGMock()) + let ciphertext: [UInt8] = [ + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0D, 0x91, 0xF2, 0x9C, 0xC6, 0x35, 0xD7, 0x5E, + 0x1E, 0x42, 0x23, 0x1E, 0xC7, 0x90, 0x57, 0xE3, + 0x8D, 0x98, 0xF3, 0x58, 0x07, 0x2C, 0x9F, 0x03, + 0xBC, 0xEA, 0x5A, 0x98, 0x3B, 0x68, 0x62, 0x89, + 0x3E, 0xBC, 0x5E, 0x5E, 0x27, 0x39, 0xCB, 0x8E, + 0xD4, 0x27, 0x61, 0x06, 0x8E, 0x7F, 0x3A, 0x4E, + 0xC7, 0x9F, 0x4D, 0x3E, 0x20, 0x57, 0xDC, 0xE4, + 0x65, 0xA5, 0xFF, 0x93, 0xC2, 0x7B, 0xD2, 0xB8, + 0x3F, 0xE3, 0xD0, 0x8C, 0xB3, 0x92, 0xED, 0x96 + ] + let decrypted = try cryptor.decryptHeader(ciphertext) + XCTAssertEqual([UInt8](repeating: 0xF0, count: 16), decrypted.nonce) + XCTAssertEqual([UInt8](repeating: 0xF0, count: 32), decrypted.contentKey) + } + + func testEncryptAndDecryptContent() throws { + // TODO: + } + func testEncryptAndDecryptSingleChunk() throws { let cryptor = Cryptor(masterKey: masterkey) let nonce = [UInt8](repeating: 0x00, count: 16) @@ -57,3 +108,9 @@ class CryptorTests: XCTestCase { XCTAssertEqual(cleartext.bytes, decrypted) } } + +class CSPRNGMock: CSPRNG { + override func createRandomBytes(size: Int) throws -> [UInt8] { + return [UInt8](repeating: 0xF0, count: size) + } +} From 623085d141d4ce4b0367032deeefba28493bb13c Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 9 Jun 2020 16:42:57 +0200 Subject: [PATCH 039/144] Refactored stream handling to separate functions --- CryptoLib/Cryptor.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index f6f7ae5..aa6265e 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -131,7 +131,6 @@ public class Cryptor { // MARK: - File Content Encryption and Decryption - // TODO: progress func encryptContent(from cleartextURL: URL, to ciphertextURL: URL) throws { // open cleartext input stream: guard let cleartextStream = InputStream(url: cleartextURL) else { @@ -149,6 +148,12 @@ public class Cryptor { ciphertextStream.open() defer { ciphertextStream.close() } + // encrypt: + try encryptContent(from: cleartextStream, to: ciphertextStream) + } + + // TODO: progress + func encryptContent(from cleartextStream: InputStream, to ciphertextStream: OutputStream) throws { // encrypt and write header: let header = try createHeader() let ciphertextHeader = try encryptHeader(header) @@ -175,7 +180,6 @@ public class Cryptor { } } - // TODO: progress func decryptContent(from ciphertextURL: URL, to cleartextURL: URL) throws { // open ciphertext input stream: guard let ciphertextStream = InputStream(url: ciphertextURL) else { @@ -193,6 +197,12 @@ public class Cryptor { cleartextStream.open() defer { cleartextStream.close() } + // decrypt: + try decryptContent(from: ciphertextStream, to: cleartextStream) + } + + // TODO: progress + func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream) throws { // read and decrypt file header: var ciphertextHeader = [UInt8](repeating: 0x00, count: 88) ciphertextStream.read(&ciphertextHeader, maxLength: ciphertextHeader.count) From 533f470e5d1cd0a9739f9a276dcf90d54032b04d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 9 Jun 2020 17:32:36 +0200 Subject: [PATCH 040/144] Refactored reading from input stream --- CryptoLib/Cryptor.swift | 44 ++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index aa6265e..adf7c39 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -26,6 +26,19 @@ extension FixedWidthInteger { } } +extension InputStream { + public func read(maxLength: Int) throws -> [UInt8] { + var buffer = [UInt8](repeating: 0x00, count: maxLength) + let length = read(&buffer, maxLength: maxLength) + guard length >= 0 else { + throw streamError ?? CryptoError.ioError + } + assert(length < buffer.count) + buffer.removeSubrange(length...) + return buffer + } +} + public enum FileNameEncoding: String { case base64url case base32 @@ -162,20 +175,9 @@ public class Cryptor { // encrypt and write content: var chunkNumber: UInt64 = 0 while cleartextStream.hasBytesAvailable { - // read chunk: - var cleartextChunk = [UInt8](repeating: 0x00, count: 32 * 1024) - let length = cleartextStream.read(&cleartextChunk, maxLength: cleartextChunk.count) - guard length >= 0 else { - throw CryptoError.ioError - } - assert(length < cleartextChunk.count) - cleartextChunk.removeSubrange(length...) - - // encrypt and write chunk: + let cleartextChunk = try cleartextStream.read(maxLength: 32 * 1024) let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) ciphertextStream.write(ciphertextChunk, maxLength: ciphertextChunk.count) - - // prepare next chunk: chunkNumber += 1 } } @@ -204,27 +206,15 @@ public class Cryptor { // TODO: progress func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream) throws { // read and decrypt file header: - var ciphertextHeader = [UInt8](repeating: 0x00, count: 88) - ciphertextStream.read(&ciphertextHeader, maxLength: ciphertextHeader.count) + let ciphertextHeader = try ciphertextStream.read(maxLength: 88) let header = try decryptHeader(ciphertextHeader) // decrypt content: var chunkNumber: UInt64 = 0 while ciphertextStream.hasBytesAvailable { - // read chunk: - var ciphertextChunk = [UInt8](repeating: 0x00, count: 16 + 32 * 1024 + 32) - let length = ciphertextStream.read(&ciphertextChunk, maxLength: ciphertextChunk.count) - guard length >= 0 else { - throw CryptoError.ioError - } - assert(length < ciphertextChunk.count) - ciphertextChunk.removeSubrange(length...) - - // decrypt and write chunk: - let cleartextChunk = try decryptSingleChunk(ciphertextHeader, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) + let ciphertextChunk = try ciphertextStream.read(maxLength: 16 + 32 * 1024 + 32) + let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) cleartextStream.write(cleartextChunk, maxLength: cleartextChunk.count) - - // prepare next chunk: chunkNumber += 1 } } From 32277c1bd208f9452ae0eab525dce841a7719bc0 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 9 Jun 2020 17:48:35 +0200 Subject: [PATCH 041/144] Renamed masterkey in some cases for consistency --- CryptoLib/Cryptor.swift | 34 ++++++++++++++--------------- CryptoLibTests/CryptorTests.swift | 12 +++++----- CryptoLibTests/MasterkeyTests.swift | 10 ++++----- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index adf7c39..e42b2d5 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -50,22 +50,22 @@ struct FileHeader { } public class Cryptor { - private let masterKey: Masterkey + private let masterkey: Masterkey private let csprng: CSPRNG - public convenience init(masterKey: Masterkey) { - self.init(masterKey: masterKey, csprng: CSPRNG()) + public convenience init(masterkey: Masterkey) { + self.init(masterkey: masterkey, csprng: CSPRNG()) } - internal init(masterKey: Masterkey, csprng: CSPRNG) { - self.masterKey = masterKey + internal init(masterkey: Masterkey, csprng: CSPRNG) { + self.masterkey = masterkey self.csprng = csprng } // MARK: - Path Encryption and Decryption public func encryptDirId(_ dirId: Data) throws -> String { - let encrypted = try AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: dirId.bytes) + let encrypted = try AesSiv.encrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, plaintext: dirId.bytes) var digest = [UInt8](repeating: 0x00, count: Int(CC_SHA1_DIGEST_LENGTH)) CC_SHA1(encrypted, UInt32(encrypted.count) as CC_LONG, &digest) return Data(digest).base32EncodedString @@ -74,7 +74,7 @@ public class Cryptor { public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) - let ciphertext = try AesSiv.encrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, plaintext: cleartext, ad: dirId.bytes) + let ciphertext = try AesSiv.encrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, plaintext: cleartext, ad: dirId.bytes) // encode: switch encoding { @@ -96,7 +96,7 @@ public class Cryptor { } // decrypt: - let cleartext = try AesSiv.decrypt(aesKey: masterKey.aesMasterKey, macKey: masterKey.macMasterKey, ciphertext: ciphertextData.bytes, ad: dirId.bytes) + let cleartext = try AesSiv.decrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, ciphertext: ciphertextData.bytes, ad: dirId.bytes) if let str = String(data: Data(cleartext), encoding: .utf8) { return str } else { @@ -114,10 +114,10 @@ public class Cryptor { func encryptHeader(_ header: FileHeader) throws -> [UInt8] { let cleartext = [UInt8](repeating: 0xFF, count: 8) + header.contentKey - let ciphertext = try AesCtr.compute(key: masterKey.aesMasterKey, iv: header.nonce, data: cleartext) + let ciphertext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: header.nonce, data: cleartext) let toBeAuthenticated = header.nonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) return header.nonce + ciphertext + mac } @@ -131,13 +131,13 @@ public class Cryptor { // check MAC: let toBeAuthenticated = nonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) guard checkMAC(expected: expectedMAC, actual: mac) else { throw CryptoError.unauthenticCiphertext } // decrypt: - let cleartext = try AesCtr.compute(key: masterKey.aesMasterKey, iv: nonce, data: ciphertext) + let cleartext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: nonce, data: ciphertext) let contentKey = [UInt8](cleartext[8...]) return FileHeader(nonce: nonce, contentKey: contentKey) } @@ -172,7 +172,7 @@ public class Cryptor { let ciphertextHeader = try encryptHeader(header) ciphertextStream.write(ciphertextHeader, maxLength: ciphertextHeader.count) - // encrypt and write content: + // encrypt and write ciphertext content: var chunkNumber: UInt64 = 0 while cleartextStream.hasBytesAvailable { let cleartextChunk = try cleartextStream.read(maxLength: 32 * 1024) @@ -205,11 +205,11 @@ public class Cryptor { // TODO: progress func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream) throws { - // read and decrypt file header: + // read and decrypt header: let ciphertextHeader = try ciphertextStream.read(maxLength: 88) let header = try decryptHeader(ciphertextHeader) - // decrypt content: + // decrypt and write cleartext content: var chunkNumber: UInt64 = 0 while ciphertextStream.hasBytesAvailable { let ciphertextChunk = try ciphertextStream.read(maxLength: 16 + 32 * 1024 + 32) @@ -224,7 +224,7 @@ public class Cryptor { let ciphertext = try AesCtr.compute(key: fileKey, iv: chunkNonce, data: chunk) let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) return chunkNonce + ciphertext + mac } @@ -240,7 +240,7 @@ public class Cryptor { // check MAC: let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterKey.macMasterKey, masterKey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) guard checkMAC(expected: expectedMAC, actual: mac) else { throw CryptoError.unauthenticCiphertext } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 2abfad2..779cb60 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -21,7 +21,7 @@ class CryptorTests: XCTestCase { } func testEncryptDirId() throws { - let cryptor = Cryptor(masterKey: masterkey) + let cryptor = Cryptor(masterkey: masterkey) let rootDir = try cryptor.encryptDirId("".data(using: .utf8)!) XCTAssertEqual("VLWEHT553J5DR7OZLRJAYDIWFCXZABOD", rootDir) @@ -33,7 +33,7 @@ class CryptorTests: XCTestCase { func testEncryptAndDecryptName() throws { continueAfterFailure = false - let cryptor = Cryptor(masterKey: masterkey) + let cryptor = Cryptor(masterkey: masterkey) let dirId = "foo".data(using: .utf8)! let originalName = "hello.txt" @@ -46,14 +46,14 @@ class CryptorTests: XCTestCase { } func testCreateHeader() throws { - let cryptor = Cryptor(masterKey: masterkey, csprng: CSPRNGMock()) + let cryptor = Cryptor(masterkey: masterkey, csprng: CSPRNGMock()) let header = try cryptor.createHeader() XCTAssertEqual([UInt8](repeating: 0xF0, count: 16), header.nonce) XCTAssertEqual([UInt8](repeating: 0xF0, count: 32), header.contentKey) } func testEncryptHeader() throws { - let cryptor = Cryptor(masterKey: masterkey, csprng: CSPRNGMock()) + let cryptor = Cryptor(masterkey: masterkey, csprng: CSPRNGMock()) let header = try cryptor.createHeader() let encrypted = try cryptor.encryptHeader(header) let expected: [UInt8] = [ @@ -73,7 +73,7 @@ class CryptorTests: XCTestCase { } func testDecryptHeader() throws { - let cryptor = Cryptor(masterKey: masterkey, csprng: CSPRNGMock()) + let cryptor = Cryptor(masterkey: masterkey, csprng: CSPRNGMock()) let ciphertext: [UInt8] = [ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, @@ -97,7 +97,7 @@ class CryptorTests: XCTestCase { } func testEncryptAndDecryptSingleChunk() throws { - let cryptor = Cryptor(masterKey: masterkey) + let cryptor = Cryptor(masterkey: masterkey) let nonce = [UInt8](repeating: 0x00, count: 16) let filekey = [UInt8](repeating: 0x00, count: 32) let cleartext = "hello world".data(using: .ascii)! diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index 2cb27f6..a77ce2a 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -42,12 +42,12 @@ class MasterkeyTests: XCTestCase { } """.data(using: .utf8)! - let masterKey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") + let masterkey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") - XCTAssertNotNil(masterKey) - XCTAssertEqual(7, masterKey.version) - XCTAssertEqual(expectedKeys, masterKey.aesMasterKey) - XCTAssertEqual(expectedKeys, masterKey.macMasterKey) + XCTAssertNotNil(masterkey) + XCTAssertEqual(7, masterkey.version) + XCTAssertEqual(expectedKeys, masterkey.aesMasterKey) + XCTAssertEqual(expectedKeys, masterkey.macMasterKey) } func testCreateFromMasterkeyFileWithWrongPassword() throws { From 36e8c763e856ff97ba8a2066fe0a859bb2707940 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 10 Jun 2020 09:40:04 +0200 Subject: [PATCH 042/144] Added utility class for crypto --- CryptoLib.xcodeproj/project.pbxproj | 8 +++--- CryptoLib/AesSiv.swift | 12 ++------- CryptoLib/CSPRNG.swift | 19 -------------- CryptoLib/CryptoSupport.swift | 35 ++++++++++++++++++++++++++ CryptoLib/Cryptor.swift | 39 ++++++----------------------- CryptoLibTests/CryptorTests.swift | 18 ++++++------- 6 files changed, 58 insertions(+), 73 deletions(-) delete mode 100644 CryptoLib/CSPRNG.swift create mode 100644 CryptoLib/CryptoSupport.swift diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index a446137..42a519a 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -9,8 +9,8 @@ /* Begin PBXBuildFile section */ 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 74F0F752248FC7F200B4C26D /* CSPRNG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F751248FC7F200B4C26D /* CSPRNG.swift */; }; 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F753248FC89B00B4C26D /* CryptoError.swift */; }; + 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */; }; 7980E962F1389ACDDCEBB596 /* libPods-CryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */; }; 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; @@ -45,8 +45,8 @@ 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 74F0F751248FC7F200B4C26D /* CSPRNG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSPRNG.swift; sourceTree = ""; }; 74F0F753248FC89B00B4C26D /* CryptoError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoError.swift; sourceTree = ""; }; + 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSupport.swift; sourceTree = ""; }; 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.debug.xcconfig"; sourceTree = ""; }; 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; 9E44EEA524599C6900A37B01 /* AesSiv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSiv.swift; sourceTree = ""; }; @@ -119,8 +119,8 @@ 9EB822C0248AF82200879838 /* AesCtr.swift */, 9E44EEA524599C6900A37B01 /* AesSiv.swift */, 74F0F753248FC89B00B4C26D /* CryptoError.swift */, + 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */, 9E9BB811245412E900F9FF51 /* Cryptor.swift */, - 74F0F751248FC7F200B4C26D /* CSPRNG.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, ); path = CryptoLib; @@ -329,8 +329,8 @@ files = ( 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */, 9EB822C1248AF82200879838 /* AesCtr.swift in Sources */, + 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */, 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */, - 74F0F752248FC7F200B4C26D /* CSPRNG.swift in Sources */, 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */, 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */, ); diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index aff3608..09d4d9d 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -10,6 +10,7 @@ import CommonCrypto import Foundation public class AesSiv { + static let cryptoSupport = CryptoSupport() static let zero = [UInt8](repeating: 0x00, count: 16) static let dblConst: UInt8 = 0x87 @@ -30,18 +31,9 @@ public class AesSiv { let actualCiphertext = Array(ciphertext[16...]) let plaintext = try ctr(aesKey: aesKey, iv: iv, plaintext: actualCiphertext) let control = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) - - // time-constant comparison - assert(iv.count == control.count) - var diff: UInt8 = 0 - for i in 0 ..< iv.count { - diff |= iv[i] ^ control[i] - } - - guard diff == 0 else { + guard cryptoSupport.compareBytes(expected: control, actual: iv) else { throw CryptoError.unauthenticCiphertext } - return plaintext } diff --git a/CryptoLib/CSPRNG.swift b/CryptoLib/CSPRNG.swift deleted file mode 100644 index 287faea..0000000 --- a/CryptoLib/CSPRNG.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// CSPRNG.swift -// CryptoLib -// -// Created by Tobias Hagemann on 09.06.20. -// Copyright © 2020 Skymatic GmbH. All rights reserved. -// - -import Foundation - -class CSPRNG { - func createRandomBytes(size: Int) throws -> [UInt8] { - var randomBytes = [UInt8](repeating: 0x00, count: size) - guard SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) == errSecSuccess else { - throw CryptoError.csprngError - } - return randomBytes - } -} diff --git a/CryptoLib/CryptoSupport.swift b/CryptoLib/CryptoSupport.swift new file mode 100644 index 0000000..bf3144d --- /dev/null +++ b/CryptoLib/CryptoSupport.swift @@ -0,0 +1,35 @@ +// +// CryptoSupport.swift +// CryptoLib +// +// Created by Tobias Hagemann on 10.06.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import Foundation + +class CryptoSupport { + func createRandomBytes(size: Int) throws -> [UInt8] { + var randomBytes = [UInt8](repeating: 0x00, count: size) + guard SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) == errSecSuccess else { + throw CryptoError.csprngError + } + return randomBytes + } + + /** + Constant-time comparison + */ + func compareBytes(expected: [UInt8], actual: [UInt8]) -> Bool { + assert(expected.count == actual.count, "parameters should be of same length") + if #available(iOS 10.1, *) { + return timingsafe_bcmp(expected, actual, expected.count) == 0 + } else { + var diff: UInt8 = 0 + for i in 0 ..< expected.count { + diff |= expected[i] ^ actual[i] + } + return diff == 0 + } + } +} diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index e42b2d5..02b781c 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -51,15 +51,11 @@ struct FileHeader { public class Cryptor { private let masterkey: Masterkey - private let csprng: CSPRNG + private let cryptoSupport: CryptoSupport - public convenience init(masterkey: Masterkey) { - self.init(masterkey: masterkey, csprng: CSPRNG()) - } - - internal init(masterkey: Masterkey, csprng: CSPRNG) { + init(masterkey: Masterkey, cryptoSupport: CryptoSupport = CryptoSupport()) { self.masterkey = masterkey - self.csprng = csprng + self.cryptoSupport = cryptoSupport } // MARK: - Path Encryption and Decryption @@ -107,8 +103,8 @@ public class Cryptor { // MARK: - File Header Encryption and Decryption func createHeader() throws -> FileHeader { - let nonce = try csprng.createRandomBytes(size: kCCBlockSizeAES128) - let contentKey = try csprng.createRandomBytes(size: kCCKeySizeAES256) + let nonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) + let contentKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) return FileHeader(nonce: nonce, contentKey: contentKey) } @@ -132,7 +128,7 @@ public class Cryptor { let toBeAuthenticated = nonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) - guard checkMAC(expected: expectedMAC, actual: mac) else { + guard cryptoSupport.compareBytes(expected: expectedMAC, actual: mac) else { throw CryptoError.unauthenticCiphertext } @@ -220,7 +216,7 @@ public class Cryptor { } func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { - let chunkNonce = try csprng.createRandomBytes(size: kCCBlockSizeAES128) + let chunkNonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) let ciphertext = try AesCtr.compute(key: fileKey, iv: chunkNonce, data: chunk) let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) @@ -241,30 +237,11 @@ public class Cryptor { let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) - guard checkMAC(expected: expectedMAC, actual: mac) else { + guard cryptoSupport.compareBytes(expected: expectedMAC, actual: mac) else { throw CryptoError.unauthenticCiphertext } // decrypt: return try AesCtr.compute(key: fileKey, iv: chunkNonce, data: ciphertext) } - - // MARK: - Internal - - /** - Constant-time comparison - */ - private func checkMAC(expected: [UInt8], actual: [UInt8]) -> Bool { - assert(expected.count == actual.count, "MACs expected to be of same length") - - if #available(iOS 10.1, *) { - return timingsafe_bcmp(expected, actual, expected.count) == 0 - } else { - var diff: UInt8 = 0 - for i in 0 ..< expected.count { - diff |= expected[i] ^ actual[i] - } - return diff == 0 - } - } } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 779cb60..d193486 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -9,6 +9,12 @@ import XCTest @testable import CryptoLib +class CryptoSupportMock: CryptoSupport { + override func createRandomBytes(size: Int) throws -> [UInt8] { + return [UInt8](repeating: 0xF0, count: size) + } +} + class CryptorTests: XCTestCase { var masterkey: Masterkey! @@ -46,14 +52,14 @@ class CryptorTests: XCTestCase { } func testCreateHeader() throws { - let cryptor = Cryptor(masterkey: masterkey, csprng: CSPRNGMock()) + let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let header = try cryptor.createHeader() XCTAssertEqual([UInt8](repeating: 0xF0, count: 16), header.nonce) XCTAssertEqual([UInt8](repeating: 0xF0, count: 32), header.contentKey) } func testEncryptHeader() throws { - let cryptor = Cryptor(masterkey: masterkey, csprng: CSPRNGMock()) + let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let header = try cryptor.createHeader() let encrypted = try cryptor.encryptHeader(header) let expected: [UInt8] = [ @@ -73,7 +79,7 @@ class CryptorTests: XCTestCase { } func testDecryptHeader() throws { - let cryptor = Cryptor(masterkey: masterkey, csprng: CSPRNGMock()) + let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let ciphertext: [UInt8] = [ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, @@ -108,9 +114,3 @@ class CryptorTests: XCTestCase { XCTAssertEqual(cleartext.bytes, decrypted) } } - -class CSPRNGMock: CSPRNG { - override func createRandomBytes(size: Int) throws -> [UInt8] { - return [UInt8](repeating: 0xF0, count: size) - } -} From 533ad03b03e49bbfc2c7f3f9b4a107996b64e977 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 10 Jun 2020 17:49:52 +0200 Subject: [PATCH 043/144] Fixed file content encryption --- CryptoLib/Cryptor.swift | 21 +++++++++++++++------ CryptoLibTests/CryptorTests.swift | 24 ++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 02b781c..a48d212 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -27,13 +27,16 @@ extension FixedWidthInteger { } extension InputStream { - public func read(maxLength: Int) throws -> [UInt8] { + public func read(maxLength: Int) throws -> [UInt8]? { var buffer = [UInt8](repeating: 0x00, count: maxLength) let length = read(&buffer, maxLength: maxLength) - guard length >= 0 else { + if length == 0 { + return nil + } + guard length > 0 else { throw streamError ?? CryptoError.ioError } - assert(length < buffer.count) + assert(length <= buffer.count) buffer.removeSubrange(length...) return buffer } @@ -171,7 +174,9 @@ public class Cryptor { // encrypt and write ciphertext content: var chunkNumber: UInt64 = 0 while cleartextStream.hasBytesAvailable { - let cleartextChunk = try cleartextStream.read(maxLength: 32 * 1024) + guard let cleartextChunk = try cleartextStream.read(maxLength: 32 * 1024) else { + continue + } let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) ciphertextStream.write(ciphertextChunk, maxLength: ciphertextChunk.count) chunkNumber += 1 @@ -202,13 +207,17 @@ public class Cryptor { // TODO: progress func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream) throws { // read and decrypt header: - let ciphertextHeader = try ciphertextStream.read(maxLength: 88) + guard let ciphertextHeader = try ciphertextStream.read(maxLength: 88) else { + throw CryptoError.ioError + } let header = try decryptHeader(ciphertextHeader) // decrypt and write cleartext content: var chunkNumber: UInt64 = 0 while ciphertextStream.hasBytesAvailable { - let ciphertextChunk = try ciphertextStream.read(maxLength: 16 + 32 * 1024 + 32) + guard let ciphertextChunk = try ciphertextStream.read(maxLength: 16 + 32 * 1024 + 32) else { + continue + } let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) cleartextStream.write(cleartextChunk, maxLength: cleartextChunk.count) chunkNumber += 1 diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index d193486..c3c86af 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -17,15 +17,22 @@ class CryptoSupportMock: CryptoSupport { class CryptorTests: XCTestCase { var masterkey: Masterkey! + var tmpDirURL: URL! - override func setUp() { + override func setUpWithError() throws { let aesKey: [UInt8] = Array(repeating: 0x55, count: 32) let macKey: [UInt8] = Array(repeating: 0x77, count: 32) masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey, version: 7) + tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString, isDirectory: true) + try FileManager.default.createDirectory(at: tmpDirURL, withIntermediateDirectories: true) XCTAssertNotNil(masterkey) } + override func tearDownWithError() throws { + try FileManager.default.removeItem(at: tmpDirURL) + } + func testEncryptDirId() throws { let cryptor = Cryptor(masterkey: masterkey) @@ -99,7 +106,20 @@ class CryptorTests: XCTestCase { } func testEncryptAndDecryptContent() throws { - // TODO: + continueAfterFailure = false + + let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) + let originalContentData = Data(repeating: 0x0F, count: 65 * 1024) + let originalContentURL = tmpDirURL.appendingPathComponent(UUID().uuidString) + try originalContentData.write(to: originalContentURL) + + let ciphertextURL = tmpDirURL.appendingPathComponent(UUID().uuidString) + let cleartextURL = tmpDirURL.appendingPathComponent(UUID().uuidString) + try cryptor.encryptContent(from: originalContentURL, to: ciphertextURL) + try cryptor.decryptContent(from: ciphertextURL, to: cleartextURL) + + let cleartextData = try Data(contentsOf: cleartextURL) + XCTAssertEqual(originalContentData, cleartextData) } func testEncryptAndDecryptSingleChunk() throws { From d4093e555fbf30fd466b172120deedeece098bad Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 11 Jun 2020 10:46:44 +0200 Subject: [PATCH 044/144] Made encryptContent/decryptContent public --- CryptoLib/Cryptor.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index a48d212..4670c27 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -143,7 +143,7 @@ public class Cryptor { // MARK: - File Content Encryption and Decryption - func encryptContent(from cleartextURL: URL, to ciphertextURL: URL) throws { + public func encryptContent(from cleartextURL: URL, to ciphertextURL: URL) throws { // open cleartext input stream: guard let cleartextStream = InputStream(url: cleartextURL) else { throw CryptoError.ioError @@ -183,7 +183,7 @@ public class Cryptor { } } - func decryptContent(from ciphertextURL: URL, to cleartextURL: URL) throws { + public func decryptContent(from ciphertextURL: URL, to cleartextURL: URL) throws { // open ciphertext input stream: guard let ciphertextStream = InputStream(url: ciphertextURL) else { throw CryptoError.ioError From da796923666118f73232c452d7b71b9435ff8c59 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 22 Jun 2020 13:32:44 +0200 Subject: [PATCH 045/144] Added isDirectory explicitly to URL methods --- CryptoLibTests/CryptorTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index c3c86af..714090b 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -110,11 +110,11 @@ class CryptorTests: XCTestCase { let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let originalContentData = Data(repeating: 0x0F, count: 65 * 1024) - let originalContentURL = tmpDirURL.appendingPathComponent(UUID().uuidString) + let originalContentURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) try originalContentData.write(to: originalContentURL) - let ciphertextURL = tmpDirURL.appendingPathComponent(UUID().uuidString) - let cleartextURL = tmpDirURL.appendingPathComponent(UUID().uuidString) + let ciphertextURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) + let cleartextURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) try cryptor.encryptContent(from: originalContentURL, to: ciphertextURL) try cryptor.decryptContent(from: ciphertextURL, to: cleartextURL) From 7eb5aa22a118c66ba4731795d84643896277dbca Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 22 Jun 2020 14:23:40 +0200 Subject: [PATCH 046/144] Added file size calculation, introduced some constants in Cryptor --- CryptoLib/Cryptor.swift | 40 ++++++++++++++++--- CryptoLibTests/CryptorTests.swift | 64 ++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 4670c27..784608e 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -53,6 +53,11 @@ struct FileHeader { } public class Cryptor { + static let fileHeaderLegacyPayloadSize = 8 + static let fileHeaderSize = kCCBlockSizeAES128 + fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + Int(CC_SHA256_DIGEST_LENGTH) + static let cleartextChunkSize = 32 * 1024 + static let ciphertextChunkSize = kCCBlockSizeAES128 + cleartextChunkSize + Int(CC_SHA256_DIGEST_LENGTH) + private let masterkey: Masterkey private let cryptoSupport: CryptoSupport @@ -112,7 +117,7 @@ public class Cryptor { } func encryptHeader(_ header: FileHeader) throws -> [UInt8] { - let cleartext = [UInt8](repeating: 0xFF, count: 8) + header.contentKey + let cleartext = [UInt8](repeating: 0xFF, count: Cryptor.fileHeaderLegacyPayloadSize) + header.contentKey let ciphertext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: header.nonce, data: cleartext) let toBeAuthenticated = header.nonce + ciphertext var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) @@ -137,7 +142,7 @@ public class Cryptor { // decrypt: let cleartext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: nonce, data: ciphertext) - let contentKey = [UInt8](cleartext[8...]) + let contentKey = [UInt8](cleartext[Cryptor.fileHeaderLegacyPayloadSize...]) return FileHeader(nonce: nonce, contentKey: contentKey) } @@ -174,7 +179,7 @@ public class Cryptor { // encrypt and write ciphertext content: var chunkNumber: UInt64 = 0 while cleartextStream.hasBytesAvailable { - guard let cleartextChunk = try cleartextStream.read(maxLength: 32 * 1024) else { + guard let cleartextChunk = try cleartextStream.read(maxLength: Cryptor.cleartextChunkSize) else { continue } let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) @@ -207,7 +212,7 @@ public class Cryptor { // TODO: progress func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream) throws { // read and decrypt header: - guard let ciphertextHeader = try ciphertextStream.read(maxLength: 88) else { + guard let ciphertextHeader = try ciphertextStream.read(maxLength: Cryptor.fileHeaderSize) else { throw CryptoError.ioError } let header = try decryptHeader(ciphertextHeader) @@ -215,7 +220,7 @@ public class Cryptor { // decrypt and write cleartext content: var chunkNumber: UInt64 = 0 while ciphertextStream.hasBytesAvailable { - guard let ciphertextChunk = try ciphertextStream.read(maxLength: 16 + 32 * 1024 + 32) else { + guard let ciphertextChunk = try ciphertextStream.read(maxLength: Cryptor.ciphertextChunkSize) else { continue } let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) @@ -253,4 +258,29 @@ public class Cryptor { // decrypt: return try AesCtr.compute(key: fileKey, iv: chunkNonce, data: ciphertext) } + + // MARK: - File Size Calculation + + public func calculateCiphertextSize(_ cleartextSize: Int) -> Int { + assert(cleartextSize >= 0, "expected cleartextSize to be positive, but was \(cleartextSize)") + let overheadPerChunk = Cryptor.ciphertextChunkSize - Cryptor.cleartextChunkSize + let numFullChunks = cleartextSize / Cryptor.cleartextChunkSize // floor by int-truncation + let additionalCleartextBytes = cleartextSize % Cryptor.cleartextChunkSize + let additionalCiphertextBytes = (additionalCleartextBytes == 0) ? 0 : additionalCleartextBytes + overheadPerChunk + assert(additionalCiphertextBytes >= 0) + return Cryptor.ciphertextChunkSize * numFullChunks + additionalCiphertextBytes + } + + public func calculateCleartextSize(_ ciphertextSize: Int) throws -> Int { + assert(ciphertextSize >= 0, "expected ciphertextSize to be positive, but was \(ciphertextSize)") + let overheadPerChunk = Cryptor.ciphertextChunkSize - Cryptor.cleartextChunkSize + let numFullChunks = ciphertextSize / Cryptor.ciphertextChunkSize // floor by int-truncation + let additionalCiphertextBytes = ciphertextSize % Cryptor.ciphertextChunkSize + guard additionalCiphertextBytes == 0 || additionalCiphertextBytes > overheadPerChunk else { + throw CryptoError.invalidParameter("Method not defined for input value \(ciphertextSize)") + } + let additionalCleartextBytes = (additionalCiphertextBytes == 0) ? 0 : additionalCiphertextBytes - overheadPerChunk + assert(additionalCleartextBytes >= 0) + return Cryptor.cleartextChunkSize * numFullChunks + additionalCleartextBytes + } } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 714090b..acd35df 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -16,17 +16,12 @@ class CryptoSupportMock: CryptoSupport { } class CryptorTests: XCTestCase { - var masterkey: Masterkey! + let cryptor = Cryptor(masterkey: Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32), version: 7), cryptoSupport: CryptoSupportMock()) var tmpDirURL: URL! override func setUpWithError() throws { - let aesKey: [UInt8] = Array(repeating: 0x55, count: 32) - let macKey: [UInt8] = Array(repeating: 0x77, count: 32) - masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey, version: 7) tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tmpDirURL, withIntermediateDirectories: true) - - XCTAssertNotNil(masterkey) } override func tearDownWithError() throws { @@ -34,8 +29,6 @@ class CryptorTests: XCTestCase { } func testEncryptDirId() throws { - let cryptor = Cryptor(masterkey: masterkey) - let rootDir = try cryptor.encryptDirId("".data(using: .utf8)!) XCTAssertEqual("VLWEHT553J5DR7OZLRJAYDIWFCXZABOD", rootDir) @@ -44,9 +37,6 @@ class CryptorTests: XCTestCase { } func testEncryptAndDecryptName() throws { - continueAfterFailure = false - - let cryptor = Cryptor(masterkey: masterkey) let dirId = "foo".data(using: .utf8)! let originalName = "hello.txt" @@ -59,14 +49,12 @@ class CryptorTests: XCTestCase { } func testCreateHeader() throws { - let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let header = try cryptor.createHeader() XCTAssertEqual([UInt8](repeating: 0xF0, count: 16), header.nonce) XCTAssertEqual([UInt8](repeating: 0xF0, count: 32), header.contentKey) } func testEncryptHeader() throws { - let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let header = try cryptor.createHeader() let encrypted = try cryptor.encryptHeader(header) let expected: [UInt8] = [ @@ -86,7 +74,6 @@ class CryptorTests: XCTestCase { } func testDecryptHeader() throws { - let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let ciphertext: [UInt8] = [ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, @@ -106,9 +93,6 @@ class CryptorTests: XCTestCase { } func testEncryptAndDecryptContent() throws { - continueAfterFailure = false - - let cryptor = Cryptor(masterkey: masterkey, cryptoSupport: CryptoSupportMock()) let originalContentData = Data(repeating: 0x0F, count: 65 * 1024) let originalContentURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) try originalContentData.write(to: originalContentURL) @@ -123,7 +107,6 @@ class CryptorTests: XCTestCase { } func testEncryptAndDecryptSingleChunk() throws { - let cryptor = Cryptor(masterkey: masterkey) let nonce = [UInt8](repeating: 0x00, count: 16) let filekey = [UInt8](repeating: 0x00, count: 32) let cleartext = "hello world".data(using: .ascii)! @@ -133,4 +116,49 @@ class CryptorTests: XCTestCase { XCTAssertEqual(cleartext.bytes, decrypted) } + + func testCalculateCiphertextSize() { + XCTAssertEqual(0, cryptor.calculateCiphertextSize(0)) + + XCTAssertEqual(1 + 48, cryptor.calculateCiphertextSize(1)) + XCTAssertEqual(32 * 1024 - 1 + 48, cryptor.calculateCiphertextSize(32 * 1024 - 1)) + XCTAssertEqual(32 * 1024 + 48, cryptor.calculateCiphertextSize(32 * 1024)) + + XCTAssertEqual(32 * 1024 + 1 + 48 * 2, cryptor.calculateCiphertextSize(32 * 1024 + 1)) + XCTAssertEqual(32 * 1024 + 2 + 48 * 2, cryptor.calculateCiphertextSize(32 * 1024 + 2)) + XCTAssertEqual(64 * 1024 - 1 + 48 * 2, cryptor.calculateCiphertextSize(64 * 1024 - 1)) + XCTAssertEqual(64 * 1024 + 48 * 2, cryptor.calculateCiphertextSize(64 * 1024)) + + XCTAssertEqual(64 * 1024 + 1 + 48 * 3, cryptor.calculateCiphertextSize(64 * 1024 + 1)) + } + + func testCalculateCleartextSize() throws { + XCTAssertEqual(0, try cryptor.calculateCleartextSize(0)) + + XCTAssertEqual(1, try cryptor.calculateCleartextSize(1 + 48)) + XCTAssertEqual(32 * 1024 - 1, try cryptor.calculateCleartextSize(32 * 1024 - 1 + 48)) + XCTAssertEqual(32 * 1024, try cryptor.calculateCleartextSize(32 * 1024 + 48)) + + XCTAssertEqual(32 * 1024 + 1, try cryptor.calculateCleartextSize(32 * 1024 + 1 + 48 * 2)) + XCTAssertEqual(32 * 1024 + 2, try cryptor.calculateCleartextSize(32 * 1024 + 2 + 48 * 2)) + XCTAssertEqual(64 * 1024 - 1, try cryptor.calculateCleartextSize(64 * 1024 - 1 + 48 * 2)) + XCTAssertEqual(64 * 1024, try cryptor.calculateCleartextSize(64 * 1024 + 48 * 2)) + + XCTAssertEqual(64 * 1024 + 1, try cryptor.calculateCleartextSize(64 * 1024 + 1 + 48 * 3)) + } + + func testCalculateCleartextSizeWithInvalidCiphertextSize() throws { + XCTAssertThrowsError(try cryptor.calculateCleartextSize(1), "invalid ciphertext size") { error in + XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 1")) + } + XCTAssertThrowsError(try cryptor.calculateCleartextSize(48), "invalid ciphertext size") { error in + XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 48")) + } + XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 1 + 48), "invalid ciphertext size") { error in + XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 32817")) + } + XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 48 * 2), "invalid ciphertext size") { error in + XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 32864")) + } + } } From ec966175420d482fe4e2590e0f915be9bd1486a4 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 23 Jun 2020 14:54:00 +0200 Subject: [PATCH 047/144] Added public init for Cryptor --- CryptoLib/Cryptor.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 784608e..75b0ba3 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -61,11 +61,15 @@ public class Cryptor { private let masterkey: Masterkey private let cryptoSupport: CryptoSupport - init(masterkey: Masterkey, cryptoSupport: CryptoSupport = CryptoSupport()) { + init(masterkey: Masterkey, cryptoSupport: CryptoSupport) { self.masterkey = masterkey self.cryptoSupport = cryptoSupport } + public convenience init(masterkey: Masterkey) { + self.init(masterkey: masterkey, cryptoSupport: CryptoSupport()) + } + // MARK: - Path Encryption and Decryption public func encryptDirId(_ dirId: Data) throws -> String { From ef89254ab7b806a62d84f54f7d62177d3b42d54c Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 23 Jun 2020 17:04:19 +0200 Subject: [PATCH 048/144] Added encodeMasterkeyJson method to Cryptor, added createNew method to Masterkey --- CryptoLib/CryptoSupport.swift | 2 +- CryptoLib/Cryptor.swift | 32 +++++++++++++++++++++++++++++++ CryptoLib/Masterkey.swift | 10 ++++++++-- CryptoLibTests/CryptorTests.swift | 12 ++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/CryptoLib/CryptoSupport.swift b/CryptoLib/CryptoSupport.swift index bf3144d..6886781 100644 --- a/CryptoLib/CryptoSupport.swift +++ b/CryptoLib/CryptoSupport.swift @@ -8,7 +8,7 @@ import Foundation -class CryptoSupport { +internal class CryptoSupport { func createRandomBytes(size: Int) throws -> [UInt8] { var randomBytes = [UInt8](repeating: 0x00, count: size) guard SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) == errSecSuccess else { diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 75b0ba3..1e480d7 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -7,6 +7,7 @@ // import CommonCrypto +import CryptoSwift import Foundation import SwiftBase32 @@ -53,6 +54,10 @@ struct FileHeader { } public class Cryptor { + static let defaultScryptSaltSize = 8 + static let defaultScryptCostParam = 1 << 15 // 2^15 + static let defaultScryptBlockSize = 8 + static let fileHeaderLegacyPayloadSize = 8 static let fileHeaderSize = kCCBlockSizeAES128 + fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + Int(CC_SHA256_DIGEST_LENGTH) static let cleartextChunkSize = 32 * 1024 @@ -70,6 +75,33 @@ public class Cryptor { self.init(masterkey: masterkey, cryptoSupport: CryptoSupport()) } + // MARK: - Masterkey Encoding + + public func encodeMasterkeyJson(password: String, pepper: [UInt8] = [UInt8]()) throws -> Data { + let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) + let salt = try cryptoSupport.createRandomBytes(size: Cryptor.defaultScryptSaltSize) + let saltAndPepper = salt + pepper + let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: Cryptor.defaultScryptCostParam, r: Cryptor.defaultScryptBlockSize, p: 1).calculate() + + let wrappedMasterKey = try Masterkey.wrapMasterKey(rawKey: masterkey.aesMasterKey, kek: kek) + let wrappedHmacKey = try Masterkey.wrapMasterKey(rawKey: masterkey.macMasterKey, kek: kek) + + var versionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + let versionBytes = withUnsafeBytes(of: UInt32(masterkey.version).bigEndian, Array.init) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, versionBytes, versionBytes.count, &versionMac) + + let masterkeyJson = MasterkeyJson( + scryptSalt: Data(salt).base64EncodedString(), + scryptCostParam: Cryptor.defaultScryptCostParam, + scryptBlockSize: Cryptor.defaultScryptBlockSize, + primaryMasterKey: Data(wrappedMasterKey).base64EncodedString(), + hmacMasterKey: Data(wrappedHmacKey).base64EncodedString(), + versionMac: Data(versionMac).base64EncodedString(), + version: masterkey.version + ) + return try JSONEncoder().encode(masterkeyJson) + } + // MARK: - Path Encryption and Decryption public func encryptDirId(_ dirId: Data) throws -> String { diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 6d846f1..1c501a0 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -49,14 +49,20 @@ public class Masterkey { // MARK: - Masterkey Factory Methods + public static func createNew() throws -> Masterkey { + let cryptoSupport = CryptoSupport() + let aesMasterKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) + let macMasterKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) + return createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: 7) + } + public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { let jsonData = try Data(contentsOf: file) return try createFromMasterkeyFile(jsonData: jsonData, password: password, pepper: pepper) } public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { - let jsonDecoder = JSONDecoder() - let decoded = try jsonDecoder.decode(MasterkeyJson.self, from: jsonData) + let decoded = try JSONDecoder().decode(MasterkeyJson.self, from: jsonData) return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper) } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index acd35df..f2fa645 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -28,6 +28,18 @@ class CryptorTests: XCTestCase { try FileManager.default.removeItem(at: tmpDirURL) } + func testEncodeMasterkeyJson() throws { + let jsonData = try cryptor.encodeMasterkeyJson(password: "asd") + let decoded = try JSONDecoder().decode(MasterkeyJson.self, from: jsonData) + XCTAssertEqual("8PDw8PDw8PA=", decoded.scryptSalt) + XCTAssertEqual(32768, decoded.scryptCostParam) + XCTAssertEqual(8, decoded.scryptBlockSize) + XCTAssertEqual("nDcSwW65LXWVnry/lAC88aVtZrkB6dUWC9QuLsrRq/ZFCAJaldMtoQ==", decoded.primaryMasterKey) + XCTAssertEqual("HCuwIhdRnXeA3U4Ohm9/m96IML9kH/OuhyR1ygEJ+zJWRnhzOrtz9A==", decoded.hmacMasterKey) + XCTAssertEqual("sAWFgFNhmtMPeNWr4zh+9Ps7GOtT0pknX11PRQ7eC9Q=", decoded.versionMac) + XCTAssertEqual(7, decoded.version) + } + func testEncryptDirId() throws { let rootDir = try cryptor.encryptDirId("".data(using: .utf8)!) XCTAssertEqual("VLWEHT553J5DR7OZLRJAYDIWFCXZABOD", rootDir) From 7d1731e1fa5440fc00cef9fdfaf35c35cd2774e7 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 24 Jun 2020 09:10:13 +0200 Subject: [PATCH 049/144] Moved masterkey export method from Cryptor to Masterkey (where it belongs), made scryptCostParam parametrizable for speedy test --- CryptoLib/Cryptor.swift | 31 ------------------------ CryptoLib/Masterkey.swift | 37 ++++++++++++++++++++++++++++- CryptoLibTests/CryptorTests.swift | 12 ---------- CryptoLibTests/MasterkeyTests.swift | 32 +++++++++++++++++-------- 4 files changed, 58 insertions(+), 54 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 1e480d7..8e2b6c7 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -54,10 +54,6 @@ struct FileHeader { } public class Cryptor { - static let defaultScryptSaltSize = 8 - static let defaultScryptCostParam = 1 << 15 // 2^15 - static let defaultScryptBlockSize = 8 - static let fileHeaderLegacyPayloadSize = 8 static let fileHeaderSize = kCCBlockSizeAES128 + fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + Int(CC_SHA256_DIGEST_LENGTH) static let cleartextChunkSize = 32 * 1024 @@ -75,33 +71,6 @@ public class Cryptor { self.init(masterkey: masterkey, cryptoSupport: CryptoSupport()) } - // MARK: - Masterkey Encoding - - public func encodeMasterkeyJson(password: String, pepper: [UInt8] = [UInt8]()) throws -> Data { - let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) - let salt = try cryptoSupport.createRandomBytes(size: Cryptor.defaultScryptSaltSize) - let saltAndPepper = salt + pepper - let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: Cryptor.defaultScryptCostParam, r: Cryptor.defaultScryptBlockSize, p: 1).calculate() - - let wrappedMasterKey = try Masterkey.wrapMasterKey(rawKey: masterkey.aesMasterKey, kek: kek) - let wrappedHmacKey = try Masterkey.wrapMasterKey(rawKey: masterkey.macMasterKey, kek: kek) - - var versionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - let versionBytes = withUnsafeBytes(of: UInt32(masterkey.version).bigEndian, Array.init) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, versionBytes, versionBytes.count, &versionMac) - - let masterkeyJson = MasterkeyJson( - scryptSalt: Data(salt).base64EncodedString(), - scryptCostParam: Cryptor.defaultScryptCostParam, - scryptBlockSize: Cryptor.defaultScryptBlockSize, - primaryMasterKey: Data(wrappedMasterKey).base64EncodedString(), - hmacMasterKey: Data(wrappedHmacKey).base64EncodedString(), - versionMac: Data(versionMac).base64EncodedString(), - version: masterkey.version - ) - return try JSONEncoder().encode(masterkeyJson) - } - // MARK: - Path Encryption and Decryption public func encryptDirId(_ dirId: Data) throws -> String { diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 1c501a0..9e73105 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -28,6 +28,10 @@ enum MasterkeyError: Error, Equatable { } public class Masterkey { + static let defaultScryptSaltSize = 8 + static let defaultScryptCostParam = 1 << 15 // 2^15 + static let defaultScryptBlockSize = 8 + private(set) var aesMasterKey: [UInt8] private(set) var macMasterKey: [UInt8] public let version: Int @@ -47,7 +51,7 @@ public class Masterkey { } } - // MARK: - Masterkey Factory Methods + // MARK: - Factory public static func createNew() throws -> Masterkey { let cryptoSupport = CryptoSupport() @@ -134,4 +138,35 @@ public class Masterkey { throw MasterkeyError.unwrapFailed(status) } } + + // MARK: - Export + + public func exportEncrypted(password: String, pepper: [UInt8] = [UInt8]()) throws -> Data { + let masterkeyJson: MasterkeyJson = try exportEncrypted(password: password, pepper: pepper) + return try JSONEncoder().encode(masterkeyJson) + } + + func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int = Masterkey.defaultScryptCostParam, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { + let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) + let salt = try cryptoSupport.createRandomBytes(size: Masterkey.defaultScryptSaltSize) + let saltAndPepper = salt + pepper + let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: scryptCostParam, r: Masterkey.defaultScryptBlockSize, p: 1).calculate() + + let wrappedMasterKey = try Masterkey.wrapMasterKey(rawKey: aesMasterKey, kek: kek) + let wrappedHmacKey = try Masterkey.wrapMasterKey(rawKey: macMasterKey, kek: kek) + + var versionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + let versionBytes = withUnsafeBytes(of: UInt32(version).bigEndian, Array.init) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macMasterKey, macMasterKey.count, versionBytes, versionBytes.count, &versionMac) + + return MasterkeyJson( + scryptSalt: Data(salt).base64EncodedString(), + scryptCostParam: scryptCostParam, + scryptBlockSize: Masterkey.defaultScryptBlockSize, + primaryMasterKey: Data(wrappedMasterKey).base64EncodedString(), + hmacMasterKey: Data(wrappedHmacKey).base64EncodedString(), + versionMac: Data(versionMac).base64EncodedString(), + version: version + ) + } } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index f2fa645..acd35df 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -28,18 +28,6 @@ class CryptorTests: XCTestCase { try FileManager.default.removeItem(at: tmpDirURL) } - func testEncodeMasterkeyJson() throws { - let jsonData = try cryptor.encodeMasterkeyJson(password: "asd") - let decoded = try JSONDecoder().decode(MasterkeyJson.self, from: jsonData) - XCTAssertEqual("8PDw8PDw8PA=", decoded.scryptSalt) - XCTAssertEqual(32768, decoded.scryptCostParam) - XCTAssertEqual(8, decoded.scryptBlockSize) - XCTAssertEqual("nDcSwW65LXWVnry/lAC88aVtZrkB6dUWC9QuLsrRq/ZFCAJaldMtoQ==", decoded.primaryMasterKey) - XCTAssertEqual("HCuwIhdRnXeA3U4Ohm9/m96IML9kH/OuhyR1ygEJ+zJWRnhzOrtz9A==", decoded.hmacMasterKey) - XCTAssertEqual("sAWFgFNhmtMPeNWr4zh+9Ps7GOtT0pknX11PRQ7eC9Q=", decoded.versionMac) - XCTAssertEqual(7, decoded.version) - } - func testEncryptDirId() throws { let rootDir = try cryptor.encryptDirId("".data(using: .utf8)!) XCTAssertEqual("VLWEHT553J5DR7OZLRJAYDIWFCXZABOD", rootDir) diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index a77ce2a..27cbc2c 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -18,16 +18,6 @@ class MasterkeyTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - func testWrapAndUnwrapKey() throws { - let rawKey = [UInt8](repeating: 0x77, count: 32) - let kek = [UInt8](repeating: 0x55, count: 32) - let wrapped = try Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek) - XCTAssertNotNil(wrapped) - let unwrapped = try Masterkey.unwrapMasterKey(wrappedKey: wrapped, kek: kek) - XCTAssertNotNil(unwrapped) - XCTAssertEqual(rawKey, unwrapped) - } - func testCreateFromMasterkeyFile() throws { let expectedKeys = [UInt8](repeating: 0x00, count: 32) let jsonData = """ @@ -139,4 +129,26 @@ class MasterkeyTests: XCTestCase { XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac")) } } + + func testWrapAndUnwrapKey() throws { + let rawKey = [UInt8](repeating: 0x77, count: 32) + let kek = [UInt8](repeating: 0x55, count: 32) + let wrapped = try Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek) + XCTAssertNotNil(wrapped) + let unwrapped = try Masterkey.unwrapMasterKey(wrappedKey: wrapped, kek: kek) + XCTAssertNotNil(unwrapped) + XCTAssertEqual(rawKey, unwrapped) + } + + func testExportEncrypted() throws { + let masterkey = Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32), version: 7) + let json = try masterkey.exportEncrypted(password: "asd", pepper: [UInt8](), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) + XCTAssertEqual("8PDw8PDw8PA=", json.scryptSalt) + XCTAssertEqual(2, json.scryptCostParam) + XCTAssertEqual(8, json.scryptBlockSize) + XCTAssertEqual("jvdghkTc01VISrFly37pgaT/UKtXrDCvZcU3tT9Y98zyzn/pJ91bxw==", json.primaryMasterKey) + XCTAssertEqual("99I+J4bT3rVpZE8yZwKRV9gHVRmQ8XQEujAL9IuwLTc2D3mg5JEjKA==", json.hmacMasterKey) + XCTAssertEqual("sAWFgFNhmtMPeNWr4zh+9Ps7GOtT0pknX11PRQ7eC9Q=", json.versionMac) + XCTAssertEqual(7, json.version) + } } From 415e2e3242a454f033c51dd485f25881a595f70a Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 25 Jun 2020 23:24:31 +0200 Subject: [PATCH 050/144] Added implicit progress reporting to file content encryption/decryption --- CryptoLib/Cryptor.swift | 43 +++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 8e2b6c7..12c8377 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -153,6 +153,9 @@ public class Cryptor { // MARK: - File Content Encryption and Decryption + /** + This method supports implicit progress composition. + */ public func encryptContent(from cleartextURL: URL, to ciphertextURL: URL) throws { // open cleartext input stream: guard let cleartextStream = InputStream(url: cleartextURL) else { @@ -170,12 +173,24 @@ public class Cryptor { ciphertextStream.open() defer { ciphertextStream.close() } + // determine cleartext size: + let attributes = try? FileManager.default.attributesOfItem(atPath: cleartextURL.path) + let cleartextSize = attributes?[FileAttributeKey.size] as? Int + // encrypt: - try encryptContent(from: cleartextStream, to: ciphertextStream) + try encryptContent(from: cleartextStream, to: ciphertextStream, cleartextSize: cleartextSize) } - // TODO: progress - func encryptContent(from cleartextStream: InputStream, to ciphertextStream: OutputStream) throws { + func encryptContent(from cleartextStream: InputStream, to ciphertextStream: OutputStream, cleartextSize: Int?) throws { + // create progress: + let progress: Progress + if let cleartextSize = cleartextSize { + let ciphertextSize = calculateCiphertextSize(cleartextSize) + progress = Progress(totalUnitCount: Int64(ciphertextSize)) + } else { + progress = Progress(totalUnitCount: -1) + } + // encrypt and write header: let header = try createHeader() let ciphertextHeader = try encryptHeader(header) @@ -190,9 +205,13 @@ public class Cryptor { let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) ciphertextStream.write(ciphertextChunk, maxLength: ciphertextChunk.count) chunkNumber += 1 + progress.completedUnitCount += Int64(ciphertextChunk.count) } } + /** + This method supports implicit progress composition. + */ public func decryptContent(from ciphertextURL: URL, to cleartextURL: URL) throws { // open ciphertext input stream: guard let ciphertextStream = InputStream(url: ciphertextURL) else { @@ -210,12 +229,23 @@ public class Cryptor { cleartextStream.open() defer { cleartextStream.close() } + // determine ciphertext size: + let attributes = try? FileManager.default.attributesOfItem(atPath: ciphertextURL.path) + let ciphertextSize = attributes?[FileAttributeKey.size] as? Int + // decrypt: - try decryptContent(from: ciphertextStream, to: cleartextStream) + try decryptContent(from: ciphertextStream, to: cleartextStream, ciphertextSize: ciphertextSize) } - // TODO: progress - func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream) throws { + func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream, ciphertextSize: Int?) throws { + // create progress: + let progress: Progress + if let ciphertextSize = ciphertextSize, let cleartextSize = try? calculateCleartextSize(ciphertextSize) { + progress = Progress(totalUnitCount: Int64(cleartextSize)) + } else { + progress = Progress(totalUnitCount: -1) + } + // read and decrypt header: guard let ciphertextHeader = try ciphertextStream.read(maxLength: Cryptor.fileHeaderSize) else { throw CryptoError.ioError @@ -231,6 +261,7 @@ public class Cryptor { let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) cleartextStream.write(cleartextChunk, maxLength: cleartextChunk.count) chunkNumber += 1 + progress.completedUnitCount += Int64(cleartextChunk.count) } } From 13e43268de73f3d94d9e890280cc9798c8c60405 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 26 Jun 2020 11:16:00 +0200 Subject: [PATCH 051/144] Added docs on public methods, adjusted access control of some methods --- CryptoLib/AesCtr.swift | 4 ++- CryptoLib/AesSiv.swift | 24 +++++++++++++-- CryptoLib/CryptoError.swift | 2 +- CryptoLib/Cryptor.swift | 50 +++++++++++++++++++++++++++++-- CryptoLibTests/AesSivTests.swift | 2 +- CryptoLibTests/CryptorTests.swift | 10 +++---- 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/CryptoLib/AesCtr.swift b/CryptoLib/AesCtr.swift index e0b0854..d584a2d 100644 --- a/CryptoLib/AesCtr.swift +++ b/CryptoLib/AesCtr.swift @@ -5,12 +5,14 @@ // Created by Sebastian Stenzel on 06.06.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. // + import CommonCrypto import Foundation internal class AesCtr { /** - High level AES-CTR wrapper around CommonCrypto primitives. Can be used for encryption and decryption (it is the same in CTR mode) + High-level AES-CTR wrapper around CommonCrypto primitives. Can be used for encryption and decryption (it is the same in CTR mode). + - Parameter key: 128 or 256 bit encryption key - Parameter iv: 128 bit initialization vector (must not be reused!) - Parameter data: data to be encrypted/decrypted diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index 09d4d9d..8ba5163 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -1,5 +1,5 @@ // -// Siv.swift +// AesSiv.swift // CryptoLib // // Created by Sebastian Stenzel on 29.04.20. @@ -9,11 +9,20 @@ import CommonCrypto import Foundation -public class AesSiv { +internal class AesSiv { static let cryptoSupport = CryptoSupport() static let zero = [UInt8](repeating: 0x00, count: 16) static let dblConst: UInt8 = 0x87 + /** + Encrypts plaintext using SIV mode. + + - Parameter aesKey: SIV mode requires two separate keys. You can use one long key, which is splitted in half. See [RFC 5297 Section 2.2](https://tools.ietf.org/html/rfc5297#section-2.2). + - Parameter macKey: SIV mode requires two separate keys. You can use one long key, which is splitted in half. See [RFC 5297 Section 2.2](https://tools.ietf.org/html/rfc5297#section-2.2). + - Parameter plaintext: Your plaintext, which shall be encrypted. + - Parameter ad: Associated data, which gets authenticated but not encrypted. + - Returns: IV + Ciphertext as a concatenated byte array. + */ public static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { if plaintext.count > UInt32.max - 16 { throw CryptoError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") @@ -23,7 +32,16 @@ public class AesSiv { return iv + ciphertext } - static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + /** + Decrypts ciphertext using SIV mode. + + - Parameter aesKey: SIV mode requires two separate keys. You can use one long key, which is splitted in half. See [RFC 5297 Section 2.2](https://tools.ietf.org/html/rfc5297#section-2.2). + - Parameter macKey: SIV mode requires two separate keys. You can use one long key, which is splitted in half. See [RFC 5297 Section 2.2](https://tools.ietf.org/html/rfc5297#section-2.2). + - Parameter ciphertext: Your ciphertext, which shall be decrypted. + - Parameter ad: Associated data, which needs to be authenticated during decryption. + - Returns: Plaintext byte array. + */ + public static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { if ciphertext.count < 16 { throw CryptoError.invalidParameter("ciphertext must be at least 16 bytes") } diff --git a/CryptoLib/CryptoError.swift b/CryptoLib/CryptoError.swift index d960c72..1b59e50 100644 --- a/CryptoLib/CryptoError.swift +++ b/CryptoLib/CryptoError.swift @@ -9,7 +9,7 @@ import CommonCrypto import Foundation -enum CryptoError: Error, Equatable { +public enum CryptoError: Error, Equatable { case invalidParameter(_ reason: String) case ccCryptorError(_ status: CCCryptorStatus) case unauthenticCiphertext diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 12c8377..0c5e515 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -73,6 +73,12 @@ public class Cryptor { // MARK: - Path Encryption and Decryption + /** + Encrypts directory ID. + + - Parameter dirId: An arbitrary directory ID to be passed to one-way hash function. + - Returns: Constant length string that is unlikely to collide with any other name. + */ public func encryptDirId(_ dirId: Data) throws -> String { let encrypted = try AesSiv.encrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, plaintext: dirId.bytes) var digest = [UInt8](repeating: 0x00, count: Int(CC_SHA1_DIGEST_LENGTH)) @@ -80,6 +86,14 @@ public class Cryptor { return Data(digest).base32EncodedString } + /** + Encrypts filename. + + - Parameter cleartextName: Original filename including cleartext file extension. + - Parameter dirId: Directory ID that will be used as associated data. It will not get encrypted but needs to be provided during decryption. + - Parameter encoding: Encoding to use to encode the returned ciphertext. Defaults to base64url. + - Returns: Encrypted filename without any file extension. + */ public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) @@ -92,6 +106,14 @@ public class Cryptor { } } + /** + Decrypts filename. + + - Parameter ciphertextName: Ciphertext only. Any additional strings like file extensions need to be stripped first. + - Parameter dirId: The same directed ID used during encryption as associated data. + - Parameter encoding: Encoding to use to decode `ciphertextName`. Defaults to base64url. + - Returns: Decrypted filename, probably including its cleartext file extension. + */ public func decryptFileName(_ ciphertextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // decode: let maybeCiphertextData: Data? = { @@ -154,7 +176,12 @@ public class Cryptor { // MARK: - File Content Encryption and Decryption /** + Encrypts file content. + This method supports implicit progress composition. + + - Parameter cleartextURL: The input URL of a cleartext file. + - Parameter ciphertextURL: The output URL of the ciphertext file. */ public func encryptContent(from cleartextURL: URL, to ciphertextURL: URL) throws { // open cleartext input stream: @@ -210,7 +237,12 @@ public class Cryptor { } /** + Decrypts file content. + This method supports implicit progress composition. + + - Parameter ciphertextURL: The input URL of a ciphertext file. + - Parameter cleartextURL: The output URL of the cleartext file. */ public func decryptContent(from ciphertextURL: URL, to cleartextURL: URL) throws { // open ciphertext input stream: @@ -297,8 +329,15 @@ public class Cryptor { // MARK: - File Size Calculation + /** + Calculates ciphertext size from cleartext size. + + - Parameter cleartextSize: Size of the unencrypted payload. + - Precondition: `cleartextSize` must be positive. + - Returns: Ciphertext size of a `cleartextSize`-sized cleartext encrypted with this `Cryptor`. Not including the file header. + */ public func calculateCiphertextSize(_ cleartextSize: Int) -> Int { - assert(cleartextSize >= 0, "expected cleartextSize to be positive, but was \(cleartextSize)") + precondition(cleartextSize >= 0, "expected cleartextSize to be positive, but was \(cleartextSize)") let overheadPerChunk = Cryptor.ciphertextChunkSize - Cryptor.cleartextChunkSize let numFullChunks = cleartextSize / Cryptor.cleartextChunkSize // floor by int-truncation let additionalCleartextBytes = cleartextSize % Cryptor.cleartextChunkSize @@ -307,8 +346,15 @@ public class Cryptor { return Cryptor.ciphertextChunkSize * numFullChunks + additionalCiphertextBytes } + /** + Calculates cleartext size from ciphertext size. + + - Parameter ciphertextSize: Size of the encrypted payload. Not including the file header. + - Precondition: `ciphertextSize` must be positive. + - Returns: Cleartext size of a `ciphertextSize`-sized ciphertext decrypted with this `Cryptor`. + */ public func calculateCleartextSize(_ ciphertextSize: Int) throws -> Int { - assert(ciphertextSize >= 0, "expected ciphertextSize to be positive, but was \(ciphertextSize)") + precondition(ciphertextSize >= 0, "expected ciphertextSize to be positive, but was \(ciphertextSize)") let overheadPerChunk = Cryptor.ciphertextChunkSize - Cryptor.cleartextChunkSize let numFullChunks = ciphertextSize / Cryptor.ciphertextChunkSize // floor by int-truncation let additionalCiphertextBytes = ciphertextSize % Cryptor.ciphertextChunkSize diff --git a/CryptoLibTests/AesSivTests.swift b/CryptoLibTests/AesSivTests.swift index 439b54e..cd16d02 100644 --- a/CryptoLibTests/AesSivTests.swift +++ b/CryptoLibTests/AesSivTests.swift @@ -1,5 +1,5 @@ // -// SivTests.swift +// AesSivTests.swift // CryptoLibTests // // Created by Sebastian Stenzel on 29.04.20. diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index acd35df..166529b 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -93,17 +93,17 @@ class CryptorTests: XCTestCase { } func testEncryptAndDecryptContent() throws { - let originalContentData = Data(repeating: 0x0F, count: 65 * 1024) - let originalContentURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) - try originalContentData.write(to: originalContentURL) + let originalData = Data(repeating: 0x0F, count: 65 * 1024) + let originalURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) + try originalData.write(to: originalURL) let ciphertextURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) let cleartextURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) - try cryptor.encryptContent(from: originalContentURL, to: ciphertextURL) + try cryptor.encryptContent(from: originalURL, to: ciphertextURL) try cryptor.decryptContent(from: ciphertextURL, to: cleartextURL) let cleartextData = try Data(contentsOf: cleartextURL) - XCTAssertEqual(originalContentData, cleartextData) + XCTAssertEqual(originalData, cleartextData) } func testEncryptAndDecryptSingleChunk() throws { From 54a402f691619fe5497cd9e48da7e32a8ab3a7c5 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 26 Jun 2020 13:42:41 +0200 Subject: [PATCH 052/144] Further adjustments on access control of some methods --- CryptoLib/Cryptor.swift | 14 +++++++------- CryptoLib/Masterkey.swift | 15 ++++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 0c5e515..dcb16f8 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -137,13 +137,13 @@ public class Cryptor { // MARK: - File Header Encryption and Decryption - func createHeader() throws -> FileHeader { + internal func createHeader() throws -> FileHeader { let nonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) let contentKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) return FileHeader(nonce: nonce, contentKey: contentKey) } - func encryptHeader(_ header: FileHeader) throws -> [UInt8] { + internal func encryptHeader(_ header: FileHeader) throws -> [UInt8] { let cleartext = [UInt8](repeating: 0xFF, count: Cryptor.fileHeaderLegacyPayloadSize) + header.contentKey let ciphertext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: header.nonce, data: cleartext) let toBeAuthenticated = header.nonce + ciphertext @@ -152,7 +152,7 @@ public class Cryptor { return header.nonce + ciphertext + mac } - func decryptHeader(_ header: [UInt8]) throws -> FileHeader { + internal func decryptHeader(_ header: [UInt8]) throws -> FileHeader { // decompose header: let beginOfMAC = header.count - Int(CC_SHA256_DIGEST_LENGTH) let nonce = [UInt8](header[0 ..< kCCBlockSizeAES128]) @@ -208,7 +208,7 @@ public class Cryptor { try encryptContent(from: cleartextStream, to: ciphertextStream, cleartextSize: cleartextSize) } - func encryptContent(from cleartextStream: InputStream, to ciphertextStream: OutputStream, cleartextSize: Int?) throws { + internal func encryptContent(from cleartextStream: InputStream, to ciphertextStream: OutputStream, cleartextSize: Int?) throws { // create progress: let progress: Progress if let cleartextSize = cleartextSize { @@ -269,7 +269,7 @@ public class Cryptor { try decryptContent(from: ciphertextStream, to: cleartextStream, ciphertextSize: ciphertextSize) } - func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream, ciphertextSize: Int?) throws { + internal func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream, ciphertextSize: Int?) throws { // create progress: let progress: Progress if let ciphertextSize = ciphertextSize, let cleartextSize = try? calculateCleartextSize(ciphertextSize) { @@ -297,7 +297,7 @@ public class Cryptor { } } - func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { + internal func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { let chunkNonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) let ciphertext = try AesCtr.compute(key: fileKey, iv: chunkNonce, data: chunk) let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext @@ -306,7 +306,7 @@ public class Cryptor { return chunkNonce + ciphertext + mac } - func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { + internal func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { assert(chunk.count >= kCCBlockSizeAES128 + Int(CC_SHA256_DIGEST_LENGTH), "ciphertext chunk must at least contain nonce + mac") // decompose chunk: diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 9e73105..f859fd3 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -28,6 +28,7 @@ enum MasterkeyError: Error, Equatable { } public class Masterkey { + static let latestVersion = 7 static let defaultScryptSaltSize = 8 static let defaultScryptCostParam = 1 << 15 // 2^15 static let defaultScryptBlockSize = 8 @@ -57,11 +58,11 @@ public class Masterkey { let cryptoSupport = CryptoSupport() let aesMasterKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) let macMasterKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) - return createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: 7) + return createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: latestVersion) } - public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { - let jsonData = try Data(contentsOf: file) + public static func createFromMasterkeyFile(fileURL: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { + let jsonData = try Data(contentsOf: fileURL) return try createFromMasterkeyFile(jsonData: jsonData, password: password, pepper: pepper) } @@ -70,7 +71,7 @@ public class Masterkey { return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper) } - static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey { + internal static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!) let saltAndPepper = salt + pepper @@ -112,7 +113,7 @@ public class Masterkey { // MARK: - RFC 3394 Key Wrapping - static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { + internal static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var wrappedKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) var wrappedKey = [UInt8](repeating: 0x00, count: wrappedKeyLen) @@ -124,7 +125,7 @@ public class Masterkey { } } - static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { + internal static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var unwrappedKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) var unwrappedKey = [UInt8](repeating: 0x00, count: unwrappedKeyLen) @@ -146,7 +147,7 @@ public class Masterkey { return try JSONEncoder().encode(masterkeyJson) } - func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int = Masterkey.defaultScryptCostParam, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { + internal func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int = Masterkey.defaultScryptCostParam, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = try cryptoSupport.createRandomBytes(size: Masterkey.defaultScryptSaltSize) let saltAndPepper = salt + pepper From 178fd592146fd652abf68670e58a3c15a6bcf8ee Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 26 Jun 2020 13:43:14 +0200 Subject: [PATCH 053/144] Added docs on public methods of Masterkey --- CryptoLib/Masterkey.swift | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index f859fd3..40dbb30 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -54,6 +54,11 @@ public class Masterkey { // MARK: - Factory + /** + Creates new masterkey. + + - Returns: New masterkey instance with secure random bytes. Version will be set to the latest version (currently 7). + */ public static func createNew() throws -> Masterkey { let cryptoSupport = CryptoSupport() let aesMasterKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) @@ -61,11 +66,27 @@ public class Masterkey { return createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: latestVersion) } + /** + Creates masterkey from masterkey file. + + - Parameter fileURL: The URL to the masterkey file that is formatted in JSON. + - Parameter password: The password to use for decrypting the masterkey file. + - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. + - Returns: New masterkey instance using the keys from the supplied `fileURL`. + */ public static func createFromMasterkeyFile(fileURL: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { let jsonData = try Data(contentsOf: fileURL) return try createFromMasterkeyFile(jsonData: jsonData, password: password, pepper: pepper) } + /** + Creates masterkey from masterkey JSON data. + + - Parameter jsonData: The JSON data of the masterkey file. + - Parameter password: The password to use for decrypting the masterkey file. + - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. + - Returns: New masterkey instance using the keys from the supplied `jsonData`. + */ public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { let decoded = try JSONDecoder().decode(MasterkeyJson.self, from: jsonData) return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper) @@ -142,6 +163,13 @@ public class Masterkey { // MARK: - Export + /** + Export encrypted/wrapped masterkey and other metadata as JSON data. + + - Parameter password: The password used to encrypt the key material. + - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. + - Returns: JSON data with encrypted/wrapped masterkey and other metadata that can be stored in insecure locations. + */ public func exportEncrypted(password: String, pepper: [UInt8] = [UInt8]()) throws -> Data { let masterkeyJson: MasterkeyJson = try exportEncrypted(password: password, pepper: pepper) return try JSONEncoder().encode(masterkeyJson) From 062881797aa50b076bd8a972bfc105091132023d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 26 Jun 2020 13:56:55 +0200 Subject: [PATCH 054/144] Updated README, added CONTRIBUTING and CODE_OF_CONDUCT --- .github/CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++ .github/CONTRIBUTING.md | 25 ++++++++ README.md | 128 ++++++++++++++++++++++++++++++++++++- 3 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..428d75a --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +support@cryptomator.org. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..2502d88 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,25 @@ +# Contributing to CryptoLib Swift + +## Did you find a bug? + +- Ensure the bug was not [already reported](https://github.com/cryptomator/cryptolib-swift/issues). +- If you're unable to find an open issue addressing the problem, [submit a new one](https://github.com/cryptomator/cryptolib-swift/issues/new). + +## Did you write a patch that fixes a bug? + +- Open a new pull request with the patch. +- Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. + +## Do you intend to add a new feature or change an existing one? + +- Suggest your change by [submitting a new issue](https://github.com/cryptomator/cryptolib-swift/issues/new) and start writing code. + +## Code of Conduct + +Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md). + +## Above all, thank you for your contributions + +Thank you for taking the time to contribute to the project! :+1: + +Cryptomator Team diff --git a/README.md b/README.md index de7554d..79ad934 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,135 @@ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) -High level wrapper for cryptographic operations used by Cryptomator iOS App +This library contains all cryptographic functions that are used by Cryptomator for iOS. The purpose of this project is to provide a separate light-weight library with its own release cycle that can be used in other projects, too. + +For more information on the Cryptomator encryption scheme, visit the security architecure page on [docs.cryptomator.org](https://docs.cryptomator.org/en/1.5/security/architecture/). + +## Requirements + +- iOS 8.0 or higher + +## Installation + +The easiest way to use CryptoLib Swift in your app is via [CocoaPods](https://cocoapods.org/ "CocoaPods"). + +1. Add the following line in the project's Podfile file: `pod 'CryptomatorCryptoLib', '~> 1.0.0'` +2. Run the command `pod install` from the Podfile folder directory. + +## Usage + +### Masterkey + +`Masterkey` is a factory for masterkey objects that contain the masterkey bytes for AES encryption/decryption and MAC authentication. The version states the vault format version. + +#### Factory + +This will create a new masterkey with secure random bytes. Version will be set to the latest version (currently 7). + +```swift +let masterkey = try Masterkey.createNew() +``` + +Another way is to create a masterkey from an existing masterkey file. This is equivalent to an unlock attempt. Either by URL: + +```swift +let fileURL = ... +let password = ... +let pepper = ... // optional +let masterkey = try Masterkey.createFromMasterkeyFile(fileURL: fileURL, password: password, pepper: pepper) +``` + +Or by JSON data: + +```swift +let jsonData = ... +let password = ... +let pepper = ... // optional +let masterkey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: password, pepper: pepper) +``` + +#### Export + +For persisting the masterkey, use this method to export its encrypted/wrapped masterkey and other metadata as JSON data. + +```swift +let masterkey = ... +let password = ... +let pepper = ... // optional +let jsonData = try masterkey.exportEncrypted(password: password, pepper: pepper) +``` + +### Cryptor + +`Cryptor` is the core class for cryptographic operations on Cryptomator vaults. + +#### Constructor + +Create a cryptor by providing a masterkey. + +```swift +let masterkey = ... +let cryptor = Cryptor(masterkey: masterkey) +``` + +#### Path Encryption and Decryption + +Encrypt the directory ID in order to determine the encrypted directory URL. + +```swift +let cryptor = ... +let dirId = ... +let encryptedDirId = try cryptor.encryptDirId(dirId) +``` + +Encrypt and decrypt filenames by providing a directory ID. + +```swift +let cryptor = ... +let filename = ... +let dirId = ... +let ciphertextName = try cryptor.encryptFileName(filename, dirId: dirId) +let cleartextName = try cryptor.decryptFileName(ciphertextName, dirId: dirId) +``` + +#### File Content Encryption and Decryption + +Encrypt and decrypt file content via URLs. + +```swift +let cryptor = ... +let fileURL = ... +let ciphertextURL = ... +let cleartextURL = ... +try cryptor.encryptContent(from: fileURL, to: ciphertextURL) +try cryptor.decryptContent(from: ciphertextURL, to: cleartextURL) +``` + +#### File Size Calculation + +Determine the cleartext and ciphertext sizes in O(1). + +```swift +let cryptor = ... +let size = ... +let ciphertextSize = cryptor.calculateCiphertextSize(size) +let cleartextSize = try cryptor.calculateCleartextSize(ciphertextSize) +``` + +## Contributing to CryptoLib Swift + +Please read our [contribution guide](.github/CONTRIBUTING.md), if you would like to report a bug, ask a question or help us with coding. In general, the following preference is used to choose the implementation of cryptographic primitives: + 1. Apple Swift Crypto (HMAC) 2. Apple CommonCrypto (AES-CTR, RFC 3394 Key Derivation) 3. CryptoSwift (scrypt) + +## Code of Conduct + +Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](.github/CODE_OF_CONDUCT.md). + +## License + +Distributed under the AGPLv3. See the LICENSE file for more info. From afe6353bf386fbe77387764741ecc0ab13961f22 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 26 Jun 2020 14:03:10 +0200 Subject: [PATCH 055/144] Updated README --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 79ad934..a77cfeb 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) +# CryptoLib Swift + This library contains all cryptographic functions that are used by Cryptomator for iOS. The purpose of this project is to provide a separate light-weight library with its own release cycle that can be used in other projects, too. For more information on the Cryptomator encryption scheme, visit the security architecure page on [docs.cryptomator.org](https://docs.cryptomator.org/en/1.5/security/architecture/). @@ -31,7 +33,9 @@ This will create a new masterkey with secure random bytes. Version will be set t let masterkey = try Masterkey.createNew() ``` -Another way is to create a masterkey from an existing masterkey file. This is equivalent to an unlock attempt. Either by URL: +Another way is to create a masterkey from an existing masterkey file. This is equivalent to an unlock attempt. + +Either by URL: ```swift let fileURL = ... From 1b73a19c4ef2003f5c1a551ffb660744fe6b4c38 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 27 Jun 2020 21:10:38 +0200 Subject: [PATCH 056/144] Fixed progress reporting during file content decryption --- CryptoLib/Cryptor.swift | 6 +++--- CryptoLibTests/CryptorTests.swift | 10 ++++++++++ Podfile.lock | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index dcb16f8..3beb6c3 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -231,8 +231,8 @@ public class Cryptor { } let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) ciphertextStream.write(ciphertextChunk, maxLength: ciphertextChunk.count) - chunkNumber += 1 progress.completedUnitCount += Int64(ciphertextChunk.count) + chunkNumber += 1 } } @@ -272,7 +272,7 @@ public class Cryptor { internal func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream, ciphertextSize: Int?) throws { // create progress: let progress: Progress - if let ciphertextSize = ciphertextSize, let cleartextSize = try? calculateCleartextSize(ciphertextSize) { + if let ciphertextSize = ciphertextSize, let cleartextSize = try? calculateCleartextSize(ciphertextSize - Cryptor.fileHeaderSize) { progress = Progress(totalUnitCount: Int64(cleartextSize)) } else { progress = Progress(totalUnitCount: -1) @@ -292,8 +292,8 @@ public class Cryptor { } let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) cleartextStream.write(cleartextChunk, maxLength: cleartextChunk.count) - chunkNumber += 1 progress.completedUnitCount += Int64(cleartextChunk.count) + chunkNumber += 1 } } diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 166529b..d1562ba 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -99,8 +99,18 @@ class CryptorTests: XCTestCase { let ciphertextURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) let cleartextURL = tmpDirURL.appendingPathComponent(UUID().uuidString, isDirectory: false) + let overallProgress = Progress(totalUnitCount: 2) + let progressObserver = overallProgress.observe(\.fractionCompleted) { progress, _ in + print("\(progress.localizedDescription ?? "") (\(progress.localizedAdditionalDescription ?? ""))") + } + overallProgress.becomeCurrent(withPendingUnitCount: 1) try cryptor.encryptContent(from: originalURL, to: ciphertextURL) + overallProgress.resignCurrent() + overallProgress.becomeCurrent(withPendingUnitCount: 1) try cryptor.decryptContent(from: ciphertextURL, to: cleartextURL) + overallProgress.resignCurrent() + progressObserver.invalidate() + XCTAssertTrue(overallProgress.completedUnitCount >= overallProgress.totalUnitCount) let cleartextData = try Data(contentsOf: cleartextURL) XCTAssertEqual(originalData, cleartextData) diff --git a/Podfile.lock b/Podfile.lock index 6ae6ad3..118062d 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -17,4 +17,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 026266d3146c90650716bef1229c141bb12d87b4 -COCOAPODS: 1.9.1 +COCOAPODS: 1.9.3 From ae125269329f259af2db7610ee074e0a83973526 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 27 Jun 2020 21:34:43 +0200 Subject: [PATCH 057/144] Added progress reporting reference to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a77cfeb..5fd6c71 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ let cleartextName = try cryptor.decryptFileName(ciphertextName, dirId: dirId) #### File Content Encryption and Decryption -Encrypt and decrypt file content via URLs. +Encrypt and decrypt file content via URLs. These methods support [implicit progress composition](https://developer.apple.com/documentation/foundation/progress#1661068). ```swift let cryptor = ... From 4dca3efae1815a262d19519bf71efb7c4763a08d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sun, 28 Jun 2020 11:01:25 +0200 Subject: [PATCH 058/144] Fixed typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fd6c71..14b8354 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This library contains all cryptographic functions that are used by Cryptomator for iOS. The purpose of this project is to provide a separate light-weight library with its own release cycle that can be used in other projects, too. -For more information on the Cryptomator encryption scheme, visit the security architecure page on [docs.cryptomator.org](https://docs.cryptomator.org/en/1.5/security/architecture/). +For more information on the Cryptomator encryption scheme, visit the security architecture page on [docs.cryptomator.org](https://docs.cryptomator.org/en/1.5/security/architecture/). ## Requirements From 30dc7ca7cd2d7caa342bec33327755d4575e10d9 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 30 Jun 2020 20:40:29 +0200 Subject: [PATCH 059/144] Updated docs --- CryptoLib/CryptoSupport.swift | 16 +++++++++++++++- CryptoLib/Masterkey.swift | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CryptoLib/CryptoSupport.swift b/CryptoLib/CryptoSupport.swift index 6886781..13980e8 100644 --- a/CryptoLib/CryptoSupport.swift +++ b/CryptoLib/CryptoSupport.swift @@ -9,6 +9,12 @@ import Foundation internal class CryptoSupport { + /** + Creates an array of cryptographically secure random bytes. + + - Parameter size: The number of random bytes to return in the array. + - Returns: An array with cryptographically secure random bytes. + */ func createRandomBytes(size: Int) throws -> [UInt8] { var randomBytes = [UInt8](repeating: 0x00, count: size) guard SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) == errSecSuccess else { @@ -18,7 +24,15 @@ internal class CryptoSupport { } /** - Constant-time comparison + Compares byte arrays in constant-time. + + The running time of this method is independent of the byte arrays compared, making it safe to use for comparing secret values such as cryptographic MACs. + + The byte arrays are expected to be of same length. + + - Parameter expected: Expected bytes for comparison. + - Parameter actual: Actual bytes for comparison. + - Returns: `true` if `expected` and `actual` are equal, otherwise `false`. */ func compareBytes(expected: [UInt8], actual: [UInt8]) -> Bool { assert(expected.count == actual.count, "parameters should be of same length") diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 40dbb30..3c6e64f 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -164,7 +164,7 @@ public class Masterkey { // MARK: - Export /** - Export encrypted/wrapped masterkey and other metadata as JSON data. + Exports encrypted/wrapped masterkey and other metadata as JSON data. - Parameter password: The password used to encrypt the key material. - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. From 4fb334fb3e1eb719ad8ef62e86657da633edbbf8 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 13 Jul 2020 14:44:10 +0200 Subject: [PATCH 060/144] Exchanged parameters in some XCTAssertEqual so that expected value is the first parameter --- CryptoLibTests/CryptorTests.swift | 8 ++++---- CryptoLibTests/MasterkeyTests.swift | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index d1562ba..482a055 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -159,16 +159,16 @@ class CryptorTests: XCTestCase { func testCalculateCleartextSizeWithInvalidCiphertextSize() throws { XCTAssertThrowsError(try cryptor.calculateCleartextSize(1), "invalid ciphertext size") { error in - XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 1")) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 1"), error as! CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(48), "invalid ciphertext size") { error in - XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 48")) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 48"), error as! CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 1 + 48), "invalid ciphertext size") { error in - XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 32817")) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32817"), error as! CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 48 * 2), "invalid ciphertext size") { error in - XCTAssertEqual(error as! CryptoError, CryptoError.invalidParameter("Method not defined for input value 32864")) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32864"), error as! CryptoError) } } } diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index 27cbc2c..c911b3b 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -54,7 +54,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password") { error in - XCTAssertEqual(error as! MasterkeyError, MasterkeyError.invalidPassword) + XCTAssertEqual(MasterkeyError.invalidPassword, error as! MasterkeyError) } } @@ -72,7 +72,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac")) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac"), error as! MasterkeyError) } } @@ -90,7 +90,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey")) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as! MasterkeyError) } } @@ -108,7 +108,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey")) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as! MasterkeyError) } } @@ -126,7 +126,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac")) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac"), error as! MasterkeyError) } } From 48015ae4a6b0f8b4007d1a85735203b28d6fe2ea Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 15 Jul 2020 14:47:09 +0200 Subject: [PATCH 061/144] Changed type cast operator from forced form to conditional form in unit tests --- CryptoLibTests/CryptorTests.swift | 8 ++++---- CryptoLibTests/MasterkeyTests.swift | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CryptoLibTests/CryptorTests.swift b/CryptoLibTests/CryptorTests.swift index 482a055..05404e6 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/CryptoLibTests/CryptorTests.swift @@ -159,16 +159,16 @@ class CryptorTests: XCTestCase { func testCalculateCleartextSizeWithInvalidCiphertextSize() throws { XCTAssertThrowsError(try cryptor.calculateCleartextSize(1), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 1"), error as! CryptoError) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 1"), error as? CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(48), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 48"), error as! CryptoError) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 48"), error as? CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 1 + 48), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32817"), error as! CryptoError) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32817"), error as? CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 48 * 2), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32864"), error as! CryptoError) + XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32864"), error as? CryptoError) } } } diff --git a/CryptoLibTests/MasterkeyTests.swift b/CryptoLibTests/MasterkeyTests.swift index c911b3b..53f0d19 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/CryptoLibTests/MasterkeyTests.swift @@ -54,7 +54,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.invalidPassword, error as! MasterkeyError) + XCTAssertEqual(MasterkeyError.invalidPassword, error as? MasterkeyError) } } @@ -72,7 +72,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac"), error as! MasterkeyError) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac"), error as? MasterkeyError) } } @@ -90,7 +90,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as! MasterkeyError) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as? MasterkeyError) } } @@ -108,7 +108,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as! MasterkeyError) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as? MasterkeyError) } } @@ -126,7 +126,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac"), error as! MasterkeyError) + XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac"), error as? MasterkeyError) } } From 98702131a57acd2c77a5ac86219198c162b884db Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 20 Jul 2020 18:01:49 +0200 Subject: [PATCH 062/144] Adjusted access control of several classes, enums, extensions, and methods --- CryptoLib/AesCtr.swift | 4 +-- CryptoLib/AesSiv.swift | 14 +++++----- CryptoLib/CryptoSupport.swift | 2 +- CryptoLib/Cryptor.swift | 49 ++++++++++++++++++++--------------- CryptoLib/Masterkey.swift | 12 ++++----- 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/CryptoLib/AesCtr.swift b/CryptoLib/AesCtr.swift index d584a2d..9d44ea1 100644 --- a/CryptoLib/AesCtr.swift +++ b/CryptoLib/AesCtr.swift @@ -9,7 +9,7 @@ import CommonCrypto import Foundation -internal class AesCtr { +class AesCtr { /** High-level AES-CTR wrapper around CommonCrypto primitives. Can be used for encryption and decryption (it is the same in CTR mode). @@ -18,7 +18,7 @@ internal class AesCtr { - Parameter data: data to be encrypted/decrypted - Returns: encrypted/decrypted data */ - public static func compute(key: [UInt8], iv: [UInt8], data: [UInt8]) throws -> [UInt8] { + static func compute(key: [UInt8], iv: [UInt8], data: [UInt8]) throws -> [UInt8] { assert(key.count == kCCKeySizeAES256 || key.count == kCCKeySizeAES128, "key expected to be 128 or 256 bit") assert(iv.count == kCCBlockSizeAES128, "iv expected to be 128 bit") diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index 8ba5163..d20185f 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -9,7 +9,7 @@ import CommonCrypto import Foundation -internal class AesSiv { +class AesSiv { static let cryptoSupport = CryptoSupport() static let zero = [UInt8](repeating: 0x00, count: 16) static let dblConst: UInt8 = 0x87 @@ -23,7 +23,7 @@ internal class AesSiv { - Parameter ad: Associated data, which gets authenticated but not encrypted. - Returns: IV + Ciphertext as a concatenated byte array. */ - public static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { if plaintext.count > UInt32.max - 16 { throw CryptoError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") } @@ -41,7 +41,7 @@ internal class AesSiv { - Parameter ad: Associated data, which needs to be authenticated during decryption. - Returns: Plaintext byte array. */ - public static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { if ciphertext.count < 16 { throw CryptoError.invalidParameter("ciphertext must be at least 16 bytes") } @@ -55,7 +55,9 @@ internal class AesSiv { return plaintext } - internal static func ctr(aesKey key: [UInt8], iv: [UInt8], plaintext: [UInt8]) throws -> [UInt8] { + // MARK: - Internal + + static func ctr(aesKey key: [UInt8], iv: [UInt8], plaintext: [UInt8]) throws -> [UInt8] { // clear out the 31st and 63rd bit (see https://tools.ietf.org/html/rfc5297#section-2.5) var ctr = iv ctr[8] &= 0x7F @@ -63,7 +65,7 @@ internal class AesSiv { return try AesCtr.compute(key: key, iv: ctr, data: plaintext) } - internal static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [[UInt8]]) throws -> [UInt8] { + static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [[UInt8]]) throws -> [UInt8] { // Maximum permitted AD length is the block size in bits - 2 if ad.count > 126 { throw CryptoError.invalidParameter("too many ad") @@ -89,7 +91,7 @@ internal class AesSiv { return try cmac(macKey: macKey, data: t) } - internal static func cmac(macKey key: [UInt8], data: [UInt8]) throws -> [UInt8] { + static func cmac(macKey key: [UInt8], data: [UInt8]) throws -> [UInt8] { // subkey generation: let l = try aes(key: key, plaintext: zero) let k1 = l[0] & 0x80 == 0x00 ? shiftLeft(l) : dbl(l) diff --git a/CryptoLib/CryptoSupport.swift b/CryptoLib/CryptoSupport.swift index 13980e8..a1b0212 100644 --- a/CryptoLib/CryptoSupport.swift +++ b/CryptoLib/CryptoSupport.swift @@ -8,7 +8,7 @@ import Foundation -internal class CryptoSupport { +class CryptoSupport { /** Creates an array of cryptographically secure random bytes. diff --git a/CryptoLib/Cryptor.swift b/CryptoLib/Cryptor.swift index 3beb6c3..4d00367 100644 --- a/CryptoLib/Cryptor.swift +++ b/CryptoLib/Cryptor.swift @@ -11,35 +11,42 @@ import CryptoSwift import Foundation import SwiftBase32 -extension Data { - public init?(base64UrlEncoded base64String: String, options: Data.Base64DecodingOptions = []) { +public extension Data { + init?(base64UrlEncoded base64String: String, options: Data.Base64DecodingOptions = []) { self.init(base64Encoded: base64String.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/"), options: options) } - public func base64UrlEncodedString(options: Data.Base64EncodingOptions = []) -> String { + func base64UrlEncodedString(options: Data.Base64EncodingOptions = []) -> String { return base64EncodedString(options: options).replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_") } } -extension FixedWidthInteger { - public func byteArray() -> [UInt8] { +public extension FixedWidthInteger { + func byteArray() -> [UInt8] { return withUnsafeBytes(of: self, { [UInt8]($0) }) } } -extension InputStream { - public func read(maxLength: Int) throws -> [UInt8]? { +public enum InputStreamError: Error { + case readOperationFailed +} + +public extension InputStream { + func read(maxLength: Int) throws -> [UInt8]? { var buffer = [UInt8](repeating: 0x00, count: maxLength) let length = read(&buffer, maxLength: maxLength) - if length == 0 { + switch length { + case _ where length > 0: + assert(length <= buffer.count) + buffer.removeSubrange(length...) + return buffer + case 0: return nil + case _ where length < 0: + throw streamError ?? InputStreamError.readOperationFailed + default: + fatalError() } - guard length > 0 else { - throw streamError ?? CryptoError.ioError - } - assert(length <= buffer.count) - buffer.removeSubrange(length...) - return buffer } } @@ -137,13 +144,13 @@ public class Cryptor { // MARK: - File Header Encryption and Decryption - internal func createHeader() throws -> FileHeader { + func createHeader() throws -> FileHeader { let nonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) let contentKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) return FileHeader(nonce: nonce, contentKey: contentKey) } - internal func encryptHeader(_ header: FileHeader) throws -> [UInt8] { + func encryptHeader(_ header: FileHeader) throws -> [UInt8] { let cleartext = [UInt8](repeating: 0xFF, count: Cryptor.fileHeaderLegacyPayloadSize) + header.contentKey let ciphertext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: header.nonce, data: cleartext) let toBeAuthenticated = header.nonce + ciphertext @@ -152,7 +159,7 @@ public class Cryptor { return header.nonce + ciphertext + mac } - internal func decryptHeader(_ header: [UInt8]) throws -> FileHeader { + func decryptHeader(_ header: [UInt8]) throws -> FileHeader { // decompose header: let beginOfMAC = header.count - Int(CC_SHA256_DIGEST_LENGTH) let nonce = [UInt8](header[0 ..< kCCBlockSizeAES128]) @@ -208,7 +215,7 @@ public class Cryptor { try encryptContent(from: cleartextStream, to: ciphertextStream, cleartextSize: cleartextSize) } - internal func encryptContent(from cleartextStream: InputStream, to ciphertextStream: OutputStream, cleartextSize: Int?) throws { + func encryptContent(from cleartextStream: InputStream, to ciphertextStream: OutputStream, cleartextSize: Int?) throws { // create progress: let progress: Progress if let cleartextSize = cleartextSize { @@ -269,7 +276,7 @@ public class Cryptor { try decryptContent(from: ciphertextStream, to: cleartextStream, ciphertextSize: ciphertextSize) } - internal func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream, ciphertextSize: Int?) throws { + func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream, ciphertextSize: Int?) throws { // create progress: let progress: Progress if let ciphertextSize = ciphertextSize, let cleartextSize = try? calculateCleartextSize(ciphertextSize - Cryptor.fileHeaderSize) { @@ -297,7 +304,7 @@ public class Cryptor { } } - internal func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { + func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { let chunkNonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) let ciphertext = try AesCtr.compute(key: fileKey, iv: chunkNonce, data: chunk) let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext @@ -306,7 +313,7 @@ public class Cryptor { return chunkNonce + ciphertext + mac } - internal func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { + func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { assert(chunk.count >= kCCBlockSizeAES128 + Int(CC_SHA256_DIGEST_LENGTH), "ciphertext chunk must at least contain nonce + mac") // decompose chunk: diff --git a/CryptoLib/Masterkey.swift b/CryptoLib/Masterkey.swift index 3c6e64f..94e61a9 100644 --- a/CryptoLib/Masterkey.swift +++ b/CryptoLib/Masterkey.swift @@ -20,7 +20,7 @@ struct MasterkeyJson: Codable { let version: Int } -enum MasterkeyError: Error, Equatable { +public enum MasterkeyError: Error, Equatable { case malformedMasterkeyFile(_ reason: String) case invalidPassword case unwrapFailed(_ status: CCCryptorStatus) @@ -92,7 +92,7 @@ public class Masterkey { return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper) } - internal static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey { + private static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!) let saltAndPepper = salt + pepper @@ -126,7 +126,7 @@ public class Masterkey { return createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey, version: jsonData.version) } - internal static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8], version: Int) -> Masterkey { + static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8], version: Int) -> Masterkey { assert(aesMasterKey.count == kCCKeySizeAES256) assert(macMasterKey.count == kCCKeySizeAES256) return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: version) @@ -134,7 +134,7 @@ public class Masterkey { // MARK: - RFC 3394 Key Wrapping - internal static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { + static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var wrappedKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) var wrappedKey = [UInt8](repeating: 0x00, count: wrappedKeyLen) @@ -146,7 +146,7 @@ public class Masterkey { } } - internal static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { + static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { assert(kek.count == kCCKeySizeAES256) var unwrappedKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) var unwrappedKey = [UInt8](repeating: 0x00, count: unwrappedKeyLen) @@ -175,7 +175,7 @@ public class Masterkey { return try JSONEncoder().encode(masterkeyJson) } - internal func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int = Masterkey.defaultScryptCostParam, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { + func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int = Masterkey.defaultScryptCostParam, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = try cryptoSupport.createRandomBytes(size: Masterkey.defaultScryptSaltSize) let saltAndPepper = salt + pepper From b6f46f5a120ba7ddcda80ff32882466109d844a4 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 20 Aug 2020 08:34:39 +0200 Subject: [PATCH 063/144] Changed some thrown errors to asserts --- CryptoLib/AesSiv.swift | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/CryptoLib/AesSiv.swift b/CryptoLib/AesSiv.swift index d20185f..6633c70 100644 --- a/CryptoLib/AesSiv.swift +++ b/CryptoLib/AesSiv.swift @@ -24,9 +24,7 @@ class AesSiv { - Returns: IV + Ciphertext as a concatenated byte array. */ static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { - if plaintext.count > UInt32.max - 16 { - throw CryptoError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes") - } + assert(plaintext.count <= UInt32.max - 16, "plaintext must not be longer than 2^32 - 16 bytes") let iv = try s2v(macKey: macKey, plaintext: plaintext, ad: ad) let ciphertext = try ctr(aesKey: aesKey, iv: iv, plaintext: plaintext) return iv + ciphertext @@ -42,9 +40,7 @@ class AesSiv { - Returns: Plaintext byte array. */ static func decrypt(aesKey: [UInt8], macKey: [UInt8], ciphertext: [UInt8], ad: [UInt8]...) throws -> [UInt8] { - if ciphertext.count < 16 { - throw CryptoError.invalidParameter("ciphertext must be at least 16 bytes") - } + assert(ciphertext.count >= 16, "ciphertext must be at least 16 bytes") let iv = Array(ciphertext[..<16]) let actualCiphertext = Array(ciphertext[16...]) let plaintext = try ctr(aesKey: aesKey, iv: iv, plaintext: actualCiphertext) @@ -67,9 +63,7 @@ class AesSiv { static func s2v(macKey: [UInt8], plaintext: [UInt8], ad: [[UInt8]]) throws -> [UInt8] { // Maximum permitted AD length is the block size in bits - 2 - if ad.count > 126 { - throw CryptoError.invalidParameter("too many ad") - } + assert(ad.count <= 126, "too many ad") // RFC 5297 defines a n == 0 case here. Where n is the length of the input vector: // S1 = associatedData1, S2 = associatedData2, ... Sn = plaintext From 017da8d8faeb4f6ba3d19b8524bee4d9a985f57d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 18 Sep 2020 17:21:42 +0200 Subject: [PATCH 064/144] Updated project to Xcode 12, increased deployment target to iOS 9.0 --- CryptoLib.xcodeproj/project.pbxproj | 8 +++++--- .../xcshareddata/xcschemes/CryptoLib.xcscheme | 2 +- CryptomatorCryptoLib.podspec | 2 +- Podfile | 2 +- Podfile.lock | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptoLib.xcodeproj/project.pbxproj index 42a519a..5f1b777 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptoLib.xcodeproj/project.pbxproj @@ -209,7 +209,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1140; + LastUpgradeCheck = 1200; ORGANIZATIONNAME = "Skymatic GmbH"; TargetAttributes = { 4A7C21312451F2AC00DE81E6 = { @@ -384,6 +384,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -409,7 +410,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -447,6 +448,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -466,7 +468,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; diff --git a/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme b/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme index 6d33408..fd4b45c 100644 --- a/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme +++ b/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme @@ -1,6 +1,6 @@ 1.3.0' diff --git a/Podfile b/Podfile index 82772b5..e1e292f 100644 --- a/Podfile +++ b/Podfile @@ -1,4 +1,4 @@ -platform :ios, '8.0' +platform :ios, '9.0' inhibit_all_warnings! target 'CryptoLib' do diff --git a/Podfile.lock b/Podfile.lock index 118062d..bac9713 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,6 +15,6 @@ SPEC CHECKSUMS: CryptoSwift: f12f037f6d0fcd6d48c96db0071b653de64e6c4d SwiftBase32: 723fd05e2fce776f2b98f26fe423bd20fa80e411 -PODFILE CHECKSUM: 026266d3146c90650716bef1229c141bb12d87b4 +PODFILE CHECKSUM: 5b6353d0dda8e3f1db87e950881a32c87f1b3e7e COCOAPODS: 1.9.3 From 1555148d115a606468379bfcfe257d13328cabb4 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 18 Sep 2020 17:37:48 +0200 Subject: [PATCH 065/144] Updated dependencies --- Podfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index bac9713..d4e27ff 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - CryptoSwift (1.3.1) + - CryptoSwift (1.3.2) - SwiftBase32 (0.8.0) DEPENDENCIES: @@ -12,7 +12,7 @@ SPEC REPOS: - SwiftBase32 SPEC CHECKSUMS: - CryptoSwift: f12f037f6d0fcd6d48c96db0071b653de64e6c4d + CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060 SwiftBase32: 723fd05e2fce776f2b98f26fe423bd20fa80e411 PODFILE CHECKSUM: 5b6353d0dda8e3f1db87e950881a32c87f1b3e7e From 3ec50778e1095a8b23d0c766829115d9e5cc73e8 Mon Sep 17 00:00:00 2001 From: Philipp Schmid Date: Thu, 24 Sep 2020 12:30:43 +0200 Subject: [PATCH 066/144] added Swift Package Manifest --- Package.swift | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Package.swift diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..df94786 --- /dev/null +++ b/Package.swift @@ -0,0 +1,30 @@ +// swift-tools-version:5.0 + +// +// Package.swift +// CryptoLib +// +// Created by Philipp Schmid on 24.09.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import PackageDescription + +let package = Package( + name: "CryptoLib", + platforms: [ + .iOS(.v9) + ], + products: [ + .library(name: "CryptoLib", targets: ["CryptoLib"]) + ], + dependencies: [ + .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.3.0")), + .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")) + ], + targets: [ + .target(name: "CryptoLib", dependencies: ["CryptoSwift", "Base32"], path: "CryptoLib"), + .testTarget(name: "CryptoLibTests", dependencies: ["CryptoLib"], path: "CryptoLibTests") + ], + swiftLanguageVersions: [.v5] +) From f4f11b3e4cecb8f8ac7430586db51ac8aceb4171 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 11:43:54 +0200 Subject: [PATCH 067/144] Restructured project so that it conforms to naming conventions of Swift Package Manager, renamed CryptoLib to CryptomatorCryptoLib across the project --- .github/workflows/build.yml | 4 +- .../contents.xcworkspacedata | 7 + CryptoLib/CryptoLib.h | 19 --- CryptomatorCryptoLib.podspec | 4 +- .../project.pbxproj | 124 +++++++++++------- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcschemes/CryptomatorCryptoLib.xcscheme | 18 +-- .../contents.xcworkspacedata | 2 +- .../xcshareddata/IDEWorkspaceChecks.plist | 0 Package.resolved | 25 ++++ Package.swift | 12 +- Podfile | 4 +- Podfile.lock | 2 +- .../CryptomatorCryptoLib}/AesCtr.swift | 2 +- .../CryptomatorCryptoLib}/AesSiv.swift | 2 +- .../CryptomatorCryptoLib}/CryptoError.swift | 2 +- .../CryptomatorCryptoLib}/CryptoSupport.swift | 2 +- .../CryptomatorCryptoLib.h | 19 +++ .../CryptomatorCryptoLib}/Cryptor.swift | 2 +- .../CryptomatorCryptoLib}/Info.plist | 0 .../CryptomatorCryptoLib}/Masterkey.swift | 2 +- .../AesCtrTests.swift | 4 +- .../AesSivTests.swift | 4 +- .../CryptorTests.swift | 4 +- .../CryptomatorCryptoLibTests}/Info.plist | 0 .../MasterkeyTests.swift | 4 +- Tests/LinuxMain.swift | 7 + 28 files changed, 169 insertions(+), 106 deletions(-) create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata delete mode 100644 CryptoLib/CryptoLib.h rename {CryptoLib.xcodeproj => CryptomatorCryptoLib.xcodeproj}/project.pbxproj (77%) rename {CryptoLib.xcodeproj => CryptomatorCryptoLib.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) rename {CryptoLib.xcodeproj => CryptomatorCryptoLib.xcodeproj}/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme => CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme (79%) rename {CryptoLib.xcworkspace => CryptomatorCryptoLib.xcworkspace}/contents.xcworkspacedata (76%) rename {CryptoLib.xcworkspace => CryptomatorCryptoLib.xcworkspace}/xcshareddata/IDEWorkspaceChecks.plist (100%) create mode 100644 Package.resolved rename {CryptoLib => Sources/CryptomatorCryptoLib}/AesCtr.swift (98%) rename {CryptoLib => Sources/CryptomatorCryptoLib}/AesSiv.swift (99%) rename {CryptoLib => Sources/CryptomatorCryptoLib}/CryptoError.swift (93%) rename {CryptoLib => Sources/CryptomatorCryptoLib}/CryptoSupport.swift (98%) create mode 100644 Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h rename {CryptoLib => Sources/CryptomatorCryptoLib}/Cryptor.swift (99%) rename {CryptoLib => Sources/CryptomatorCryptoLib}/Info.plist (100%) rename {CryptoLib => Sources/CryptomatorCryptoLib}/Masterkey.swift (99%) rename {CryptoLibTests => Tests/CryptomatorCryptoLibTests}/AesCtrTests.swift (96%) rename {CryptoLibTests => Tests/CryptomatorCryptoLibTests}/AesSivTests.swift (98%) rename {CryptoLibTests => Tests/CryptomatorCryptoLibTests}/CryptorTests.swift (99%) rename {CryptoLibTests => Tests/CryptomatorCryptoLibTests}/Info.plist (100%) rename {CryptoLibTests => Tests/CryptomatorCryptoLibTests}/MasterkeyTests.swift (98%) create mode 100644 Tests/LinuxMain.swift diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c9f87bd..cc9ba09 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,11 +18,11 @@ jobs: - name: Install dependencies run: pod install - name: Build and test - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace 'CryptoLib.xcworkspace' -scheme 'CryptoLib' -destination 'name=iPhone 11 Pro' -enableCodeCoverage YES clean test | xcpretty + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace 'CryptomatorCryptoLib.xcworkspace' -scheme 'CryptomatorCryptoLib' -destination 'name=iPhone 11 Pro' -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report run: | gem install slather - slather coverage -x --scheme CryptoLib --workspace CryptoLib.xcworkspace CryptoLib.xcodeproj + slather coverage -x --scheme CryptomatorCryptoLib --workspace CryptomatorCryptoLib.xcworkspace CryptomatorCryptoLib.xcodeproj bash <(curl -Ls https://coverage.codacy.com/get.sh) env: CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/CryptoLib/CryptoLib.h b/CryptoLib/CryptoLib.h deleted file mode 100644 index c3d72e8..0000000 --- a/CryptoLib/CryptoLib.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// CryptoLib.h -// CryptoLib -// -// Created by Philipp Schmid on 23.04.20. -// Copyright © 2020 Skymatic GmbH. All rights reserved. -// - -#import - -//! Project version number for CryptoLib. -FOUNDATION_EXPORT double CryptoLibVersionNumber; - -//! Project version string for CryptoLib. -FOUNDATION_EXPORT const unsigned char CryptoLibVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 50e8ade..6b70f14 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -11,8 +11,8 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/cryptomator/cryptolib-swift.git', :tag => s.version.to_s } s.social_media_url = 'https://twitter.com/Cryptomator' - s.public_header_files = 'CryptoLib/CryptoLib.h' - s.source_files = 'CryptoLib/**/*.{swift,h,m}' + s.public_header_files = 'Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h' + s.source_files = 'Sources/CryptomatorCryptoLib/**/*.swift' s.ios.deployment_target = '9.0' s.swift_version = '5.0' diff --git a/CryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj similarity index 77% rename from CryptoLib.xcodeproj/project.pbxproj rename to CryptomatorCryptoLib.xcodeproj/project.pbxproj index 5f1b777..22a68ed 100644 --- a/CryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -7,11 +7,11 @@ objects = { /* Begin PBXBuildFile section */ - 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; }; - 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0C8F90937EFDC9B9D55AF19E /* libPods-CryptomatorCryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B55D54DD75319956B4A5F46A /* libPods-CryptomatorCryptoLibTests.a */; }; + 4A7C213C2451F2AC00DE81E6 /* CryptomatorCryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; }; + 4A7C21432451F2AD00DE81E6 /* CryptomatorCryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F753248FC89B00B4C26D /* CryptoError.swift */; }; 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */; }; - 7980E962F1389ACDDCEBB596 /* libPods-CryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */; }; 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; @@ -21,7 +21,7 @@ 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */; }; 9EB822C1248AF82200879838 /* AesCtr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB822C0248AF82200879838 /* AesCtr.swift */; }; 9EB822C3248AF9C500879838 /* AesCtrTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB822C2248AF9C500879838 /* AesCtrTests.swift */; }; - AAC7D038BEF14DA7788F6090 /* libPods-CryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 085645A8AA97369573B917E8 /* libPods-CryptoLib.a */; }; + F4D8BE98D2ACCFC2ACEB4B4D /* libPods-CryptomatorCryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9EFD5232B8A20191311193CF /* libPods-CryptomatorCryptoLib.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -36,17 +36,17 @@ /* Begin PBXFileReference section */ 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.debug.xcconfig"; sourceTree = ""; }; - 085645A8AA97369573B917E8 /* libPods-CryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.release.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.release.xcconfig"; sourceTree = ""; }; - 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptoLibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2C6DE4F29372585DE89EE034 /* Pods-CryptomatorCryptoLibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLibTests.debug.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLibTests/Pods-CryptomatorCryptoLibTests.debug.xcconfig"; sourceTree = ""; }; 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.release.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.release.xcconfig"; sourceTree = ""; }; - 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CryptoLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptoLib.h; sourceTree = ""; }; + 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CryptomatorCryptoLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptomatorCryptoLib.h; sourceTree = ""; }; 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 4A7C213B2451F2AC00DE81E6 /* CryptomatorCryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptomatorCryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74F0F753248FC89B00B4C26D /* CryptoError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoError.swift; sourceTree = ""; }; 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSupport.swift; sourceTree = ""; }; + 94D3052EF784FC4C00FF4C29 /* Pods-CryptomatorCryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLib/Pods-CryptomatorCryptoLib.debug.xcconfig"; sourceTree = ""; }; 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.debug.xcconfig"; sourceTree = ""; }; 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; 9E44EEA524599C6900A37B01 /* AesSiv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSiv.swift; sourceTree = ""; }; @@ -57,6 +57,10 @@ 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libCryptoSwift.a; sourceTree = BUILT_PRODUCTS_DIR; }; 9EB822C0248AF82200879838 /* AesCtr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesCtr.swift; sourceTree = ""; }; 9EB822C2248AF9C500879838 /* AesCtrTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesCtrTests.swift; sourceTree = ""; }; + 9EFD5232B8A20191311193CF /* libPods-CryptomatorCryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptomatorCryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B55D54DD75319956B4A5F46A /* libPods-CryptomatorCryptoLibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptomatorCryptoLibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + E7931D45710E24EA79F24F37 /* Pods-CryptomatorCryptoLibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLibTests.release.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLibTests/Pods-CryptomatorCryptoLibTests.release.xcconfig"; sourceTree = ""; }; + EAA11A0503C99BA047061991 /* Pods-CryptomatorCryptoLib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLib.release.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLib/Pods-CryptomatorCryptoLib.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,7 +69,7 @@ buildActionMask = 2147483647; files = ( 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */, - AAC7D038BEF14DA7788F6090 /* libPods-CryptoLib.a in Frameworks */, + F4D8BE98D2ACCFC2ACEB4B4D /* libPods-CryptomatorCryptoLib.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -73,8 +77,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4A7C213C2451F2AC00DE81E6 /* CryptoLib.framework in Frameworks */, - 7980E962F1389ACDDCEBB596 /* libPods-CryptoLibTests.a in Frameworks */, + 4A7C213C2451F2AC00DE81E6 /* CryptomatorCryptoLib.framework in Frameworks */, + 0C8F90937EFDC9B9D55AF19E /* libPods-CryptomatorCryptoLibTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -85,8 +89,8 @@ isa = PBXGroup; children = ( 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */, - 085645A8AA97369573B917E8 /* libPods-CryptoLib.a */, - 26296374923D6A536A006295 /* libPods-CryptoLibTests.a */, + 9EFD5232B8A20191311193CF /* libPods-CryptomatorCryptoLib.a */, + B55D54DD75319956B4A5F46A /* libPods-CryptomatorCryptoLibTests.a */, ); name = Frameworks; sourceTree = ""; @@ -94,8 +98,8 @@ 4A7C21282451F2AC00DE81E6 = { isa = PBXGroup; children = ( - 4A7C21342451F2AC00DE81E6 /* CryptoLib */, - 4A7C213F2451F2AC00DE81E6 /* CryptoLibTests */, + 74FFC966251F3EBB004C4927 /* Sources */, + 74FFC967251F3ED0004C4927 /* Tests */, 4A7C21332451F2AC00DE81E6 /* Products */, D7D76AD3F7F6155DB2723364 /* Pods */, 33F96BB57BB41A059EFBA16D /* Frameworks */, @@ -105,28 +109,28 @@ 4A7C21332451F2AC00DE81E6 /* Products */ = { isa = PBXGroup; children = ( - 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */, - 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */, + 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */, + 4A7C213B2451F2AC00DE81E6 /* CryptomatorCryptoLibTests.xctest */, ); name = Products; sourceTree = ""; }; - 4A7C21342451F2AC00DE81E6 /* CryptoLib */ = { + 4A7C21342451F2AC00DE81E6 /* CryptomatorCryptoLib */ = { isa = PBXGroup; children = ( - 4A7C21352451F2AC00DE81E6 /* CryptoLib.h */, + 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */, 4A7C21362451F2AC00DE81E6 /* Info.plist */, 9EB822C0248AF82200879838 /* AesCtr.swift */, 9E44EEA524599C6900A37B01 /* AesSiv.swift */, 74F0F753248FC89B00B4C26D /* CryptoError.swift */, - 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */, 9E9BB811245412E900F9FF51 /* Cryptor.swift */, + 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, ); - path = CryptoLib; + path = CryptomatorCryptoLib; sourceTree = ""; }; - 4A7C213F2451F2AC00DE81E6 /* CryptoLibTests */ = { + 4A7C213F2451F2AC00DE81E6 /* CryptomatorCryptoLibTests */ = { isa = PBXGroup; children = ( 4A7C21422451F2AD00DE81E6 /* Info.plist */, @@ -135,7 +139,23 @@ 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */, 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, ); - path = CryptoLibTests; + path = CryptomatorCryptoLibTests; + sourceTree = ""; + }; + 74FFC966251F3EBB004C4927 /* Sources */ = { + isa = PBXGroup; + children = ( + 4A7C21342451F2AC00DE81E6 /* CryptomatorCryptoLib */, + ); + path = Sources; + sourceTree = ""; + }; + 74FFC967251F3ED0004C4927 /* Tests */ = { + isa = PBXGroup; + children = ( + 4A7C213F2451F2AC00DE81E6 /* CryptomatorCryptoLibTests */, + ); + path = Tests; sourceTree = ""; }; D7D76AD3F7F6155DB2723364 /* Pods */ = { @@ -145,6 +165,10 @@ 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */, 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */, 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */, + 94D3052EF784FC4C00FF4C29 /* Pods-CryptomatorCryptoLib.debug.xcconfig */, + EAA11A0503C99BA047061991 /* Pods-CryptomatorCryptoLib.release.xcconfig */, + 2C6DE4F29372585DE89EE034 /* Pods-CryptomatorCryptoLibTests.debug.xcconfig */, + E7931D45710E24EA79F24F37 /* Pods-CryptomatorCryptoLibTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -156,16 +180,16 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 4A7C21432451F2AD00DE81E6 /* CryptoLib.h in Headers */, + 4A7C21432451F2AD00DE81E6 /* CryptomatorCryptoLib.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 4A7C21312451F2AC00DE81E6 /* CryptoLib */ = { + 4A7C21312451F2AC00DE81E6 /* CryptomatorCryptoLib */ = { isa = PBXNativeTarget; - buildConfigurationList = 4A7C21462451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptoLib" */; + buildConfigurationList = 4A7C21462451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptomatorCryptoLib" */; buildPhases = ( 6DFD694B51EDFB57A524EA41 /* [CP] Check Pods Manifest.lock */, 4A7C212D2451F2AC00DE81E6 /* Headers */, @@ -178,14 +202,14 @@ ); dependencies = ( ); - name = CryptoLib; + name = CryptomatorCryptoLib; productName = CryptoLib; - productReference = 4A7C21322451F2AC00DE81E6 /* CryptoLib.framework */; + productReference = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; productType = "com.apple.product-type.framework"; }; - 4A7C213A2451F2AC00DE81E6 /* CryptoLibTests */ = { + 4A7C213A2451F2AC00DE81E6 /* CryptomatorCryptoLibTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 4A7C21492451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptoLibTests" */; + buildConfigurationList = 4A7C21492451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptomatorCryptoLibTests" */; buildPhases = ( B0398845BD49B4481C76093F /* [CP] Check Pods Manifest.lock */, 4A7C21372451F2AC00DE81E6 /* Sources */, @@ -197,9 +221,9 @@ dependencies = ( 4A7C213E2451F2AC00DE81E6 /* PBXTargetDependency */, ); - name = CryptoLibTests; + name = CryptomatorCryptoLibTests; productName = CryptoLibTests; - productReference = 4A7C213B2451F2AC00DE81E6 /* CryptoLibTests.xctest */; + productReference = 4A7C213B2451F2AC00DE81E6 /* CryptomatorCryptoLibTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -221,7 +245,7 @@ }; }; }; - buildConfigurationList = 4A7C212C2451F2AC00DE81E6 /* Build configuration list for PBXProject "CryptoLib" */; + buildConfigurationList = 4A7C212C2451F2AC00DE81E6 /* Build configuration list for PBXProject "CryptomatorCryptoLib" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; @@ -234,8 +258,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 4A7C21312451F2AC00DE81E6 /* CryptoLib */, - 4A7C213A2451F2AC00DE81E6 /* CryptoLibTests */, + 4A7C21312451F2AC00DE81E6 /* CryptomatorCryptoLib */, + 4A7C213A2451F2AC00DE81E6 /* CryptomatorCryptoLibTests */, ); }; /* End PBXProject section */ @@ -273,7 +297,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CryptoLib-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-CryptomatorCryptoLib-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -313,7 +337,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CryptoLibTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-CryptomatorCryptoLibTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -352,7 +376,7 @@ /* Begin PBXTargetDependency section */ 4A7C213E2451F2AC00DE81E6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 4A7C21312451F2AC00DE81E6 /* CryptoLib */; + target = 4A7C21312451F2AC00DE81E6 /* CryptomatorCryptoLib */; targetProxy = 4A7C213D2451F2AC00DE81E6 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -482,7 +506,7 @@ }; 4A7C21472451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */; + baseConfigurationReference = 94D3052EF784FC4C00FF4C29 /* Pods-CryptomatorCryptoLib.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; @@ -490,7 +514,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = CryptoLib/Info.plist; + INFOPLIST_FILE = Sources/CryptomatorCryptoLib/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -509,7 +533,7 @@ }; 4A7C21482451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */; + baseConfigurationReference = EAA11A0503C99BA047061991 /* Pods-CryptomatorCryptoLib.release.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; @@ -517,7 +541,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = CryptoLib/Info.plist; + INFOPLIST_FILE = Sources/CryptomatorCryptoLib/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -535,10 +559,10 @@ }; 4A7C214A2451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */; + baseConfigurationReference = 2C6DE4F29372585DE89EE034 /* Pods-CryptomatorCryptoLibTests.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = CryptoLibTests/Info.plist; + INFOPLIST_FILE = Tests/CryptomatorCryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -553,10 +577,10 @@ }; 4A7C214B2451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */; + baseConfigurationReference = E7931D45710E24EA79F24F37 /* Pods-CryptomatorCryptoLibTests.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = CryptoLibTests/Info.plist; + INFOPLIST_FILE = Tests/CryptomatorCryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -572,7 +596,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 4A7C212C2451F2AC00DE81E6 /* Build configuration list for PBXProject "CryptoLib" */ = { + 4A7C212C2451F2AC00DE81E6 /* Build configuration list for PBXProject "CryptomatorCryptoLib" */ = { isa = XCConfigurationList; buildConfigurations = ( 4A7C21442451F2AD00DE81E6 /* Debug */, @@ -581,7 +605,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 4A7C21462451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptoLib" */ = { + 4A7C21462451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptomatorCryptoLib" */ = { isa = XCConfigurationList; buildConfigurations = ( 4A7C21472451F2AD00DE81E6 /* Debug */, @@ -590,7 +614,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 4A7C21492451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptoLibTests" */ = { + 4A7C21492451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptomatorCryptoLibTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 4A7C214A2451F2AD00DE81E6 /* Debug */, diff --git a/CryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from CryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to CryptomatorCryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/CryptoLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from CryptoLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme similarity index 79% rename from CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme rename to CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme index fd4b45c..a63ecf5 100644 --- a/CryptoLib.xcodeproj/xcshareddata/xcschemes/CryptoLib.xcscheme +++ b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme @@ -15,9 +15,9 @@ + BuildableName = "CryptomatorCryptoLib.framework" + BlueprintName = "CryptomatorCryptoLib" + ReferencedContainer = "container:CryptomatorCryptoLib.xcodeproj"> @@ -33,9 +33,9 @@ + BuildableName = "CryptomatorCryptoLibTests.xctest" + BlueprintName = "CryptomatorCryptoLibTests" + ReferencedContainer = "container:CryptomatorCryptoLib.xcodeproj"> @@ -61,9 +61,9 @@ + BuildableName = "CryptomatorCryptoLib.framework" + BlueprintName = "CryptomatorCryptoLib" + ReferencedContainer = "container:CryptomatorCryptoLib.xcodeproj"> diff --git a/CryptoLib.xcworkspace/contents.xcworkspacedata b/CryptomatorCryptoLib.xcworkspace/contents.xcworkspacedata similarity index 76% rename from CryptoLib.xcworkspace/contents.xcworkspacedata rename to CryptomatorCryptoLib.xcworkspace/contents.xcworkspacedata index c9346be..5c14b96 100644 --- a/CryptoLib.xcworkspace/contents.xcworkspacedata +++ b/CryptomatorCryptoLib.xcworkspace/contents.xcworkspacedata @@ -2,7 +2,7 @@ + location = "group:CryptomatorCryptoLib.xcodeproj"> diff --git a/CryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CryptomatorCryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from CryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to CryptomatorCryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..0effe0b --- /dev/null +++ b/Package.resolved @@ -0,0 +1,25 @@ +{ + "object": { + "pins": [ + { + "package": "SwiftBase32", + "repositoryURL": "https://github.com/cryptomator/Base32.git", + "state": { + "branch": null, + "revision": "aa919f98c6e884945385d76121a4a29ca48afc6f", + "version": "0.8.1" + } + }, + { + "package": "CryptoSwift", + "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", + "state": { + "branch": null, + "revision": "af1b58fc569bfde777462349b9f7314b61762be0", + "version": "1.3.2" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift index df94786..5935a4b 100644 --- a/Package.swift +++ b/Package.swift @@ -2,7 +2,7 @@ // // Package.swift -// CryptoLib +// CryptomatorCryptoLib // // Created by Philipp Schmid on 24.09.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. @@ -11,20 +11,20 @@ import PackageDescription let package = Package( - name: "CryptoLib", + name: "CryptomatorCryptoLib", platforms: [ .iOS(.v9) ], products: [ - .library(name: "CryptoLib", targets: ["CryptoLib"]) + .library(name: "CryptomatorCryptoLib", targets: ["CryptomatorCryptoLib"]) ], dependencies: [ .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.3.0")), - .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")) + .package(url: "https://github.com/cryptomator/Base32.git", .upToNextMinor(from: "0.8.0")) ], targets: [ - .target(name: "CryptoLib", dependencies: ["CryptoSwift", "Base32"], path: "CryptoLib"), - .testTarget(name: "CryptoLibTests", dependencies: ["CryptoLib"], path: "CryptoLibTests") + .target(name: "CryptomatorCryptoLib", dependencies: ["CryptoSwift", "SwiftBase32"]), + .testTarget(name: "CryptomatorCryptoLibTests", dependencies: ["CryptomatorCryptoLib"]) ], swiftLanguageVersions: [.v5] ) diff --git a/Podfile b/Podfile index e1e292f..270315b 100644 --- a/Podfile +++ b/Podfile @@ -1,11 +1,11 @@ platform :ios, '9.0' inhibit_all_warnings! -target 'CryptoLib' do +target 'CryptomatorCryptoLib' do pod 'CryptoSwift', '~> 1.3.0' pod 'SwiftBase32', '~> 0.8.0' - target 'CryptoLibTests' do + target 'CryptomatorCryptoLibTests' do inherit! :search_paths end end diff --git a/Podfile.lock b/Podfile.lock index d4e27ff..cbe8672 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,6 +15,6 @@ SPEC CHECKSUMS: CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060 SwiftBase32: 723fd05e2fce776f2b98f26fe423bd20fa80e411 -PODFILE CHECKSUM: 5b6353d0dda8e3f1db87e950881a32c87f1b3e7e +PODFILE CHECKSUM: a970dcd7d46271a2a2b1702e791d45eac89a4cfb COCOAPODS: 1.9.3 diff --git a/CryptoLib/AesCtr.swift b/Sources/CryptomatorCryptoLib/AesCtr.swift similarity index 98% rename from CryptoLib/AesCtr.swift rename to Sources/CryptomatorCryptoLib/AesCtr.swift index 9d44ea1..3d87df3 100644 --- a/CryptoLib/AesCtr.swift +++ b/Sources/CryptomatorCryptoLib/AesCtr.swift @@ -1,6 +1,6 @@ // // AesCtr.swift -// CryptoLib +// CryptomatorCryptoLib // // Created by Sebastian Stenzel on 06.06.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. diff --git a/CryptoLib/AesSiv.swift b/Sources/CryptomatorCryptoLib/AesSiv.swift similarity index 99% rename from CryptoLib/AesSiv.swift rename to Sources/CryptomatorCryptoLib/AesSiv.swift index 6633c70..417c1c2 100644 --- a/CryptoLib/AesSiv.swift +++ b/Sources/CryptomatorCryptoLib/AesSiv.swift @@ -1,6 +1,6 @@ // // AesSiv.swift -// CryptoLib +// CryptomatorCryptoLib // // Created by Sebastian Stenzel on 29.04.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. diff --git a/CryptoLib/CryptoError.swift b/Sources/CryptomatorCryptoLib/CryptoError.swift similarity index 93% rename from CryptoLib/CryptoError.swift rename to Sources/CryptomatorCryptoLib/CryptoError.swift index 1b59e50..d626c55 100644 --- a/CryptoLib/CryptoError.swift +++ b/Sources/CryptomatorCryptoLib/CryptoError.swift @@ -1,6 +1,6 @@ // // CryptoError.swift -// CryptoLib +// CryptomatorCryptoLib // // Created by Tobias Hagemann on 09.06.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. diff --git a/CryptoLib/CryptoSupport.swift b/Sources/CryptomatorCryptoLib/CryptoSupport.swift similarity index 98% rename from CryptoLib/CryptoSupport.swift rename to Sources/CryptomatorCryptoLib/CryptoSupport.swift index a1b0212..c468695 100644 --- a/CryptoLib/CryptoSupport.swift +++ b/Sources/CryptomatorCryptoLib/CryptoSupport.swift @@ -1,6 +1,6 @@ // // CryptoSupport.swift -// CryptoLib +// CryptomatorCryptoLib // // Created by Tobias Hagemann on 10.06.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. diff --git a/Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h b/Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h new file mode 100644 index 0000000..d94524a --- /dev/null +++ b/Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h @@ -0,0 +1,19 @@ +// +// CryptomatorCryptoLib.h +// CryptomatorCryptoLib +// +// Created by Philipp Schmid on 23.04.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +#import + +//! Project version number for CryptomatorCryptoLib. +FOUNDATION_EXPORT double CryptomatorCryptoLibVersionNumber; + +//! Project version string for CryptomatorCryptoLib. +FOUNDATION_EXPORT const unsigned char CryptomatorCryptoLibVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/CryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift similarity index 99% rename from CryptoLib/Cryptor.swift rename to Sources/CryptomatorCryptoLib/Cryptor.swift index 4d00367..46acfed 100644 --- a/CryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -1,6 +1,6 @@ // // Cryptor.swift -// CryptoLib +// CryptomatorCryptoLib // // Created by Sebastian Stenzel on 25.04.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. diff --git a/CryptoLib/Info.plist b/Sources/CryptomatorCryptoLib/Info.plist similarity index 100% rename from CryptoLib/Info.plist rename to Sources/CryptomatorCryptoLib/Info.plist diff --git a/CryptoLib/Masterkey.swift b/Sources/CryptomatorCryptoLib/Masterkey.swift similarity index 99% rename from CryptoLib/Masterkey.swift rename to Sources/CryptomatorCryptoLib/Masterkey.swift index 94e61a9..abbe8b8 100644 --- a/CryptoLib/Masterkey.swift +++ b/Sources/CryptomatorCryptoLib/Masterkey.swift @@ -1,6 +1,6 @@ // // Masterkey.swift -// CryptoLib +// CryptomatorCryptoLib // // Created by Sebastian Stenzel on 25.04.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. diff --git a/CryptoLibTests/AesCtrTests.swift b/Tests/CryptomatorCryptoLibTests/AesCtrTests.swift similarity index 96% rename from CryptoLibTests/AesCtrTests.swift rename to Tests/CryptomatorCryptoLibTests/AesCtrTests.swift index 5aa0d80..710b69f 100644 --- a/CryptoLibTests/AesCtrTests.swift +++ b/Tests/CryptomatorCryptoLibTests/AesCtrTests.swift @@ -1,13 +1,13 @@ // // AesCtrTests.swift -// CryptoLibTests +// CryptomatorCryptoLibTests // // Created by Sebastian Stenzel on 06.06.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. // import XCTest -@testable import CryptoLib +@testable import CryptomatorCryptoLib // test vectors F5.5 and F5.6 from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf class AesCtrTests: XCTestCase { diff --git a/CryptoLibTests/AesSivTests.swift b/Tests/CryptomatorCryptoLibTests/AesSivTests.swift similarity index 98% rename from CryptoLibTests/AesSivTests.swift rename to Tests/CryptomatorCryptoLibTests/AesSivTests.swift index cd16d02..14c8824 100644 --- a/CryptoLibTests/AesSivTests.swift +++ b/Tests/CryptomatorCryptoLibTests/AesSivTests.swift @@ -1,13 +1,13 @@ // // AesSivTests.swift -// CryptoLibTests +// CryptomatorCryptoLibTests // // Created by Sebastian Stenzel on 29.04.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. // import XCTest -@testable import CryptoLib +@testable import CryptomatorCryptoLib class AesSivTests: XCTestCase { let aesKey: [UInt8] = [ diff --git a/CryptoLibTests/CryptorTests.swift b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift similarity index 99% rename from CryptoLibTests/CryptorTests.swift rename to Tests/CryptomatorCryptoLibTests/CryptorTests.swift index 05404e6..adba292 100644 --- a/CryptoLibTests/CryptorTests.swift +++ b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift @@ -1,13 +1,13 @@ // // CryptorTests.swift -// CryptoLibTests +// CryptomatorCryptoLibTests // // Created by Sebastian Stenzel on 27.04.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. // import XCTest -@testable import CryptoLib +@testable import CryptomatorCryptoLib class CryptoSupportMock: CryptoSupport { override func createRandomBytes(size: Int) throws -> [UInt8] { diff --git a/CryptoLibTests/Info.plist b/Tests/CryptomatorCryptoLibTests/Info.plist similarity index 100% rename from CryptoLibTests/Info.plist rename to Tests/CryptomatorCryptoLibTests/Info.plist diff --git a/CryptoLibTests/MasterkeyTests.swift b/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift similarity index 98% rename from CryptoLibTests/MasterkeyTests.swift rename to Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift index 53f0d19..c13c791 100644 --- a/CryptoLibTests/MasterkeyTests.swift +++ b/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift @@ -1,13 +1,13 @@ // // MasterkeyTests.swift -// CryptoLibTests +// CryptomatorCryptoLibTests // // Created by Sebastian Stenzel on 26.04.20. // Copyright © 2020 Skymatic GmbH. All rights reserved. // import XCTest -@testable import CryptoLib +@testable import CryptomatorCryptoLib class MasterkeyTests: XCTestCase { override func setUp() { diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift new file mode 100644 index 0000000..67d024c --- /dev/null +++ b/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import TestTests + +var tests = [XCTestCaseEntry]() +tests += TestTests.allTests() +XCTMain(tests) From 78838ca49960a577ce2e235b376d1a3f1e519c74 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 11:52:09 +0200 Subject: [PATCH 068/144] Removed Pods integration and added Swift Package Manager as dependency management --- .github/workflows/build.yml | 4 +- .../project.pbxproj | 140 ++++++------------ .../contents.xcworkspacedata | 2 +- .../xcshareddata/swiftpm/Package.resolved | 25 ++++ .../contents.xcworkspacedata | 10 -- .../xcshareddata/IDEWorkspaceChecks.plist | 8 - Podfile | 11 -- Podfile.lock | 20 --- README.md | 19 ++- 9 files changed, 88 insertions(+), 151 deletions(-) create mode 100644 CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 CryptomatorCryptoLib.xcworkspace/contents.xcworkspacedata delete mode 100644 CryptomatorCryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 Podfile delete mode 100644 Podfile.lock diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc9ba09..c8d67df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,11 +18,11 @@ jobs: - name: Install dependencies run: pod install - name: Build and test - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace 'CryptomatorCryptoLib.xcworkspace' -scheme 'CryptomatorCryptoLib' -destination 'name=iPhone 11 Pro' -enableCodeCoverage YES clean test | xcpretty + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'name=iPhone 11 Pro' -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report run: | gem install slather - slather coverage -x --scheme CryptomatorCryptoLib --workspace CryptomatorCryptoLib.xcworkspace CryptomatorCryptoLib.xcodeproj + slather coverage -x --scheme CryptomatorCryptoLib CryptomatorCryptoLib.xcodeproj bash <(curl -Ls https://coverage.codacy.com/get.sh) env: CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 22a68ed..4c58d96 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -3,25 +3,24 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ - 0C8F90937EFDC9B9D55AF19E /* libPods-CryptomatorCryptoLibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B55D54DD75319956B4A5F46A /* libPods-CryptomatorCryptoLibTests.a */; }; 4A7C213C2451F2AC00DE81E6 /* CryptomatorCryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptomatorCryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F753248FC89B00B4C26D /* CryptoError.swift */; }; 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */; }; + 74F9352A251F44E0001F4ADA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 74F93529251F44E0001F4ADA /* CryptoSwift */; }; + 74F9352F251F44FE001F4ADA /* SwiftBase32 in Frameworks */ = {isa = PBXBuildFile; productRef = 74F9352E251F44FE001F4ADA /* SwiftBase32 */; }; 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB811245412E900F9FF51 /* Cryptor.swift */; }; 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB8132454708600F9FF51 /* Masterkey.swift */; }; 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */; }; - 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */; }; 9EB822C1248AF82200879838 /* AesCtr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB822C0248AF82200879838 /* AesCtr.swift */; }; 9EB822C3248AF9C500879838 /* AesCtrTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB822C2248AF9C500879838 /* AesCtrTests.swift */; }; - F4D8BE98D2ACCFC2ACEB4B4D /* libPods-CryptomatorCryptoLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9EFD5232B8A20191311193CF /* libPods-CryptomatorCryptoLib.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -35,10 +34,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.debug.xcconfig"; sourceTree = ""; }; - 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLibTests.release.xcconfig"; path = "Target Support Files/Pods-CryptoLibTests/Pods-CryptoLibTests.release.xcconfig"; sourceTree = ""; }; - 2C6DE4F29372585DE89EE034 /* Pods-CryptomatorCryptoLibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLibTests.debug.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLibTests/Pods-CryptomatorCryptoLibTests.debug.xcconfig"; sourceTree = ""; }; - 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.release.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.release.xcconfig"; sourceTree = ""; }; 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CryptomatorCryptoLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptomatorCryptoLib.h; sourceTree = ""; }; 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -46,21 +41,14 @@ 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74F0F753248FC89B00B4C26D /* CryptoError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoError.swift; sourceTree = ""; }; 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSupport.swift; sourceTree = ""; }; - 94D3052EF784FC4C00FF4C29 /* Pods-CryptomatorCryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLib/Pods-CryptomatorCryptoLib.debug.xcconfig"; sourceTree = ""; }; - 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptoLib.debug.xcconfig"; path = "Target Support Files/Pods-CryptoLib/Pods-CryptoLib.debug.xcconfig"; sourceTree = ""; }; 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; 9E44EEA524599C6900A37B01 /* AesSiv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSiv.swift; sourceTree = ""; }; 9E44EEA724599C7800A37B01 /* AesSivTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSivTests.swift; sourceTree = ""; }; 9E9BB811245412E900F9FF51 /* Cryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = ""; }; 9E9BB8132454708600F9FF51 /* Masterkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Masterkey.swift; sourceTree = ""; }; 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyTests.swift; sourceTree = ""; }; - 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libCryptoSwift.a; sourceTree = BUILT_PRODUCTS_DIR; }; 9EB822C0248AF82200879838 /* AesCtr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesCtr.swift; sourceTree = ""; }; 9EB822C2248AF9C500879838 /* AesCtrTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesCtrTests.swift; sourceTree = ""; }; - 9EFD5232B8A20191311193CF /* libPods-CryptomatorCryptoLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptomatorCryptoLib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - B55D54DD75319956B4A5F46A /* libPods-CryptomatorCryptoLibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CryptomatorCryptoLibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - E7931D45710E24EA79F24F37 /* Pods-CryptomatorCryptoLibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLibTests.release.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLibTests/Pods-CryptomatorCryptoLibTests.release.xcconfig"; sourceTree = ""; }; - EAA11A0503C99BA047061991 /* Pods-CryptomatorCryptoLib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CryptomatorCryptoLib.release.xcconfig"; path = "Target Support Files/Pods-CryptomatorCryptoLib/Pods-CryptomatorCryptoLib.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -68,8 +56,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9E9BB818245590C100F9FF51 /* libCryptoSwift.a in Frameworks */, - F4D8BE98D2ACCFC2ACEB4B4D /* libPods-CryptomatorCryptoLib.a in Frameworks */, + 74F9352F251F44FE001F4ADA /* SwiftBase32 in Frameworks */, + 74F9352A251F44E0001F4ADA /* CryptoSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -78,31 +66,18 @@ buildActionMask = 2147483647; files = ( 4A7C213C2451F2AC00DE81E6 /* CryptomatorCryptoLib.framework in Frameworks */, - 0C8F90937EFDC9B9D55AF19E /* libPods-CryptomatorCryptoLibTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 33F96BB57BB41A059EFBA16D /* Frameworks */ = { - isa = PBXGroup; - children = ( - 9E9BB817245590C100F9FF51 /* libCryptoSwift.a */, - 9EFD5232B8A20191311193CF /* libPods-CryptomatorCryptoLib.a */, - B55D54DD75319956B4A5F46A /* libPods-CryptomatorCryptoLibTests.a */, - ); - name = Frameworks; - sourceTree = ""; - }; 4A7C21282451F2AC00DE81E6 = { isa = PBXGroup; children = ( 74FFC966251F3EBB004C4927 /* Sources */, 74FFC967251F3ED0004C4927 /* Tests */, 4A7C21332451F2AC00DE81E6 /* Products */, - D7D76AD3F7F6155DB2723364 /* Pods */, - 33F96BB57BB41A059EFBA16D /* Frameworks */, ); sourceTree = ""; }; @@ -158,21 +133,6 @@ path = Tests; sourceTree = ""; }; - D7D76AD3F7F6155DB2723364 /* Pods */ = { - isa = PBXGroup; - children = ( - 46CE315EF43B1FF833C7A4FB /* Pods-CryptoLib.release.xcconfig */, - 9C0CDC395FD6C0670C07B37C /* Pods-CryptoLib.debug.xcconfig */, - 080BF9098EF20B4D273353B2 /* Pods-CryptoLibTests.debug.xcconfig */, - 187D073CD93DB036796D77DC /* Pods-CryptoLibTests.release.xcconfig */, - 94D3052EF784FC4C00FF4C29 /* Pods-CryptomatorCryptoLib.debug.xcconfig */, - EAA11A0503C99BA047061991 /* Pods-CryptomatorCryptoLib.release.xcconfig */, - 2C6DE4F29372585DE89EE034 /* Pods-CryptomatorCryptoLibTests.debug.xcconfig */, - E7931D45710E24EA79F24F37 /* Pods-CryptomatorCryptoLibTests.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -191,7 +151,6 @@ isa = PBXNativeTarget; buildConfigurationList = 4A7C21462451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptomatorCryptoLib" */; buildPhases = ( - 6DFD694B51EDFB57A524EA41 /* [CP] Check Pods Manifest.lock */, 4A7C212D2451F2AC00DE81E6 /* Headers */, 4A7C212E2451F2AC00DE81E6 /* Sources */, 4A7C212F2451F2AC00DE81E6 /* Frameworks */, @@ -203,6 +162,10 @@ dependencies = ( ); name = CryptomatorCryptoLib; + packageProductDependencies = ( + 74F93529251F44E0001F4ADA /* CryptoSwift */, + 74F9352E251F44FE001F4ADA /* SwiftBase32 */, + ); productName = CryptoLib; productReference = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; productType = "com.apple.product-type.framework"; @@ -211,7 +174,6 @@ isa = PBXNativeTarget; buildConfigurationList = 4A7C21492451F2AD00DE81E6 /* Build configuration list for PBXNativeTarget "CryptomatorCryptoLibTests" */; buildPhases = ( - B0398845BD49B4481C76093F /* [CP] Check Pods Manifest.lock */, 4A7C21372451F2AC00DE81E6 /* Sources */, 4A7C21382451F2AC00DE81E6 /* Frameworks */, 4A7C21392451F2AC00DE81E6 /* Resources */, @@ -254,6 +216,10 @@ Base, ); mainGroup = 4A7C21282451F2AC00DE81E6; + packageReferences = ( + 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */, + 74F9352D251F44FE001F4ADA /* XCRemoteSwiftPackageReference "Base32" */, + ); productRefGroup = 4A7C21332451F2AC00DE81E6 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -282,28 +248,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 6DFD694B51EDFB57A524EA41 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CryptomatorCryptoLib-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 74862A712469A6B2003D81CB /* Lint With SwiftFormat */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -322,28 +266,6 @@ shellPath = /bin/sh; shellScript = "if which swiftformat >/dev/null; then\n swiftformat --lint --lenient .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; }; - B0398845BD49B4481C76093F /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CryptomatorCryptoLibTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -506,7 +428,6 @@ }; 4A7C21472451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 94D3052EF784FC4C00FF4C29 /* Pods-CryptomatorCryptoLib.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; @@ -533,7 +454,6 @@ }; 4A7C21482451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EAA11A0503C99BA047061991 /* Pods-CryptomatorCryptoLib.release.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; @@ -559,7 +479,6 @@ }; 4A7C214A2451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2C6DE4F29372585DE89EE034 /* Pods-CryptomatorCryptoLibTests.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = Tests/CryptomatorCryptoLibTests/Info.plist; @@ -577,7 +496,6 @@ }; 4A7C214B2451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E7931D45710E24EA79F24F37 /* Pods-CryptomatorCryptoLibTests.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = Tests/CryptomatorCryptoLibTests/Info.plist; @@ -624,6 +542,38 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift.git"; + requirement = { + kind = upToNextMinorVersion; + minimumVersion = 1.3.0; + }; + }; + 74F9352D251F44FE001F4ADA /* XCRemoteSwiftPackageReference "Base32" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/cryptomator/Base32.git"; + requirement = { + kind = upToNextMinorVersion; + minimumVersion = 0.8.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 74F93529251F44E0001F4ADA /* CryptoSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */; + productName = CryptoSwift; + }; + 74F9352E251F44FE001F4ADA /* SwiftBase32 */ = { + isa = XCSwiftPackageProductDependency; + package = 74F9352D251F44FE001F4ADA /* XCRemoteSwiftPackageReference "Base32" */; + productName = SwiftBase32; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 4A7C21292451F2AC00DE81E6 /* Project object */; } diff --git a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 72fd35e..919434a 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..0effe0b --- /dev/null +++ b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,25 @@ +{ + "object": { + "pins": [ + { + "package": "SwiftBase32", + "repositoryURL": "https://github.com/cryptomator/Base32.git", + "state": { + "branch": null, + "revision": "aa919f98c6e884945385d76121a4a29ca48afc6f", + "version": "0.8.1" + } + }, + { + "package": "CryptoSwift", + "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", + "state": { + "branch": null, + "revision": "af1b58fc569bfde777462349b9f7314b61762be0", + "version": "1.3.2" + } + } + ] + }, + "version": 1 +} diff --git a/CryptomatorCryptoLib.xcworkspace/contents.xcworkspacedata b/CryptomatorCryptoLib.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 5c14b96..0000000 --- a/CryptomatorCryptoLib.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/CryptomatorCryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CryptomatorCryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/CryptomatorCryptoLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Podfile b/Podfile deleted file mode 100644 index 270315b..0000000 --- a/Podfile +++ /dev/null @@ -1,11 +0,0 @@ -platform :ios, '9.0' -inhibit_all_warnings! - -target 'CryptomatorCryptoLib' do - pod 'CryptoSwift', '~> 1.3.0' - pod 'SwiftBase32', '~> 0.8.0' - - target 'CryptomatorCryptoLibTests' do - inherit! :search_paths - end -end diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index cbe8672..0000000 --- a/Podfile.lock +++ /dev/null @@ -1,20 +0,0 @@ -PODS: - - CryptoSwift (1.3.2) - - SwiftBase32 (0.8.0) - -DEPENDENCIES: - - CryptoSwift (~> 1.3.0) - - SwiftBase32 (~> 0.8.0) - -SPEC REPOS: - trunk: - - CryptoSwift - - SwiftBase32 - -SPEC CHECKSUMS: - CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060 - SwiftBase32: 723fd05e2fce776f2b98f26fe423bd20fa80e411 - -PODFILE CHECKSUM: a970dcd7d46271a2a2b1702e791d45eac89a4cfb - -COCOAPODS: 1.9.3 diff --git a/README.md b/README.md index 14b8354..0f5a450 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,25 @@ For more information on the Cryptomator encryption scheme, visit the security ar ## Requirements -- iOS 8.0 or higher +- iOS 9.0 or higher ## Installation -The easiest way to use CryptoLib Swift in your app is via [CocoaPods](https://cocoapods.org/ "CocoaPods"). +### Swift Package Manager -1. Add the following line in the project's Podfile file: `pod 'CryptomatorCryptoLib', '~> 1.0.0'` -2. Run the command `pod install` from the Podfile folder directory. +You can use [Swift Package Manager](https://swift.org/package-manager/ "Swift Package Manager"). + +```swift +.package(url: "https://github.com/cryptomator/cryptolib-swift.git", .upToNextMinor(from: "1.0.0")) +``` + +### CocoaPods + +You can use [CocoaPods](https://cocoapods.org/ "CocoaPods"). + +```ruby +`pod 'CryptomatorCryptoLib', '~> 1.0.0'` +``` ## Usage From 10eafee9e56c7856d67337421eb7a68f7982422b Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 11:54:50 +0200 Subject: [PATCH 069/144] Forgot to remove Pods integration in build workflow --- .github/workflows/build.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c8d67df..083ca00 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,14 +9,6 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: Pods - key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} - restore-keys: | - ${{ runner.os }}-pods- - - name: Install dependencies - run: pod install - name: Build and test run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'name=iPhone 11 Pro' -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report From c12b6a48751bf7e6d67d12e547745b00a5bce069 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 12:05:19 +0200 Subject: [PATCH 070/144] Regenerated LinuxMain, added macOS deployment target --- CryptomatorCryptoLib.podspec | 1 + Package.swift | 3 +- .../XCTestManifests.swift | 72 +++++++++++++++++++ Tests/LinuxMain.swift | 5 +- 4 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 Tests/CryptomatorCryptoLibTests/XCTestManifests.swift diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 6b70f14..dfc774e 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -14,6 +14,7 @@ Pod::Spec.new do |s| s.public_header_files = 'Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h' s.source_files = 'Sources/CryptomatorCryptoLib/**/*.swift' s.ios.deployment_target = '9.0' + s.osx.deployment_target = '10.13' s.swift_version = '5.0' s.dependency 'CryptoSwift', '~> 1.3.0' diff --git a/Package.swift b/Package.swift index 5935a4b..66c1d1c 100644 --- a/Package.swift +++ b/Package.swift @@ -13,7 +13,8 @@ import PackageDescription let package = Package( name: "CryptomatorCryptoLib", platforms: [ - .iOS(.v9) + .iOS(.v9), + .macOS(.v10_13) ], products: [ .library(name: "CryptomatorCryptoLib", targets: ["CryptomatorCryptoLib"]) diff --git a/Tests/CryptomatorCryptoLibTests/XCTestManifests.swift b/Tests/CryptomatorCryptoLibTests/XCTestManifests.swift new file mode 100644 index 0000000..dad33df --- /dev/null +++ b/Tests/CryptomatorCryptoLibTests/XCTestManifests.swift @@ -0,0 +1,72 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension AesCtrTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__AesCtrTests = [ + ("testDecrypt", testDecrypt), + ("testEncrypt", testEncrypt), + ] +} + +extension AesSivTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__AesSivTests = [ + ("testCmac1", testCmac1), + ("testCmac2", testCmac2), + ("testCmac3", testCmac3), + ("testCmac4", testCmac4), + ("testCtr", testCtr), + ("testDecrypt", testDecrypt), + ("testEncrypt", testEncrypt), + ("testS2v", testS2v), + ] +} + +extension CryptorTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__CryptorTests = [ + ("testCalculateCiphertextSize", testCalculateCiphertextSize), + ("testCalculateCleartextSize", testCalculateCleartextSize), + ("testCalculateCleartextSizeWithInvalidCiphertextSize", testCalculateCleartextSizeWithInvalidCiphertextSize), + ("testCreateHeader", testCreateHeader), + ("testDecryptHeader", testDecryptHeader), + ("testEncryptAndDecryptContent", testEncryptAndDecryptContent), + ("testEncryptAndDecryptName", testEncryptAndDecryptName), + ("testEncryptAndDecryptSingleChunk", testEncryptAndDecryptSingleChunk), + ("testEncryptDirId", testEncryptDirId), + ("testEncryptHeader", testEncryptHeader), + ] +} + +extension MasterkeyTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__MasterkeyTests = [ + ("testCreateFromMasterkeyFile", testCreateFromMasterkeyFile), + ("testCreateFromMasterkeyFileWithInvalidVersionMac", testCreateFromMasterkeyFileWithInvalidVersionMac), + ("testCreateFromMasterkeyFileWithMalformedJson1", testCreateFromMasterkeyFileWithMalformedJson1), + ("testCreateFromMasterkeyFileWithMalformedJson2", testCreateFromMasterkeyFileWithMalformedJson2), + ("testCreateFromMasterkeyFileWithMalformedJson3", testCreateFromMasterkeyFileWithMalformedJson3), + ("testCreateFromMasterkeyFileWithWrongPassword", testCreateFromMasterkeyFileWithWrongPassword), + ("testExportEncrypted", testExportEncrypted), + ("testWrapAndUnwrapKey", testWrapAndUnwrapKey), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(AesCtrTests.__allTests__AesCtrTests), + testCase(AesSivTests.__allTests__AesSivTests), + testCase(CryptorTests.__allTests__CryptorTests), + testCase(MasterkeyTests.__allTests__MasterkeyTests), + ] +} +#endif diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 67d024c..8389565 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,7 +1,8 @@ import XCTest -import TestTests +import CryptomatorCryptoLibTests var tests = [XCTestCaseEntry]() -tests += TestTests.allTests() +tests += CryptomatorCryptoLibTests.__allTests() + XCTMain(tests) From b049396e18163c6270416d8318ef5a7c1ed89502 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 12:21:01 +0200 Subject: [PATCH 071/144] Build on macOS, cleanup --- .github/workflows/build.yml | 2 +- .swiftformat | 2 +- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 2 ++ README.md | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 083ca00..6656993 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build and test - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'name=iPhone 11 Pro' -enableCodeCoverage YES clean test | xcpretty + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'platform=macOS' -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report run: | gem install slather diff --git a/.swiftformat b/.swiftformat index a9971bd..7b4d360 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,6 +1,6 @@ # file options ---exclude Pods +--exclude Tests/LinuxMain.swift,Tests/**/XCTestManifests.swift # format options diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 4c58d96..505e953 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -480,6 +480,7 @@ 4A7C214A2451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = Tests/CryptomatorCryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -497,6 +498,7 @@ 4A7C214B2451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = Tests/CryptomatorCryptoLibTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/README.md b/README.md index 0f5a450..b9e873f 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ For more information on the Cryptomator encryption scheme, visit the security ar ## Requirements - iOS 9.0 or higher +- macOS 10.13 or higher ## Installation From 0fc31385c5d4721eadef2a4ef3757afabfa806c0 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 12:24:30 +0200 Subject: [PATCH 072/144] Added cache for Swift Package Manager in build workflow --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6656993..7b49c02 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,6 +9,12 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: .build + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + restore-keys: | + ${{ runner.os }}-spm- - name: Build and test run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'platform=macOS' -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report From 40227f7625e69b6472ec8fab1053bcfe659e3c80 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 12:58:12 +0200 Subject: [PATCH 073/144] Added .h files to source files in Podspec --- CryptomatorCryptoLib.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index dfc774e..9efcd63 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.social_media_url = 'https://twitter.com/Cryptomator' s.public_header_files = 'Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h' - s.source_files = 'Sources/CryptomatorCryptoLib/**/*.swift' + s.source_files = 'Sources/CryptomatorCryptoLib/**/*.{swift,h}' s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.13' s.swift_version = '5.0' From 6f7128e882791bf192536214cff2baa5d775d235 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 14:30:13 +0200 Subject: [PATCH 074/144] Decreased deployment target to macOS 10.12 --- CryptomatorCryptoLib.podspec | 2 +- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 2 ++ Package.swift | 2 +- Sources/CryptomatorCryptoLib/CryptoSupport.swift | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 9efcd63..67f2aa4 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.public_header_files = 'Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h' s.source_files = 'Sources/CryptomatorCryptoLib/**/*.{swift,h}' s.ios.deployment_target = '9.0' - s.osx.deployment_target = '10.13' + s.osx.deployment_target = '10.12' s.swift_version = '5.0' s.dependency 'CryptoSwift', '~> 1.3.0' diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 505e953..cac77ef 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -357,6 +357,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -415,6 +416,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; diff --git a/Package.swift b/Package.swift index 66c1d1c..48fdab1 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,7 @@ let package = Package( name: "CryptomatorCryptoLib", platforms: [ .iOS(.v9), - .macOS(.v10_13) + .macOS(.v10_12) ], products: [ .library(name: "CryptomatorCryptoLib", targets: ["CryptomatorCryptoLib"]) diff --git a/Sources/CryptomatorCryptoLib/CryptoSupport.swift b/Sources/CryptomatorCryptoLib/CryptoSupport.swift index c468695..441ad02 100644 --- a/Sources/CryptomatorCryptoLib/CryptoSupport.swift +++ b/Sources/CryptomatorCryptoLib/CryptoSupport.swift @@ -36,7 +36,7 @@ class CryptoSupport { */ func compareBytes(expected: [UInt8], actual: [UInt8]) -> Bool { assert(expected.count == actual.count, "parameters should be of same length") - if #available(iOS 10.1, *) { + if #available(iOS 10.1, macOS 10.12.1, *) { return timingsafe_bcmp(expected, actual, expected.count) == 0 } else { var diff: UInt8 = 0 From 6b8485e913d5e14990a429cad5bc1f97aee4520d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 14:31:36 +0200 Subject: [PATCH 075/144] Updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9e873f..a0b6a29 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ For more information on the Cryptomator encryption scheme, visit the security ar ## Requirements - iOS 9.0 or higher -- macOS 10.13 or higher +- macOS 10.12 or higher ## Installation From 391b49993b2ca5bd9a8137367775f7100d3b894e Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 14:33:22 +0200 Subject: [PATCH 076/144] Updated SwiftFormat rules --- .swiftformat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index 7b4d360..8e87d58 100644 --- a/.swiftformat +++ b/.swiftformat @@ -9,7 +9,7 @@ --indent tab --self init-only --stripunusedargs closure-only ---swiftversion 5.1 +--swiftversion 5.0 --tabwidth 4 # rules From d43f0cb3baecdd83e55119508c883612ed52e38a Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 14:41:12 +0200 Subject: [PATCH 077/144] Updated API availability check in CryptoSupport, which should be more precise --- Sources/CryptomatorCryptoLib/CryptoSupport.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CryptomatorCryptoLib/CryptoSupport.swift b/Sources/CryptomatorCryptoLib/CryptoSupport.swift index 441ad02..8cdfe0f 100644 --- a/Sources/CryptomatorCryptoLib/CryptoSupport.swift +++ b/Sources/CryptomatorCryptoLib/CryptoSupport.swift @@ -36,7 +36,7 @@ class CryptoSupport { */ func compareBytes(expected: [UInt8], actual: [UInt8]) -> Bool { assert(expected.count == actual.count, "parameters should be of same length") - if #available(iOS 10.1, macOS 10.12.1, *) { + if #available(iOS 10.1, macCatalyst 13.0, macOS 10.12.1, *) { return timingsafe_bcmp(expected, actual, expected.count) == 0 } else { var diff: UInt8 = 0 From 1b77eb5c17938fe7c37161a5c71129832c440c7a Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sat, 26 Sep 2020 16:27:49 +0200 Subject: [PATCH 078/144] Updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0b6a29..4094e9d 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ You can use [Swift Package Manager](https://swift.org/package-manager/ "Swift Pa You can use [CocoaPods](https://cocoapods.org/ "CocoaPods"). ```ruby -`pod 'CryptomatorCryptoLib', '~> 1.0.0'` +pod 'CryptomatorCryptoLib', '~> 1.0.0' ``` ## Usage From 6843fbdd9f667ee2d80141c8aa67f6d806afaa16 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 28 Sep 2020 14:38:17 +0200 Subject: [PATCH 079/144] Increased Swift version to 5.1 since 5.0 never worked --- .swiftformat | 2 +- CryptomatorCryptoLib.podspec | 2 +- Package.swift | 2 +- README.md | 6 ++++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.swiftformat b/.swiftformat index 8e87d58..7b4d360 100644 --- a/.swiftformat +++ b/.swiftformat @@ -9,7 +9,7 @@ --indent tab --self init-only --stripunusedargs closure-only ---swiftversion 5.0 +--swiftversion 5.1 --tabwidth 4 # rules diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 67f2aa4..79fd79c 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.source_files = 'Sources/CryptomatorCryptoLib/**/*.{swift,h}' s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.12' - s.swift_version = '5.0' + s.swift_version = '5.1' s.dependency 'CryptoSwift', '~> 1.3.0' s.dependency 'SwiftBase32', '~> 0.8.0' diff --git a/Package.swift b/Package.swift index 48fdab1..8572d70 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.1 // // Package.swift diff --git a/README.md b/README.md index 4094e9d..6ba2e9c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ +[![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcryptomator%2Fcryptolib-swift%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/cryptomator/cryptolib-swift) +[![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcryptomator%2Fcryptolib-swift%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/cryptomator/cryptolib-swift) [![Version](http://img.shields.io/cocoapods/v/CryptomatorCryptoLib.svg)](https://cocoapods.org/pods/CryptomatorCryptoLib) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) -[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) +[![Codacy Code Quality](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) +[![Codacy Coverage](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) # CryptoLib Swift From 0c015bf44641bb41a76ab6b5b7ddd7f3d68cdaa8 Mon Sep 17 00:00:00 2001 From: JaniruTEC <52893617+JaniruTEC@users.noreply.github.com> Date: Tue, 27 Oct 2020 16:26:36 +0100 Subject: [PATCH 080/144] Added ability to skip CI --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b49c02..1cd813b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,8 @@ jobs: build: name: Build and test runs-on: macos-latest + #This check is case insensitive + if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')" steps: - uses: actions/checkout@v2 - uses: actions/cache@v2 From 4d3891921cc3dab79ab8e90b747589ee1e216bb6 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 27 Oct 2020 23:09:55 +0100 Subject: [PATCH 081/144] Removed comment --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1cd813b..6ab87fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,6 @@ jobs: build: name: Build and test runs-on: macos-latest - #This check is case insensitive if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')" steps: - uses: actions/checkout@v2 From a53572ae5ccb6974037452105c6a8615ee0632f6 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 27 Oct 2020 23:12:57 +0100 Subject: [PATCH 082/144] scryptCostParam is now configurable when exporting masterkey --- Sources/CryptomatorCryptoLib/Masterkey.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/Masterkey.swift b/Sources/CryptomatorCryptoLib/Masterkey.swift index abbe8b8..648cf29 100644 --- a/Sources/CryptomatorCryptoLib/Masterkey.swift +++ b/Sources/CryptomatorCryptoLib/Masterkey.swift @@ -28,10 +28,10 @@ public enum MasterkeyError: Error, Equatable { } public class Masterkey { - static let latestVersion = 7 + public static let defaultScryptCostParam = 1 << 15 // 2^15 static let defaultScryptSaltSize = 8 - static let defaultScryptCostParam = 1 << 15 // 2^15 static let defaultScryptBlockSize = 8 + static let latestVersion = 7 private(set) var aesMasterKey: [UInt8] private(set) var macMasterKey: [UInt8] @@ -168,14 +168,15 @@ public class Masterkey { - Parameter password: The password used to encrypt the key material. - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. + - Parameter scryptCostParam: The work factor for the key derivation function (scrypt). Defaults to 32768. - Returns: JSON data with encrypted/wrapped masterkey and other metadata that can be stored in insecure locations. */ - public func exportEncrypted(password: String, pepper: [UInt8] = [UInt8]()) throws -> Data { - let masterkeyJson: MasterkeyJson = try exportEncrypted(password: password, pepper: pepper) + public func exportEncrypted(password: String, pepper: [UInt8] = [UInt8](), scryptCostParam: Int = Masterkey.defaultScryptCostParam) throws -> Data { + let masterkeyJson: MasterkeyJson = try exportEncrypted(password: password, pepper: pepper, scryptCostParam: scryptCostParam) return try JSONEncoder().encode(masterkeyJson) } - func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int = Masterkey.defaultScryptCostParam, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { + func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) let salt = try cryptoSupport.createRandomBytes(size: Masterkey.defaultScryptSaltSize) let saltAndPepper = salt + pepper From 60f944979ba9540cfae6b3153b162120f1e728c3 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 29 Oct 2020 21:49:11 +0100 Subject: [PATCH 083/144] Exposed masterkey version in Cryptor --- Sources/CryptomatorCryptoLib/Cryptor.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 46acfed..2de2240 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -66,6 +66,10 @@ public class Cryptor { static let cleartextChunkSize = 32 * 1024 static let ciphertextChunkSize = kCCBlockSizeAES128 + cleartextChunkSize + Int(CC_SHA256_DIGEST_LENGTH) + public var masterkeyVersion: Int { + return masterkey.version + } + private let masterkey: Masterkey private let cryptoSupport: CryptoSupport From 7e6e143dcf1fc928d3d9be7fc5f40261f5ec042e Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 29 Oct 2020 22:18:24 +0100 Subject: [PATCH 084/144] Cleaned up unit tests --- Tests/CryptomatorCryptoLibTests/CryptorTests.swift | 8 ++++---- Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift index adba292..a18627a 100644 --- a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift +++ b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift @@ -159,16 +159,16 @@ class CryptorTests: XCTestCase { func testCalculateCleartextSizeWithInvalidCiphertextSize() throws { XCTAssertThrowsError(try cryptor.calculateCleartextSize(1), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 1"), error as? CryptoError) + XCTAssertEqual(.invalidParameter("Method not defined for input value 1"), error as? CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(48), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 48"), error as? CryptoError) + XCTAssertEqual(.invalidParameter("Method not defined for input value 48"), error as? CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 1 + 48), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32817"), error as? CryptoError) + XCTAssertEqual(.invalidParameter("Method not defined for input value 32817"), error as? CryptoError) } XCTAssertThrowsError(try cryptor.calculateCleartextSize(32 * 1024 + 48 * 2), "invalid ciphertext size") { error in - XCTAssertEqual(CryptoError.invalidParameter("Method not defined for input value 32864"), error as? CryptoError) + XCTAssertEqual(.invalidParameter("Method not defined for input value 32864"), error as? CryptoError) } } } diff --git a/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift b/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift index c13c791..c1d1c73 100644 --- a/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift +++ b/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift @@ -54,7 +54,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.invalidPassword, error as? MasterkeyError) + XCTAssertEqual(.invalidPassword, error as? MasterkeyError) } } @@ -72,7 +72,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac"), error as? MasterkeyError) + XCTAssertEqual(.malformedMasterkeyFile("incorrect version or versionMac"), error as? MasterkeyError) } } @@ -90,7 +90,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as? MasterkeyError) + XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as? MasterkeyError) } } @@ -108,7 +108,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as? MasterkeyError) + XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as? MasterkeyError) } } @@ -126,7 +126,7 @@ class MasterkeyTests: XCTestCase { """.data(using: .utf8)! XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac"), error as? MasterkeyError) + XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in versionMac"), error as? MasterkeyError) } } From b8a7fe75031b2696aa6f02dc6a5f78e2e0a110c7 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 29 Oct 2020 22:21:25 +0100 Subject: [PATCH 085/144] Performed upgrade check with Xcode 12.1 --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index cac77ef..22bf3a4 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -195,7 +195,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1200; + LastUpgradeCheck = 1210; ORGANIZATIONNAME = "Skymatic GmbH"; TargetAttributes = { 4A7C21312451F2AC00DE81E6 = { diff --git a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme index a63ecf5..720d3d8 100644 --- a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme +++ b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme @@ -1,6 +1,6 @@ Date: Fri, 30 Oct 2020 15:00:39 +0100 Subject: [PATCH 086/144] Switched back to the original SwiftBase32 repo as dependency --- Package.resolved | 8 ++++---- Package.swift | 4 ++-- Sources/CryptomatorCryptoLib/Cryptor.swift | 4 ++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Package.resolved b/Package.resolved index 0effe0b..130d7e6 100644 --- a/Package.resolved +++ b/Package.resolved @@ -2,12 +2,12 @@ "object": { "pins": [ { - "package": "SwiftBase32", - "repositoryURL": "https://github.com/cryptomator/Base32.git", + "package": "Base32", + "repositoryURL": "https://github.com/norio-nomura/Base32.git", "state": { "branch": null, - "revision": "aa919f98c6e884945385d76121a4a29ca48afc6f", - "version": "0.8.1" + "revision": "69fc95dd2e4229a2fd82e14d9358c23b0fd8cf74", + "version": "0.8.0" } }, { diff --git a/Package.swift b/Package.swift index 8572d70..533b931 100644 --- a/Package.swift +++ b/Package.swift @@ -21,10 +21,10 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.3.0")), - .package(url: "https://github.com/cryptomator/Base32.git", .upToNextMinor(from: "0.8.0")) + .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")) ], targets: [ - .target(name: "CryptomatorCryptoLib", dependencies: ["CryptoSwift", "SwiftBase32"]), + .target(name: "CryptomatorCryptoLib", dependencies: ["CryptoSwift", "Base32"]), .testTarget(name: "CryptomatorCryptoLibTests", dependencies: ["CryptomatorCryptoLib"]) ], swiftLanguageVersions: [.v5] diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 2de2240..76dab1d 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -9,7 +9,11 @@ import CommonCrypto import CryptoSwift import Foundation +#if SWIFT_PACKAGE +import Base32 +#else import SwiftBase32 +#endif public extension Data { init?(base64UrlEncoded base64String: String, options: Data.Base64DecodingOptions = []) { From 609100b74e8e0b5c2017085ade1b4d97443c6f66 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 6 Nov 2020 16:30:55 +0100 Subject: [PATCH 087/144] Fixed Package.resolved in some weird project file/workspace --- .../xcshareddata/swiftpm/Package.resolved | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0effe0b..856b0b7 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -2,12 +2,12 @@ "object": { "pins": [ { - "package": "SwiftBase32", + "package": "Base32", "repositoryURL": "https://github.com/cryptomator/Base32.git", "state": { "branch": null, - "revision": "aa919f98c6e884945385d76121a4a29ca48afc6f", - "version": "0.8.1" + "revision": "69fc95dd2e4229a2fd82e14d9358c23b0fd8cf74", + "version": "0.8.0" } }, { From 0cee41cbc2040648bc8e770d7a44c129c682c094 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 6 Nov 2020 16:40:15 +0100 Subject: [PATCH 088/144] Fixed dependencies --- .../project.pbxproj | 33 ++++++++++--------- .../xcshareddata/swiftpm/Package.resolved | 2 +- Package.swift | 6 ++-- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 22bf3a4..45d0fc5 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -9,10 +9,10 @@ /* Begin PBXBuildFile section */ 4A7C213C2451F2AC00DE81E6 /* CryptomatorCryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptomatorCryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 742023E72555A4A200822899 /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 742023E62555A4A200822899 /* Base32 */; }; 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F753248FC89B00B4C26D /* CryptoError.swift */; }; 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */; }; 74F9352A251F44E0001F4ADA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 74F93529251F44E0001F4ADA /* CryptoSwift */; }; - 74F9352F251F44FE001F4ADA /* SwiftBase32 in Frameworks */ = {isa = PBXBuildFile; productRef = 74F9352E251F44FE001F4ADA /* SwiftBase32 */; }; 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; @@ -56,7 +56,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 74F9352F251F44FE001F4ADA /* SwiftBase32 in Frameworks */, + 742023E72555A4A200822899 /* Base32 in Frameworks */, 74F9352A251F44E0001F4ADA /* CryptoSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -164,7 +164,7 @@ name = CryptomatorCryptoLib; packageProductDependencies = ( 74F93529251F44E0001F4ADA /* CryptoSwift */, - 74F9352E251F44FE001F4ADA /* SwiftBase32 */, + 742023E62555A4A200822899 /* Base32 */, ); productName = CryptoLib; productReference = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; @@ -218,7 +218,7 @@ mainGroup = 4A7C21282451F2AC00DE81E6; packageReferences = ( 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */, - 74F9352D251F44FE001F4ADA /* XCRemoteSwiftPackageReference "Base32" */, + 742023E52555A4A200822899 /* XCRemoteSwiftPackageReference "Base32" */, ); productRefGroup = 4A7C21332451F2AC00DE81E6 /* Products */; projectDirPath = ""; @@ -347,8 +347,8 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( + "SWIFT_PACKAGE=1", "DEBUG=1", - "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -409,6 +409,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "SWIFT_PACKAGE=1"; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -548,35 +549,35 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */ = { + 742023E52555A4A200822899 /* XCRemoteSwiftPackageReference "Base32" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift.git"; + repositoryURL = "https://github.com/norio-nomura/Base32.git"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 1.3.0; + minimumVersion = 0.8.0; }; }; - 74F9352D251F44FE001F4ADA /* XCRemoteSwiftPackageReference "Base32" */ = { + 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/cryptomator/Base32.git"; + repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift.git"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 0.8.0; + minimumVersion = 1.3.0; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 742023E62555A4A200822899 /* Base32 */ = { + isa = XCSwiftPackageProductDependency; + package = 742023E52555A4A200822899 /* XCRemoteSwiftPackageReference "Base32" */; + productName = Base32; + }; 74F93529251F44E0001F4ADA /* CryptoSwift */ = { isa = XCSwiftPackageProductDependency; package = 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */; productName = CryptoSwift; }; - 74F9352E251F44FE001F4ADA /* SwiftBase32 */ = { - isa = XCSwiftPackageProductDependency; - package = 74F9352D251F44FE001F4ADA /* XCRemoteSwiftPackageReference "Base32" */; - productName = SwiftBase32; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 4A7C21292451F2AC00DE81E6 /* Project object */; diff --git a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 856b0b7..130d7e6 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -3,7 +3,7 @@ "pins": [ { "package": "Base32", - "repositoryURL": "https://github.com/cryptomator/Base32.git", + "repositoryURL": "https://github.com/norio-nomura/Base32.git", "state": { "branch": null, "revision": "69fc95dd2e4229a2fd82e14d9358c23b0fd8cf74", diff --git a/Package.swift b/Package.swift index 533b931..1a113af 100644 --- a/Package.swift +++ b/Package.swift @@ -20,11 +20,11 @@ let package = Package( .library(name: "CryptomatorCryptoLib", targets: ["CryptomatorCryptoLib"]) ], dependencies: [ - .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.3.0")), - .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")) + .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")), + .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.3.0")) ], targets: [ - .target(name: "CryptomatorCryptoLib", dependencies: ["CryptoSwift", "Base32"]), + .target(name: "CryptomatorCryptoLib", dependencies: ["Base32", "CryptoSwift"]), .testTarget(name: "CryptomatorCryptoLibTests", dependencies: ["CryptomatorCryptoLib"]) ], swiftLanguageVersions: [.v5] From 7fd5dc7128828456cf785feea152f66226afea39 Mon Sep 17 00:00:00 2001 From: Philipp Schmid Date: Fri, 6 Nov 2020 16:56:56 +0100 Subject: [PATCH 089/144] Fixed SWIFT_PACKAGE flag --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 8 ++++++-- Sources/CryptomatorCryptoLib/Cryptor.swift | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 45d0fc5..00425df 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -362,7 +362,7 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -409,7 +409,10 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = "SWIFT_PACKAGE=1"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "SWIFT_PACKAGE=1", + "DEBUG=1", + ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -421,6 +424,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 76dab1d..87474f2 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -10,9 +10,9 @@ import CommonCrypto import CryptoSwift import Foundation #if SWIFT_PACKAGE -import Base32 + import Base32 #else -import SwiftBase32 + import SwiftBase32 #endif public extension Data { From dbbec0161b4651df6ab2e528e5c618610fa659fe Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 16 Nov 2020 15:28:36 +0100 Subject: [PATCH 090/144] Cryptor encrypt/decryptContent fails if source does not exist --- Sources/CryptomatorCryptoLib/Cryptor.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 87474f2..44f68ff 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -216,8 +216,8 @@ public class Cryptor { defer { ciphertextStream.close() } // determine cleartext size: - let attributes = try? FileManager.default.attributesOfItem(atPath: cleartextURL.path) - let cleartextSize = attributes?[FileAttributeKey.size] as? Int + let attributes = try FileManager.default.attributesOfItem(atPath: cleartextURL.path) + let cleartextSize = attributes[FileAttributeKey.size] as? Int // encrypt: try encryptContent(from: cleartextStream, to: ciphertextStream, cleartextSize: cleartextSize) @@ -277,8 +277,8 @@ public class Cryptor { defer { cleartextStream.close() } // determine ciphertext size: - let attributes = try? FileManager.default.attributesOfItem(atPath: ciphertextURL.path) - let ciphertextSize = attributes?[FileAttributeKey.size] as? Int + let attributes = try FileManager.default.attributesOfItem(atPath: ciphertextURL.path) + let ciphertextSize = attributes[FileAttributeKey.size] as? Int // decrypt: try decryptContent(from: ciphertextStream, to: cleartextStream, ciphertextSize: ciphertextSize) From 8b3ea19b5fc1fe77e6f5c5faf4c670ffdb117666 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 16 Nov 2020 15:29:06 +0100 Subject: [PATCH 091/144] Performed upgrade check with Xcode 12.2 --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 00425df..c024960 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -195,7 +195,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1210; + LastUpgradeCheck = 1220; ORGANIZATIONNAME = "Skymatic GmbH"; TargetAttributes = { 4A7C21312451F2AC00DE81E6 = { diff --git a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme index 720d3d8..7acc718 100644 --- a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme +++ b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme @@ -1,6 +1,6 @@ Date: Tue, 15 Dec 2020 09:26:38 +0100 Subject: [PATCH 092/144] Trying out some flags to improve build performance --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index c024960..23beff6 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -361,9 +361,11 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=100 -Xfrontend -warn-long-function-bodies=100"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -423,10 +425,12 @@ MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=100 -Xfrontend -warn-long-function-bodies=100"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_WHOLE_MODULE_OPTIMIZATION = NO; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; From 01717b6d0c94b2fc29a1da84d2eab57ac957f61d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 15 Dec 2020 11:44:32 +0100 Subject: [PATCH 093/144] Updated SwiftFormat rules --- .swiftformat | 1 + Sources/CryptomatorCryptoLib/Cryptor.swift | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.swiftformat b/.swiftformat index 7b4d360..2a76127 100644 --- a/.swiftformat +++ b/.swiftformat @@ -5,6 +5,7 @@ # format options --commas inline +--ifdef no-indent --importgrouping testable-bottom --indent tab --self init-only diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 44f68ff..1587702 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -10,9 +10,9 @@ import CommonCrypto import CryptoSwift import Foundation #if SWIFT_PACKAGE - import Base32 +import Base32 #else - import SwiftBase32 +import SwiftBase32 #endif public extension Data { From bbd0437e29fddff9160eca4f22a4d3f16afaa83e Mon Sep 17 00:00:00 2001 From: Philipp Schmid Date: Wed, 23 Dec 2020 16:17:26 +0100 Subject: [PATCH 094/144] Visibility of fileHeaderSize in Cryptor changed to public --- Sources/CryptomatorCryptoLib/Cryptor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 1587702..35f7cc5 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -66,7 +66,7 @@ struct FileHeader { public class Cryptor { static let fileHeaderLegacyPayloadSize = 8 - static let fileHeaderSize = kCCBlockSizeAES128 + fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + Int(CC_SHA256_DIGEST_LENGTH) + public static let fileHeaderSize = kCCBlockSizeAES128 + fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + Int(CC_SHA256_DIGEST_LENGTH) static let cleartextChunkSize = 32 * 1024 static let ciphertextChunkSize = kCCBlockSizeAES128 + cleartextChunkSize + Int(CC_SHA256_DIGEST_LENGTH) From 02d28a0d6b666462b9a32a93f11ca357bc2ffb4d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 5 Jan 2021 10:52:57 +0100 Subject: [PATCH 095/144] Removed social_media_url in Podspec to fix `pod lib lint`, see https://github.com/CocoaPods/CocoaPods/issues/10291 --- CryptomatorCryptoLib.podspec | 1 - 1 file changed, 1 deletion(-) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 79fd79c..211726e 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -9,7 +9,6 @@ Pod::Spec.new do |s| 'Sebastian Stenzel' => 'sebastian.stenzel@skymatic.de', 'Tobias Hagemann' => 'tobias.hagemann@skymatic.de' } s.source = { :git => 'https://github.com/cryptomator/cryptolib-swift.git', :tag => s.version.to_s } - s.social_media_url = 'https://twitter.com/Cryptomator' s.public_header_files = 'Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h' s.source_files = 'Sources/CryptomatorCryptoLib/**/*.{swift,h}' From 7b570f27cc18aa910c86b15ce20745a01a37a4cc Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 8 Jan 2021 15:08:36 +0100 Subject: [PATCH 096/144] Added new Masterkey-specific APIs --- .../project.pbxproj | 12 ++ Sources/CryptomatorCryptoLib/Cryptor.swift | 4 - Sources/CryptomatorCryptoLib/Masterkey.swift | 164 +------------- .../CryptomatorCryptoLib/MasterkeyFile.swift | 202 ++++++++++++++++++ .../CryptoSupportMock.swift | 16 ++ .../CryptorTests.swift | 8 +- .../MasterkeyFileTests.swift | 202 ++++++++++++++++++ .../MasterkeyTests.swift | 146 +------------ 8 files changed, 448 insertions(+), 306 deletions(-) create mode 100644 Sources/CryptomatorCryptoLib/MasterkeyFile.swift create mode 100644 Tests/CryptomatorCryptoLibTests/CryptoSupportMock.swift create mode 100644 Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 23beff6..e069c30 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 4A7C213C2451F2AC00DE81E6 /* CryptomatorCryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptomatorCryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; 742023E72555A4A200822899 /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 742023E62555A4A200822899 /* Base32 */; }; + 74A5B57625A869DD002D10F7 /* MasterkeyFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A5B57525A869DD002D10F7 /* MasterkeyFileTests.swift */; }; + 74A5B57E25A86A69002D10F7 /* CryptoSupportMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A5B57D25A86A69002D10F7 /* CryptoSupportMock.swift */; }; + 74B4D38D2588CD60006C0567 /* MasterkeyFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4D38C2588CD60006C0567 /* MasterkeyFile.swift */; }; 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F753248FC89B00B4C26D /* CryptoError.swift */; }; 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */; }; 74F9352A251F44E0001F4ADA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 74F93529251F44E0001F4ADA /* CryptoSwift */; }; @@ -39,6 +42,9 @@ 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4A7C213B2451F2AC00DE81E6 /* CryptomatorCryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptomatorCryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 74A5B57525A869DD002D10F7 /* MasterkeyFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyFileTests.swift; sourceTree = ""; }; + 74A5B57D25A86A69002D10F7 /* CryptoSupportMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSupportMock.swift; sourceTree = ""; }; + 74B4D38C2588CD60006C0567 /* MasterkeyFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyFile.swift; sourceTree = ""; }; 74F0F753248FC89B00B4C26D /* CryptoError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoError.swift; sourceTree = ""; }; 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSupport.swift; sourceTree = ""; }; 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; @@ -101,6 +107,7 @@ 9E9BB811245412E900F9FF51 /* Cryptor.swift */, 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, + 74B4D38C2588CD60006C0567 /* MasterkeyFile.swift */, ); path = CryptomatorCryptoLib; sourceTree = ""; @@ -112,6 +119,8 @@ 9EB822C2248AF9C500879838 /* AesCtrTests.swift */, 9E44EEA724599C7800A37B01 /* AesSivTests.swift */, 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */, + 74A5B57D25A86A69002D10F7 /* CryptoSupportMock.swift */, + 74A5B57525A869DD002D10F7 /* MasterkeyFileTests.swift */, 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */, ); path = CryptomatorCryptoLibTests; @@ -279,6 +288,7 @@ 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */, 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */, 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */, + 74B4D38D2588CD60006C0567 /* MasterkeyFile.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -286,8 +296,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 74A5B57E25A86A69002D10F7 /* CryptoSupportMock.swift in Sources */, 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */, 9EB822C3248AF9C500879838 /* AesCtrTests.swift in Sources */, + 74A5B57625A869DD002D10F7 /* MasterkeyFileTests.swift in Sources */, 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */, 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */, ); diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 35f7cc5..cb9645e 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -70,10 +70,6 @@ public class Cryptor { static let cleartextChunkSize = 32 * 1024 static let ciphertextChunkSize = kCCBlockSizeAES128 + cleartextChunkSize + Int(CC_SHA256_DIGEST_LENGTH) - public var masterkeyVersion: Int { - return masterkey.version - } - private let masterkey: Masterkey private let cryptoSupport: CryptoSupport diff --git a/Sources/CryptomatorCryptoLib/Masterkey.swift b/Sources/CryptomatorCryptoLib/Masterkey.swift index 648cf29..af5cbf5 100644 --- a/Sources/CryptomatorCryptoLib/Masterkey.swift +++ b/Sources/CryptomatorCryptoLib/Masterkey.swift @@ -7,40 +7,15 @@ // import CommonCrypto -import CryptoSwift import Foundation -struct MasterkeyJson: Codable { - let scryptSalt: String - let scryptCostParam: Int - let scryptBlockSize: Int - let primaryMasterKey: String - let hmacMasterKey: String - let versionMac: String - let version: Int -} - -public enum MasterkeyError: Error, Equatable { - case malformedMasterkeyFile(_ reason: String) - case invalidPassword - case unwrapFailed(_ status: CCCryptorStatus) - case wrapFailed(_ status: CCCryptorStatus) -} - public class Masterkey { - public static let defaultScryptCostParam = 1 << 15 // 2^15 - static let defaultScryptSaltSize = 8 - static let defaultScryptBlockSize = 8 - static let latestVersion = 7 - private(set) var aesMasterKey: [UInt8] private(set) var macMasterKey: [UInt8] - public let version: Int - private init(aesMasterKey: [UInt8], macMasterKey: [UInt8], version: Int) { + private init(aesMasterKey: [UInt8], macMasterKey: [UInt8]) { self.aesMasterKey = aesMasterKey self.macMasterKey = macMasterKey - self.version = version } deinit { @@ -57,146 +32,25 @@ public class Masterkey { /** Creates new masterkey. - - Returns: New masterkey instance with secure random bytes. Version will be set to the latest version (currently 7). + - Returns: New masterkey instance with secure random bytes. */ public static func createNew() throws -> Masterkey { let cryptoSupport = CryptoSupport() let aesMasterKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) let macMasterKey = try cryptoSupport.createRandomBytes(size: kCCKeySizeAES256) - return createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: latestVersion) + return createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) } /** - Creates masterkey from masterkey file. + Creates masterkey from raw bytes. - - Parameter fileURL: The URL to the masterkey file that is formatted in JSON. - - Parameter password: The password to use for decrypting the masterkey file. - - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. - - Returns: New masterkey instance using the keys from the supplied `fileURL`. + - Parameter aesMasterKey: Key used for encryption of file specific keys. + - Parameter macMasterKey: Key used for file authentication. + - Returns: New masterkey instance using the keys from the supplied raw bytes. */ - public static func createFromMasterkeyFile(fileURL: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { - let jsonData = try Data(contentsOf: fileURL) - return try createFromMasterkeyFile(jsonData: jsonData, password: password, pepper: pepper) - } - - /** - Creates masterkey from masterkey JSON data. - - - Parameter jsonData: The JSON data of the masterkey file. - - Parameter password: The password to use for decrypting the masterkey file. - - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. - - Returns: New masterkey instance using the keys from the supplied `jsonData`. - */ - public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { - let decoded = try JSONDecoder().decode(MasterkeyJson.self, from: jsonData) - return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper) - } - - private static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey { - let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) - let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!) - let saltAndPepper = salt + pepper - let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() - - guard let wrappedMasterKey = Data(base64Encoded: jsonData.primaryMasterKey) else { - throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey") - } - let aesKey = try unwrapMasterKey(wrappedKey: wrappedMasterKey.bytes, kek: kek) - - guard let wrappedHmacKey = Data(base64Encoded: jsonData.hmacMasterKey) else { - throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey") - } - let macKey = try unwrapMasterKey(wrappedKey: wrappedHmacKey.bytes, kek: kek) - - // time-constant version MAC check: - guard let storedVersionMac = Data(base64Encoded: jsonData.versionMac), storedVersionMac.count == CC_SHA256_DIGEST_LENGTH else { - throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in versionMac") - } - var calculatedVersionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - let versionBytes = withUnsafeBytes(of: UInt32(jsonData.version).bigEndian, Array.init) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macKey, macKey.count, versionBytes, versionBytes.count, &calculatedVersionMac) - var diff: UInt8 = 0x00 - for i in 0 ..< calculatedVersionMac.count { - diff |= calculatedVersionMac[i] ^ storedVersionMac[i] - } - if diff != 0x00 { - throw MasterkeyError.malformedMasterkeyFile("incorrect version or versionMac") - } - - return createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey, version: jsonData.version) - } - - static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8], version: Int) -> Masterkey { + public static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8]) -> Masterkey { assert(aesMasterKey.count == kCCKeySizeAES256) assert(macMasterKey.count == kCCKeySizeAES256) - return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey, version: version) - } - - // MARK: - RFC 3394 Key Wrapping - - static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { - assert(kek.count == kCCKeySizeAES256) - var wrappedKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) - var wrappedKey = [UInt8](repeating: 0x00, count: wrappedKeyLen) - let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrappedKey, &wrappedKeyLen) - if status == kCCSuccess { - return wrappedKey - } else { - throw MasterkeyError.wrapFailed(status) - } - } - - static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { - assert(kek.count == kCCKeySizeAES256) - var unwrappedKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) - var unwrappedKey = [UInt8](repeating: 0x00, count: unwrappedKeyLen) - let status = CCSymmetricKeyUnwrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, wrappedKey, wrappedKey.count, &unwrappedKey, &unwrappedKeyLen) - if status == kCCSuccess { - assert(unwrappedKeyLen == kCCKeySizeAES256) - return unwrappedKey - } else if status == kCCDecodeError { - throw MasterkeyError.invalidPassword - } else { - throw MasterkeyError.unwrapFailed(status) - } - } - - // MARK: - Export - - /** - Exports encrypted/wrapped masterkey and other metadata as JSON data. - - - Parameter password: The password used to encrypt the key material. - - Parameter pepper: An application-specific pepper added to the salt during key derivation. Defaults to empty byte array. - - Parameter scryptCostParam: The work factor for the key derivation function (scrypt). Defaults to 32768. - - Returns: JSON data with encrypted/wrapped masterkey and other metadata that can be stored in insecure locations. - */ - public func exportEncrypted(password: String, pepper: [UInt8] = [UInt8](), scryptCostParam: Int = Masterkey.defaultScryptCostParam) throws -> Data { - let masterkeyJson: MasterkeyJson = try exportEncrypted(password: password, pepper: pepper, scryptCostParam: scryptCostParam) - return try JSONEncoder().encode(masterkeyJson) - } - - func exportEncrypted(password: String, pepper: [UInt8], scryptCostParam: Int, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> MasterkeyJson { - let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8) - let salt = try cryptoSupport.createRandomBytes(size: Masterkey.defaultScryptSaltSize) - let saltAndPepper = salt + pepper - let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: scryptCostParam, r: Masterkey.defaultScryptBlockSize, p: 1).calculate() - - let wrappedMasterKey = try Masterkey.wrapMasterKey(rawKey: aesMasterKey, kek: kek) - let wrappedHmacKey = try Masterkey.wrapMasterKey(rawKey: macMasterKey, kek: kek) - - var versionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - let versionBytes = withUnsafeBytes(of: UInt32(version).bigEndian, Array.init) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macMasterKey, macMasterKey.count, versionBytes, versionBytes.count, &versionMac) - - return MasterkeyJson( - scryptSalt: Data(salt).base64EncodedString(), - scryptCostParam: scryptCostParam, - scryptBlockSize: Masterkey.defaultScryptBlockSize, - primaryMasterKey: Data(wrappedMasterKey).base64EncodedString(), - hmacMasterKey: Data(wrappedHmacKey).base64EncodedString(), - versionMac: Data(versionMac).base64EncodedString(), - version: version - ) + return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) } } diff --git a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift new file mode 100644 index 0000000..9caa4e5 --- /dev/null +++ b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift @@ -0,0 +1,202 @@ +// +// MasterkeyFile.swift +// CryptomatorCryptoLib +// +// Created by Tobias Hagemann on 15.12.20. +// Copyright © 2020 Skymatic GmbH. All rights reserved. +// + +import CommonCrypto +import CryptoSwift +import Foundation + +struct Content: Codable, Equatable { + let version: Int + let scryptSalt: String + let scryptCostParam: Int + let scryptBlockSize: Int + let primaryMasterKey: String + let hmacMasterKey: String + let versionMac: String +} + +public enum MasterkeyFileError: Error, Equatable { + case malformedMasterkeyFile(_ reason: String) + case invalidPassword + case keyWrappingFailed(_ status: CCCryptorStatus) +} + +public class MasterkeyFile { + public static let defaultScryptCostParam = 1 << 15 // 2^15 + static let defaultScryptSaltSize = 8 + static let defaultScryptBlockSize = 8 + + let content: Content + public var version: Int { + return content.version + } + + init(content: Content) { + self.content = content + } + + // MARK: - Factory + + /** + Creates masterkey file with content provided from URL. + + - Parameter url: The URL to the masterkey file that is formatted in JSON. + - Returns: New masterkey instance using the keys from the supplied `url`. + */ + public static func withContentFromURL(url: URL) throws -> MasterkeyFile { + let data = try Data(contentsOf: url) + return try withContentFromData(data: data) + } + + /** + Creates masterkey file with content provided from JSON data. + + - Parameter data: The JSON representation of the masterkey file. + - Returns: New masterkey instance using the keys from the supplied `data`. + */ + public static func withContentFromData(data: Data) throws -> MasterkeyFile { + let decoded = try JSONDecoder().decode(Content.self, from: data) + return MasterkeyFile(content: decoded) + } + + // MARK: - Actions + + /** + Derives a KEK from the given passphrase and the params from this masterkey file using scrypt and unwraps the stored encryption and MAC keys. + + - Parameter passphrase: The passphrase used during key derivation. + - Parameter pepper: An optional application-specific pepper added to the scrypt's salt. Defaults to empty byte array. + - Parameter expectedVaultVersion: An optional expected vault version. + - Returns: A masterkey with the unwrapped keys. + */ + public func unlock(passphrase: String, pepper: [UInt8] = [UInt8](), expectedVaultVersion: Int? = nil) throws -> Masterkey { + // derive keys: + let pw = [UInt8](passphrase.precomposedStringWithCanonicalMapping.utf8) + let salt = [UInt8](Data(base64Encoded: content.scryptSalt)!) + let kek = try Scrypt(password: pw, salt: salt + pepper, dkLen: kCCKeySizeAES256, N: content.scryptCostParam, r: content.scryptBlockSize, p: 1).calculate() + guard let wrappedMasterKey = Data(base64Encoded: content.primaryMasterKey) else { + throw MasterkeyFileError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey") + } + let aesKey = try MasterkeyFile.unwrapKey(wrappedMasterKey.bytes, kek: kek) + guard let wrappedHmacKey = Data(base64Encoded: content.hmacMasterKey) else { + throw MasterkeyFileError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey") + } + let macKey = try MasterkeyFile.unwrapKey(wrappedHmacKey.bytes, kek: kek) + + // check MAC: + if let expectedVaultVersion = expectedVaultVersion { + try checkVaultVersion(expectedVaultVersion: expectedVaultVersion, macKey: macKey) + } + + // construct key: + return Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) + } + + private func checkVaultVersion(expectedVaultVersion: Int, macKey: [UInt8]) throws { + guard let storedVersionMac = Data(base64Encoded: content.versionMac), storedVersionMac.count == CC_SHA256_DIGEST_LENGTH else { + throw MasterkeyFileError.malformedMasterkeyFile("invalid base64 data in versionMac") + } + var calculatedVersionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + let versionBytes = withUnsafeBytes(of: UInt32(version).bigEndian, Array.init) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macKey, macKey.count, versionBytes, versionBytes.count, &calculatedVersionMac) + var diff: UInt8 = 0x00 + for i in 0 ..< calculatedVersionMac.count { + diff |= calculatedVersionMac[i] ^ storedVersionMac[i] + } + if diff != 0x00 { + throw MasterkeyFileError.malformedMasterkeyFile("incorrect version or versionMac") + } + } + + /** + Derives a KEK from the given passphrase and wraps the key material from `masterkey`. + Then serializes the encrypted keys as well as used key derivation parameters into a JSON representation that can be stored into a masterkey file. + + - Parameter masterkey: The key to protect. + - Parameter vaultVersion: The vault version that should be stored in this masterkey file (for downwards compatibility). + - Parameter passphrase: The passphrase used during key derivation. + - Parameter pepper: An optional application-specific pepper added to the scrypt's salt. Defaults to empty byte array. + - Parameter scryptCostParam: The work factor for the key derivation function (scrypt). Defaults to 32768. + - Returns: A JSON representation of the encrypted masterkey with its key derivation parameters. + */ + public static func lock(masterkey: Masterkey, vaultVersion: Int, passphrase: String, pepper: [UInt8] = [UInt8](), scryptCostParam: Int = defaultScryptCostParam) throws -> Data { + let content: Content = try lock(masterkey: masterkey, vaultVersion: vaultVersion, passphrase: passphrase, pepper: pepper, scryptCostParam: scryptCostParam) + return try JSONEncoder().encode(content) + } + + static func lock(masterkey: Masterkey, vaultVersion: Int, passphrase: String, pepper: [UInt8], scryptCostParam: Int, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> Content { + let pw = [UInt8](passphrase.precomposedStringWithCanonicalMapping.utf8) + let salt = try cryptoSupport.createRandomBytes(size: defaultScryptSaltSize) + let kek = try Scrypt(password: pw, salt: salt + pepper, dkLen: kCCKeySizeAES256, N: scryptCostParam, r: defaultScryptBlockSize, p: 1).calculate() + + let wrappedMasterKey = try wrapKey(masterkey.aesMasterKey, kek: kek) + let wrappedHmacKey = try wrapKey(masterkey.macMasterKey, kek: kek) + + var versionMac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) + let versionBytes = withUnsafeBytes(of: UInt32(vaultVersion).bigEndian, Array.init) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, versionBytes, versionBytes.count, &versionMac) + + return Content( + version: vaultVersion, + scryptSalt: Data(salt).base64EncodedString(), + scryptCostParam: scryptCostParam, + scryptBlockSize: defaultScryptBlockSize, + primaryMasterKey: Data(wrappedMasterKey).base64EncodedString(), + hmacMasterKey: Data(wrappedHmacKey).base64EncodedString(), + versionMac: Data(versionMac).base64EncodedString() + ) + } + + /** + Re-encrypts a masterkey with a new passphrase. + + - Parameter masterkeyFileData: The original JSON representation of the masterkey. + - Parameter oldPassphrase: The old passphrase. + - Parameter newPassphrase: The new passphrase + - Parameter pepper: An optional application-specific pepper added to the scrypt's salt. Defaults to empty byte array. + - Parameter scryptCostParam: The work factor for the key derivation function (scrypt). Defaults to 32768. + - Returns: A JSON representation of the masterkey, now encrypted with `newPassphrase`. + */ + public static func changePassphrase(masterkeyFileData: Data, oldPassphrase: String, newPassphrase: String, pepper: [UInt8] = [UInt8](), scryptCostParam: Int = defaultScryptCostParam) throws -> Data { + let content: Content = try changePassphrase(masterkeyFileData: masterkeyFileData, oldPassphrase: oldPassphrase, newPassphrase: newPassphrase, pepper: pepper, scryptCostParam: scryptCostParam) + return try JSONEncoder().encode(content) + } + + static func changePassphrase(masterkeyFileData: Data, oldPassphrase: String, newPassphrase: String, pepper: [UInt8], scryptCostParam: Int, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> Content { + let masterkeyFile = try MasterkeyFile.withContentFromData(data: masterkeyFileData) + let masterkey = try masterkeyFile.unlock(passphrase: oldPassphrase, pepper: pepper) + return try MasterkeyFile.lock(masterkey: masterkey, vaultVersion: masterkeyFile.version, passphrase: newPassphrase, pepper: pepper, scryptCostParam: scryptCostParam, cryptoSupport: cryptoSupport) + } + + // MARK: - RFC 3394 Key Wrapping + + static func wrapKey(_ rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { + var wrappedKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count) + var wrappedKey = [UInt8](repeating: 0x00, count: wrappedKeyLen) + let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrappedKey, &wrappedKeyLen) + if status == kCCSuccess { + return wrappedKey + } else { + throw MasterkeyFileError.keyWrappingFailed(status) + } + } + + static func unwrapKey(_ wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] { + var unwrappedKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count) + var unwrappedKey = [UInt8](repeating: 0x00, count: unwrappedKeyLen) + let status = CCSymmetricKeyUnwrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, wrappedKey, wrappedKey.count, &unwrappedKey, &unwrappedKeyLen) + if status == kCCSuccess { + assert(unwrappedKeyLen == kCCKeySizeAES256) + return unwrappedKey + } else if status == kCCDecodeError { + throw MasterkeyFileError.invalidPassword + } else { + throw MasterkeyFileError.keyWrappingFailed(status) + } + } +} diff --git a/Tests/CryptomatorCryptoLibTests/CryptoSupportMock.swift b/Tests/CryptomatorCryptoLibTests/CryptoSupportMock.swift new file mode 100644 index 0000000..a3d165b --- /dev/null +++ b/Tests/CryptomatorCryptoLibTests/CryptoSupportMock.swift @@ -0,0 +1,16 @@ +// +// CryptoSupportMock.swift +// CryptomatorCryptoLibTests +// +// Created by Tobias Hagemann on 08.01.21. +// Copyright © 2021 Skymatic GmbH. All rights reserved. +// + +import Foundation +@testable import CryptomatorCryptoLib + +class CryptoSupportMock: CryptoSupport { + override func createRandomBytes(size: Int) throws -> [UInt8] { + return [UInt8](repeating: 0xF0, count: size) + } +} diff --git a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift index a18627a..1522c48 100644 --- a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift +++ b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift @@ -9,14 +9,8 @@ import XCTest @testable import CryptomatorCryptoLib -class CryptoSupportMock: CryptoSupport { - override func createRandomBytes(size: Int) throws -> [UInt8] { - return [UInt8](repeating: 0xF0, count: size) - } -} - class CryptorTests: XCTestCase { - let cryptor = Cryptor(masterkey: Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32), version: 7), cryptoSupport: CryptoSupportMock()) + let cryptor = Cryptor(masterkey: Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32)), cryptoSupport: CryptoSupportMock()) var tmpDirURL: URL! override func setUpWithError() throws { diff --git a/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift new file mode 100644 index 0000000..da987fe --- /dev/null +++ b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift @@ -0,0 +1,202 @@ +// +// MasterkeyFileTests.swift +// CryptomatorCryptoLibTests +// +// Created by Tobias Hagemann on 08.01.21. +// Copyright © 2021 Skymatic GmbH. All rights reserved. +// + +import CommonCrypto +import XCTest +@testable import CryptomatorCryptoLib + +class MasterkeyFileTests: XCTestCase { + func testCreateWithContentFromData() throws { + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + XCTAssertEqual(7, masterkeyFile.content.version) + XCTAssertEqual("AAAAAAAAAAA=", masterkeyFile.content.scryptSalt) + XCTAssertEqual(2, masterkeyFile.content.scryptCostParam) + XCTAssertEqual(8, masterkeyFile.content.scryptBlockSize) + XCTAssertEqual("mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", masterkeyFile.content.primaryMasterKey) + XCTAssertEqual("mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", masterkeyFile.content.hmacMasterKey) + XCTAssertEqual("cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=", masterkeyFile.content.versionMac) + } + + func testUnlock() throws { + let expectedKey = [UInt8](repeating: 0x00, count: 32) + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + let masterkey = try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7) + XCTAssertEqual(expectedKey, masterkey.aesMasterKey) + XCTAssertEqual(expectedKey, masterkey.macMasterKey) + } + + func testUnlockWithWrongPassphrase() throws { + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "qwe", pepper: [UInt8](), expectedVaultVersion: 7), "wrong passphrase") { error in + XCTAssertEqual(.invalidPassword, error as? MasterkeyFileError) + } + } + + func testUnlockWithInvalidVersionMac() throws { + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+G=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "invalid version mac") { error in + XCTAssertEqual(.malformedMasterkeyFile("incorrect version or versionMac"), error as? MasterkeyFileError) + } + } + + func testUnlockWithMalformedJson1() throws { + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "malformed json") { error in + XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as? MasterkeyFileError) + } + } + + func testUnlockWithMalformedJson2() throws { + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "malformed json") { error in + XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as? MasterkeyFileError) + } + } + + func testUnlockWithMalformedJson3() throws { + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "malformed json") { error in + XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in versionMac"), error as? MasterkeyFileError) + } + } + + func testLock() throws { + let masterkey = Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32)) + let content = try MasterkeyFile.lock(masterkey: masterkey, vaultVersion: 7, passphrase: "asd", pepper: [UInt8](), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) + XCTAssertEqual(7, content.version) + XCTAssertEqual("8PDw8PDw8PA=", content.scryptSalt) + XCTAssertEqual(2, content.scryptCostParam) + XCTAssertEqual(8, content.scryptBlockSize) + XCTAssertEqual("jvdghkTc01VISrFly37pgaT/UKtXrDCvZcU3tT9Y98zyzn/pJ91bxw==", content.primaryMasterKey) + XCTAssertEqual("99I+J4bT3rVpZE8yZwKRV9gHVRmQ8XQEujAL9IuwLTc2D3mg5JEjKA==", content.hmacMasterKey) + XCTAssertEqual("sAWFgFNhmtMPeNWr4zh+9Ps7GOtT0pknX11PRQ7eC9Q=", content.versionMac) + } + + func testLockWithDifferentPeppers() throws { + let masterkey = Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32)) + let content1 = try MasterkeyFile.lock(masterkey: masterkey, vaultVersion: 7, passphrase: "asd", pepper: [UInt8](arrayLiteral: 0x01), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) + let content2 = try MasterkeyFile.lock(masterkey: masterkey, vaultVersion: 7, passphrase: "asd", pepper: [UInt8](arrayLiteral: 0x02), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) + XCTAssertNotEqual(content1, content2) + } + + func testChangePassphrase() throws { + let expectedKey = [UInt8](repeating: 0x00, count: 32) + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let content = try MasterkeyFile.changePassphrase(masterkeyFileData: data, oldPassphrase: "asd", newPassphrase: "qwe", pepper: [UInt8](), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) + let masterkeyFile = MasterkeyFile(content: content) + let masterkey = try masterkeyFile.unlock(passphrase: "qwe", pepper: [UInt8](), expectedVaultVersion: 7) + XCTAssertEqual(expectedKey, masterkey.aesMasterKey) + XCTAssertEqual(expectedKey, masterkey.macMasterKey) + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "wrong passphrase") { error in + XCTAssertEqual(.invalidPassword, error as? MasterkeyFileError) + } + } + + func testWrapAndUnwrapKey() throws { + let key = [UInt8](repeating: 0x77, count: 32) + let kek = [UInt8](repeating: 0x55, count: 32) + let wrapped = try MasterkeyFile.wrapKey(key, kek: kek) + let unwrapped = try MasterkeyFile.unwrapKey(wrapped, kek: kek) + XCTAssertEqual(key, unwrapped) + } + + func testWrapKeyWithInvalidKey() throws { + let key = [UInt8](repeating: 0x77, count: 17) + let kek = [UInt8](repeating: 0x55, count: 32) + XCTAssertThrowsError(try MasterkeyFile.wrapKey(key, kek: kek), "invalid key") { error in + XCTAssertEqual(.keyWrappingFailed(CCCryptorStatus(kCCParamError)), error as? MasterkeyFileError) + } + } +} diff --git a/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift b/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift index c1d1c73..485f7db 100644 --- a/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift +++ b/Tests/CryptomatorCryptoLibTests/MasterkeyTests.swift @@ -10,145 +10,11 @@ import XCTest @testable import CryptomatorCryptoLib class MasterkeyTests: XCTestCase { - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testCreateFromMasterkeyFile() throws { - let expectedKeys = [UInt8](repeating: 0x00, count: 32) - let jsonData = """ - { - "version": 7, - "scryptSalt": "AAAAAAAAAAA=", - "scryptCostParam": 2, - "scryptBlockSize": 8, - "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" - } - """.data(using: .utf8)! - - let masterkey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd") - - XCTAssertNotNil(masterkey) - XCTAssertEqual(7, masterkey.version) - XCTAssertEqual(expectedKeys, masterkey.aesMasterKey) - XCTAssertEqual(expectedKeys, masterkey.macMasterKey) - } - - func testCreateFromMasterkeyFileWithWrongPassword() throws { - let jsonData = """ - { - "version": 7, - "scryptSalt": "AAAAAAAAAAA=", - "scryptCostParam": 2, - "scryptBlockSize": 8, - "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" - } - """.data(using: .utf8)! - - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password") { error in - XCTAssertEqual(.invalidPassword, error as? MasterkeyError) - } - } - - func testCreateFromMasterkeyFileWithInvalidVersionMac() throws { - let jsonData = """ - { - "version": 7, - "scryptSalt": "AAAAAAAAAAA=", - "scryptCostParam": 2, - "scryptBlockSize": 8, - "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+G=" - } - """.data(using: .utf8)! - - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(.malformedMasterkeyFile("incorrect version or versionMac"), error as? MasterkeyError) - } - } - - func testCreateFromMasterkeyFileWithMalformedJson1() throws { - let jsonData = """ - { - "version": 7, - "scryptSalt": "AAAAAAAAAAA=", - "scryptCostParam": 2, - "scryptBlockSize": 8, - "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", - "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" - } - """.data(using: .utf8)! - - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as? MasterkeyError) - } - } - - func testCreateFromMasterkeyFileWithMalformedJson2() throws { - let jsonData = """ - { - "version": 7, - "scryptSalt": "AAAAAAAAAAA=", - "scryptCostParam": 2, - "scryptBlockSize": 8, - "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!", - "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" - } - """.data(using: .utf8)! - - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as? MasterkeyError) - } - } - - func testCreateFromMasterkeyFileWithMalformedJson3() throws { - let jsonData = """ - { - "version": 7, - "scryptSalt": "AAAAAAAAAAA=", - "scryptCostParam": 2, - "scryptBlockSize": 8, - "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", - "versionMac": "cn2sAK6l" - } - """.data(using: .utf8)! - - XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password") { error in - XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in versionMac"), error as? MasterkeyError) - } - } - - func testWrapAndUnwrapKey() throws { - let rawKey = [UInt8](repeating: 0x77, count: 32) - let kek = [UInt8](repeating: 0x55, count: 32) - let wrapped = try Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek) - XCTAssertNotNil(wrapped) - let unwrapped = try Masterkey.unwrapMasterKey(wrappedKey: wrapped, kek: kek) - XCTAssertNotNil(unwrapped) - XCTAssertEqual(rawKey, unwrapped) - } - - func testExportEncrypted() throws { - let masterkey = Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32), version: 7) - let json = try masterkey.exportEncrypted(password: "asd", pepper: [UInt8](), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) - XCTAssertEqual("8PDw8PDw8PA=", json.scryptSalt) - XCTAssertEqual(2, json.scryptCostParam) - XCTAssertEqual(8, json.scryptBlockSize) - XCTAssertEqual("jvdghkTc01VISrFly37pgaT/UKtXrDCvZcU3tT9Y98zyzn/pJ91bxw==", json.primaryMasterKey) - XCTAssertEqual("99I+J4bT3rVpZE8yZwKRV9gHVRmQ8XQEujAL9IuwLTc2D3mg5JEjKA==", json.hmacMasterKey) - XCTAssertEqual("sAWFgFNhmtMPeNWr4zh+9Ps7GOtT0pknX11PRQ7eC9Q=", json.versionMac) - XCTAssertEqual(7, json.version) + func testCreateFromRaw() throws { + let aesMasterKey = [UInt8](repeating: 0x77, count: 32) + let macMasterKey = [UInt8](repeating: 0x55, count: 32) + let masterkey = Masterkey.createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) + XCTAssertEqual(aesMasterKey, masterkey.aesMasterKey) + XCTAssertEqual(macMasterKey, masterkey.macMasterKey) } } From a2008eab54d458039089c95cac77d917601d3beb Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 11 Jan 2021 14:17:09 +0100 Subject: [PATCH 097/144] Updated project to Xcode 12.3, set min version for SwiftFormat --- .swiftformat | 2 ++ CryptomatorCryptoLib.xcodeproj/project.pbxproj | 4 +--- .../xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.swiftformat b/.swiftformat index 2a76127..a243a63 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,3 +1,5 @@ +--minversion 0.47.10 + # file options --exclude Tests/LinuxMain.swift,Tests/**/XCTestManifests.swift diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index e069c30..50d5ac1 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -204,7 +204,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1220; + LastUpgradeCheck = 1230; ORGANIZATIONNAME = "Skymatic GmbH"; TargetAttributes = { 4A7C21312451F2AC00DE81E6 = { @@ -377,7 +377,6 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -442,7 +441,6 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_WHOLE_MODULE_OPTIMIZATION = NO; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme index 7acc718..c9436ed 100644 --- a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme +++ b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme @@ -1,6 +1,6 @@ Date: Mon, 11 Jan 2021 16:03:24 +0100 Subject: [PATCH 098/144] Updated README [ci skip] --- README.md | 65 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 6ba2e9c..195301a 100644 --- a/README.md +++ b/README.md @@ -37,45 +37,80 @@ pod 'CryptomatorCryptoLib', '~> 1.0.0' ### Masterkey -`Masterkey` is a factory for masterkey objects that contain the masterkey bytes for AES encryption/decryption and MAC authentication. The version states the vault format version. +`Masterkey` is a class that only contains the key material for AES encryption/decryption and MAC authentication. #### Factory -This will create a new masterkey with secure random bytes. Version will be set to the latest version (currently 7). +This will create a new masterkey with secure random bytes. ```swift let masterkey = try Masterkey.createNew() ``` -Another way is to create a masterkey from an existing masterkey file. This is equivalent to an unlock attempt. +Another way is to create a masterkey from raw bytes. -Either by URL: +```swift +let aesMasterKey = ... +let macMasterKey = ... +let masterkey = Masterkey.createFromRaw(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey) +``` + +### MasterkeyFile + +`MasterkeyFile` is a representation of the masterkey file. With that, you can unlock a masterkey file (and get a `Masterkey`), lock a masterkey file (and serialize it as JSON), or change the passphrase. + +#### Factory + +Create a masterkey file with content provided either from URL: ```swift -let fileURL = ... -let password = ... -let pepper = ... // optional -let masterkey = try Masterkey.createFromMasterkeyFile(fileURL: fileURL, password: password, pepper: pepper) +let url = ... +let masterkey = try MasterkeyFile.withContentFromURL(url: url) +``` + +Or from JSON data: + +```swift +let data = ... +let masterkey = try MasterkeyFile.withContentFromData(data: data) ``` -Or by JSON data: +#### Unlock + +When you have a masterkey file, you can attempt an unlock. When successful, it unwraps the stored encryption and MAC keys into the masterkey, which can be used for the cryptor. ```swift -let jsonData = ... -let password = ... +let masterkeyFile = ... +let passphrase = ... let pepper = ... // optional -let masterkey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: password, pepper: pepper) +let expectedVaultVersion = ... // optional +let masterkey = try masterkeyFile.unlock(passphrase: passphrase, pepper: pepper, expectedVaultVersion: expectedVaultVersion) ``` -#### Export +#### Lock For persisting the masterkey, use this method to export its encrypted/wrapped masterkey and other metadata as JSON data. ```swift let masterkey = ... -let password = ... +let vaultVersion = ... +let passphrase = ... +let pepper = ... // optional +let scryptCostParam = ... // optional +let data = try MasterkeyFile.lock(masterkey: masterkey, vaultVersion: vaultVersion, passphrase: passphrase, pepper: pepper, scryptCostParam: scryptCostParam) +``` + +#### Change Passphrase + +The masterkey can be re-encrypted with a new passphrase. + +```swift +let masterkeyFileData = ... +let oldPassphrase = ... +let newPassphrase = ... let pepper = ... // optional -let jsonData = try masterkey.exportEncrypted(password: password, pepper: pepper) +let scryptCostParam = ... // optional +try MasterkeyFile.changePassphrase(masterkeyFileData: masterkeyFileData, oldPassphrase: oldPassphrase, newPassphrase: newPassphrase, pepper: pepper, scryptCostParam: scryptCostParam) ``` ### Cryptor From 587fd394e74f4e85223a61c241a8f5163f8c0a4c Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 15 Jan 2021 09:15:42 +0100 Subject: [PATCH 099/144] Removed CryptoSwift dependency, added scrypt as C code --- .../project.pbxproj | 219 ++++++- .../xcshareddata/swiftpm/Package.resolved | 9 - Package.resolved | 9 - Package.swift | 6 +- Sources/CryptomatorCryptoLib/Cryptor.swift | 7 +- .../CryptomatorCryptoLib/MasterkeyFile.swift | 19 +- Sources/scrypt/Info.plist | 22 + Sources/scrypt/crypto_scrypt.c | 284 +++++++++ Sources/scrypt/include/crypto_scrypt.h | 48 ++ Sources/scrypt/include/insecure_memzero.h | 64 ++ Sources/scrypt/include/sha256.h | 122 ++++ Sources/scrypt/include/sysendian.h | 173 ++++++ Sources/scrypt/insecure_memzero.c | 46 ++ Sources/scrypt/scrypt.h | 20 + Sources/scrypt/sha256.c | 554 ++++++++++++++++++ .../CryptorTests.swift | 6 +- 16 files changed, 1558 insertions(+), 50 deletions(-) create mode 100644 Sources/scrypt/Info.plist create mode 100644 Sources/scrypt/crypto_scrypt.c create mode 100644 Sources/scrypt/include/crypto_scrypt.h create mode 100644 Sources/scrypt/include/insecure_memzero.h create mode 100644 Sources/scrypt/include/sha256.h create mode 100644 Sources/scrypt/include/sysendian.h create mode 100644 Sources/scrypt/insecure_memzero.c create mode 100644 Sources/scrypt/scrypt.h create mode 100644 Sources/scrypt/sha256.c diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 50d5ac1..078b248 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -10,12 +10,21 @@ 4A7C213C2451F2AC00DE81E6 /* CryptomatorCryptoLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */; }; 4A7C21432451F2AD00DE81E6 /* CryptomatorCryptoLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; 742023E72555A4A200822899 /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 742023E62555A4A200822899 /* Base32 */; }; + 749260D225B17A9A004B3426 /* scrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 749260D025B17A9A004B3426 /* scrypt.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 749260DC25B17AB2004B3426 /* insecure_memzero.c in Sources */ = {isa = PBXBuildFile; fileRef = 74D57DE225B095FA006D81B8 /* insecure_memzero.c */; }; + 749260DD25B17AB2004B3426 /* crypto_scrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 74D57DE825B095FA006D81B8 /* crypto_scrypt.c */; }; + 749260DE25B17AB2004B3426 /* sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 74D57DE725B095FA006D81B8 /* sha256.c */; }; + 749260E525B17AC3004B3426 /* crypto_scrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 74D57DE325B095FA006D81B8 /* crypto_scrypt.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 749260E625B17AC3004B3426 /* sha256.h in Headers */ = {isa = PBXBuildFile; fileRef = 74D57DE425B095FA006D81B8 /* sha256.h */; }; + 749260E725B17AC3004B3426 /* insecure_memzero.h in Headers */ = {isa = PBXBuildFile; fileRef = 74D57DE625B095FA006D81B8 /* insecure_memzero.h */; }; + 749260E825B17AC3004B3426 /* sysendian.h in Headers */ = {isa = PBXBuildFile; fileRef = 74D57DE525B095FA006D81B8 /* sysendian.h */; }; + 749260F325B17B0F004B3426 /* scrypt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 749260CE25B17A9A004B3426 /* scrypt.framework */; }; + 749260F425B17B0F004B3426 /* scrypt.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 749260CE25B17A9A004B3426 /* scrypt.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 74A5B57625A869DD002D10F7 /* MasterkeyFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A5B57525A869DD002D10F7 /* MasterkeyFileTests.swift */; }; 74A5B57E25A86A69002D10F7 /* CryptoSupportMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A5B57D25A86A69002D10F7 /* CryptoSupportMock.swift */; }; 74B4D38D2588CD60006C0567 /* MasterkeyFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4D38C2588CD60006C0567 /* MasterkeyFile.swift */; }; 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F753248FC89B00B4C26D /* CryptoError.swift */; }; 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */; }; - 74F9352A251F44E0001F4ADA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 74F93529251F44E0001F4ADA /* CryptoSwift */; }; 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; @@ -34,17 +43,48 @@ remoteGlobalIDString = 4A7C21312451F2AC00DE81E6; remoteInfo = CryptoLib; }; + 749260F525B17B0F004B3426 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4A7C21292451F2AC00DE81E6 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 749260CD25B17A9A004B3426; + remoteInfo = scrypt; + }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 749260F725B17B10004B3426 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 749260F425B17B0F004B3426 /* scrypt.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CryptomatorCryptoLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21352451F2AC00DE81E6 /* CryptomatorCryptoLib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptomatorCryptoLib.h; sourceTree = ""; }; 4A7C21362451F2AC00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4A7C213B2451F2AC00DE81E6 /* CryptomatorCryptoLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CryptomatorCryptoLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A7C21422451F2AD00DE81E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 749260CE25B17A9A004B3426 /* scrypt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = scrypt.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 749260D025B17A9A004B3426 /* scrypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = scrypt.h; sourceTree = ""; }; + 749260D125B17A9A004B3426 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74A5B57525A869DD002D10F7 /* MasterkeyFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyFileTests.swift; sourceTree = ""; }; 74A5B57D25A86A69002D10F7 /* CryptoSupportMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSupportMock.swift; sourceTree = ""; }; 74B4D38C2588CD60006C0567 /* MasterkeyFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyFile.swift; sourceTree = ""; }; + 74D57DE225B095FA006D81B8 /* insecure_memzero.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = insecure_memzero.c; sourceTree = ""; }; + 74D57DE325B095FA006D81B8 /* crypto_scrypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto_scrypt.h; sourceTree = ""; }; + 74D57DE425B095FA006D81B8 /* sha256.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sha256.h; sourceTree = ""; }; + 74D57DE525B095FA006D81B8 /* sysendian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sysendian.h; sourceTree = ""; }; + 74D57DE625B095FA006D81B8 /* insecure_memzero.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = insecure_memzero.h; sourceTree = ""; }; + 74D57DE725B095FA006D81B8 /* sha256.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sha256.c; sourceTree = ""; }; + 74D57DE825B095FA006D81B8 /* crypto_scrypt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = crypto_scrypt.c; sourceTree = ""; }; 74F0F753248FC89B00B4C26D /* CryptoError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoError.swift; sourceTree = ""; }; 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSupport.swift; sourceTree = ""; }; 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; @@ -63,7 +103,7 @@ buildActionMask = 2147483647; files = ( 742023E72555A4A200822899 /* Base32 in Frameworks */, - 74F9352A251F44E0001F4ADA /* CryptoSwift in Frameworks */, + 749260F325B17B0F004B3426 /* scrypt.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -75,6 +115,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 749260CB25B17A9A004B3426 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -84,6 +131,7 @@ 74FFC966251F3EBB004C4927 /* Sources */, 74FFC967251F3ED0004C4927 /* Tests */, 4A7C21332451F2AC00DE81E6 /* Products */, + 749260F225B17B0F004B3426 /* Frameworks */, ); sourceTree = ""; }; @@ -92,6 +140,7 @@ children = ( 4A7C21322451F2AC00DE81E6 /* CryptomatorCryptoLib.framework */, 4A7C213B2451F2AC00DE81E6 /* CryptomatorCryptoLibTests.xctest */, + 749260CE25B17A9A004B3426 /* scrypt.framework */, ); name = Products; sourceTree = ""; @@ -126,10 +175,42 @@ path = CryptomatorCryptoLibTests; sourceTree = ""; }; + 749260F225B17B0F004B3426 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 74D57DE125B095FA006D81B8 /* scrypt */ = { + isa = PBXGroup; + children = ( + 74D57DE825B095FA006D81B8 /* crypto_scrypt.c */, + 74D57DE225B095FA006D81B8 /* insecure_memzero.c */, + 74D57DE725B095FA006D81B8 /* sha256.c */, + 749260D025B17A9A004B3426 /* scrypt.h */, + 749260D125B17A9A004B3426 /* Info.plist */, + 74D57DF225B09603006D81B8 /* include */, + ); + path = scrypt; + sourceTree = ""; + }; + 74D57DF225B09603006D81B8 /* include */ = { + isa = PBXGroup; + children = ( + 74D57DE325B095FA006D81B8 /* crypto_scrypt.h */, + 74D57DE625B095FA006D81B8 /* insecure_memzero.h */, + 74D57DE425B095FA006D81B8 /* sha256.h */, + 74D57DE525B095FA006D81B8 /* sysendian.h */, + ); + path = include; + sourceTree = ""; + }; 74FFC966251F3EBB004C4927 /* Sources */ = { isa = PBXGroup; children = ( 4A7C21342451F2AC00DE81E6 /* CryptomatorCryptoLib */, + 74D57DE125B095FA006D81B8 /* scrypt */, ); path = Sources; sourceTree = ""; @@ -153,6 +234,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 749260C925B17A9A004B3426 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 749260E725B17AC3004B3426 /* insecure_memzero.h in Headers */, + 749260E525B17AC3004B3426 /* crypto_scrypt.h in Headers */, + 749260D225B17A9A004B3426 /* scrypt.h in Headers */, + 749260E625B17AC3004B3426 /* sha256.h in Headers */, + 749260E825B17AC3004B3426 /* sysendian.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -165,14 +258,15 @@ 4A7C212F2451F2AC00DE81E6 /* Frameworks */, 4A7C21302451F2AC00DE81E6 /* Resources */, 74862A712469A6B2003D81CB /* Lint With SwiftFormat */, + 749260F725B17B10004B3426 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 749260F625B17B0F004B3426 /* PBXTargetDependency */, ); name = CryptomatorCryptoLib; packageProductDependencies = ( - 74F93529251F44E0001F4ADA /* CryptoSwift */, 742023E62555A4A200822899 /* Base32 */, ); productName = CryptoLib; @@ -197,6 +291,24 @@ productReference = 4A7C213B2451F2AC00DE81E6 /* CryptomatorCryptoLibTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 749260CD25B17A9A004B3426 /* scrypt */ = { + isa = PBXNativeTarget; + buildConfigurationList = 749260D325B17A9A004B3426 /* Build configuration list for PBXNativeTarget "scrypt" */; + buildPhases = ( + 749260C925B17A9A004B3426 /* Headers */, + 749260CA25B17A9A004B3426 /* Sources */, + 749260CB25B17A9A004B3426 /* Frameworks */, + 749260CC25B17A9A004B3426 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = scrypt; + productName = scrypt; + productReference = 749260CE25B17A9A004B3426 /* scrypt.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -214,6 +326,9 @@ 4A7C213A2451F2AC00DE81E6 = { CreatedOnToolsVersion = 11.4; }; + 749260CD25B17A9A004B3426 = { + CreatedOnToolsVersion = 12.3; + }; }; }; buildConfigurationList = 4A7C212C2451F2AC00DE81E6 /* Build configuration list for PBXProject "CryptomatorCryptoLib" */; @@ -226,7 +341,6 @@ ); mainGroup = 4A7C21282451F2AC00DE81E6; packageReferences = ( - 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */, 742023E52555A4A200822899 /* XCRemoteSwiftPackageReference "Base32" */, ); productRefGroup = 4A7C21332451F2AC00DE81E6 /* Products */; @@ -234,6 +348,7 @@ projectRoot = ""; targets = ( 4A7C21312451F2AC00DE81E6 /* CryptomatorCryptoLib */, + 749260CD25B17A9A004B3426 /* scrypt */, 4A7C213A2451F2AC00DE81E6 /* CryptomatorCryptoLibTests */, ); }; @@ -254,6 +369,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 749260CC25B17A9A004B3426 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -305,6 +427,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 749260CA25B17A9A004B3426 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 749260DC25B17AB2004B3426 /* insecure_memzero.c in Sources */, + 749260DE25B17AB2004B3426 /* sha256.c in Sources */, + 749260DD25B17AB2004B3426 /* crypto_scrypt.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -313,6 +445,11 @@ target = 4A7C21312451F2AC00DE81E6 /* CryptomatorCryptoLib */; targetProxy = 4A7C213D2451F2AC00DE81E6 /* PBXContainerItemProxy */; }; + 749260F625B17B0F004B3426 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 749260CD25B17A9A004B3426 /* scrypt */; + targetProxy = 749260F525B17B0F004B3426 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -534,6 +671,58 @@ }; name = Release; }; + 749260D425B17A9A004B3426 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = Sources/scrypt/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.2.1; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.scrypt; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 749260D525B17A9A004B3426 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Sources/scrypt/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.2.1; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.scrypt; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -564,6 +753,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 749260D325B17A9A004B3426 /* Build configuration list for PBXNativeTarget "scrypt" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 749260D425B17A9A004B3426 /* Debug */, + 749260D525B17A9A004B3426 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -575,14 +773,6 @@ minimumVersion = 0.8.0; }; }; - 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift.git"; - requirement = { - kind = upToNextMinorVersion; - minimumVersion = 1.3.0; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -591,11 +781,6 @@ package = 742023E52555A4A200822899 /* XCRemoteSwiftPackageReference "Base32" */; productName = Base32; }; - 74F93529251F44E0001F4ADA /* CryptoSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 74F93528251F44E0001F4ADA /* XCRemoteSwiftPackageReference "CryptoSwift" */; - productName = CryptoSwift; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 4A7C21292451F2AC00DE81E6 /* Project object */; diff --git a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 130d7e6..cf1b87a 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -9,15 +9,6 @@ "revision": "69fc95dd2e4229a2fd82e14d9358c23b0fd8cf74", "version": "0.8.0" } - }, - { - "package": "CryptoSwift", - "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", - "state": { - "branch": null, - "revision": "af1b58fc569bfde777462349b9f7314b61762be0", - "version": "1.3.2" - } } ] }, diff --git a/Package.resolved b/Package.resolved index 130d7e6..cf1b87a 100644 --- a/Package.resolved +++ b/Package.resolved @@ -9,15 +9,6 @@ "revision": "69fc95dd2e4229a2fd82e14d9358c23b0fd8cf74", "version": "0.8.0" } - }, - { - "package": "CryptoSwift", - "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", - "state": { - "branch": null, - "revision": "af1b58fc569bfde777462349b9f7314b61762be0", - "version": "1.3.2" - } } ] }, diff --git a/Package.swift b/Package.swift index 1a113af..59e1282 100644 --- a/Package.swift +++ b/Package.swift @@ -20,11 +20,11 @@ let package = Package( .library(name: "CryptomatorCryptoLib", targets: ["CryptomatorCryptoLib"]) ], dependencies: [ - .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")), - .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.3.0")) + .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")) ], targets: [ - .target(name: "CryptomatorCryptoLib", dependencies: ["Base32", "CryptoSwift"]), + .target(name: "CryptomatorCryptoLib", dependencies: ["Base32", "scrypt"]), + .target(name: "scrypt"), .testTarget(name: "CryptomatorCryptoLibTests", dependencies: ["CryptomatorCryptoLib"]) ], swiftLanguageVersions: [.v5] diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index cb9645e..622c449 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -7,7 +7,6 @@ // import CommonCrypto -import CryptoSwift import Foundation #if SWIFT_PACKAGE import Base32 @@ -91,7 +90,7 @@ public class Cryptor { - Returns: Constant length string that is unlikely to collide with any other name. */ public func encryptDirId(_ dirId: Data) throws -> String { - let encrypted = try AesSiv.encrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, plaintext: dirId.bytes) + let encrypted = try AesSiv.encrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, plaintext: [UInt8](dirId)) var digest = [UInt8](repeating: 0x00, count: Int(CC_SHA1_DIGEST_LENGTH)) CC_SHA1(encrypted, UInt32(encrypted.count) as CC_LONG, &digest) return Data(digest).base32EncodedString @@ -108,7 +107,7 @@ public class Cryptor { public func encryptFileName(_ cleartextName: String, dirId: Data, encoding: FileNameEncoding = .base64url) throws -> String { // encrypt: let cleartext = [UInt8](cleartextName.precomposedStringWithCanonicalMapping.utf8) - let ciphertext = try AesSiv.encrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, plaintext: cleartext, ad: dirId.bytes) + let ciphertext = try AesSiv.encrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, plaintext: cleartext, ad: [UInt8](dirId)) // encode: switch encoding { @@ -138,7 +137,7 @@ public class Cryptor { } // decrypt: - let cleartext = try AesSiv.decrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, ciphertext: ciphertextData.bytes, ad: dirId.bytes) + let cleartext = try AesSiv.decrypt(aesKey: masterkey.aesMasterKey, macKey: masterkey.macMasterKey, ciphertext: [UInt8](ciphertextData), ad: [UInt8](dirId)) if let str = String(data: Data(cleartext), encoding: .utf8) { return str } else { diff --git a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift index 9caa4e5..d633179 100644 --- a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift +++ b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift @@ -7,8 +7,8 @@ // import CommonCrypto -import CryptoSwift import Foundation +import scrypt struct Content: Codable, Equatable { let version: Int @@ -23,6 +23,7 @@ struct Content: Codable, Equatable { public enum MasterkeyFileError: Error, Equatable { case malformedMasterkeyFile(_ reason: String) case invalidPassword + case keyDerivationFailed case keyWrappingFailed(_ status: CCCryptorStatus) } @@ -78,15 +79,19 @@ public class MasterkeyFile { // derive keys: let pw = [UInt8](passphrase.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: content.scryptSalt)!) - let kek = try Scrypt(password: pw, salt: salt + pepper, dkLen: kCCKeySizeAES256, N: content.scryptCostParam, r: content.scryptBlockSize, p: 1).calculate() + var kek = [UInt8](repeating: 0x00, count: kCCKeySizeAES256) + let scryptResult = crypto_scrypt(pw, pw.count, salt + pepper, salt.count + pepper.count, UInt64(content.scryptCostParam), UInt32(content.scryptBlockSize), 1, &kek, kCCKeySizeAES256) + guard scryptResult == 0 else { + throw MasterkeyFileError.keyDerivationFailed + } guard let wrappedMasterKey = Data(base64Encoded: content.primaryMasterKey) else { throw MasterkeyFileError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey") } - let aesKey = try MasterkeyFile.unwrapKey(wrappedMasterKey.bytes, kek: kek) + let aesKey = try MasterkeyFile.unwrapKey([UInt8](wrappedMasterKey), kek: kek) guard let wrappedHmacKey = Data(base64Encoded: content.hmacMasterKey) else { throw MasterkeyFileError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey") } - let macKey = try MasterkeyFile.unwrapKey(wrappedHmacKey.bytes, kek: kek) + let macKey = try MasterkeyFile.unwrapKey([UInt8](wrappedHmacKey), kek: kek) // check MAC: if let expectedVaultVersion = expectedVaultVersion { @@ -132,7 +137,11 @@ public class MasterkeyFile { static func lock(masterkey: Masterkey, vaultVersion: Int, passphrase: String, pepper: [UInt8], scryptCostParam: Int, cryptoSupport: CryptoSupport = CryptoSupport()) throws -> Content { let pw = [UInt8](passphrase.precomposedStringWithCanonicalMapping.utf8) let salt = try cryptoSupport.createRandomBytes(size: defaultScryptSaltSize) - let kek = try Scrypt(password: pw, salt: salt + pepper, dkLen: kCCKeySizeAES256, N: scryptCostParam, r: defaultScryptBlockSize, p: 1).calculate() + var kek = [UInt8](repeating: 0x00, count: kCCKeySizeAES256) + let scryptResult = crypto_scrypt(pw, pw.count, salt + pepper, salt.count + pepper.count, UInt64(scryptCostParam), UInt32(defaultScryptBlockSize), 1, &kek, kCCKeySizeAES256) + guard scryptResult == 0 else { + throw MasterkeyFileError.keyDerivationFailed + } let wrappedMasterKey = try wrapKey(masterkey.aesMasterKey, kek: kek) let wrappedHmacKey = try wrapKey(masterkey.macMasterKey, kek: kek) diff --git a/Sources/scrypt/Info.plist b/Sources/scrypt/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Sources/scrypt/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Sources/scrypt/crypto_scrypt.c b/Sources/scrypt/crypto_scrypt.c new file mode 100644 index 0000000..c062895 --- /dev/null +++ b/Sources/scrypt/crypto_scrypt.c @@ -0,0 +1,284 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#include +#include +#include +#include + +#include "include/sha256.h" +#include "include/sysendian.h" + +#include "include/crypto_scrypt.h" + +static void blkcpy(uint8_t *, uint8_t *, size_t); +static void blkxor(uint8_t *, uint8_t *, size_t); +static void salsa20_8(uint8_t[64]); +static void blockmix_salsa8(uint8_t *, uint8_t *, size_t); +static uint64_t integerify(uint8_t *, size_t); +static void smix(uint8_t *, size_t, uint64_t, uint8_t *, uint8_t *); + +static void +blkcpy(uint8_t * dest, uint8_t * src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + dest[i] = src[i]; +} + +static void +blkxor(uint8_t * dest, uint8_t * src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + dest[i] ^= src[i]; +} + +/** + * salsa20_8(B): + * Apply the salsa20/8 core to the provided block. + */ +static void +salsa20_8(uint8_t B[64]) +{ + uint32_t B32[16]; + uint32_t x[16]; + size_t i; + + /* Convert little-endian values in. */ + for (i = 0; i < 16; i++) + B32[i] = le32dec(&B[i * 4]); + + /* Compute x = doubleround^4(B32). */ + for (i = 0; i < 16; i++) + x[i] = B32[i]; + for (i = 0; i < 8; i += 2) { +#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) + /* Operate on columns. */ + x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + + x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + + x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + + x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + + /* Operate on rows. */ + x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + + x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + + x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + + x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); +#undef R + } + + /* Compute B32 = B32 + x. */ + for (i = 0; i < 16; i++) + B32[i] += x[i]; + + /* Convert little-endian values out. */ + for (i = 0; i < 16; i++) + le32enc(&B[4 * i], B32[i]); +} + +/** + * blockmix_salsa8(B, Y, r): + * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in + * length; the temporary space Y must also be the same size. + */ +static void +blockmix_salsa8(uint8_t * B, uint8_t * Y, size_t r) +{ + uint8_t X[64]; + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &B[(2 * r - 1) * 64], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < 2 * r; i++) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &B[i * 64], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + blkcpy(&Y[i * 64], X, 64); + } + + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + for (i = 0; i < r; i++) + blkcpy(&B[i * 64], &Y[(i * 2) * 64], 64); + for (i = 0; i < r; i++) + blkcpy(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64); +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(uint8_t * B, size_t r) +{ + uint8_t * X = &B[(2 * r - 1) * 64]; + + return (le64dec(X)); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the + * temporary storage V must be 128rN bytes in length; the temporary storage + * XY must be 256r bytes in length. The value N must be a power of 2. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, uint8_t * V, uint8_t * XY) +{ + uint8_t * X = XY; + uint8_t * Y = &XY[128 * r]; + uint64_t i; + uint64_t j; + + /* 1: X <-- B */ + blkcpy(X, B, 128 * r); + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i++) { + /* 3: V_i <-- X */ + blkcpy(&V[i * (128 * r)], X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i++) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, &V[j * (128 * r)], 128 * r); + blockmix_salsa8(X, Y, r); + } + + /* 10: B' <-- X */ + blkcpy(B, X, 128 * r); +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2. + * + * Return 0 on success; or -1 on error. + */ +int +crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p, + uint8_t * buf, size_t buflen) +{ + uint8_t * B; + uint8_t * V; + uint8_t * XY; + size_t r = _r, p = _p; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (((N & (N - 1)) != 0) || (N == 0)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > SIZE_MAX / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ + if ((B = malloc(128 * r * p)) == NULL) + goto err0; + if ((XY = malloc(256 * r)) == NULL) + goto err1; + if ((V = malloc(128 * r * N)) == NULL) + goto err2; + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); + + /* Free memory. */ + free(V); + free(XY); + free(B); + + /* Success! */ + return (0); + +err2: + free(XY); +err1: + free(B); +err0: + /* Failure! */ + return (-1); +} diff --git a/Sources/scrypt/include/crypto_scrypt.h b/Sources/scrypt/include/crypto_scrypt.h new file mode 100644 index 0000000..43eb388 --- /dev/null +++ b/Sources/scrypt/include/crypto_scrypt.h @@ -0,0 +1,48 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#ifndef _CRYPTO_SCRYPT_H_ +#define _CRYPTO_SCRYPT_H_ + +#include +#include + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error. + */ +int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, + uint32_t, uint32_t, uint8_t *, size_t); + +#endif /* !_CRYPTO_SCRYPT_H_ */ diff --git a/Sources/scrypt/include/insecure_memzero.h b/Sources/scrypt/include/insecure_memzero.h new file mode 100644 index 0000000..757c417 --- /dev/null +++ b/Sources/scrypt/include/insecure_memzero.h @@ -0,0 +1,64 @@ +/*- + * Copyright 2005-2016 Colin Percival. All rights reserved. + * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2014 Sean Kelly. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _INSECURE_MEMZERO_H_ +#define _INSECURE_MEMZERO_H_ + +#include + +/* Pointer to memory-zeroing function. */ +extern void (* volatile insecure_memzero_ptr)(volatile void *, size_t); + +/** + * insecure_memzero(buf, len): + * Attempt to zero ${len} bytes at ${buf} in spite of optimizing compilers' + * best (standards-compliant) attempts to remove the buffer-zeroing. In + * particular, to avoid performing the zeroing, a compiler would need to + * use optimistic devirtualization; recognize that non-volatile objects do not + * need to be treated as volatile, even if they are accessed via volatile + * qualified pointers; and perform link-time optimization; in addition to the + * dead-code elimination which often causes buffer-zeroing to be elided. + * + * Note however that zeroing a buffer does not guarantee that the data held + * in the buffer is not stored elsewhere; in particular, there may be copies + * held in CPU registers or in anonymous allocations on the stack, even if + * every named variable is successfully sanitized. Solving the "wipe data + * from the system" problem will require a C language extension which does not + * yet exist. + * + * For more information, see: + * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html + * http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html + */ +static inline void +insecure_memzero(volatile void * buf, size_t len) +{ + + (insecure_memzero_ptr)(buf, len); +} + +#endif /* !_INSECURE_MEMZERO_H_ */ diff --git a/Sources/scrypt/include/sha256.h b/Sources/scrypt/include/sha256.h new file mode 100644 index 0000000..f91cfa1 --- /dev/null +++ b/Sources/scrypt/include/sha256.h @@ -0,0 +1,122 @@ +/*- + * Copyright 2005-2016 Colin Percival. All rights reserved. + * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2014 Sean Kelly. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#include +#include + +/* + * Use #defines in order to avoid namespace collisions with anyone else's + * SHA256 code (e.g., the code in OpenSSL). + */ +#define SHA256_Init libcperciva_SHA256_Init +#define SHA256_Update libcperciva_SHA256_Update +#define SHA256_Final libcperciva_SHA256_Final +#define SHA256_Buf libcperciva_SHA256_Buf +#define SHA256_CTX libcperciva_SHA256_CTX +#define HMAC_SHA256_Init libcperciva_HMAC_SHA256_Init +#define HMAC_SHA256_Update libcperciva_HMAC_SHA256_Update +#define HMAC_SHA256_Final libcperciva_HMAC_SHA256_Final +#define HMAC_SHA256_Buf libcperciva_HMAC_SHA256_Buf +#define HMAC_SHA256_CTX libcperciva_HMAC_SHA256_CTX + +/* Context structure for SHA256 operations. */ +typedef struct { + uint32_t state[8]; + uint64_t count; + uint8_t buf[64]; +} SHA256_CTX; + +/** + * SHA256_Init(ctx): + * Initialize the SHA256 context ${ctx}. + */ +void SHA256_Init(SHA256_CTX *); + +/** + * SHA256_Update(ctx, in, len): + * Input ${len} bytes from ${in} into the SHA256 context ${ctx}. + */ +void SHA256_Update(SHA256_CTX *, const void *, size_t); + +/** + * SHA256_Final(digest, ctx): + * Output the SHA256 hash of the data input to the context ${ctx} into the + * buffer ${digest}. + */ +void SHA256_Final(uint8_t[32], SHA256_CTX *); + +/** + * SHA256_Buf(in, len, digest): + * Compute the SHA256 hash of ${len} bytes from ${in} and write it to ${digest}. + */ +void SHA256_Buf(const void *, size_t, uint8_t[32]); + +/* Context structure for HMAC-SHA256 operations. */ +typedef struct { + SHA256_CTX ictx; + SHA256_CTX octx; +} HMAC_SHA256_CTX; + +/** + * HMAC_SHA256_Init(ctx, K, Klen): + * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from + * ${K}. + */ +void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); + +/** + * HMAC_SHA256_Update(ctx, in, len): + * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}. + */ +void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); + +/** + * HMAC_SHA256_Final(digest, ctx): + * Output the HMAC-SHA256 of the data input to the context ${ctx} into the + * buffer ${digest}. + */ +void HMAC_SHA256_Final(uint8_t[32], HMAC_SHA256_CTX *); + +/** + * HMAC_SHA256_Buf(K, Klen, in, len, digest): + * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of + * length ${Klen}, and write the result to ${digest}. + */ +void HMAC_SHA256_Buf(const void *, size_t, const void *, size_t, uint8_t[32]); + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, + uint64_t, uint8_t *, size_t); + +#endif /* !_SHA256_H_ */ diff --git a/Sources/scrypt/include/sysendian.h b/Sources/scrypt/include/sysendian.h new file mode 100644 index 0000000..ca9a923 --- /dev/null +++ b/Sources/scrypt/include/sysendian.h @@ -0,0 +1,173 @@ +/*- + * Copyright 2005-2016 Colin Percival. All rights reserved. + * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2014 Sean Kelly. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SYSENDIAN_H_ +#define _SYSENDIAN_H_ + +#include + +/* Avoid namespace collisions with BSD . */ +#define be16dec libcperciva_be16dec +#define be16enc libcperciva_be16enc +#define be32dec libcperciva_be32dec +#define be32enc libcperciva_be32enc +#define be64dec libcperciva_be64dec +#define be64enc libcperciva_be64enc +#define le16dec libcperciva_le16dec +#define le16enc libcperciva_le16enc +#define le32dec libcperciva_le32dec +#define le32enc libcperciva_le32enc +#define le64dec libcperciva_le64dec +#define le64enc libcperciva_le64enc + +static inline uint16_t +be16dec(const void * pp) +{ + const uint8_t * p = (uint8_t const *)pp; + + return ((uint16_t)(p[1]) + ((uint16_t)(p[0]) << 8)); +} + +static inline void +be16enc(void * pp, uint16_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[1] = x & 0xff; + p[0] = (x >> 8) & 0xff; +} + +static inline uint32_t +be32dec(const void * pp) +{ + const uint8_t * p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} + +static inline void +be32enc(void * pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +static inline uint64_t +be64dec(const void * pp) +{ + const uint8_t * p = (uint8_t const *)pp; + + return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); +} + +static inline void +be64enc(void * pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[7] = x & 0xff; + p[6] = (x >> 8) & 0xff; + p[5] = (x >> 16) & 0xff; + p[4] = (x >> 24) & 0xff; + p[3] = (x >> 32) & 0xff; + p[2] = (x >> 40) & 0xff; + p[1] = (x >> 48) & 0xff; + p[0] = (x >> 56) & 0xff; +} + +static inline uint16_t +le16dec(const void * pp) +{ + const uint8_t * p = (uint8_t const *)pp; + + return ((uint16_t)(p[0]) + ((uint16_t)(p[1]) << 8)); +} + +static inline void +le16enc(void * pp, uint16_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; +} + +static inline uint32_t +le32dec(const void * pp) +{ + const uint8_t * p = (uint8_t const *)pp; + + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static inline void +le32enc(void * pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static inline uint64_t +le64dec(const void * pp) +{ + const uint8_t * p = (uint8_t const *)pp; + + return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); +} + +static inline void +le64enc(void * pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; + p[4] = (x >> 32) & 0xff; + p[5] = (x >> 40) & 0xff; + p[6] = (x >> 48) & 0xff; + p[7] = (x >> 56) & 0xff; +} + +#endif /* !_SYSENDIAN_H_ */ diff --git a/Sources/scrypt/insecure_memzero.c b/Sources/scrypt/insecure_memzero.c new file mode 100644 index 0000000..2f1c4f6 --- /dev/null +++ b/Sources/scrypt/insecure_memzero.c @@ -0,0 +1,46 @@ +/*- + * Copyright 2005-2016 Colin Percival. All rights reserved. + * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2014 Sean Kelly. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include "include/insecure_memzero.h" + +/* Function which does the zeroing. */ +static void +insecure_memzero_func(volatile void * buf, size_t len) +{ + volatile uint8_t * _buf = buf; + size_t i; + + for (i = 0; i < len; i++) + _buf[i] = 0; +} + +/* Pointer to memory-zeroing function. */ +void (* volatile insecure_memzero_ptr)(volatile void *, size_t) = + insecure_memzero_func; diff --git a/Sources/scrypt/scrypt.h b/Sources/scrypt/scrypt.h new file mode 100644 index 0000000..94b4633 --- /dev/null +++ b/Sources/scrypt/scrypt.h @@ -0,0 +1,20 @@ +// +// scrypt.h +// scrypt +// +// Created by Tobias Hagemann on 15.01.21. +// Copyright © 2021 Skymatic GmbH. All rights reserved. +// + +#import + +//! Project version number for scrypt. +FOUNDATION_EXPORT double scryptVersionNumber; + +//! Project version string for scrypt. +FOUNDATION_EXPORT const unsigned char scryptVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import +#import + + diff --git a/Sources/scrypt/sha256.c b/Sources/scrypt/sha256.c new file mode 100644 index 0000000..8d55a50 --- /dev/null +++ b/Sources/scrypt/sha256.c @@ -0,0 +1,554 @@ +/*- + * Copyright 2005-2016 Colin Percival. All rights reserved. + * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2014 Sean Kelly. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include "include/insecure_memzero.h" +#include "include/sysendian.h" + +#include "include/sha256.h" + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (uint8_t) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(uint8_t * dst, const uint32_t * src, size_t len) +{ + size_t i; + + /* Sanity-check. */ + assert(len % 4 == 0); + + /* Encode vector, one word at a time. */ + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (uint8_t) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t * dst, const uint8_t * src, size_t len) +{ + size_t i; + + /* Sanity-check. */ + assert(len % 4 == 0); + + /* Decode vector, one word at a time. */ + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +/* SHA256 round constants. */ +static const uint32_t Krnd[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + h += S1(e) + Ch(e, f, g) + k; \ + d += h; \ + h += S0(a) + Maj(a, b, c); + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, ii) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i + ii] + Krnd[i + ii]) + +/* Message schedule computation */ +#define MSCH(W, ii, i) \ + W[i + ii + 16] = s1(W[i + ii + 14]) + W[i + ii + 9] + s0(W[i + ii + 1]) + W[i + ii] + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t state[static restrict 8], + const uint8_t block[static restrict 64], + uint32_t W[static restrict 64], uint32_t S[static restrict 8]) +{ + int i; + + /* 1. Prepare the first part of the message schedule W. */ + be32dec_vect(W, block, 64); + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + for (i = 0; i < 64; i += 16) { + RNDr(S, W, 0, i); + RNDr(S, W, 1, i); + RNDr(S, W, 2, i); + RNDr(S, W, 3, i); + RNDr(S, W, 4, i); + RNDr(S, W, 5, i); + RNDr(S, W, 6, i); + RNDr(S, W, 7, i); + RNDr(S, W, 8, i); + RNDr(S, W, 9, i); + RNDr(S, W, 10, i); + RNDr(S, W, 11, i); + RNDr(S, W, 12, i); + RNDr(S, W, 13, i); + RNDr(S, W, 14, i); + RNDr(S, W, 15, i); + + if (i == 48) + break; + MSCH(W, 0, i); + MSCH(W, 1, i); + MSCH(W, 2, i); + MSCH(W, 3, i); + MSCH(W, 4, i); + MSCH(W, 5, i); + MSCH(W, 6, i); + MSCH(W, 7, i); + MSCH(W, 8, i); + MSCH(W, 9, i); + MSCH(W, 10, i); + MSCH(W, 11, i); + MSCH(W, 12, i); + MSCH(W, 13, i); + MSCH(W, 14, i); + MSCH(W, 15, i); + } + + /* 4. Mix local working variables into global state. */ + for (i = 0; i < 8; i++) + state[i] += S[i]; +} + +static const uint8_t PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx, uint32_t tmp32[static restrict 72]) +{ + size_t r; + + /* Figure out how many bytes we have buffered. */ + r = (ctx->count >> 3) & 0x3f; + + /* Pad to 56 mod 64, transforming if we finish a block en route. */ + if (r < 56) { + /* Pad to 56 mod 64. */ + memcpy(&ctx->buf[r], PAD, 56 - r); + } else { + /* Finish the current block and mix. */ + memcpy(&ctx->buf[r], PAD, 64 - r); + SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); + + /* The start of the final block is all zeroes. */ + memset(&ctx->buf[0], 0, 56); + } + + /* Add the terminating bit-count. */ + be64enc(&ctx->buf[56], ctx->count); + + /* Mix in the final block. */ + SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); +} + +/* Magic initialization constants. */ +static const uint32_t initial_state[8] = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +/** + * SHA256_Init(ctx): + * Initialize the SHA256 context ${ctx}. + */ +void +SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far. */ + ctx->count = 0; + + /* Initialize state. */ + memcpy(ctx->state, initial_state, sizeof(initial_state)); +} + +/** + * SHA256_Update(ctx, in, len): + * Input ${len} bytes from ${in} into the SHA256 context ${ctx}. + */ +static void +_SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len, + uint32_t tmp32[static restrict 72]) +{ + uint32_t r; + const uint8_t * src = in; + + /* Return immediately if we have nothing to do. */ + if (len == 0) + return; + + /* Number of bytes left in the buffer from previous updates. */ + r = (ctx->count >> 3) & 0x3f; + + /* Update number of bits. */ + ctx->count += (uint64_t)(len) << 3; + + /* Handle the case where we don't need to perform any transforms. */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block. */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks. */ + while (len >= 64) { + SHA256_Transform(ctx->state, src, &tmp32[0], &tmp32[64]); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer. */ + memcpy(ctx->buf, src, len); +} + +/* Wrapper function for intermediate-values sanitization. */ +void +SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len) +{ + uint32_t tmp32[72]; + + /* Call the real function. */ + _SHA256_Update(ctx, in, len, tmp32); + + /* Clean the stack. */ + insecure_memzero(tmp32, 288); +} + +/** + * SHA256_Final(digest, ctx): + * Output the SHA256 hash of the data input to the context ${ctx} into the + * buffer ${digest}. + */ +static void +_SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx, + uint32_t tmp32[static restrict 72]) +{ + + /* Add padding. */ + SHA256_Pad(ctx, tmp32); + + /* Write the hash. */ + be32enc_vect(digest, ctx->state, 32); +} + +/* Wrapper function for intermediate-values sanitization. */ +void +SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx) +{ + uint32_t tmp32[72]; + + /* Call the real function. */ + _SHA256_Final(digest, ctx, tmp32); + + /* Clear the context state. */ + insecure_memzero(ctx, sizeof(SHA256_CTX)); + + /* Clean the stack. */ + insecure_memzero(tmp32, 288); +} + +/** + * SHA256_Buf(in, len, digest): + * Compute the SHA256 hash of ${len} bytes from ${in} and write it to ${digest}. + */ +void +SHA256_Buf(const void * in, size_t len, uint8_t digest[32]) +{ + SHA256_CTX ctx; + uint32_t tmp32[72]; + + SHA256_Init(&ctx); + _SHA256_Update(&ctx, in, len, tmp32); + _SHA256_Final(digest, &ctx, tmp32); + + /* Clean the stack. */ + insecure_memzero(&ctx, sizeof(SHA256_CTX)); + insecure_memzero(tmp32, 288); +} + +/** + * HMAC_SHA256_Init(ctx, K, Klen): + * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from + * ${K}. + */ +static void +_HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen, + uint32_t tmp32[static restrict 72], uint8_t pad[static restrict 64], + uint8_t khash[static restrict 32]) +{ + const uint8_t * K = _K; + size_t i; + + /* If Klen > 64, the key is really SHA256(K). */ + if (Klen > 64) { + SHA256_Init(&ctx->ictx); + _SHA256_Update(&ctx->ictx, K, Klen, tmp32); + _SHA256_Final(khash, &ctx->ictx, tmp32); + K = khash; + Klen = 32; + } + + /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ + SHA256_Init(&ctx->ictx); + memset(pad, 0x36, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + _SHA256_Update(&ctx->ictx, pad, 64, tmp32); + + /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ + SHA256_Init(&ctx->octx); + memset(pad, 0x5c, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + _SHA256_Update(&ctx->octx, pad, 64, tmp32); +} + +/* Wrapper function for intermediate-values sanitization. */ +void +HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) +{ + uint32_t tmp32[72]; + uint8_t pad[64]; + uint8_t khash[32]; + + /* Call the real function. */ + _HMAC_SHA256_Init(ctx, _K, Klen, tmp32, pad, khash); + + /* Clean the stack. */ + insecure_memzero(tmp32, 288); + insecure_memzero(khash, 32); + insecure_memzero(pad, 64); +} + +/** + * HMAC_SHA256_Update(ctx, in, len): + * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}. + */ +static void +_HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len, + uint32_t tmp32[static restrict 72]) +{ + + /* Feed data to the inner SHA256 operation. */ + _SHA256_Update(&ctx->ictx, in, len, tmp32); +} + +/* Wrapper function for intermediate-values sanitization. */ +void +HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len) +{ + uint32_t tmp32[72]; + + /* Call the real function. */ + _HMAC_SHA256_Update(ctx, in, len, tmp32); + + /* Clean the stack. */ + insecure_memzero(tmp32, 288); +} + +/** + * HMAC_SHA256_Final(digest, ctx): + * Output the HMAC-SHA256 of the data input to the context ${ctx} into the + * buffer ${digest}. + */ +static void +_HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx, + uint32_t tmp32[static restrict 72], uint8_t ihash[static restrict 32]) +{ + + /* Finish the inner SHA256 operation. */ + _SHA256_Final(ihash, &ctx->ictx, tmp32); + + /* Feed the inner hash to the outer SHA256 operation. */ + _SHA256_Update(&ctx->octx, ihash, 32, tmp32); + + /* Finish the outer SHA256 operation. */ + _SHA256_Final(digest, &ctx->octx, tmp32); +} + +/* Wrapper function for intermediate-values sanitization. */ +void +HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx) +{ + uint32_t tmp32[72]; + uint8_t ihash[32]; + + /* Call the real function. */ + _HMAC_SHA256_Final(digest, ctx, tmp32, ihash); + + /* Clean the stack. */ + insecure_memzero(tmp32, 288); + insecure_memzero(ihash, 32); +} + +/** + * HMAC_SHA256_Buf(K, Klen, in, len, digest): + * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of + * length ${Klen}, and write the result to ${digest}. + */ +void +HMAC_SHA256_Buf(const void * K, size_t Klen, const void * in, size_t len, + uint8_t digest[32]) +{ + HMAC_SHA256_CTX ctx; + uint32_t tmp32[72]; + uint8_t tmp8[96]; + + _HMAC_SHA256_Init(&ctx, K, Klen, tmp32, &tmp8[0], &tmp8[64]); + _HMAC_SHA256_Update(&ctx, in, len, tmp32); + _HMAC_SHA256_Final(digest, &ctx, tmp32, &tmp8[0]); + + /* Clean the stack. */ + insecure_memzero(&ctx, sizeof(HMAC_SHA256_CTX)); + insecure_memzero(tmp32, 288); + insecure_memzero(tmp8, 96); +} + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void +PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, + size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) +{ + HMAC_SHA256_CTX Phctx, PShctx, hctx; + uint32_t tmp32[72]; + uint8_t tmp8[96]; + size_t i; + uint8_t ivec[4]; + uint8_t U[32]; + uint8_t T[32]; + uint64_t j; + int k; + size_t clen; + + /* Sanity-check. */ + assert(dkLen <= 32 * (size_t)(UINT32_MAX)); + + /* Compute HMAC state after processing P. */ + _HMAC_SHA256_Init(&Phctx, passwd, passwdlen, + tmp32, &tmp8[0], &tmp8[64]); + + /* Compute HMAC state after processing P and S. */ + memcpy(&PShctx, &Phctx, sizeof(HMAC_SHA256_CTX)); + _HMAC_SHA256_Update(&PShctx, salt, saltlen, tmp32); + + /* Iterate through the blocks. */ + for (i = 0; i * 32 < dkLen; i++) { + /* Generate INT(i + 1). */ + be32enc(ivec, (uint32_t)(i + 1)); + + /* Compute U_1 = PRF(P, S || INT(i)). */ + memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); + _HMAC_SHA256_Update(&hctx, ivec, 4, tmp32); + _HMAC_SHA256_Final(U, &hctx, tmp32, tmp8); + + /* T_i = U_1 ... */ + memcpy(T, U, 32); + + for (j = 2; j <= c; j++) { + /* Compute U_j. */ + memcpy(&hctx, &Phctx, sizeof(HMAC_SHA256_CTX)); + _HMAC_SHA256_Update(&hctx, U, 32, tmp32); + _HMAC_SHA256_Final(U, &hctx, tmp32, tmp8); + + /* ... xor U_j ... */ + for (k = 0; k < 32; k++) + T[k] ^= U[k]; + } + + /* Copy as many bytes as necessary into buf. */ + clen = dkLen - i * 32; + if (clen > 32) + clen = 32; + memcpy(&buf[i * 32], T, clen); + } + + /* Clean the stack. */ + insecure_memzero(&Phctx, sizeof(HMAC_SHA256_CTX)); + insecure_memzero(&PShctx, sizeof(HMAC_SHA256_CTX)); + insecure_memzero(&hctx, sizeof(HMAC_SHA256_CTX)); + insecure_memzero(tmp32, 288); + insecure_memzero(tmp8, 96); + insecure_memzero(U, 32); + insecure_memzero(T, 32); +} diff --git a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift index 1522c48..849e48e 100644 --- a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift +++ b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift @@ -113,12 +113,12 @@ class CryptorTests: XCTestCase { func testEncryptAndDecryptSingleChunk() throws { let nonce = [UInt8](repeating: 0x00, count: 16) let filekey = [UInt8](repeating: 0x00, count: 32) - let cleartext = "hello world".data(using: .ascii)! + let cleartext = [UInt8]("hello world".data(using: .ascii)!) - let encrypted = try cryptor.encryptSingleChunk(cleartext.bytes, chunkNumber: 0, headerNonce: nonce, fileKey: filekey) + let encrypted = try cryptor.encryptSingleChunk(cleartext, chunkNumber: 0, headerNonce: nonce, fileKey: filekey) let decrypted = try cryptor.decryptSingleChunk(encrypted, chunkNumber: 0, headerNonce: nonce, fileKey: filekey) - XCTAssertEqual(cleartext.bytes, decrypted) + XCTAssertEqual(cleartext, decrypted) } func testCalculateCiphertextSize() { From 2258946aaa7846b1fc07958253a6532c76ebe965 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 15 Jan 2021 09:39:48 +0100 Subject: [PATCH 100/144] Updated Podspec so that it matches 587fd39 --- CryptomatorCryptoLib.podspec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 211726e..446e733 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -16,6 +16,9 @@ Pod::Spec.new do |s| s.osx.deployment_target = '10.12' s.swift_version = '5.1' - s.dependency 'CryptoSwift', '~> 1.3.0' s.dependency 'SwiftBase32', '~> 0.8.0' + + s.subspec 'scrypt' do |ss| + ss.source_files = 'Sources/scrypt/**/*.{h,c}' + end end From cec8283b2b2b049de4e912c344c038cdb05c46d0 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 15 Jan 2021 12:15:02 +0100 Subject: [PATCH 101/144] Fixed Podspec --- .github/workflows/build.yml | 4 ++-- CryptomatorCryptoLib.podspec | 1 + Sources/CryptomatorCryptoLib/MasterkeyFile.swift | 4 ++++ Sources/scrypt/scrypt.h | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ab87fb..69447ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,8 +38,8 @@ jobs: run: | set -eo pipefail export LIB_VERSION=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags' - pod lib lint - pod trunk push + pod lib lint --allow-warnings + pod trunk push --allow-warnings env: COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} - name: Draft release diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec index 446e733..0a810d6 100644 --- a/CryptomatorCryptoLib.podspec +++ b/CryptomatorCryptoLib.podspec @@ -19,6 +19,7 @@ Pod::Spec.new do |s| s.dependency 'SwiftBase32', '~> 0.8.0' s.subspec 'scrypt' do |ss| + ss.public_header_files = 'Sources/scrypt/scrypt.h', 'Sources/scrypt/include/crypto_scrypt.h' ss.source_files = 'Sources/scrypt/**/*.{h,c}' end end diff --git a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift index d633179..9edf67b 100644 --- a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift +++ b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift @@ -8,7 +8,11 @@ import CommonCrypto import Foundation +#if COCOAPODS +import CryptomatorCryptoLib.scrypt +#else import scrypt +#endif struct Content: Codable, Equatable { let version: Int diff --git a/Sources/scrypt/scrypt.h b/Sources/scrypt/scrypt.h index 94b4633..6286a3c 100644 --- a/Sources/scrypt/scrypt.h +++ b/Sources/scrypt/scrypt.h @@ -15,6 +15,10 @@ FOUNDATION_EXPORT double scryptVersionNumber; FOUNDATION_EXPORT const unsigned char scryptVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import +#if COCOAPODS +#import +#else #import +#endif From 448bbe3f66faca6ac262d5a75a8be55c57c3aa2a Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 15 Jan 2021 12:21:36 +0100 Subject: [PATCH 102/144] Removed SWIFT_PACKAGE flag --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 7 +++---- Sources/CryptomatorCryptoLib/Cryptor.swift | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 078b248..498f452 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -496,8 +496,8 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( - "SWIFT_PACKAGE=1", "DEBUG=1", + "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -512,7 +512,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=100 -Xfrontend -warn-long-function-bodies=100"; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -560,8 +560,8 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( - "SWIFT_PACKAGE=1", "DEBUG=1", + "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -575,7 +575,6 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=100 -Xfrontend -warn-long-function-bodies=100"; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 622c449..974c7e5 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -8,10 +8,10 @@ import CommonCrypto import Foundation -#if SWIFT_PACKAGE -import Base32 -#else +#if COCOAPODS import SwiftBase32 +#else +import Base32 #endif public extension Data { From 4ea008581956cd989b2cf3f16b811db7720ff1ba Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 15 Jan 2021 12:28:27 +0100 Subject: [PATCH 103/144] Removed DEBUG flag from release build --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 498f452..0540501 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -559,10 +559,6 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; From 6b998c93971940f11e29c76edf1312d006b63dea Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 15 Jan 2021 17:48:17 +0100 Subject: [PATCH 104/144] Apparently, CocoaPods doesn't like include and "quotes" should be used instead --- Sources/scrypt/scrypt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/scrypt/scrypt.h b/Sources/scrypt/scrypt.h index 6286a3c..1de09ec 100644 --- a/Sources/scrypt/scrypt.h +++ b/Sources/scrypt/scrypt.h @@ -16,7 +16,7 @@ FOUNDATION_EXPORT const unsigned char scryptVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import #if COCOAPODS -#import +#import "crypto_scrypt.h" #else #import #endif From c6341f5c3b08d5a91122c37d502261de67eede42 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 18 Jan 2021 21:17:48 +0100 Subject: [PATCH 105/144] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 195301a..ead7224 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,6 @@ In general, the following preference is used to choose the implementation of cry 1. Apple Swift Crypto (HMAC) 2. Apple CommonCrypto (AES-CTR, RFC 3394 Key Derivation) -3. CryptoSwift (scrypt) ## Code of Conduct From ffb574cf3f259688e6b75a78598a892bfe907398 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 4 Feb 2021 16:58:03 +0100 Subject: [PATCH 106/144] Updated project to Xcode 12.4 --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 0540501..e360612 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -316,7 +316,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1230; + LastUpgradeCheck = 1240; ORGANIZATIONNAME = "Skymatic GmbH"; TargetAttributes = { 4A7C21312451F2AC00DE81E6 = { diff --git a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme index c9436ed..12be3dd 100644 --- a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme +++ b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme @@ -1,6 +1,6 @@ Date: Thu, 4 Feb 2021 16:58:33 +0100 Subject: [PATCH 107/144] Updated MasterkeyFileError names --- Sources/CryptomatorCryptoLib/MasterkeyFile.swift | 10 +++++----- .../CryptomatorCryptoLibTests/MasterkeyFileTests.swift | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift index 9edf67b..4886dc0 100644 --- a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift +++ b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift @@ -26,9 +26,9 @@ struct Content: Codable, Equatable { public enum MasterkeyFileError: Error, Equatable { case malformedMasterkeyFile(_ reason: String) - case invalidPassword + case invalidPassphrase case keyDerivationFailed - case keyWrappingFailed(_ status: CCCryptorStatus) + case keyWrapFailed(_ status: CCCryptorStatus) } public class MasterkeyFile { @@ -195,7 +195,7 @@ public class MasterkeyFile { if status == kCCSuccess { return wrappedKey } else { - throw MasterkeyFileError.keyWrappingFailed(status) + throw MasterkeyFileError.keyWrapFailed(status) } } @@ -207,9 +207,9 @@ public class MasterkeyFile { assert(unwrappedKeyLen == kCCKeySizeAES256) return unwrappedKey } else if status == kCCDecodeError { - throw MasterkeyFileError.invalidPassword + throw MasterkeyFileError.invalidPassphrase } else { - throw MasterkeyFileError.keyWrappingFailed(status) + throw MasterkeyFileError.keyWrapFailed(status) } } } diff --git a/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift index da987fe..53cdacc 100644 --- a/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift +++ b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift @@ -66,7 +66,7 @@ class MasterkeyFileTests: XCTestCase { """.data(using: .utf8)! let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "qwe", pepper: [UInt8](), expectedVaultVersion: 7), "wrong passphrase") { error in - XCTAssertEqual(.invalidPassword, error as? MasterkeyFileError) + XCTAssertEqual(.invalidPassphrase, error as? MasterkeyFileError) } } @@ -180,7 +180,7 @@ class MasterkeyFileTests: XCTestCase { XCTAssertEqual(expectedKey, masterkey.aesMasterKey) XCTAssertEqual(expectedKey, masterkey.macMasterKey) XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "wrong passphrase") { error in - XCTAssertEqual(.invalidPassword, error as? MasterkeyFileError) + XCTAssertEqual(.invalidPassphrase, error as? MasterkeyFileError) } } @@ -196,7 +196,7 @@ class MasterkeyFileTests: XCTestCase { let key = [UInt8](repeating: 0x77, count: 17) let kek = [UInt8](repeating: 0x55, count: 32) XCTAssertThrowsError(try MasterkeyFile.wrapKey(key, kek: kek), "invalid key") { error in - XCTAssertEqual(.keyWrappingFailed(CCCryptorStatus(kCCParamError)), error as? MasterkeyFileError) + XCTAssertEqual(.keyWrapFailed(CCCryptorStatus(kCCParamError)), error as? MasterkeyFileError) } } } From d246f3f6112bf7e82b0cd99369ae26d8c56f472f Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sun, 7 Feb 2021 10:22:54 +0100 Subject: [PATCH 108/144] Updated README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ead7224..e823088 100644 --- a/README.md +++ b/README.md @@ -65,14 +65,14 @@ Create a masterkey file with content provided either from URL: ```swift let url = ... -let masterkey = try MasterkeyFile.withContentFromURL(url: url) +let masterkeyFile = try MasterkeyFile.withContentFromURL(url: url) ``` Or from JSON data: ```swift let data = ... -let masterkey = try MasterkeyFile.withContentFromData(data: data) +let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) ``` #### Unlock From d324ab152bc4405ce18c79765919615ddd761f6d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Sun, 7 Feb 2021 10:27:59 +0100 Subject: [PATCH 109/144] Removed expectedVaultVersion param during unlock --- Sources/CryptomatorCryptoLib/MasterkeyFile.swift | 9 +++------ .../MasterkeyFileTests.swift | 16 ++++++++-------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift index 4886dc0..ea3e910 100644 --- a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift +++ b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift @@ -76,10 +76,9 @@ public class MasterkeyFile { - Parameter passphrase: The passphrase used during key derivation. - Parameter pepper: An optional application-specific pepper added to the scrypt's salt. Defaults to empty byte array. - - Parameter expectedVaultVersion: An optional expected vault version. - Returns: A masterkey with the unwrapped keys. */ - public func unlock(passphrase: String, pepper: [UInt8] = [UInt8](), expectedVaultVersion: Int? = nil) throws -> Masterkey { + public func unlock(passphrase: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { // derive keys: let pw = [UInt8](passphrase.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: content.scryptSalt)!) @@ -98,15 +97,13 @@ public class MasterkeyFile { let macKey = try MasterkeyFile.unwrapKey([UInt8](wrappedHmacKey), kek: kek) // check MAC: - if let expectedVaultVersion = expectedVaultVersion { - try checkVaultVersion(expectedVaultVersion: expectedVaultVersion, macKey: macKey) - } + try checkVaultVersion(macKey: macKey) // construct key: return Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) } - private func checkVaultVersion(expectedVaultVersion: Int, macKey: [UInt8]) throws { + private func checkVaultVersion(macKey: [UInt8]) throws { guard let storedVersionMac = Data(base64Encoded: content.versionMac), storedVersionMac.count == CC_SHA256_DIGEST_LENGTH else { throw MasterkeyFileError.malformedMasterkeyFile("invalid base64 data in versionMac") } diff --git a/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift index 53cdacc..2ec13e4 100644 --- a/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift +++ b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift @@ -47,7 +47,7 @@ class MasterkeyFileTests: XCTestCase { } """.data(using: .utf8)! let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) - let masterkey = try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7) + let masterkey = try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8]()) XCTAssertEqual(expectedKey, masterkey.aesMasterKey) XCTAssertEqual(expectedKey, masterkey.macMasterKey) } @@ -65,7 +65,7 @@ class MasterkeyFileTests: XCTestCase { } """.data(using: .utf8)! let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) - XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "qwe", pepper: [UInt8](), expectedVaultVersion: 7), "wrong passphrase") { error in + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "qwe", pepper: [UInt8]()), "wrong passphrase") { error in XCTAssertEqual(.invalidPassphrase, error as? MasterkeyFileError) } } @@ -83,7 +83,7 @@ class MasterkeyFileTests: XCTestCase { } """.data(using: .utf8)! let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) - XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "invalid version mac") { error in + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8]()), "invalid version mac") { error in XCTAssertEqual(.malformedMasterkeyFile("incorrect version or versionMac"), error as? MasterkeyFileError) } } @@ -101,7 +101,7 @@ class MasterkeyFileTests: XCTestCase { } """.data(using: .utf8)! let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) - XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "malformed json") { error in + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8]()), "malformed json") { error in XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"), error as? MasterkeyFileError) } } @@ -119,7 +119,7 @@ class MasterkeyFileTests: XCTestCase { } """.data(using: .utf8)! let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) - XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "malformed json") { error in + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8]()), "malformed json") { error in XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"), error as? MasterkeyFileError) } } @@ -137,7 +137,7 @@ class MasterkeyFileTests: XCTestCase { } """.data(using: .utf8)! let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) - XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "malformed json") { error in + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8]()), "malformed json") { error in XCTAssertEqual(.malformedMasterkeyFile("invalid base64 data in versionMac"), error as? MasterkeyFileError) } } @@ -176,10 +176,10 @@ class MasterkeyFileTests: XCTestCase { """.data(using: .utf8)! let content = try MasterkeyFile.changePassphrase(masterkeyFileData: data, oldPassphrase: "asd", newPassphrase: "qwe", pepper: [UInt8](), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) let masterkeyFile = MasterkeyFile(content: content) - let masterkey = try masterkeyFile.unlock(passphrase: "qwe", pepper: [UInt8](), expectedVaultVersion: 7) + let masterkey = try masterkeyFile.unlock(passphrase: "qwe", pepper: [UInt8]()) XCTAssertEqual(expectedKey, masterkey.aesMasterKey) XCTAssertEqual(expectedKey, masterkey.macMasterKey) - XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8](), expectedVaultVersion: 7), "wrong passphrase") { error in + XCTAssertThrowsError(try masterkeyFile.unlock(passphrase: "asd", pepper: [UInt8]()), "wrong passphrase") { error in XCTAssertEqual(.invalidPassphrase, error as? MasterkeyFileError) } } From 7a97b2540e91195ea6867659019945f6acfbd927 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 22 Feb 2021 16:40:43 +0100 Subject: [PATCH 110/144] Increased limits for performance warnings --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index e360612..fd83d46 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -510,7 +510,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=100 -Xfrontend -warn-long-function-bodies=100"; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=200 -Xfrontend -warn-long-function-bodies=200"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -569,7 +569,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=100 -Xfrontend -warn-long-function-bodies=100"; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=200 -Xfrontend -warn-long-function-bodies=200"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; From 693216c62737b44ff92e06c401db8288849805fc Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 22 Feb 2021 16:41:45 +0100 Subject: [PATCH 111/144] Updated README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e823088..7e91fa8 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,7 @@ When you have a masterkey file, you can attempt an unlock. When successful, it u let masterkeyFile = ... let passphrase = ... let pepper = ... // optional -let expectedVaultVersion = ... // optional -let masterkey = try masterkeyFile.unlock(passphrase: passphrase, pepper: pepper, expectedVaultVersion: expectedVaultVersion) +let masterkey = try masterkeyFile.unlock(passphrase: passphrase, pepper: pepper) ``` #### Lock From 695ddb22b9c37098e55d28cc555f1fed0b3a810b Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 2 Mar 2021 12:43:33 +0100 Subject: [PATCH 112/144] Added encoded property to Masterkey --- Sources/CryptomatorCryptoLib/Masterkey.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/CryptomatorCryptoLib/Masterkey.swift b/Sources/CryptomatorCryptoLib/Masterkey.swift index af5cbf5..709ad59 100644 --- a/Sources/CryptomatorCryptoLib/Masterkey.swift +++ b/Sources/CryptomatorCryptoLib/Masterkey.swift @@ -12,6 +12,9 @@ import Foundation public class Masterkey { private(set) var aesMasterKey: [UInt8] private(set) var macMasterKey: [UInt8] + public var encoded: [UInt8] { + return aesMasterKey + macMasterKey + } private init(aesMasterKey: [UInt8], macMasterKey: [UInt8]) { self.aesMasterKey = aesMasterKey From 365ede1f3f0eb20526d521e579b9f2a83227a60e Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 3 Mar 2021 08:36:45 +0100 Subject: [PATCH 113/144] Renamed encoded property to rawKey in Masterkey --- Sources/CryptomatorCryptoLib/Masterkey.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CryptomatorCryptoLib/Masterkey.swift b/Sources/CryptomatorCryptoLib/Masterkey.swift index 709ad59..230a405 100644 --- a/Sources/CryptomatorCryptoLib/Masterkey.swift +++ b/Sources/CryptomatorCryptoLib/Masterkey.swift @@ -12,7 +12,7 @@ import Foundation public class Masterkey { private(set) var aesMasterKey: [UInt8] private(set) var macMasterKey: [UInt8] - public var encoded: [UInt8] { + public var rawKey: [UInt8] { return aesMasterKey + macMasterKey } From 6bc5afa72d0896d469563b0352776c4ec5b6f896 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 5 Mar 2021 16:20:33 +0100 Subject: [PATCH 114/144] Updated scrypt dependency to version 1.3.1 --- Sources/scrypt/crypto_scrypt.c | 1 - Sources/scrypt/include/crypto_scrypt.h | 7 +++---- Sources/scrypt/include/insecure_memzero.h | 5 ++--- Sources/scrypt/include/sha256.h | 5 ++--- Sources/scrypt/include/sysendian.h | 9 ++++----- Sources/scrypt/insecure_memzero.c | 5 ++--- Sources/scrypt/sha256.c | 22 ++++++++++++---------- 7 files changed, 25 insertions(+), 29 deletions(-) diff --git a/Sources/scrypt/crypto_scrypt.c b/Sources/scrypt/crypto_scrypt.c index c062895..28e0ebc 100644 --- a/Sources/scrypt/crypto_scrypt.c +++ b/Sources/scrypt/crypto_scrypt.c @@ -26,7 +26,6 @@ * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ - #include #include #include diff --git a/Sources/scrypt/include/crypto_scrypt.h b/Sources/scrypt/include/crypto_scrypt.h index 43eb388..7c7ddb6 100644 --- a/Sources/scrypt/include/crypto_scrypt.h +++ b/Sources/scrypt/include/crypto_scrypt.h @@ -26,19 +26,18 @@ * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ - #ifndef _CRYPTO_SCRYPT_H_ #define _CRYPTO_SCRYPT_H_ +#include #include -#include /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, * p, buflen) and write the result into buf. The parameters r, p, and buflen - * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N - * must be a power of 2 greater than 1. + * must satisfy 0 < r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter + * N must be a power of 2 greater than 1. * * Return 0 on success; or -1 on error. */ diff --git a/Sources/scrypt/include/insecure_memzero.h b/Sources/scrypt/include/insecure_memzero.h index 757c417..9aca8fc 100644 --- a/Sources/scrypt/include/insecure_memzero.h +++ b/Sources/scrypt/include/insecure_memzero.h @@ -1,6 +1,6 @@ /*- - * Copyright 2005-2016 Colin Percival. All rights reserved. - * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2005-2020 Colin Percival. All rights reserved. + * Copyright 2011-2020 Tarsnap Backup Inc. All rights reserved. * Copyright 2014 Sean Kelly. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +24,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #ifndef _INSECURE_MEMZERO_H_ #define _INSECURE_MEMZERO_H_ diff --git a/Sources/scrypt/include/sha256.h b/Sources/scrypt/include/sha256.h index f91cfa1..b58d33e 100644 --- a/Sources/scrypt/include/sha256.h +++ b/Sources/scrypt/include/sha256.h @@ -1,6 +1,6 @@ /*- - * Copyright 2005-2016 Colin Percival. All rights reserved. - * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2005-2020 Colin Percival. All rights reserved. + * Copyright 2011-2020 Tarsnap Backup Inc. All rights reserved. * Copyright 2014 Sean Kelly. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +24,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #ifndef _SHA256_H_ #define _SHA256_H_ diff --git a/Sources/scrypt/include/sysendian.h b/Sources/scrypt/include/sysendian.h index ca9a923..9b1436b 100644 --- a/Sources/scrypt/include/sysendian.h +++ b/Sources/scrypt/include/sysendian.h @@ -1,6 +1,6 @@ /*- - * Copyright 2005-2016 Colin Percival. All rights reserved. - * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2005-2020 Colin Percival. All rights reserved. + * Copyright 2011-2020 Tarsnap Backup Inc. All rights reserved. * Copyright 2014 Sean Kelly. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +24,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #ifndef _SYSENDIAN_H_ #define _SYSENDIAN_H_ @@ -49,7 +48,7 @@ be16dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; - return ((uint16_t)(p[1]) + ((uint16_t)(p[0]) << 8)); + return (uint16_t)((uint16_t)(p[1]) + ((uint16_t)(p[0]) << 8)); } static inline void @@ -112,7 +111,7 @@ le16dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; - return ((uint16_t)(p[0]) + ((uint16_t)(p[1]) << 8)); + return (uint16_t)((uint16_t)(p[0]) + ((uint16_t)(p[1]) << 8)); } static inline void diff --git a/Sources/scrypt/insecure_memzero.c b/Sources/scrypt/insecure_memzero.c index 2f1c4f6..b8a19d9 100644 --- a/Sources/scrypt/insecure_memzero.c +++ b/Sources/scrypt/insecure_memzero.c @@ -1,6 +1,6 @@ /*- - * Copyright 2005-2016 Colin Percival. All rights reserved. - * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2005-2020 Colin Percival. All rights reserved. + * Copyright 2011-2020 Tarsnap Backup Inc. All rights reserved. * Copyright 2014 Sean Kelly. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +24,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #include #include diff --git a/Sources/scrypt/sha256.c b/Sources/scrypt/sha256.c index 8d55a50..012310a 100644 --- a/Sources/scrypt/sha256.c +++ b/Sources/scrypt/sha256.c @@ -1,6 +1,6 @@ /*- - * Copyright 2005-2016 Colin Percival. All rights reserved. - * Copyright 2005-2016 Tarsnap Backup Inc. All rights reserved. + * Copyright 2005-2020 Colin Percival. All rights reserved. + * Copyright 2011-2020 Tarsnap Backup Inc. All rights reserved. * Copyright 2014 Sean Kelly. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +24,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #include #include #include @@ -88,6 +87,12 @@ static const uint32_t Krnd[64] = { 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; +/* Magic initialization constants. */ +static const uint32_t initial_state[8] = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + /* Elementary functions used by SHA256 */ #define Ch(x, y, z) ((x & (y ^ z)) ^ z) #define Maj(x, y, z) ((x & (y | z)) | (y & z)) @@ -102,7 +107,7 @@ static const uint32_t Krnd[64] = { #define RND(a, b, c, d, e, f, g, h, k) \ h += S1(e) + Ch(e, f, g) + k; \ d += h; \ - h += S0(a) + Maj(a, b, c); + h += S0(a) + Maj(a, b, c) /* Adjusted round function for rotating state */ #define RNDr(S, W, i, ii) \ @@ -213,12 +218,6 @@ SHA256_Pad(SHA256_CTX * ctx, uint32_t tmp32[static restrict 72]) SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); } -/* Magic initialization constants. */ -static const uint32_t initial_state[8] = { - 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, - 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 -}; - /** * SHA256_Init(ctx): * Initialize the SHA256 context ${ctx}. @@ -453,6 +452,9 @@ HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx) /* Call the real function. */ _HMAC_SHA256_Final(digest, ctx, tmp32, ihash); + /* Clear the context state. */ + insecure_memzero(ctx, sizeof(HMAC_SHA256_CTX)); + /* Clean the stack. */ insecure_memzero(tmp32, 288); insecure_memzero(ihash, 32); From 98c97861308d82dc384991e7fb9d83c05ec0dd4d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 9 Mar 2021 12:30:01 +0100 Subject: [PATCH 115/144] moved chunk encryption to ContenCryptor --- .../project.pbxproj | 4 + .../CryptomatorCryptoLib/ContentCryptor.swift | 89 +++++++++++++++++++ Sources/CryptomatorCryptoLib/Cryptor.swift | 20 ++--- .../CryptorTests.swift | 11 ++- 4 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 Sources/CryptomatorCryptoLib/ContentCryptor.swift diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index fd83d46..f2a8d54 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 9E35C4EB24576A3D0006E50C /* CryptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */; }; 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA524599C6900A37B01 /* AesSiv.swift */; }; 9E44EEA92459AB1500A37B01 /* AesSivTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44EEA724599C7800A37B01 /* AesSivTests.swift */; }; + 9E97DC8D25F77BA40046C83E /* ContentCryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E97DC8C25F77BA40046C83E /* ContentCryptor.swift */; }; 9E9BB812245412E900F9FF51 /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB811245412E900F9FF51 /* Cryptor.swift */; }; 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB8132454708600F9FF51 /* Masterkey.swift */; }; 9E9BB81624558DFF00F9FF51 /* MasterkeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */; }; @@ -90,6 +91,7 @@ 9E35C4EA24576A3D0006E50C /* CryptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorTests.swift; sourceTree = ""; }; 9E44EEA524599C6900A37B01 /* AesSiv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSiv.swift; sourceTree = ""; }; 9E44EEA724599C7800A37B01 /* AesSivTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AesSivTests.swift; sourceTree = ""; }; + 9E97DC8C25F77BA40046C83E /* ContentCryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentCryptor.swift; sourceTree = ""; }; 9E9BB811245412E900F9FF51 /* Cryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = ""; }; 9E9BB8132454708600F9FF51 /* Masterkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Masterkey.swift; sourceTree = ""; }; 9E9BB81524558DFF00F9FF51 /* MasterkeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterkeyTests.swift; sourceTree = ""; }; @@ -157,6 +159,7 @@ 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, 74B4D38C2588CD60006C0567 /* MasterkeyFile.swift */, + 9E97DC8C25F77BA40046C83E /* ContentCryptor.swift */, ); path = CryptomatorCryptoLib; sourceTree = ""; @@ -409,6 +412,7 @@ 74F0F75A2490C1EB00B4C26D /* CryptoSupport.swift in Sources */, 9E9BB8142454708600F9FF51 /* Masterkey.swift in Sources */, 9E44EEA624599C6900A37B01 /* AesSiv.swift in Sources */, + 9E97DC8D25F77BA40046C83E /* ContentCryptor.swift in Sources */, 74F0F754248FC89B00B4C26D /* CryptoError.swift in Sources */, 74B4D38D2588CD60006C0567 /* MasterkeyFile.swift in Sources */, ); diff --git a/Sources/CryptomatorCryptoLib/ContentCryptor.swift b/Sources/CryptomatorCryptoLib/ContentCryptor.swift new file mode 100644 index 0000000..420149c --- /dev/null +++ b/Sources/CryptomatorCryptoLib/ContentCryptor.swift @@ -0,0 +1,89 @@ +// +// ContentCryptor.swift +// CryptomatorCryptoLib +// +// Created by Sebastian Stenzel on 09.03.21. +// Copyright © 2021 Skymatic GmbH. All rights reserved. +// + +import CommonCrypto +import Foundation + +protocol ContentCryptor { + + var nonceLen: Int { get } + var tagLen: Int { get } + + /** + Encrypts one single chunk of cleartext data. + + - Parameter chunk: The cleartext to be encrypted. + - Parameter nonce: The nonce/IV to use. + - Parameter ad: Associated data, which needs to be authenticated during decryption. + - Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array + */ + func encrypt(_ chunk: [UInt8], key: [UInt8], nonce: [UInt8], ad: [UInt8]...) throws -> [UInt8] + + /** + Decrypts one single chunk of encrypted data. + + - Parameter chunk: The nonce/IV + ciphertext + MAC/tag, as a concatenated byte array. + - Parameter ad: Associated data, which needs to be authenticated during decryption. + - Returns: The original cleartext + */ + func decrypt(_ chunk: [UInt8], key: [UInt8], ad: [UInt8]...) throws -> [UInt8] +} + +class CtrThenHmacContentCryptor : ContentCryptor { + + private let macKey: [UInt8] + private let cryptoSupport: CryptoSupport + + var nonceLen: Int { + get { + return kCCBlockSizeAES128 + } + } + var tagLen: Int { + get { + return Int(CC_SHA256_DIGEST_LENGTH) + } + } + + init(macKey: [UInt8], cryptoSupport: CryptoSupport) { + self.macKey = macKey + self.cryptoSupport = cryptoSupport + } + + func encrypt(_ chunk: [UInt8], key: [UInt8], nonce: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + let ciphertext = try AesCtr.compute(key: key, iv: nonce, data: chunk) + let mac = computeHmac(ciphertext, nonce: nonce, ad: ad) + return nonce + ciphertext + mac + } + + func decrypt(_ chunk: [UInt8], key: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + assert(chunk.count >= nonceLen + tagLen, "ciphertext chunk must at least contain nonce + tag") + + // decompose chunk: + let beginOfMAC = chunk.count - tagLen + let chunkNonce = [UInt8](chunk[0 ..< nonceLen]) + let ciphertext = [UInt8](chunk[nonceLen ..< beginOfMAC]) + let expectedMAC = [UInt8](chunk[beginOfMAC...]) + + // check MAC: + let mac = computeHmac(ciphertext, nonce: chunkNonce, ad: ad) + guard cryptoSupport.compareBytes(expected: expectedMAC, actual: mac) else { + throw CryptoError.unauthenticCiphertext + } + + // decrypt: + return try AesCtr.compute(key: key, iv: chunkNonce, data: ciphertext) + } + + private func computeHmac(_ ciphertext: [UInt8], nonce: [UInt8], ad: [[UInt8]]) -> [UInt8] { + let data = ad.reduce([UInt8](), +) + nonce + ciphertext + var mac = [UInt8](repeating: 0x00, count: tagLen) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macKey, macKey.count, data, data.count, &mac) + return mac + } +} diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 974c7e5..23706a7 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -71,14 +71,18 @@ public class Cryptor { private let masterkey: Masterkey private let cryptoSupport: CryptoSupport + private let contentCryptor: ContentCryptor - init(masterkey: Masterkey, cryptoSupport: CryptoSupport) { + init(masterkey: Masterkey, cryptoSupport: CryptoSupport, contentCryptor: ContentCryptor) { self.masterkey = masterkey self.cryptoSupport = cryptoSupport + self.contentCryptor = contentCryptor } public convenience init(masterkey: Masterkey) { - self.init(masterkey: masterkey, cryptoSupport: CryptoSupport()) + let cryptoSupport = CryptoSupport(); + let contentCryptor = CtrThenHmacContentCryptor(macKey: masterkey.macMasterKey, cryptoSupport: cryptoSupport) + self.init(masterkey: masterkey, cryptoSupport: cryptoSupport, contentCryptor: contentCryptor) } // MARK: - Path Encryption and Decryption @@ -155,11 +159,7 @@ public class Cryptor { func encryptHeader(_ header: FileHeader) throws -> [UInt8] { let cleartext = [UInt8](repeating: 0xFF, count: Cryptor.fileHeaderLegacyPayloadSize) + header.contentKey - let ciphertext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: header.nonce, data: cleartext) - let toBeAuthenticated = header.nonce + ciphertext - var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) - return header.nonce + ciphertext + mac + return try contentCryptor.encrypt(cleartext, key: masterkey.aesMasterKey, nonce: header.nonce) } func decryptHeader(_ header: [UInt8]) throws -> FileHeader { @@ -309,11 +309,7 @@ public class Cryptor { func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { let chunkNonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) - let ciphertext = try AesCtr.compute(key: fileKey, iv: chunkNonce, data: chunk) - let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext - var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) - return chunkNonce + ciphertext + mac + return try contentCryptor.encrypt(chunk, key: fileKey, nonce: chunkNonce, ad: headerNonce, chunkNumber.bigEndian.byteArray()) } func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { diff --git a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift index 849e48e..362d9b1 100644 --- a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift +++ b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift @@ -10,11 +10,18 @@ import XCTest @testable import CryptomatorCryptoLib class CryptorTests: XCTestCase { - let cryptor = Cryptor(masterkey: Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32)), cryptoSupport: CryptoSupportMock()) + var cryptor: Cryptor! var tmpDirURL: URL! override func setUpWithError() throws { - tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString, isDirectory: true) + let aesKey = [UInt8](repeating: 0x55, count: 32) + let macKey = [UInt8](repeating: 0x77, count: 32) + let masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) + let cryptoSupport = CryptoSupportMock() + let contentCryptor = CtrThenHmacContentCryptor(macKey: macKey, cryptoSupport: cryptoSupport) + cryptor = Cryptor(masterkey: masterkey, cryptoSupport: cryptoSupport, contentCryptor: contentCryptor) + + tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tmpDirURL, withIntermediateDirectories: true) } From 0db203d274cfb321d91a9ab2daf50a1712bcd3ca Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 9 Mar 2021 12:30:19 +0100 Subject: [PATCH 116/144] moved chunk decryption to ContenCryptor --- Sources/CryptomatorCryptoLib/Cryptor.swift | 39 +++------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 23706a7..5517a0c 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -163,23 +163,9 @@ public class Cryptor { } func decryptHeader(_ header: [UInt8]) throws -> FileHeader { - // decompose header: - let beginOfMAC = header.count - Int(CC_SHA256_DIGEST_LENGTH) - let nonce = [UInt8](header[0 ..< kCCBlockSizeAES128]) - let ciphertext = [UInt8](header[kCCBlockSizeAES128 ..< beginOfMAC]) - let expectedMAC = [UInt8](header[beginOfMAC...]) - - // check MAC: - let toBeAuthenticated = nonce + ciphertext - var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) - guard cryptoSupport.compareBytes(expected: expectedMAC, actual: mac) else { - throw CryptoError.unauthenticCiphertext - } - - // decrypt: - let cleartext = try AesCtr.compute(key: masterkey.aesMasterKey, iv: nonce, data: ciphertext) - let contentKey = [UInt8](cleartext[Cryptor.fileHeaderLegacyPayloadSize...]) + let nonce = [UInt8](header[0 ..< contentCryptor.nonceLen]) + let cleartext = try contentCryptor.decrypt(header, key: masterkey.aesMasterKey) + let contentKey = [UInt8](cleartext[Cryptor.fileHeaderLegacyPayloadSize...]) return FileHeader(nonce: nonce, contentKey: contentKey) } @@ -313,24 +299,7 @@ public class Cryptor { } func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { - assert(chunk.count >= kCCBlockSizeAES128 + Int(CC_SHA256_DIGEST_LENGTH), "ciphertext chunk must at least contain nonce + mac") - - // decompose chunk: - let beginOfMAC = chunk.count - Int(CC_SHA256_DIGEST_LENGTH) - let chunkNonce = [UInt8](chunk[0 ..< kCCBlockSizeAES128]) - let ciphertext = [UInt8](chunk[kCCBlockSizeAES128 ..< beginOfMAC]) - let expectedMAC = [UInt8](chunk[beginOfMAC...]) - - // check MAC: - let toBeAuthenticated = headerNonce + chunkNumber.bigEndian.byteArray() + chunkNonce + ciphertext - var mac = [UInt8](repeating: 0x00, count: Int(CC_SHA256_DIGEST_LENGTH)) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), masterkey.macMasterKey, masterkey.macMasterKey.count, toBeAuthenticated, toBeAuthenticated.count, &mac) - guard cryptoSupport.compareBytes(expected: expectedMAC, actual: mac) else { - throw CryptoError.unauthenticCiphertext - } - - // decrypt: - return try AesCtr.compute(key: fileKey, iv: chunkNonce, data: ciphertext) + return try contentCryptor.decrypt(chunk, key: fileKey, ad: headerNonce, chunkNumber.bigEndian.byteArray()) } // MARK: - File Size Calculation From 262cdb2800a53c31f6a73fcdb5bcb4bc4227f025 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 9 Mar 2021 13:06:35 +0100 Subject: [PATCH 117/144] calculated ciphertext/cleartext ratio using overhead from ContentCryptor instead of hard-coded fields --- Sources/CryptomatorCryptoLib/Cryptor.swift | 46 +++++++++++++--------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 5517a0c..e54fab2 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -64,10 +64,19 @@ struct FileHeader { } public class Cryptor { - static let fileHeaderLegacyPayloadSize = 8 - public static let fileHeaderSize = kCCBlockSizeAES128 + fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + Int(CC_SHA256_DIGEST_LENGTH) - static let cleartextChunkSize = 32 * 1024 - static let ciphertextChunkSize = kCCBlockSizeAES128 + cleartextChunkSize + Int(CC_SHA256_DIGEST_LENGTH) + private let fileHeaderLegacyPayloadSize = 8 + private let cleartextChunkSize = 32 * 1024 + private var ciphertextChunkSize: Int { + get { + return contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen + } + } + public var fileHeaderSize: Int { + get { + let fileHeaderPayloadSize = fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + return contentCryptor.nonceLen + fileHeaderPayloadSize + contentCryptor.tagLen + } + } private let masterkey: Masterkey private let cryptoSupport: CryptoSupport @@ -158,14 +167,14 @@ public class Cryptor { } func encryptHeader(_ header: FileHeader) throws -> [UInt8] { - let cleartext = [UInt8](repeating: 0xFF, count: Cryptor.fileHeaderLegacyPayloadSize) + header.contentKey + let cleartext = [UInt8](repeating: 0xFF, count: fileHeaderLegacyPayloadSize) + header.contentKey return try contentCryptor.encrypt(cleartext, key: masterkey.aesMasterKey, nonce: header.nonce) } func decryptHeader(_ header: [UInt8]) throws -> FileHeader { let nonce = [UInt8](header[0 ..< contentCryptor.nonceLen]) let cleartext = try contentCryptor.decrypt(header, key: masterkey.aesMasterKey) - let contentKey = [UInt8](cleartext[Cryptor.fileHeaderLegacyPayloadSize...]) + let contentKey = [UInt8](cleartext[fileHeaderLegacyPayloadSize...]) return FileHeader(nonce: nonce, contentKey: contentKey) } @@ -222,7 +231,7 @@ public class Cryptor { // encrypt and write ciphertext content: var chunkNumber: UInt64 = 0 while cleartextStream.hasBytesAvailable { - guard let cleartextChunk = try cleartextStream.read(maxLength: Cryptor.cleartextChunkSize) else { + guard let cleartextChunk = try cleartextStream.read(maxLength: cleartextChunkSize) else { continue } let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) @@ -268,22 +277,23 @@ public class Cryptor { func decryptContent(from ciphertextStream: InputStream, to cleartextStream: OutputStream, ciphertextSize: Int?) throws { // create progress: let progress: Progress - if let ciphertextSize = ciphertextSize, let cleartextSize = try? calculateCleartextSize(ciphertextSize - Cryptor.fileHeaderSize) { + if let ciphertextSize = ciphertextSize, let cleartextSize = try? calculateCleartextSize(ciphertextSize - fileHeaderSize) { progress = Progress(totalUnitCount: Int64(cleartextSize)) } else { progress = Progress(totalUnitCount: -1) } // read and decrypt header: - guard let ciphertextHeader = try ciphertextStream.read(maxLength: Cryptor.fileHeaderSize) else { + guard let ciphertextHeader = try ciphertextStream.read(maxLength: fileHeaderSize) else { throw CryptoError.ioError } let header = try decryptHeader(ciphertextHeader) // decrypt and write cleartext content: + let ciphertextChunkSize = contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen var chunkNumber: UInt64 = 0 while ciphertextStream.hasBytesAvailable { - guard let ciphertextChunk = try ciphertextStream.read(maxLength: Cryptor.ciphertextChunkSize) else { + guard let ciphertextChunk = try ciphertextStream.read(maxLength: ciphertextChunkSize) else { continue } let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) @@ -313,12 +323,12 @@ public class Cryptor { */ public func calculateCiphertextSize(_ cleartextSize: Int) -> Int { precondition(cleartextSize >= 0, "expected cleartextSize to be positive, but was \(cleartextSize)") - let overheadPerChunk = Cryptor.ciphertextChunkSize - Cryptor.cleartextChunkSize - let numFullChunks = cleartextSize / Cryptor.cleartextChunkSize // floor by int-truncation - let additionalCleartextBytes = cleartextSize % Cryptor.cleartextChunkSize + let overheadPerChunk = ciphertextChunkSize - cleartextChunkSize + let numFullChunks = cleartextSize / cleartextChunkSize // floor by int-truncation + let additionalCleartextBytes = cleartextSize % cleartextChunkSize let additionalCiphertextBytes = (additionalCleartextBytes == 0) ? 0 : additionalCleartextBytes + overheadPerChunk assert(additionalCiphertextBytes >= 0) - return Cryptor.ciphertextChunkSize * numFullChunks + additionalCiphertextBytes + return ciphertextChunkSize * numFullChunks + additionalCiphertextBytes } /** @@ -330,14 +340,14 @@ public class Cryptor { */ public func calculateCleartextSize(_ ciphertextSize: Int) throws -> Int { precondition(ciphertextSize >= 0, "expected ciphertextSize to be positive, but was \(ciphertextSize)") - let overheadPerChunk = Cryptor.ciphertextChunkSize - Cryptor.cleartextChunkSize - let numFullChunks = ciphertextSize / Cryptor.ciphertextChunkSize // floor by int-truncation - let additionalCiphertextBytes = ciphertextSize % Cryptor.ciphertextChunkSize + let overheadPerChunk = ciphertextChunkSize - cleartextChunkSize + let numFullChunks = ciphertextSize / ciphertextChunkSize // floor by int-truncation + let additionalCiphertextBytes = ciphertextSize % ciphertextChunkSize guard additionalCiphertextBytes == 0 || additionalCiphertextBytes > overheadPerChunk else { throw CryptoError.invalidParameter("Method not defined for input value \(ciphertextSize)") } let additionalCleartextBytes = (additionalCiphertextBytes == 0) ? 0 : additionalCiphertextBytes - overheadPerChunk assert(additionalCleartextBytes >= 0) - return Cryptor.cleartextChunkSize * numFullChunks + additionalCleartextBytes + return cleartextChunkSize * numFullChunks + additionalCleartextBytes } } From 2a06bd875bea5b8ff861faf5b7b7845b1c5220ac Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 9 Mar 2021 13:19:07 +0100 Subject: [PATCH 118/144] ran swiftformat --- .../CryptomatorCryptoLib/ContentCryptor.swift | 143 +++++++++--------- Sources/CryptomatorCryptoLib/Cryptor.swift | 55 ++++--- .../CryptorTests.swift | 18 +-- 3 files changed, 104 insertions(+), 112 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/ContentCryptor.swift b/Sources/CryptomatorCryptoLib/ContentCryptor.swift index 420149c..726a144 100644 --- a/Sources/CryptomatorCryptoLib/ContentCryptor.swift +++ b/Sources/CryptomatorCryptoLib/ContentCryptor.swift @@ -10,80 +10,75 @@ import CommonCrypto import Foundation protocol ContentCryptor { - - var nonceLen: Int { get } - var tagLen: Int { get } - - /** - Encrypts one single chunk of cleartext data. - - - Parameter chunk: The cleartext to be encrypted. - - Parameter nonce: The nonce/IV to use. - - Parameter ad: Associated data, which needs to be authenticated during decryption. - - Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array - */ - func encrypt(_ chunk: [UInt8], key: [UInt8], nonce: [UInt8], ad: [UInt8]...) throws -> [UInt8] - - /** - Decrypts one single chunk of encrypted data. - - - Parameter chunk: The nonce/IV + ciphertext + MAC/tag, as a concatenated byte array. - - Parameter ad: Associated data, which needs to be authenticated during decryption. - - Returns: The original cleartext - */ - func decrypt(_ chunk: [UInt8], key: [UInt8], ad: [UInt8]...) throws -> [UInt8] + var nonceLen: Int { get } + var tagLen: Int { get } + + /** + Encrypts one single chunk of cleartext data. + + - Parameter chunk: The cleartext to be encrypted. + - Parameter nonce: The nonce/IV to use. + - Parameter ad: Associated data, which needs to be authenticated during decryption. + - Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array + */ + func encrypt(_ chunk: [UInt8], key: [UInt8], nonce: [UInt8], ad: [UInt8]...) throws -> [UInt8] + + /** + Decrypts one single chunk of encrypted data. + + - Parameter chunk: The nonce/IV + ciphertext + MAC/tag, as a concatenated byte array. + - Parameter ad: Associated data, which needs to be authenticated during decryption. + - Returns: The original cleartext + */ + func decrypt(_ chunk: [UInt8], key: [UInt8], ad: [UInt8]...) throws -> [UInt8] } -class CtrThenHmacContentCryptor : ContentCryptor { - - private let macKey: [UInt8] - private let cryptoSupport: CryptoSupport - - var nonceLen: Int { - get { - return kCCBlockSizeAES128 - } - } - var tagLen: Int { - get { - return Int(CC_SHA256_DIGEST_LENGTH) - } - } - - init(macKey: [UInt8], cryptoSupport: CryptoSupport) { - self.macKey = macKey - self.cryptoSupport = cryptoSupport - } - - func encrypt(_ chunk: [UInt8], key: [UInt8], nonce: [UInt8], ad: [UInt8]...) throws -> [UInt8] { - let ciphertext = try AesCtr.compute(key: key, iv: nonce, data: chunk) - let mac = computeHmac(ciphertext, nonce: nonce, ad: ad) - return nonce + ciphertext + mac - } - - func decrypt(_ chunk: [UInt8], key: [UInt8], ad: [UInt8]...) throws -> [UInt8] { - assert(chunk.count >= nonceLen + tagLen, "ciphertext chunk must at least contain nonce + tag") - - // decompose chunk: - let beginOfMAC = chunk.count - tagLen - let chunkNonce = [UInt8](chunk[0 ..< nonceLen]) - let ciphertext = [UInt8](chunk[nonceLen ..< beginOfMAC]) - let expectedMAC = [UInt8](chunk[beginOfMAC...]) - - // check MAC: - let mac = computeHmac(ciphertext, nonce: chunkNonce, ad: ad) - guard cryptoSupport.compareBytes(expected: expectedMAC, actual: mac) else { - throw CryptoError.unauthenticCiphertext - } - - // decrypt: - return try AesCtr.compute(key: key, iv: chunkNonce, data: ciphertext) - } - - private func computeHmac(_ ciphertext: [UInt8], nonce: [UInt8], ad: [[UInt8]]) -> [UInt8] { - let data = ad.reduce([UInt8](), +) + nonce + ciphertext - var mac = [UInt8](repeating: 0x00, count: tagLen) - CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macKey, macKey.count, data, data.count, &mac) - return mac - } +class CtrThenHmacContentCryptor: ContentCryptor { + private let macKey: [UInt8] + private let cryptoSupport: CryptoSupport + + var nonceLen: Int { + return kCCBlockSizeAES128 + } + + var tagLen: Int { + return Int(CC_SHA256_DIGEST_LENGTH) + } + + init(macKey: [UInt8], cryptoSupport: CryptoSupport) { + self.macKey = macKey + self.cryptoSupport = cryptoSupport + } + + func encrypt(_ chunk: [UInt8], key: [UInt8], nonce: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + let ciphertext = try AesCtr.compute(key: key, iv: nonce, data: chunk) + let mac = computeHmac(ciphertext, nonce: nonce, ad: ad) + return nonce + ciphertext + mac + } + + func decrypt(_ chunk: [UInt8], key: [UInt8], ad: [UInt8]...) throws -> [UInt8] { + assert(chunk.count >= nonceLen + tagLen, "ciphertext chunk must at least contain nonce + tag") + + // decompose chunk: + let beginOfMAC = chunk.count - tagLen + let chunkNonce = [UInt8](chunk[0 ..< nonceLen]) + let ciphertext = [UInt8](chunk[nonceLen ..< beginOfMAC]) + let expectedMAC = [UInt8](chunk[beginOfMAC...]) + + // check MAC: + let mac = computeHmac(ciphertext, nonce: chunkNonce, ad: ad) + guard cryptoSupport.compareBytes(expected: expectedMAC, actual: mac) else { + throw CryptoError.unauthenticCiphertext + } + + // decrypt: + return try AesCtr.compute(key: key, iv: chunkNonce, data: ciphertext) + } + + private func computeHmac(_ ciphertext: [UInt8], nonce: [UInt8], ad: [[UInt8]]) -> [UInt8] { + let data = ad.reduce([UInt8](), +) + nonce + ciphertext + var mac = [UInt8](repeating: 0x00, count: tagLen) + CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), macKey, macKey.count, data, data.count, &mac) + return mac + } } diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index e54fab2..4ac78dd 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -64,34 +64,31 @@ struct FileHeader { } public class Cryptor { - private let fileHeaderLegacyPayloadSize = 8 - private let cleartextChunkSize = 32 * 1024 - private var ciphertextChunkSize: Int { - get { - return contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen - } - } - public var fileHeaderSize: Int { - get { - let fileHeaderPayloadSize = fileHeaderLegacyPayloadSize + kCCKeySizeAES256 - return contentCryptor.nonceLen + fileHeaderPayloadSize + contentCryptor.tagLen - } - } + private let fileHeaderLegacyPayloadSize = 8 + private let cleartextChunkSize = 32 * 1024 + private var ciphertextChunkSize: Int { + return contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen + } + + public var fileHeaderSize: Int { + let fileHeaderPayloadSize = fileHeaderLegacyPayloadSize + kCCKeySizeAES256 + return contentCryptor.nonceLen + fileHeaderPayloadSize + contentCryptor.tagLen + } private let masterkey: Masterkey private let cryptoSupport: CryptoSupport - private let contentCryptor: ContentCryptor + private let contentCryptor: ContentCryptor - init(masterkey: Masterkey, cryptoSupport: CryptoSupport, contentCryptor: ContentCryptor) { + init(masterkey: Masterkey, cryptoSupport: CryptoSupport, contentCryptor: ContentCryptor) { self.masterkey = masterkey self.cryptoSupport = cryptoSupport - self.contentCryptor = contentCryptor + self.contentCryptor = contentCryptor } public convenience init(masterkey: Masterkey) { - let cryptoSupport = CryptoSupport(); - let contentCryptor = CtrThenHmacContentCryptor(macKey: masterkey.macMasterKey, cryptoSupport: cryptoSupport) - self.init(masterkey: masterkey, cryptoSupport: cryptoSupport, contentCryptor: contentCryptor) + let cryptoSupport = CryptoSupport() + let contentCryptor = CtrThenHmacContentCryptor(macKey: masterkey.macMasterKey, cryptoSupport: cryptoSupport) + self.init(masterkey: masterkey, cryptoSupport: cryptoSupport, contentCryptor: contentCryptor) } // MARK: - Path Encryption and Decryption @@ -168,13 +165,13 @@ public class Cryptor { func encryptHeader(_ header: FileHeader) throws -> [UInt8] { let cleartext = [UInt8](repeating: 0xFF, count: fileHeaderLegacyPayloadSize) + header.contentKey - return try contentCryptor.encrypt(cleartext, key: masterkey.aesMasterKey, nonce: header.nonce) + return try contentCryptor.encrypt(cleartext, key: masterkey.aesMasterKey, nonce: header.nonce) } func decryptHeader(_ header: [UInt8]) throws -> FileHeader { - let nonce = [UInt8](header[0 ..< contentCryptor.nonceLen]) - let cleartext = try contentCryptor.decrypt(header, key: masterkey.aesMasterKey) - let contentKey = [UInt8](cleartext[fileHeaderLegacyPayloadSize...]) + let nonce = [UInt8](header[0 ..< contentCryptor.nonceLen]) + let cleartext = try contentCryptor.decrypt(header, key: masterkey.aesMasterKey) + let contentKey = [UInt8](cleartext[fileHeaderLegacyPayloadSize...]) return FileHeader(nonce: nonce, contentKey: contentKey) } @@ -290,7 +287,7 @@ public class Cryptor { let header = try decryptHeader(ciphertextHeader) // decrypt and write cleartext content: - let ciphertextChunkSize = contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen + let ciphertextChunkSize = contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen var chunkNumber: UInt64 = 0 while ciphertextStream.hasBytesAvailable { guard let ciphertextChunk = try ciphertextStream.read(maxLength: ciphertextChunkSize) else { @@ -305,11 +302,11 @@ public class Cryptor { func encryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { let chunkNonce = try cryptoSupport.createRandomBytes(size: kCCBlockSizeAES128) - return try contentCryptor.encrypt(chunk, key: fileKey, nonce: chunkNonce, ad: headerNonce, chunkNumber.bigEndian.byteArray()) + return try contentCryptor.encrypt(chunk, key: fileKey, nonce: chunkNonce, ad: headerNonce, chunkNumber.bigEndian.byteArray()) } func decryptSingleChunk(_ chunk: [UInt8], chunkNumber: UInt64, headerNonce: [UInt8], fileKey: [UInt8]) throws -> [UInt8] { - return try contentCryptor.decrypt(chunk, key: fileKey, ad: headerNonce, chunkNumber.bigEndian.byteArray()) + return try contentCryptor.decrypt(chunk, key: fileKey, ad: headerNonce, chunkNumber.bigEndian.byteArray()) } // MARK: - File Size Calculation @@ -323,7 +320,7 @@ public class Cryptor { */ public func calculateCiphertextSize(_ cleartextSize: Int) -> Int { precondition(cleartextSize >= 0, "expected cleartextSize to be positive, but was \(cleartextSize)") - let overheadPerChunk = ciphertextChunkSize - cleartextChunkSize + let overheadPerChunk = ciphertextChunkSize - cleartextChunkSize let numFullChunks = cleartextSize / cleartextChunkSize // floor by int-truncation let additionalCleartextBytes = cleartextSize % cleartextChunkSize let additionalCiphertextBytes = (additionalCleartextBytes == 0) ? 0 : additionalCleartextBytes + overheadPerChunk @@ -340,7 +337,7 @@ public class Cryptor { */ public func calculateCleartextSize(_ ciphertextSize: Int) throws -> Int { precondition(ciphertextSize >= 0, "expected ciphertextSize to be positive, but was \(ciphertextSize)") - let overheadPerChunk = ciphertextChunkSize - cleartextChunkSize + let overheadPerChunk = ciphertextChunkSize - cleartextChunkSize let numFullChunks = ciphertextSize / ciphertextChunkSize // floor by int-truncation let additionalCiphertextBytes = ciphertextSize % ciphertextChunkSize guard additionalCiphertextBytes == 0 || additionalCiphertextBytes > overheadPerChunk else { @@ -348,6 +345,6 @@ public class Cryptor { } let additionalCleartextBytes = (additionalCiphertextBytes == 0) ? 0 : additionalCiphertextBytes - overheadPerChunk assert(additionalCleartextBytes >= 0) - return cleartextChunkSize * numFullChunks + additionalCleartextBytes + return cleartextChunkSize * numFullChunks + additionalCleartextBytes } } diff --git a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift index 362d9b1..b795f83 100644 --- a/Tests/CryptomatorCryptoLibTests/CryptorTests.swift +++ b/Tests/CryptomatorCryptoLibTests/CryptorTests.swift @@ -10,18 +10,18 @@ import XCTest @testable import CryptomatorCryptoLib class CryptorTests: XCTestCase { - var cryptor: Cryptor! + var cryptor: Cryptor! var tmpDirURL: URL! override func setUpWithError() throws { - let aesKey = [UInt8](repeating: 0x55, count: 32) - let macKey = [UInt8](repeating: 0x77, count: 32) - let masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) - let cryptoSupport = CryptoSupportMock() - let contentCryptor = CtrThenHmacContentCryptor(macKey: macKey, cryptoSupport: cryptoSupport) - cryptor = Cryptor(masterkey: masterkey, cryptoSupport: cryptoSupport, contentCryptor: contentCryptor) - - tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString, isDirectory: true) + let aesKey = [UInt8](repeating: 0x55, count: 32) + let macKey = [UInt8](repeating: 0x77, count: 32) + let masterkey = Masterkey.createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey) + let cryptoSupport = CryptoSupportMock() + let contentCryptor = CtrThenHmacContentCryptor(macKey: macKey, cryptoSupport: cryptoSupport) + cryptor = Cryptor(masterkey: masterkey, cryptoSupport: cryptoSupport, contentCryptor: contentCryptor) + + tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tmpDirURL, withIntermediateDirectories: true) } From 1af6155a14898cb1a3ed8810506dccabfc860d4a Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 9 Mar 2021 14:23:24 +0100 Subject: [PATCH 119/144] fixed docs --- Sources/CryptomatorCryptoLib/ContentCryptor.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/CryptomatorCryptoLib/ContentCryptor.swift b/Sources/CryptomatorCryptoLib/ContentCryptor.swift index 726a144..45593e3 100644 --- a/Sources/CryptomatorCryptoLib/ContentCryptor.swift +++ b/Sources/CryptomatorCryptoLib/ContentCryptor.swift @@ -17,6 +17,7 @@ protocol ContentCryptor { Encrypts one single chunk of cleartext data. - Parameter chunk: The cleartext to be encrypted. + - Parameter key: The encryption key. - Parameter nonce: The nonce/IV to use. - Parameter ad: Associated data, which needs to be authenticated during decryption. - Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array @@ -27,6 +28,7 @@ protocol ContentCryptor { Decrypts one single chunk of encrypted data. - Parameter chunk: The nonce/IV + ciphertext + MAC/tag, as a concatenated byte array. + - Parameter key: The encryption key. - Parameter ad: Associated data, which needs to be authenticated during decryption. - Returns: The original cleartext */ From e1855062c3e10d7ee925b300d91d98e828587f6c Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 9 Mar 2021 14:23:42 +0100 Subject: [PATCH 120/144] lengths are constant --- Sources/CryptomatorCryptoLib/ContentCryptor.swift | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/ContentCryptor.swift b/Sources/CryptomatorCryptoLib/ContentCryptor.swift index 45593e3..ce60e57 100644 --- a/Sources/CryptomatorCryptoLib/ContentCryptor.swift +++ b/Sources/CryptomatorCryptoLib/ContentCryptor.swift @@ -39,13 +39,8 @@ class CtrThenHmacContentCryptor: ContentCryptor { private let macKey: [UInt8] private let cryptoSupport: CryptoSupport - var nonceLen: Int { - return kCCBlockSizeAES128 - } - - var tagLen: Int { - return Int(CC_SHA256_DIGEST_LENGTH) - } + let nonceLen = kCCBlockSizeAES128 + let tagLen = Int(CC_SHA256_DIGEST_LENGTH) init(macKey: [UInt8], cryptoSupport: CryptoSupport) { self.macKey = macKey From b8332acfd10a3daabce080ec55dc6c8344e44244 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 22 Mar 2021 12:36:44 +0100 Subject: [PATCH 121/144] Update README.md [ci skip] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7e91fa8..8342af6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ [![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcryptomator%2Fcryptolib-swift%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/cryptomator/cryptolib-swift) [![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcryptomator%2Fcryptolib-swift%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/cryptomator/cryptolib-swift) [![Version](http://img.shields.io/cocoapods/v/CryptomatorCryptoLib.svg)](https://cocoapods.org/pods/CryptomatorCryptoLib) -[![Codacy Code Quality](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) -[![Codacy Coverage](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift) +[![Codacy Code Quality](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift/dashboard) +[![Codacy Coverage](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift/dashboard) # CryptoLib Swift From ed2526c8e042dd19c17c57b4cd09c8fb443c1845 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 1 Apr 2021 10:45:41 +0200 Subject: [PATCH 122/144] Removed CocooaPods support --- .github/workflows/build.yml | 8 -------- .gitignore | 8 -------- CryptomatorCryptoLib.podspec | 25 ------------------------- README.md | 9 --------- 4 files changed, 50 deletions(-) delete mode 100644 CryptomatorCryptoLib.podspec diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 69447ee..a25ba0a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,14 +34,6 @@ jobs: if: startsWith(github.ref, 'refs/tags/') steps: - uses: actions/checkout@v2 - - name: Deploy to CocoaPods - run: | - set -eo pipefail - export LIB_VERSION=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags' - pod lib lint --allow-warnings - pod trunk push --allow-warnings - env: - COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} - name: Draft release uses: actions/create-release@v1 env: diff --git a/.gitignore b/.gitignore index 1446b5b..e49f6b4 100644 --- a/.gitignore +++ b/.gitignore @@ -31,11 +31,3 @@ playground.xcworkspace # .swiftpm .build/ - -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -Pods/ diff --git a/CryptomatorCryptoLib.podspec b/CryptomatorCryptoLib.podspec deleted file mode 100644 index 0a810d6..0000000 --- a/CryptomatorCryptoLib.podspec +++ /dev/null @@ -1,25 +0,0 @@ -Pod::Spec.new do |s| - s.name = 'CryptomatorCryptoLib' - s.version = ENV['LIB_VERSION'] || '0.0.1-snapshot' - s.summary = 'CryptomatorCryptoLib is an iOS crypto library to access Cryptomator vaults.' - - s.homepage = 'https://github.com/cryptomator/cryptolib-swift' - s.license = { :type => 'AGPLv3', :file => 'LICENSE.txt' } - s.author = { 'Philipp Schmid' => 'philipp.schmid@skymatic.de', - 'Sebastian Stenzel' => 'sebastian.stenzel@skymatic.de', - 'Tobias Hagemann' => 'tobias.hagemann@skymatic.de' } - s.source = { :git => 'https://github.com/cryptomator/cryptolib-swift.git', :tag => s.version.to_s } - - s.public_header_files = 'Sources/CryptomatorCryptoLib/CryptomatorCryptoLib.h' - s.source_files = 'Sources/CryptomatorCryptoLib/**/*.{swift,h}' - s.ios.deployment_target = '9.0' - s.osx.deployment_target = '10.12' - s.swift_version = '5.1' - - s.dependency 'SwiftBase32', '~> 0.8.0' - - s.subspec 'scrypt' do |ss| - ss.public_header_files = 'Sources/scrypt/scrypt.h', 'Sources/scrypt/include/crypto_scrypt.h' - ss.source_files = 'Sources/scrypt/**/*.{h,c}' - end -end diff --git a/README.md b/README.md index 8342af6..d93ec33 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcryptomator%2Fcryptolib-swift%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/cryptomator/cryptolib-swift) [![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcryptomator%2Fcryptolib-swift%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/cryptomator/cryptolib-swift) -[![Version](http://img.shields.io/cocoapods/v/CryptomatorCryptoLib.svg)](https://cocoapods.org/pods/CryptomatorCryptoLib) [![Codacy Code Quality](https://app.codacy.com/project/badge/Grade/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift/dashboard) [![Codacy Coverage](https://app.codacy.com/project/badge/Coverage/dba85991a19942bab0d3d587522397ef)](https://www.codacy.com/gh/cryptomator/cryptolib-swift/dashboard) @@ -25,14 +24,6 @@ You can use [Swift Package Manager](https://swift.org/package-manager/ "Swift Pa .package(url: "https://github.com/cryptomator/cryptolib-swift.git", .upToNextMinor(from: "1.0.0")) ``` -### CocoaPods - -You can use [CocoaPods](https://cocoapods.org/ "CocoaPods"). - -```ruby -pod 'CryptomatorCryptoLib', '~> 1.0.0' -``` - ## Usage ### Masterkey From 0139baeea3a2c8812a05d4c0108dac5e041ad96d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 1 Apr 2021 16:49:49 +0200 Subject: [PATCH 123/144] Added SwiftLint --- .swiftlint.yml | 13 +++++++++++++ .../project.pbxproj | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 .swiftlint.yml diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..14bf80f --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,13 @@ +disabled_rules: + - line_length + +included: + - Sources/CryptomatorCryptoLib + - Tests/CryptomatorCryptoLibTests +excluded: + - Tests/CryptomatorCryptoLibTests/XCTestManifests.swift + +identifier_name: + min_length: 1 +inclusive_language: + override_allowed_terms: ["masterkey"] diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index f2a8d54..1186cb4 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -261,6 +261,7 @@ 4A7C212F2451F2AC00DE81E6 /* Frameworks */, 4A7C21302451F2AC00DE81E6 /* Resources */, 74862A712469A6B2003D81CB /* Lint With SwiftFormat */, + 749441272616051400435B0B /* Lint with SwiftLint */, 749260F725B17B10004B3426 /* Embed Frameworks */, ); buildRules = ( @@ -400,6 +401,24 @@ shellPath = /bin/sh; shellScript = "if which swiftformat >/dev/null; then\n swiftformat --lint --lenient .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; }; + 749441272616051400435B0B /* Lint with SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Lint with SwiftLint"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ From 168dc6326c8e277c2869d62a093ba5e096614360 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 1 Apr 2021 17:19:19 +0200 Subject: [PATCH 124/144] Enforcing SwiftFormat and SwiftLint via CI, please make sure to set up a pre-commit hook according to README --- .github/workflows/build.yml | 5 +++ README.md | 19 ++++++++- Scripts/process.sh | 78 +++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100755 Scripts/process.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a25ba0a..e080617 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,11 @@ jobs: key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- + - name: Run process.sh script + run: | + ./Scripts/process.sh --fail-on-errors + failed=$? + exit $failed - name: Build and test run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'platform=macOS' -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report diff --git a/README.md b/README.md index d93ec33..acda315 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ let ciphertextSize = cryptor.calculateCiphertextSize(size) let cleartextSize = try cryptor.calculateCleartextSize(ciphertextSize) ``` -## Contributing to CryptoLib Swift +## Contributing Please read our [contribution guide](.github/CONTRIBUTING.md), if you would like to report a bug, ask a question or help us with coding. @@ -169,6 +169,23 @@ In general, the following preference is used to choose the implementation of cry 1. Apple Swift Crypto (HMAC) 2. Apple CommonCrypto (AES-CTR, RFC 3394 Key Derivation) +The project uses [SwiftFormat](https://github.com/nicklockwood/SwiftFormat) and [SwiftLint](https://github.com/realm/SwiftLint) to enforce code style and conventions. Install these tools if you haven't already. + +Please make sure that your code is correctly formatted. The easiest way to do that is to set up a pre-commit hook. Create a file at `.git/hooks/pre-commit` with this content: + +```sh +./Scripts/process.sh --fail-on-errors +failed=$? +exit $failed +``` + +You may have to make the scripts executable: + +```sh +chmod +x Scripts/process.sh +chmod +x .git/hooks/pre-commit +``` + ## Code of Conduct Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](.github/CODE_OF_CONDUCT.md). diff --git a/Scripts/process.sh b/Scripts/process.sh new file mode 100755 index 0000000..69e1650 --- /dev/null +++ b/Scripts/process.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# +# Based on http://merowing.info/2021/01/improve-build-times-by-extracting-3rd-party-tooling-to-processing-script./ + +cd "$(dirname "$0")/.." + +if [[ -n "$CI" ]] || [[ $1 == "--fail-on-errors" ]] ; then + FAIL_ON_ERRORS=true + echo "Running in --fail-on-errors mode" + ERROR_START="" + COLOR_END="" + INFO_START="" +else + echo "Running in local mode" + ERROR_START="\e[31m" + COLOR_END="\e[0m" + INFO_START="\e[34m" +fi + +final_status=0 + +function process() { + echo "\n${INFO_START}# Running $1 #${COLOR_END}" + local initial_git_diff=`git diff --no-color` + local start=`date +%s` + + eval "$2" + + if [ "$FAIL_ON_ERRORS" = "true" ] && [[ "$initial_git_diff" != `git diff --no-color` ]] + then + echo "${ERROR_START}$1 generates git changes, run './Scripts/process.sh' and review the changes${COLOR_END}" + final_status=1 + fi + + local end=`date +%s` + echo Execution time was `expr $end - $start` seconds. +} + +function process_return_code() { + echo "\n${INFO_START}# Running $1 #${COLOR_END}" + eval "$2" + local return_value=$? + if [ "$FAIL_ON_ERRORS" = "true" ] && [ "$return_value" != "0" ]; + then + echo "${ERROR_START}$1 failed${COLOR_END}" + final_status=1 + fi +} + +function process_output() { + echo "\n${INFO_START}# Running $1 #${COLOR_END}" + local start=`date +%s` + + local output=$(eval "$2") + + if [[ ! -z "$output" ]] + then + echo "${ERROR_START}$1 reports issues:\n-----\n$output\n-----\nrun './Scripts/process.sh' and fix them${COLOR_END}" + + if [ "$FAIL_ON_ERRORS" = "true" ] + then + final_status=1 + fi + fi + + local end=`date +%s` + echo Execution time was `expr $end - $start` seconds. +} + +process "SwiftFormat" "swiftformat --quiet ." +process_output "SwiftLint" "swiftlint --quiet" + +if [[ $final_status -gt 0 ]] +then + echo "\n${ERROR_START}Changes required. Run './Scripts/process.sh' and review the changes${COLOR_END}" +fi + +exit $final_status From 113dac61109c7025ee15e18ab332b9b00eb04eb9 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 8 Apr 2021 22:57:55 +0200 Subject: [PATCH 125/144] Process script now only affects the actual changes (git staged), simplified and modernized the script --- Scripts/git-format-staged.py | 267 +++++++++++++++++++++++++++++++++++ Scripts/process.sh | 71 ++-------- 2 files changed, 282 insertions(+), 56 deletions(-) create mode 100644 Scripts/git-format-staged.py diff --git a/Scripts/git-format-staged.py b/Scripts/git-format-staged.py new file mode 100644 index 0000000..ea06b76 --- /dev/null +++ b/Scripts/git-format-staged.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# +# Git command to transform staged files according to a command that accepts file +# content on stdin and produces output on stdout. This command is useful in +# combination with `git add -p` which allows you to stage specific changes in +# a file. This command runs a formatter on the file with staged changes while +# ignoring unstaged changes. +# +# Usage: git-format-staged [OPTION]... [FILE]... +# Example: git-format-staged --formatter 'prettier --stdin' '*.js' +# +# Tested with Python 3.6 and Python 2.7. +# +# Original author: Jesse Hallett + +from __future__ import print_function +import argparse +from fnmatch import fnmatch +from gettext import gettext as _ +import os +import re +import subprocess +import sys + +# The string $VERSION is replaced during the publish process. +VERSION = '$VERSION' +PROG = sys.argv[0] + +def info(msg): + print(msg, file=sys.stderr) + +def warn(msg): + print('{}: warning: {}'.format(PROG, msg), file=sys.stderr) + +def fatal(msg): + print('{}: error: {}'.format(PROG, msg), file=sys.stderr) + exit(1) + +def format_staged_files(file_patterns, formatter, git_root, update_working_tree=True, write=True): + try: + output = subprocess.check_output([ + 'git', 'diff-index', + '--cached', + '--diff-filter=AM', # select only file additions and modifications + '--no-renames', + 'HEAD' + ]) + for line in output.splitlines(): + entry = parse_diff(line.decode('utf-8')) + entry_path = normalize_path(entry['src_path'], relative_to=git_root) + if not (matches_some_path(file_patterns, entry_path)): + continue + if format_file_in_index(formatter, entry, update_working_tree=update_working_tree, write=write): + info('Reformatted {} with {}'.format(entry['src_path'], formatter)) + except Exception as err: + fatal(str(err)) + +# Run formatter on file in the git index. Creates a new git object with the +# result, and replaces the content of the file in the index with that object. +# Returns hash of the new object if formatting produced any changes. +def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=True): + orig_hash = diff_entry['dst_hash'] + new_hash = format_object(formatter, orig_hash, diff_entry['src_path']) + + # If the new hash is the same then the formatter did not make any changes. + if not write or new_hash == orig_hash: + return None + + # If the content of the new object is empty then the formatter did not + # produce any output. We want to abort instead of replacing the file with an + # empty one. + if object_is_empty(new_hash): + return None + + replace_file_in_index(diff_entry, new_hash) + + if update_working_tree: + try: + patch_working_file(diff_entry['src_path'], orig_hash, new_hash) + except Exception as err: + # Errors patching working tree files are not fatal + warn(str(err)) + + return new_hash + +file_path_placeholder = re.compile('\{\}') + +# Run formatter on a git blob identified by its hash. Writes output to a new git +# blob, and returns the hash of the new blob. +def format_object(formatter, object_hash, file_path): + get_content = subprocess.Popen( + ['git', 'cat-file', '-p', object_hash], + stdout=subprocess.PIPE + ) + format_content = subprocess.Popen( + re.sub(file_path_placeholder, file_path, formatter), + shell=True, + stdin=get_content.stdout, + stdout=subprocess.PIPE + ) + write_object = subprocess.Popen( + ['git', 'hash-object', '-w', '--stdin'], + stdin=format_content.stdout, + stdout=subprocess.PIPE + ) + + get_content.stdout.close() + format_content.stdout.close() + + if get_content.wait() != 0: + raise ValueError('unable to read file content from object database: ' + object_hash) + + if format_content.wait() != 0: + raise Exception('formatter exited with non-zero status') # TODO: capture stderr from format command + + new_hash, err = write_object.communicate() + + if write_object.returncode != 0: + raise Exception('unable to write formatted content to object database') + + return new_hash.decode('utf-8').rstrip() + +def object_is_empty(object_hash): + get_content = subprocess.Popen( + ['git', 'cat-file', '-p', object_hash], + stdout=subprocess.PIPE + ) + content, err = get_content.communicate() + + if get_content.returncode != 0: + raise Exception('unable to verify content of formatted object') + + return not content + +def replace_file_in_index(diff_entry, new_object_hash): + subprocess.check_call(['git', 'update-index', + '--cacheinfo', '{},{},{}'.format( + diff_entry['dst_mode'], + new_object_hash, + diff_entry['src_path'] + )]) + +def patch_working_file(path, orig_object_hash, new_object_hash): + patch = subprocess.check_output( + ['git', 'diff', orig_object_hash, new_object_hash] + ) + + # Substitute object hashes in patch header with path to working tree file + patch_b = patch.replace(orig_object_hash.encode(), path.encode()).replace(new_object_hash.encode(), path.encode()) + + apply_patch = subprocess.Popen( + ['git', 'apply', '-'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + + output, err = apply_patch.communicate(input=patch_b) + + if apply_patch.returncode != 0: + raise Exception('could not apply formatting changes to working tree file {}'.format(path)) + +# Format: src_mode dst_mode src_hash dst_hash status/score? src_path dst_path? +diff_pat = re.compile('^:(\d+) (\d+) ([a-f0-9]+) ([a-f0-9]+) ([A-Z])(\d+)?\t([^\t]+)(?:\t([^\t]+))?$') + +# Parse output from `git diff-index` +def parse_diff(diff): + m = diff_pat.match(diff) + if not m: + raise ValueError('Failed to parse diff-index line: ' + diff) + return { + 'src_mode': unless_zeroed(m.group(1)), + 'dst_mode': unless_zeroed(m.group(2)), + 'src_hash': unless_zeroed(m.group(3)), + 'dst_hash': unless_zeroed(m.group(4)), + 'status': m.group(5), + 'score': int(m.group(6)) if m.group(6) else None, + 'src_path': m.group(7), + 'dst_path': m.group(8) + } + +zeroed_pat = re.compile('^0+$') + +# Returns the argument unless the argument is a string of zeroes, in which case +# returns `None` +def unless_zeroed(s): + return s if not zeroed_pat.match(s) else None + +def get_git_root(): + return subprocess.check_output( + ['git', 'rev-parse', '--show-toplevel'] + ).decode('utf-8').rstrip() + +def normalize_path(p, relative_to=None): + return os.path.abspath( + os.path.join(relative_to, p) if relative_to else p + ) + +def matches_some_path(patterns, target): + is_match = False + for signed_pattern in patterns: + (is_pattern_positive, pattern) = from_signed_pattern(signed_pattern) + if fnmatch(target, normalize_path(pattern)): + is_match = is_pattern_positive + return is_match + +# Checks for a '!' as the first character of a pattern, returns the rest of the +# pattern in a tuple. The tuple takes the form (is_pattern_positive, pattern). +# For example: +# from_signed_pattern('!pat') == (False, 'pat') +# from_signed_pattern('pat') == (True, 'pat') +def from_signed_pattern(pattern): + if pattern[0] == '!': + return (False, pattern[1:]) + else: + return (True, pattern) + +class CustomArgumentParser(argparse.ArgumentParser): + def parse_args(self, args=None, namespace=None): + args, argv = self.parse_known_args(args, namespace) + if argv: + msg = argparse._( + 'unrecognized arguments: %s. Do you need to quote your formatter command?' + ) + self.error(msg % ' '.join(argv)) + return args + +if __name__ == '__main__': + parser = CustomArgumentParser( + description='Transform staged files using a formatting command that accepts content via stdin and produces a result via stdout.', + epilog='Example: %(prog)s --formatter "prettier --stdin" "src/*.js" "test/*.js"' + ) + parser.add_argument( + '--formatter', '-f', + required=True, + help='Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will be replaced with a path to the file being formatted. (Example: "prettier --stdin --stdin-filepath \'{}\'")' + ) + parser.add_argument( + '--no-update-working-tree', + action='store_true', + help='By default formatting changes made to staged file content will also be applied to working tree files via a patch. This option disables that behavior, leaving working tree files untouched.' + ) + parser.add_argument( + '--no-write', + action='store_true', + help='Prevents %(prog)s from modifying staged or working tree files. You can use this option to check staged changes with a linter instead of formatting. With this option stdout from the formatter command is ignored. Example: %(prog)s --no-write -f "eslint --stdin --stdin-filename \'{}\' >&2" "*.js"' + ) + parser.add_argument( + '--version', + action='version', + version='%(prog)s version {}'.format(VERSION), + help='Display version of %(prog)s' + ) + parser.add_argument( + 'files', + nargs='+', + help='Patterns that specify files to format. The formatter will only transform staged files that are given here. Patterns may be literal file paths, or globs which will be tested against staged file paths using Python\'s fnmatch function. For example "src/*.js" will match all files with a .js extension in src/ and its subdirectories. Patterns may be negated to exclude files using a "!" character. Patterns are evaluated left-to-right. (Example: "main.js" "src/*.js" "test/*.js" "!test/todo/*")' + ) + args = parser.parse_args() + files = vars(args)['files'] + format_staged_files( + file_patterns=files, + formatter=vars(args)['formatter'], + git_root=get_git_root(), + update_working_tree=not vars(args)['no_update_working_tree'], + write=not vars(args)['no_write'] + ) diff --git a/Scripts/process.sh b/Scripts/process.sh index 69e1650..33cd10a 100755 --- a/Scripts/process.sh +++ b/Scripts/process.sh @@ -1,78 +1,37 @@ #!/bin/sh # -# Based on http://merowing.info/2021/01/improve-build-times-by-extracting-3rd-party-tooling-to-processing-script./ +# Based on https://merowing.info/2021/01/improve-build-times-by-extracting-3rd-party-tooling-to-processing-script./ cd "$(dirname "$0")/.." -if [[ -n "$CI" ]] || [[ $1 == "--fail-on-errors" ]] ; then - FAIL_ON_ERRORS=true +if [[ -n "$CI" ]] || [[ "$1" == "--fail-on-errors" ]]; then + fail_on_errors=true echo "Running in --fail-on-errors mode" - ERROR_START="" - COLOR_END="" - INFO_START="" else echo "Running in local mode" - ERROR_START="\e[31m" - COLOR_END="\e[0m" - INFO_START="\e[34m" fi final_status=0 -function process() { - echo "\n${INFO_START}# Running $1 #${COLOR_END}" - local initial_git_diff=`git diff --no-color` - local start=`date +%s` - - eval "$2" - - if [ "$FAIL_ON_ERRORS" = "true" ] && [[ "$initial_git_diff" != `git diff --no-color` ]] - then - echo "${ERROR_START}$1 generates git changes, run './Scripts/process.sh' and review the changes${COLOR_END}" - final_status=1 - fi - - local end=`date +%s` - echo Execution time was `expr $end - $start` seconds. -} - -function process_return_code() { - echo "\n${INFO_START}# Running $1 #${COLOR_END}" - eval "$2" - local return_value=$? - if [ "$FAIL_ON_ERRORS" = "true" ] && [ "$return_value" != "0" ]; - then - echo "${ERROR_START}$1 failed${COLOR_END}" - final_status=1 - fi -} - function process_output() { - echo "\n${INFO_START}# Running $1 #${COLOR_END}" - local start=`date +%s` - - local output=$(eval "$2") - - if [[ ! -z "$output" ]] - then - echo "${ERROR_START}$1 reports issues:\n-----\n$output\n-----\nrun './Scripts/process.sh' and fix them${COLOR_END}" - - if [ "$FAIL_ON_ERRORS" = "true" ] - then + printf '\n# Running %s\n' "$1" + local start=$(date +%s) + local output=$(eval "$2" 2>&1) + if [[ ! -z "$output" ]]; then + printf -- '---\n%s\n---\n' "$output" + if [ "$fail_on_errors" = true ]; then final_status=1 fi fi - - local end=`date +%s` - echo Execution time was `expr $end - $start` seconds. + local end=$(date +%s) + printf 'Execution time was %s seconds.\n' "$(($end - $start))" } -process "SwiftFormat" "swiftformat --quiet ." -process_output "SwiftLint" "swiftlint --quiet" +process_output "SwiftFormat" "python ./Scripts/git-format-staged.py -f 'swiftformat stdin --stdinpath \"{}\" --quiet' '*.swift'" +process_output "SwiftLint" "python ./Scripts/git-format-staged.py --no-write -f 'swiftlint --use-stdin --quiet >&2' '*.swift'" -if [[ $final_status -gt 0 ]] -then - echo "\n${ERROR_START}Changes required. Run './Scripts/process.sh' and review the changes${COLOR_END}" +if [[ "$final_status" -gt 0 ]]; then + echo "\nChanges werde made or are required. Please review the output above for further details." fi exit $final_status From dfeb36c9a5ab0c8264fff5e96556e39f4a8cdffe Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 8 Apr 2021 23:07:06 +0200 Subject: [PATCH 126/144] Ignoring .swiftpm folder --- .gitignore | 2 +- .../xcode/package.xcworkspace/contents.xcworkspacedata | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/.gitignore b/.gitignore index e49f6b4..7467633 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,6 @@ playground.xcworkspace # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project -# .swiftpm +.swiftpm .build/ diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - From 7cb2bcbd1c296b6adbe93c762806d38e4dc48096 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Thu, 8 Apr 2021 23:18:57 +0200 Subject: [PATCH 127/144] Fixed issues reported by Codacy --- Scripts/process.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Scripts/process.sh b/Scripts/process.sh index 33cd10a..e82d27d 100755 --- a/Scripts/process.sh +++ b/Scripts/process.sh @@ -24,14 +24,12 @@ function process_output() { fi fi local end=$(date +%s) - printf 'Execution time was %s seconds.\n' "$(($end - $start))" + printf 'Execution time was %s seconds.\n' "$((end - start))" } process_output "SwiftFormat" "python ./Scripts/git-format-staged.py -f 'swiftformat stdin --stdinpath \"{}\" --quiet' '*.swift'" process_output "SwiftLint" "python ./Scripts/git-format-staged.py --no-write -f 'swiftlint --use-stdin --quiet >&2' '*.swift'" -if [[ "$final_status" -gt 0 ]]; then - echo "\nChanges werde made or are required. Please review the output above for further details." -fi +printf '\nChanges werde made or are required. Please review the output above for further details.\n' exit $final_status From aafd43d2d61b02c355ba143dd9714b970d491457 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 9 Apr 2021 09:37:03 +0200 Subject: [PATCH 128/144] CI needs a full process because it doesn't stage anything (113dac6 only considered processing during development) --- .github/workflows/build.yml | 5 ++-- .../project.pbxproj | 6 ++-- README.md | 9 +++--- Scripts/process.sh | 29 ++++++++++++------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e080617..18486a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,9 +18,8 @@ jobs: ${{ runner.os }}-spm- - name: Run process.sh script run: | - ./Scripts/process.sh --fail-on-errors - failed=$? - exit $failed + ./Scripts/process.sh + exit $? - name: Build and test run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'platform=macOS' -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 1186cb4..9190915 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -261,7 +261,7 @@ 4A7C212F2451F2AC00DE81E6 /* Frameworks */, 4A7C21302451F2AC00DE81E6 /* Resources */, 74862A712469A6B2003D81CB /* Lint With SwiftFormat */, - 749441272616051400435B0B /* Lint with SwiftLint */, + 749441272616051400435B0B /* Lint With SwiftLint */, 749260F725B17B10004B3426 /* Embed Frameworks */, ); buildRules = ( @@ -401,7 +401,7 @@ shellPath = /bin/sh; shellScript = "if which swiftformat >/dev/null; then\n swiftformat --lint --lenient .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; }; - 749441272616051400435B0B /* Lint with SwiftLint */ = { + 749441272616051400435B0B /* Lint With SwiftLint */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -410,7 +410,7 @@ ); inputPaths = ( ); - name = "Lint with SwiftLint"; + name = "Lint With SwiftLint"; outputFileListPaths = ( ); outputPaths = ( diff --git a/README.md b/README.md index acda315..ced3a28 100644 --- a/README.md +++ b/README.md @@ -169,14 +169,13 @@ In general, the following preference is used to choose the implementation of cry 1. Apple Swift Crypto (HMAC) 2. Apple CommonCrypto (AES-CTR, RFC 3394 Key Derivation) -The project uses [SwiftFormat](https://github.com/nicklockwood/SwiftFormat) and [SwiftLint](https://github.com/realm/SwiftLint) to enforce code style and conventions. Install these tools if you haven't already. +This project uses [SwiftFormat](https://github.com/nicklockwood/SwiftFormat) and [SwiftLint](https://github.com/realm/SwiftLint) to enforce code style and conventions. Install these tools if you haven't already. -Please make sure that your code is correctly formatted. The easiest way to do that is to set up a pre-commit hook. Create a file at `.git/hooks/pre-commit` with this content: +Please make sure that your code is correctly formatted and passes linter validations. The easiest way to do that is to set up a pre-commit hook. Create a file at `.git/hooks/pre-commit` with this content: ```sh -./Scripts/process.sh --fail-on-errors -failed=$? -exit $failed +./Scripts/process.sh --staged +exit $? ``` You may have to make the scripts executable: diff --git a/Scripts/process.sh b/Scripts/process.sh index e82d27d..9a0da33 100755 --- a/Scripts/process.sh +++ b/Scripts/process.sh @@ -4,11 +4,11 @@ cd "$(dirname "$0")/.." -if [[ -n "$CI" ]] || [[ "$1" == "--fail-on-errors" ]]; then - fail_on_errors=true - echo "Running in --fail-on-errors mode" +if [[ "$1" == "--staged" ]]; then + staged_mode=true + echo "Running in --staged mode" else - echo "Running in local mode" + echo "Running in full mode" fi final_status=0 @@ -19,17 +19,24 @@ function process_output() { local output=$(eval "$2" 2>&1) if [[ ! -z "$output" ]]; then printf -- '---\n%s\n---\n' "$output" - if [ "$fail_on_errors" = true ]; then - final_status=1 - fi + final_status=1 fi local end=$(date +%s) printf 'Execution time was %s seconds.\n' "$((end - start))" } -process_output "SwiftFormat" "python ./Scripts/git-format-staged.py -f 'swiftformat stdin --stdinpath \"{}\" --quiet' '*.swift'" -process_output "SwiftLint" "python ./Scripts/git-format-staged.py --no-write -f 'swiftlint --use-stdin --quiet >&2' '*.swift'" - -printf '\nChanges werde made or are required. Please review the output above for further details.\n' +if [ "$staged_mode" = true ]; then + process_output "SwiftFormat" "python ./Scripts/git-format-staged.py -f 'swiftformat stdin --stdinpath \"{}\" --quiet' '*.swift'" + process_output "SwiftLint" "python ./Scripts/git-format-staged.py --no-write -f 'swiftlint --use-stdin --quiet >&2' '*.swift'" + if [[ "$final_status" -gt 0 ]]; then + printf '\nChanges werde made or are required. Please review the output above for further details.\n' + fi +else + process_output "SwiftFormat" "swiftformat --lint --quiet ." + process_output "SwiftLint" "swiftlint --quiet ." + if [[ "$final_status" -gt 0 ]]; then + printf '\nChanges are required. Please review the output above for further details.\n' + fi +fi exit $final_status From 42985546730f1a86f09d1fb5d346442dfa71c0d3 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 9 Apr 2021 10:11:27 +0200 Subject: [PATCH 129/144] Install SwiftFormat on CI (not sure how process script ever worked) --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 18486a9..39ef652 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,9 @@ jobs: key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- + - name: Install SwiftFormat + run: | + brew install swiftformat - name: Run process.sh script run: | ./Scripts/process.sh From 3cecc87ab71f97e4dcd37ad5406e876262c57ec5 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 12 Apr 2021 17:30:33 +0200 Subject: [PATCH 130/144] Updated SwiftLint configuration --- .swiftlint.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.swiftlint.yml b/.swiftlint.yml index 14bf80f..88e3f5d 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,5 +1,8 @@ disabled_rules: + - file_length - line_length + - todo + - type_body_length included: - Sources/CryptomatorCryptoLib @@ -9,5 +12,9 @@ excluded: identifier_name: min_length: 1 + max_length: 50 inclusive_language: override_allowed_terms: ["masterkey"] +type_name: + max_length: 50 + allowed_symbols: "_" From d03ddb130a8a7948a0d11e3e286662cf4dc77f1a Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 13 Apr 2021 12:25:10 +0200 Subject: [PATCH 131/144] Update .swiftformat [ci skip] --- .swiftformat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index a243a63..f6f4141 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,4 +1,4 @@ ---minversion 0.47.10 +--minversion 0.47.13 # file options From a51e2b8a6e71e99d21107b53d0818aae896bc07c Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 18 May 2021 10:38:53 +0200 Subject: [PATCH 132/144] Removed SwiftFormat install phase because it's now pre-installed --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39ef652..18486a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,9 +16,6 @@ jobs: key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- - - name: Install SwiftFormat - run: | - brew install swiftformat - name: Run process.sh script run: | ./Scripts/process.sh From a4ae407d29298bd84eebe7e9323043dfe96802a3 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 18 May 2021 10:39:19 +0200 Subject: [PATCH 133/144] Ignoring SPM dependencies in code coverage report --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 18486a9..7a641e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: - name: Upload code coverage report run: | gem install slather - slather coverage -x --scheme CryptomatorCryptoLib CryptomatorCryptoLib.xcodeproj + slather coverage -x -i '../../../../../Library/*' --scheme CryptomatorCryptoLib CryptomatorCryptoLib.xcodeproj bash <(curl -Ls https://coverage.codacy.com/get.sh) env: CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} From f0371edb56ce45694236f1b05bd01b4c077d59b1 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 18 May 2021 14:17:41 +0200 Subject: [PATCH 134/144] Cleaned up project and updated dependencies --- .../project.pbxproj | 6 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../xcschemes/CryptomatorCryptoLib.xcscheme | 2 +- .../xcshareddata/xcschemes/scrypt.xcscheme | 67 +++++++++++++++++++ Package.resolved | 4 +- Package.swift | 2 +- .../CryptomatorCryptoLib/ContentCryptor.swift | 10 +-- Sources/CryptomatorCryptoLib/Cryptor.swift | 16 ++--- .../CryptomatorCryptoLib/MasterkeyFile.swift | 4 -- Sources/scrypt/scrypt.h | 6 -- 10 files changed, 87 insertions(+), 34 deletions(-) create mode 100644 CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/scrypt.xcscheme diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 9190915..80dcf31 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -154,12 +154,12 @@ 4A7C21362451F2AC00DE81E6 /* Info.plist */, 9EB822C0248AF82200879838 /* AesCtr.swift */, 9E44EEA524599C6900A37B01 /* AesSiv.swift */, + 9E97DC8C25F77BA40046C83E /* ContentCryptor.swift */, 74F0F753248FC89B00B4C26D /* CryptoError.swift */, 9E9BB811245412E900F9FF51 /* Cryptor.swift */, 74F0F7592490C1EB00B4C26D /* CryptoSupport.swift */, 9E9BB8132454708600F9FF51 /* Masterkey.swift */, 74B4D38C2588CD60006C0567 /* MasterkeyFile.swift */, - 9E97DC8C25F77BA40046C83E /* ContentCryptor.swift */, ); path = CryptomatorCryptoLib; sourceTree = ""; @@ -320,7 +320,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1240; + LastUpgradeCheck = 1250; ORGANIZATIONNAME = "Skymatic GmbH"; TargetAttributes = { 4A7C21312451F2AC00DE81E6 = { @@ -788,7 +788,7 @@ repositoryURL = "https://github.com/norio-nomura/Base32.git"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 0.8.0; + minimumVersion = 0.9.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cf1b87a..5de3924 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CryptomatorCryptoLib.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/norio-nomura/Base32.git", "state": { "branch": null, - "revision": "69fc95dd2e4229a2fd82e14d9358c23b0fd8cf74", - "version": "0.8.0" + "revision": "c4bc0a49689999ae2c7c778f3830a6a6e694efb8", + "version": "0.9.0" } } ] diff --git a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme index 12be3dd..6962aa6 100644 --- a/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme +++ b/CryptomatorCryptoLib.xcodeproj/xcshareddata/xcschemes/CryptomatorCryptoLib.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.resolved b/Package.resolved index cf1b87a..5de3924 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/norio-nomura/Base32.git", "state": { "branch": null, - "revision": "69fc95dd2e4229a2fd82e14d9358c23b0fd8cf74", - "version": "0.8.0" + "revision": "c4bc0a49689999ae2c7c778f3830a6a6e694efb8", + "version": "0.9.0" } } ] diff --git a/Package.swift b/Package.swift index 59e1282..db6c710 100644 --- a/Package.swift +++ b/Package.swift @@ -20,7 +20,7 @@ let package = Package( .library(name: "CryptomatorCryptoLib", targets: ["CryptomatorCryptoLib"]) ], dependencies: [ - .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.8.0")) + .package(url: "https://github.com/norio-nomura/Base32.git", .upToNextMinor(from: "0.9.0")) ], targets: [ .target(name: "CryptomatorCryptoLib", dependencies: ["Base32", "scrypt"]), diff --git a/Sources/CryptomatorCryptoLib/ContentCryptor.swift b/Sources/CryptomatorCryptoLib/ContentCryptor.swift index ce60e57..ffc1014 100644 --- a/Sources/CryptomatorCryptoLib/ContentCryptor.swift +++ b/Sources/CryptomatorCryptoLib/ContentCryptor.swift @@ -20,7 +20,7 @@ protocol ContentCryptor { - Parameter key: The encryption key. - Parameter nonce: The nonce/IV to use. - Parameter ad: Associated data, which needs to be authenticated during decryption. - - Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array + - Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array. */ func encrypt(_ chunk: [UInt8], key: [UInt8], nonce: [UInt8], ad: [UInt8]...) throws -> [UInt8] @@ -30,18 +30,18 @@ protocol ContentCryptor { - Parameter chunk: The nonce/IV + ciphertext + MAC/tag, as a concatenated byte array. - Parameter key: The encryption key. - Parameter ad: Associated data, which needs to be authenticated during decryption. - - Returns: The original cleartext + - Returns: The original cleartext. */ func decrypt(_ chunk: [UInt8], key: [UInt8], ad: [UInt8]...) throws -> [UInt8] } class CtrThenHmacContentCryptor: ContentCryptor { - private let macKey: [UInt8] - private let cryptoSupport: CryptoSupport - let nonceLen = kCCBlockSizeAES128 let tagLen = Int(CC_SHA256_DIGEST_LENGTH) + private let macKey: [UInt8] + private let cryptoSupport: CryptoSupport + init(macKey: [UInt8], cryptoSupport: CryptoSupport) { self.macKey = macKey self.cryptoSupport = cryptoSupport diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 4ac78dd..8d40c94 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -6,13 +6,9 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // +import Base32 import CommonCrypto import Foundation -#if COCOAPODS -import SwiftBase32 -#else -import Base32 -#endif public extension Data { init?(base64UrlEncoded base64String: String, options: Data.Base64DecodingOptions = []) { @@ -65,16 +61,16 @@ struct FileHeader { public class Cryptor { private let fileHeaderLegacyPayloadSize = 8 - private let cleartextChunkSize = 32 * 1024 - private var ciphertextChunkSize: Int { - return contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen - } - public var fileHeaderSize: Int { let fileHeaderPayloadSize = fileHeaderLegacyPayloadSize + kCCKeySizeAES256 return contentCryptor.nonceLen + fileHeaderPayloadSize + contentCryptor.tagLen } + private let cleartextChunkSize = 32 * 1024 + private var ciphertextChunkSize: Int { + return contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen + } + private let masterkey: Masterkey private let cryptoSupport: CryptoSupport private let contentCryptor: ContentCryptor diff --git a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift index ea3e910..4a20e78 100644 --- a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift +++ b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift @@ -8,11 +8,7 @@ import CommonCrypto import Foundation -#if COCOAPODS -import CryptomatorCryptoLib.scrypt -#else import scrypt -#endif struct Content: Codable, Equatable { let version: Int diff --git a/Sources/scrypt/scrypt.h b/Sources/scrypt/scrypt.h index 1de09ec..ecf2124 100644 --- a/Sources/scrypt/scrypt.h +++ b/Sources/scrypt/scrypt.h @@ -15,10 +15,4 @@ FOUNDATION_EXPORT double scryptVersionNumber; FOUNDATION_EXPORT const unsigned char scryptVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import -#if COCOAPODS -#import "crypto_scrypt.h" -#else #import -#endif - - From 67b1a243c101e8b351d1cdf7d41207f05f3cfc3d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 18 May 2021 22:25:19 +0200 Subject: [PATCH 135/144] Fixed code coverage report (not an issue yet but could've been one, see cloud-access-swift project) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a641e2..f2862e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: - name: Upload code coverage report run: | gem install slather - slather coverage -x -i '../../../../../Library/*' --scheme CryptomatorCryptoLib CryptomatorCryptoLib.xcodeproj + slather coverage -x -i '../../../Library/*' --scheme CryptomatorCryptoLib CryptomatorCryptoLib.xcodeproj bash <(curl -Ls https://coverage.codacy.com/get.sh) env: CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} From 69d49c5dfd19a8d3251c752f803ab812d06fa70c Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 19 May 2021 15:11:42 +0200 Subject: [PATCH 136/144] Updated bundle identifiers --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 80dcf31..fefd5eb 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -619,7 +619,7 @@ "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLib; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.cryptolib; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -645,7 +645,7 @@ "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLib; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.cryptolib; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; @@ -664,7 +664,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLibTests; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.cryptolib.tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -682,7 +682,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.CryptoLibTests; + PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.cryptolib.tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; From 74764aa8baeb5a4fdd14cfa353efe9303dc4ff3f Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 19 May 2021 15:51:45 +0200 Subject: [PATCH 137/144] Cleaned up project --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index fefd5eb..3aa99df 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -605,7 +605,6 @@ 4A7C21472451F2AD00DE81E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -622,7 +621,6 @@ PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.cryptolib; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -631,7 +629,6 @@ 4A7C21482451F2AD00DE81E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -697,19 +694,14 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); INFOPLIST_FILE = Sources/scrypt/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 14.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.2.1; + MARKETING_VERSION = 1.3.1; PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.scrypt; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -727,13 +719,12 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/scrypt/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 14.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.2.1; + MARKETING_VERSION = 1.3.1; PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.scrypt; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; From 188bedf7c990bada7fab32bdc978a2ddd6759887 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 21 Jun 2021 10:35:33 +0200 Subject: [PATCH 138/144] Updated README [ci skip] --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ced3a28..89ca03a 100644 --- a/README.md +++ b/README.md @@ -178,10 +178,9 @@ Please make sure that your code is correctly formatted and passes linter validat exit $? ``` -You may have to make the scripts executable: +And make your pre-commit hook executable: ```sh -chmod +x Scripts/process.sh chmod +x .git/hooks/pre-commit ``` From 56f6b3b9d1e07100fbe10f017a5a0e2f9bdabf69 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 25 Jun 2021 16:28:47 +0200 Subject: [PATCH 139/144] Added support for Homebrew directory on Apple Silicon in SwiftFormat/SwiftLint build phases --- CryptomatorCryptoLib.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CryptomatorCryptoLib.xcodeproj/project.pbxproj b/CryptomatorCryptoLib.xcodeproj/project.pbxproj index 3aa99df..a545370 100644 --- a/CryptomatorCryptoLib.xcodeproj/project.pbxproj +++ b/CryptomatorCryptoLib.xcodeproj/project.pbxproj @@ -399,7 +399,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftformat >/dev/null; then\n swiftformat --lint --lenient .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; + shellScript = "PATH=$PATH:/opt/homebrew/bin\nif which swiftformat >/dev/null; then\n swiftformat --lint --lenient .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; }; 749441272616051400435B0B /* Lint With SwiftLint */ = { isa = PBXShellScriptBuildPhase; @@ -417,7 +417,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + shellScript = "PATH=$PATH:/opt/homebrew/bin\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ From 1055df471814958cfc6c2070e2e4ea432f13c55d Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 7 Jul 2021 17:46:24 +0200 Subject: [PATCH 140/144] Split up unlock method so that the KEK can be retrieved/used --- README.md | 12 +++++ .../CryptomatorCryptoLib/MasterkeyFile.swift | 23 ++++++++- .../MasterkeyFileTests.swift | 50 ++++++++++++++++++- 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 89ca03a..b15c518 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,18 @@ let pepper = ... // optional let masterkey = try masterkeyFile.unlock(passphrase: passphrase, pepper: pepper) ``` +The unlock process can also be performed in two steps: + +```swift +let masterkeyFile = ... +let passphrase = ... +let pepper = ... // optional +let kek = try masterkeyFile.deriveKey(passphrase: passphrase, pepper: pepper) +let masterkey = try masterkeyFile.unlock(kek: kek) +``` + +This is useful if you'd like to derive the key in an extra step since the function is memory-intensive (using scrypt). The result can then be used elsewhere, e.g. in a memory-restricted process. + #### Lock For persisting the masterkey, use this method to export its encrypted/wrapped masterkey and other metadata as JSON data. diff --git a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift index 4a20e78..19aaa31 100644 --- a/Sources/CryptomatorCryptoLib/MasterkeyFile.swift +++ b/Sources/CryptomatorCryptoLib/MasterkeyFile.swift @@ -75,7 +75,18 @@ public class MasterkeyFile { - Returns: A masterkey with the unwrapped keys. */ public func unlock(passphrase: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey { - // derive keys: + let kek = try deriveKey(passphrase: passphrase, pepper: pepper) + return try unlock(kek: kek) + } + + /** + Derives a KEK from the given passphrase and the params from this masterkey file using scrypt. + + - Parameter passphrase: The passphrase used during key derivation. + - Parameter pepper: An optional application-specific pepper added to the scrypt's salt. Defaults to empty byte array. + - Returns: A 256-bit key derived from passphrase using scrypt. + */ + public func deriveKey(passphrase: String, pepper: [UInt8] = [UInt8]()) throws -> [UInt8] { let pw = [UInt8](passphrase.precomposedStringWithCanonicalMapping.utf8) let salt = [UInt8](Data(base64Encoded: content.scryptSalt)!) var kek = [UInt8](repeating: 0x00, count: kCCKeySizeAES256) @@ -83,6 +94,16 @@ public class MasterkeyFile { guard scryptResult == 0 else { throw MasterkeyFileError.keyDerivationFailed } + return kek + } + + /** + Unwraps the stored encryption and MAC keys with the given KEK. + + - Parameter kek: The KEK for unwrapping the keys from this masterkey file. + - Returns: A masterkey with the unwrapped keys. + */ + public func unlock(kek: [UInt8]) throws -> Masterkey { guard let wrappedMasterKey = Data(base64Encoded: content.primaryMasterKey) else { throw MasterkeyFileError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey") } diff --git a/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift index 2ec13e4..0057323 100644 --- a/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift +++ b/Tests/CryptomatorCryptoLibTests/MasterkeyFileTests.swift @@ -33,7 +33,7 @@ class MasterkeyFileTests: XCTestCase { XCTAssertEqual("cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=", masterkeyFile.content.versionMac) } - func testUnlock() throws { + func testUnlockWithPassphrase() throws { let expectedKey = [UInt8](repeating: 0x00, count: 32) let data = """ { @@ -142,6 +142,54 @@ class MasterkeyFileTests: XCTestCase { } } + func testDeriveKey() throws { + let expectedKey: [UInt8] = [ + 0x8C, 0xF4, 0xA0, 0x4E, 0xC8, 0x45, 0xF4, 0x28, + 0xB2, 0xF9, 0xF9, 0xE1, 0xD9, 0xDF, 0x08, 0xD2, + 0x62, 0x11, 0xD9, 0xAF, 0xE2, 0xF5, 0x5F, 0xDE, + 0xDF, 0xCB, 0xB5, 0xE7, 0x5A, 0xEF, 0x34, 0xF9 + ] + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + let kek = try masterkeyFile.deriveKey(passphrase: "asd", pepper: [UInt8]()) + XCTAssertEqual(expectedKey, kek) + } + + func testUnlockWithKEK() throws { + let expectedKey = [UInt8](repeating: 0x00, count: 32) + let data = """ + { + "version": 7, + "scryptSalt": "AAAAAAAAAAA=", + "scryptCostParam": 2, + "scryptBlockSize": 8, + "primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==", + "versionMac": "cn2sAK6l9p1/w9deJVUuW3h7br056mpv5srvALiYw+g=" + } + """.data(using: .utf8)! + let masterkeyFile = try MasterkeyFile.withContentFromData(data: data) + let kek: [UInt8] = [ + 0x8C, 0xF4, 0xA0, 0x4E, 0xC8, 0x45, 0xF4, 0x28, + 0xB2, 0xF9, 0xF9, 0xE1, 0xD9, 0xDF, 0x08, 0xD2, + 0x62, 0x11, 0xD9, 0xAF, 0xE2, 0xF5, 0x5F, 0xDE, + 0xDF, 0xCB, 0xB5, 0xE7, 0x5A, 0xEF, 0x34, 0xF9 + ] + let masterkey = try masterkeyFile.unlock(kek: kek) + XCTAssertEqual(expectedKey, masterkey.aesMasterKey) + XCTAssertEqual(expectedKey, masterkey.macMasterKey) + } + func testLock() throws { let masterkey = Masterkey.createFromRaw(aesMasterKey: [UInt8](repeating: 0x55, count: 32), macMasterKey: [UInt8](repeating: 0x77, count: 32)) let content = try MasterkeyFile.lock(masterkey: masterkey, vaultVersion: 7, passphrase: "asd", pepper: [UInt8](), scryptCostParam: 2, cryptoSupport: CryptoSupportMock()) From 8df25dfb2451125efb9fa6e4b385611ac4360753 Mon Sep 17 00:00:00 2001 From: Philipp Schmid <25935690+phil1995@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:26:26 +0200 Subject: [PATCH 141/144] Use `autoreleasepool` inside the encryption / decryption loop Fixes #8 --- Sources/CryptomatorCryptoLib/Cryptor.swift | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Sources/CryptomatorCryptoLib/Cryptor.swift b/Sources/CryptomatorCryptoLib/Cryptor.swift index 8d40c94..3dae122 100644 --- a/Sources/CryptomatorCryptoLib/Cryptor.swift +++ b/Sources/CryptomatorCryptoLib/Cryptor.swift @@ -224,13 +224,15 @@ public class Cryptor { // encrypt and write ciphertext content: var chunkNumber: UInt64 = 0 while cleartextStream.hasBytesAvailable { - guard let cleartextChunk = try cleartextStream.read(maxLength: cleartextChunkSize) else { - continue + try autoreleasepool { + guard let cleartextChunk = try cleartextStream.read(maxLength: cleartextChunkSize) else { + return + } + let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) + ciphertextStream.write(ciphertextChunk, maxLength: ciphertextChunk.count) + progress.completedUnitCount += Int64(ciphertextChunk.count) + chunkNumber += 1 } - let ciphertextChunk = try encryptSingleChunk(cleartextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) - ciphertextStream.write(ciphertextChunk, maxLength: ciphertextChunk.count) - progress.completedUnitCount += Int64(ciphertextChunk.count) - chunkNumber += 1 } } @@ -286,13 +288,15 @@ public class Cryptor { let ciphertextChunkSize = contentCryptor.nonceLen + cleartextChunkSize + contentCryptor.tagLen var chunkNumber: UInt64 = 0 while ciphertextStream.hasBytesAvailable { - guard let ciphertextChunk = try ciphertextStream.read(maxLength: ciphertextChunkSize) else { - continue + try autoreleasepool { + guard let ciphertextChunk = try ciphertextStream.read(maxLength: ciphertextChunkSize) else { + return + } + let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) + cleartextStream.write(cleartextChunk, maxLength: cleartextChunk.count) + progress.completedUnitCount += Int64(cleartextChunk.count) + chunkNumber += 1 } - let cleartextChunk = try decryptSingleChunk(ciphertextChunk, chunkNumber: chunkNumber, headerNonce: header.nonce, fileKey: header.contentKey) - cleartextStream.write(cleartextChunk, maxLength: cleartextChunk.count) - progress.completedUnitCount += Int64(cleartextChunk.count) - chunkNumber += 1 } } From 91bfb3ffdf58658b6c99b623d912ef7cfac8fe52 Mon Sep 17 00:00:00 2001 From: Philipp Schmid <25935690+phil1995@users.noreply.github.com> Date: Fri, 22 Oct 2021 12:43:35 +0200 Subject: [PATCH 142/144] =?UTF-8?q?Explicitly=20set=20derived=20data=20pat?= =?UTF-8?q?h=20for=20=E2=80=9CBuild=20and=20test"=20and=20"Upload=20code?= =?UTF-8?q?=20coverage=20report"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f2862e0..1e5419a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,8 @@ jobs: build: name: Build and test runs-on: macos-latest + env: + DERIVED_DATA_PATH: 'DerivedData' if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')" steps: - uses: actions/checkout@v2 @@ -21,11 +23,11 @@ jobs: ./Scripts/process.sh exit $? - name: Build and test - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'platform=macOS' -enableCodeCoverage YES clean test | xcpretty + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme 'CryptomatorCryptoLib' -destination 'platform=macOS' -derivedDataPath $DERIVED_DATA_PATH -enableCodeCoverage YES clean test | xcpretty - name: Upload code coverage report run: | gem install slather - slather coverage -x -i '../../../Library/*' --scheme CryptomatorCryptoLib CryptomatorCryptoLib.xcodeproj + slather coverage -x --build-directory $DERIVED_DATA_PATH --ignore "$DERIVED_DATA_PATH/SourcePackages/*" --scheme CryptomatorCryptoLib CryptomatorCryptoLib.xcodeproj bash <(curl -Ls https://coverage.codacy.com/get.sh) env: CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} From 401ff16fe32da5bb5890d70dd8f097f708c08bc6 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 13 Dec 2021 16:29:12 +0100 Subject: [PATCH 143/144] Removed deprecated LinuxMain and XCTestManifests --- .../XCTestManifests.swift | 72 ------------------- Tests/LinuxMain.swift | 8 --- 2 files changed, 80 deletions(-) delete mode 100644 Tests/CryptomatorCryptoLibTests/XCTestManifests.swift delete mode 100644 Tests/LinuxMain.swift diff --git a/Tests/CryptomatorCryptoLibTests/XCTestManifests.swift b/Tests/CryptomatorCryptoLibTests/XCTestManifests.swift deleted file mode 100644 index dad33df..0000000 --- a/Tests/CryptomatorCryptoLibTests/XCTestManifests.swift +++ /dev/null @@ -1,72 +0,0 @@ -#if !canImport(ObjectiveC) -import XCTest - -extension AesCtrTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__AesCtrTests = [ - ("testDecrypt", testDecrypt), - ("testEncrypt", testEncrypt), - ] -} - -extension AesSivTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__AesSivTests = [ - ("testCmac1", testCmac1), - ("testCmac2", testCmac2), - ("testCmac3", testCmac3), - ("testCmac4", testCmac4), - ("testCtr", testCtr), - ("testDecrypt", testDecrypt), - ("testEncrypt", testEncrypt), - ("testS2v", testS2v), - ] -} - -extension CryptorTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__CryptorTests = [ - ("testCalculateCiphertextSize", testCalculateCiphertextSize), - ("testCalculateCleartextSize", testCalculateCleartextSize), - ("testCalculateCleartextSizeWithInvalidCiphertextSize", testCalculateCleartextSizeWithInvalidCiphertextSize), - ("testCreateHeader", testCreateHeader), - ("testDecryptHeader", testDecryptHeader), - ("testEncryptAndDecryptContent", testEncryptAndDecryptContent), - ("testEncryptAndDecryptName", testEncryptAndDecryptName), - ("testEncryptAndDecryptSingleChunk", testEncryptAndDecryptSingleChunk), - ("testEncryptDirId", testEncryptDirId), - ("testEncryptHeader", testEncryptHeader), - ] -} - -extension MasterkeyTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__MasterkeyTests = [ - ("testCreateFromMasterkeyFile", testCreateFromMasterkeyFile), - ("testCreateFromMasterkeyFileWithInvalidVersionMac", testCreateFromMasterkeyFileWithInvalidVersionMac), - ("testCreateFromMasterkeyFileWithMalformedJson1", testCreateFromMasterkeyFileWithMalformedJson1), - ("testCreateFromMasterkeyFileWithMalformedJson2", testCreateFromMasterkeyFileWithMalformedJson2), - ("testCreateFromMasterkeyFileWithMalformedJson3", testCreateFromMasterkeyFileWithMalformedJson3), - ("testCreateFromMasterkeyFileWithWrongPassword", testCreateFromMasterkeyFileWithWrongPassword), - ("testExportEncrypted", testExportEncrypted), - ("testWrapAndUnwrapKey", testWrapAndUnwrapKey), - ] -} - -public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(AesCtrTests.__allTests__AesCtrTests), - testCase(AesSivTests.__allTests__AesSivTests), - testCase(CryptorTests.__allTests__CryptorTests), - testCase(MasterkeyTests.__allTests__MasterkeyTests), - ] -} -#endif diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 8389565..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,8 +0,0 @@ -import XCTest - -import CryptomatorCryptoLibTests - -var tests = [XCTestCaseEntry]() -tests += CryptomatorCryptoLibTests.__allTests() - -XCTMain(tests) From 24f6567bd455a7bdfabe482246d778b1de2728bb Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 13 Dec 2021 16:31:56 +0100 Subject: [PATCH 144/144] Updated SwiftFormat rules and bumped version --- .swiftformat | 7 ++----- Sources/CryptomatorCryptoLib/AesCtr.swift | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.swiftformat b/.swiftformat index f6f4141..d0c5fed 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,11 +1,8 @@ ---minversion 0.47.13 - -# file options - ---exclude Tests/LinuxMain.swift,Tests/**/XCTestManifests.swift +--minversion 0.49.0 # format options +--closurevoid preserve --commas inline --ifdef no-indent --importgrouping testable-bottom diff --git a/Sources/CryptomatorCryptoLib/AesCtr.swift b/Sources/CryptomatorCryptoLib/AesCtr.swift index 3d87df3..5a8f4b0 100644 --- a/Sources/CryptomatorCryptoLib/AesCtr.swift +++ b/Sources/CryptomatorCryptoLib/AesCtr.swift @@ -34,7 +34,7 @@ class AesCtr { let outlen = CCCryptorGetOutputLength(cryptor, data.count, true) var ciphertext = [UInt8](repeating: 0x00, count: outlen) - var numEncryptedBytes: Int = 0 + var numEncryptedBytes = 0 status = CCCryptorUpdate(cryptor, data, data.count, &ciphertext, ciphertext.count, &numEncryptedBytes) guard status == kCCSuccess else { throw CryptoError.ccCryptorError(status)