Skip to content

Commit 135b6f3

Browse files
Improved error handling
1 parent 58fb08c commit 135b6f3

File tree

3 files changed

+95
-49
lines changed

3 files changed

+95
-49
lines changed

CryptoLib/AesSiv.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class AesSiv {
2222

2323
public static func encrypt(aesKey: [UInt8], macKey: [UInt8], plaintext: [UInt8], ad: [UInt8]...) throws -> [UInt8] {
2424
if (plaintext.count > UInt32.max - 16) {
25-
throw AesSivError.invalidParameter("ciphertext must be at least 16 bytes")
25+
throw AesSivError.invalidParameter("plaintext must not be longer than 2^32 - 16 bytes")
2626
}
2727
let iv = try s2v(macKey: macKey, plaintext: plaintext, ad: ad)
2828
let ciphertext = try aesCtr(aesKey: aesKey, iv: iv, plaintext: plaintext)

CryptoLib/Masterkey.swift

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ struct MasterkeyJson: Codable {
2020
let version: Int
2121
}
2222

23+
enum MasterkeyError: Error, Equatable {
24+
case malformedMasterkeyFile(_ reason: String)
25+
case invalidPassword
26+
case unwrapFailed(_ status: CCCryptorStatus)
27+
case wrapFailed(_ status: CCCryptorStatus)
28+
}
29+
2330
public class Masterkey {
2431

2532
private(set) var aesMasterKey: [UInt8]
@@ -42,85 +49,70 @@ public class Masterkey {
4249
// MARK: -
4350
// MARK: Masterkey Factory Methods
4451

45-
public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) -> Masterkey? {
46-
if let jsonData = try? Data(contentsOf: file) {
47-
return createFromMasterkeyFile(jsonData: jsonData, password: password)
48-
} else {
49-
return nil
50-
}
52+
public static func createFromMasterkeyFile(file: URL, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey {
53+
let jsonData = try Data(contentsOf: file)
54+
return try createFromMasterkeyFile(jsonData: jsonData, password: password)
55+
5156
}
5257

53-
public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) -> Masterkey? {
58+
public static func createFromMasterkeyFile(jsonData: Data, password: String, pepper: [UInt8] = [UInt8]()) throws -> Masterkey {
5459
let jsonDecoder = JSONDecoder()
55-
if let decoded = try? jsonDecoder.decode(MasterkeyJson.self, from: jsonData) {
56-
return createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper);
57-
} else {
58-
return nil
59-
}
60+
let decoded = try jsonDecoder.decode(MasterkeyJson.self, from: jsonData)
61+
return try createFromMasterkeyFile(jsonData: decoded, password: password, pepper: pepper);
6062
}
6163

62-
static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) -> Masterkey? {
64+
static func createFromMasterkeyFile(jsonData: MasterkeyJson, password: String, pepper: [UInt8]) throws -> Masterkey {
6365
let pw = [UInt8](password.precomposedStringWithCanonicalMapping.utf8)
6466
let salt = [UInt8](Data(base64Encoded: jsonData.scryptSalt)!)
6567
let saltAndPepper = salt + pepper
66-
guard let kek = try? Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate() else {
67-
debugPrint("scrypt failed")
68-
return nil;
69-
}
70-
71-
let wrappedMasterKey = [UInt8](Data(base64Encoded: jsonData.primaryMasterKey)!)
72-
let unwrappedMasterKey = unwrapMasterKey(wrappedKey: wrappedMasterKey, kek: kek)
68+
let kek = try Scrypt(password: pw, salt: saltAndPepper, dkLen: kCCKeySizeAES256, N: jsonData.scryptCostParam, r: jsonData.scryptBlockSize, p: 1).calculate()
7369

74-
let wrappedHmacKey = [UInt8](Data(base64Encoded: jsonData.hmacMasterKey)!)
75-
let unwrappedHmacKey = unwrapMasterKey(wrappedKey: wrappedHmacKey, kek: kek)
70+
guard let wrappedMasterKey = Data(base64Encoded: jsonData.primaryMasterKey) else {
71+
throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey")
72+
}
73+
let aesKey = try unwrapMasterKey(wrappedKey: wrappedMasterKey.bytes, kek: kek)
7674

77-
if (unwrappedMasterKey != nil) && (unwrappedHmacKey != nil) {
78-
return createFromRaw(aesMasterKey: unwrappedMasterKey!, macMasterKey: unwrappedHmacKey!)
79-
} else {
80-
return nil
75+
guard let wrappedHmacKey = Data(base64Encoded: jsonData.hmacMasterKey) else {
76+
throw MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey")
8177
}
78+
let macKey = try unwrapMasterKey(wrappedKey: wrappedHmacKey.bytes, kek: kek)
79+
80+
return createFromRaw(aesMasterKey: aesKey, macMasterKey: macKey)
8281
}
8382

8483
internal static func createFromRaw(aesMasterKey: [UInt8], macMasterKey: [UInt8]) -> Masterkey {
85-
// TODO CMAC implementation doesn't support 256 bit keys yet -.-
86-
// assert(aesMasterKey.count == kCCKeySizeAES256)
87-
// assert(macMasterKey.count == kCCKeySizeAES256)
84+
assert(aesMasterKey.count == kCCKeySizeAES256)
85+
assert(macMasterKey.count == kCCKeySizeAES256)
8886
return Masterkey(aesMasterKey: aesMasterKey, macMasterKey: macMasterKey)
8987
}
9088

9189
// MARK: -
9290
// MARK: RFC 3394 Key Wrapping
9391

94-
static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) -> [UInt8]? {
92+
static func wrapMasterKey(rawKey: [UInt8], kek: [UInt8]) throws -> [UInt8] {
9593
assert(kek.count == kCCKeySizeAES256)
9694
var wrapepdKeyLen = CCSymmetricWrappedSize(CCWrappingAlgorithm(kCCWRAPAES), rawKey.count)
9795
var wrapepdKey = [UInt8](repeating: 0x00, count: wrapepdKeyLen);
9896
let status = CCSymmetricKeyWrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, rawKey, rawKey.count, &wrapepdKey, &wrapepdKeyLen)
9997
if status == kCCSuccess {
10098
return wrapepdKey
101-
} else if status == kCCParamError {
102-
// wrong password
103-
return nil
10499
} else {
105-
debugPrint("unwrapping masterkey failed with status code ", status)
106-
return nil
100+
throw MasterkeyError.wrapFailed(status)
107101
}
108102
}
109103

110-
static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) -> [UInt8]? {
104+
static func unwrapMasterKey(wrappedKey: [UInt8], kek: [UInt8]) throws -> [UInt8] {
111105
assert(kek.count == kCCKeySizeAES256)
112106
var unwrapepdKeyLen = CCSymmetricUnwrappedSize(CCWrappingAlgorithm(kCCWRAPAES), wrappedKey.count)
113107
var unwrapepdKey = [UInt8](repeating: 0x00, count: unwrapepdKeyLen);
114108
let status = CCSymmetricKeyUnwrap(CCWrappingAlgorithm(kCCWRAPAES), CCrfc3394_iv, CCrfc3394_ivLen, kek, kek.count, wrappedKey, wrappedKey.count, &unwrapepdKey, &unwrapepdKeyLen)
115109
if status == kCCSuccess {
116110
assert(unwrapepdKeyLen == kCCKeySizeAES256)
117111
return unwrapepdKey
118-
} else if status == kCCParamError {
119-
// wrong password
120-
return nil
112+
} else if status == kCCDecodeError {
113+
throw MasterkeyError.invalidPassword
121114
} else {
122-
debugPrint("unwrapping masterkey failed with status code ", status)
123-
return nil
115+
throw MasterkeyError.unwrapFailed(status)
124116
}
125117
}
126118

CryptoLibTests/MasterkeyTests.swift

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ class MasterkeyTests: XCTestCase {
1919
// Put teardown code here. This method is called after the invocation of each test method in the class.
2020
}
2121

22-
func testWrapAndUnwrapKey() {
22+
func testWrapAndUnwrapKey() throws {
2323
let rawKey = [UInt8](repeating: 0x77, count: 32)
2424
let kek = [UInt8](repeating: 0x55, count: 32)
25-
let wrapped = Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek)
25+
let wrapped = try Masterkey.wrapMasterKey(rawKey: rawKey, kek: kek)
2626
XCTAssertNotNil(wrapped)
27-
let unwrapped = Masterkey.unwrapMasterKey(wrappedKey: wrapped!, kek: kek)
27+
let unwrapped = try Masterkey.unwrapMasterKey(wrappedKey: wrapped, kek: kek)
2828
XCTAssertNotNil(unwrapped)
2929
XCTAssertEqual(rawKey, unwrapped)
3030
}
3131

32-
func testCreateFromMasterkeyFile() {
32+
func testCreateFromMasterkeyFile() throws {
3333
let expectedKeys = [UInt8](repeating: 0x00, count: 32)
3434
let jsonData = """
3535
{
@@ -43,11 +43,65 @@ class MasterkeyTests: XCTestCase {
4343
}
4444
""".data(using: .utf8)!
4545

46-
let masterKey = Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd")
46+
let masterKey = try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd")
4747

4848
XCTAssertNotNil(masterKey)
49-
XCTAssertEqual(expectedKeys, masterKey?.aesMasterKey)
50-
XCTAssertEqual(expectedKeys, masterKey?.macMasterKey)
49+
XCTAssertEqual(expectedKeys, masterKey.aesMasterKey)
50+
XCTAssertEqual(expectedKeys, masterKey.macMasterKey)
51+
}
52+
53+
func testCreateFromMasterkeyFileWithWrongPassword() throws {
54+
let jsonData = """
55+
{
56+
"version": 3,
57+
"scryptSalt": "AAAAAAAAAAA=",
58+
"scryptCostParam": 2,
59+
"scryptBlockSize": 8,
60+
"primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==",
61+
"hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==",
62+
"versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA="
63+
}
64+
""".data(using: .utf8)!
65+
66+
XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "qwe"), "invalid password", { error in
67+
XCTAssertEqual(error as! MasterkeyError, MasterkeyError.invalidPassword)
68+
})
69+
}
70+
71+
func testCreateFromMasterkeyFileWithMalformedJson1() throws {
72+
let jsonData = """
73+
{
74+
"version": 3,
75+
"scryptSalt": "AAAAAAAAAAA=",
76+
"scryptCostParam": 2,
77+
"scryptBlockSize": 8,
78+
"primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!",
79+
"hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==",
80+
"versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA="
81+
}
82+
""".data(using: .utf8)!
83+
84+
XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in
85+
XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in primaryMasterKey"))
86+
})
87+
}
88+
89+
func testCreateFromMasterkeyFileWithMalformedJson2() throws {
90+
let jsonData = """
91+
{
92+
"version": 3,
93+
"scryptSalt": "AAAAAAAAAAA=",
94+
"scryptCostParam": 2,
95+
"scryptBlockSize": 8,
96+
"primaryMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==",
97+
"hmacMasterKey": "mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q!!",
98+
"versionMac": "iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA="
99+
}
100+
""".data(using: .utf8)!
101+
102+
XCTAssertThrowsError(try Masterkey.createFromMasterkeyFile(jsonData: jsonData, password: "asd"), "invalid password", { error in
103+
XCTAssertEqual(error as! MasterkeyError, MasterkeyError.malformedMasterkeyFile("invalid base64 data in hmacMasterKey"))
104+
})
51105
}
52106

53107
}

0 commit comments

Comments
 (0)