@@ -23,7 +23,7 @@ protocol ContentCryptor {
23
23
- Parameter ad: Associated data, which needs to be authenticated during decryption.
24
24
- Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array.
25
25
*/
26
- func encrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , nonce: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ]
26
+ func encrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , nonce: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ]
27
27
28
28
/**
29
29
Decrypts one single chunk of encrypted data.
@@ -33,29 +33,60 @@ protocol ContentCryptor {
33
33
- Parameter ad: Associated data, which needs to be authenticated during decryption.
34
34
- Returns: The original cleartext.
35
35
*/
36
- func decrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ]
36
+ func decrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ]
37
+
38
+ /**
39
+ Constructs the associated data which will be authenticated during encryption/decryption of a single chunk
40
+
41
+ - Parameter chunkNumber: The index of the chunk (starting at 0), preventing swapping of chunks
42
+ - Parameter headerNonce: The nonce used in the file header, binding the chunk to this particular file.
43
+ - Returns: The combined associated data.
44
+ */
45
+ func ad( chunkNumber: UInt64 , headerNonce: [ UInt8 ] ) -> [ UInt8 ]
46
+ }
47
+
48
+ extension ContentCryptor {
49
+ func encryptHeader( _ header: [ UInt8 ] , key: [ UInt8 ] , nonce: [ UInt8 ] ) throws -> [ UInt8 ] {
50
+ return try encrypt ( header, key: key, nonce: nonce, ad: [ ] )
51
+ }
52
+
53
+ func decryptHeader( _ header: [ UInt8 ] , key: [ UInt8 ] ) throws -> [ UInt8 ] {
54
+ return try decrypt ( header, key: key, ad: [ ] )
55
+ }
56
+
57
+ func encryptChunk( _ chunk: [ UInt8 ] , chunkNumber: UInt64 , chunkNonce: [ UInt8 ] , fileKey: [ UInt8 ] , headerNonce: [ UInt8 ] ) throws -> [ UInt8 ] {
58
+ let ad = ad ( chunkNumber: chunkNumber, headerNonce: headerNonce)
59
+ return try encrypt ( chunk, key: fileKey, nonce: chunkNonce, ad: ad)
60
+ }
61
+
62
+ func decryptChunk( _ chunk: [ UInt8 ] , chunkNumber: UInt64 , fileKey: [ UInt8 ] , headerNonce: [ UInt8 ] ) throws -> [ UInt8 ] {
63
+ let ad = ad ( chunkNumber: chunkNumber, headerNonce: headerNonce)
64
+ return try decrypt ( chunk, key: fileKey, ad: ad)
65
+ }
37
66
}
38
67
39
68
class GcmContentCryptor : ContentCryptor {
40
69
let nonceLen = 12 // 96 bit
41
70
let tagLen = 16 // 128 bit
42
71
43
- func encrypt( _ chunk: [ UInt8 ] , key keyBytes: [ UInt8 ] , nonce nonceBytes: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ] {
44
- let concatAd = ad. reduce ( [ ] , + )
72
+ func ad( chunkNumber: UInt64 , headerNonce: [ UInt8 ] ) -> [ UInt8 ] {
73
+ return chunkNumber. bigEndian. byteArray ( ) + headerNonce
74
+ }
75
+
76
+ func encrypt( _ chunk: [ UInt8 ] , key keyBytes: [ UInt8 ] , nonce nonceBytes: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ] {
45
77
let key = SymmetricKey ( data: keyBytes)
46
78
let nonce = try AES . GCM. Nonce ( data: nonceBytes)
47
- let encrypted = try AES . GCM. seal ( chunk, using: key, nonce: nonce, authenticating: concatAd )
79
+ let encrypted = try AES . GCM. seal ( chunk, using: key, nonce: nonce, authenticating: ad )
48
80
49
81
return [ UInt8] ( encrypted. nonce + encrypted. ciphertext + encrypted. tag)
50
82
}
51
83
52
- func decrypt( _ chunk: [ UInt8 ] , key keyBytes: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ] {
84
+ func decrypt( _ chunk: [ UInt8 ] , key keyBytes: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ] {
53
85
assert ( chunk. count >= nonceLen + tagLen, " ciphertext chunk must at least contain nonce + tag " )
54
86
55
- let concatAd = ad. reduce ( [ ] , + )
56
87
let key = SymmetricKey ( data: keyBytes)
57
88
let encrypted = try AES . GCM. SealedBox ( combined: chunk)
58
- let decrypted = try AES . GCM. open ( encrypted, using: key, authenticating: concatAd )
89
+ let decrypted = try AES . GCM. open ( encrypted, using: key, authenticating: ad )
59
90
60
91
return [ UInt8] ( decrypted)
61
92
}
@@ -73,13 +104,17 @@ class CtrThenHmacContentCryptor: ContentCryptor {
73
104
self . cryptoSupport = cryptoSupport
74
105
}
75
106
76
- func encrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , nonce: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ] {
107
+ func ad( chunkNumber: UInt64 , headerNonce: [ UInt8 ] ) -> [ UInt8 ] {
108
+ return headerNonce + chunkNumber. bigEndian. byteArray ( )
109
+ }
110
+
111
+ func encrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , nonce: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ] {
77
112
let ciphertext = try AesCtr . compute ( key: key, iv: nonce, data: chunk)
78
113
let mac = computeHmac ( ciphertext, nonce: nonce, ad: ad)
79
114
return nonce + ciphertext + mac
80
115
}
81
116
82
- func decrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ] {
117
+ func decrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ] {
83
118
assert ( chunk. count >= nonceLen + tagLen, " ciphertext chunk must at least contain nonce + tag " )
84
119
85
120
// decompose chunk:
@@ -98,8 +133,8 @@ class CtrThenHmacContentCryptor: ContentCryptor {
98
133
return try AesCtr . compute ( key: key, iv: chunkNonce, data: ciphertext)
99
134
}
100
135
101
- private func computeHmac( _ ciphertext: [ UInt8 ] , nonce: [ UInt8 ] , ad: [ [ UInt8 ] ] ) -> [ UInt8 ] {
102
- let data = ad. reduce ( [ UInt8 ] ( ) , + ) + nonce + ciphertext
136
+ private func computeHmac( _ ciphertext: [ UInt8 ] , nonce: [ UInt8 ] , ad: [ UInt8 ] ) -> [ UInt8 ] {
137
+ let data = ad + nonce + ciphertext
103
138
var mac = [ UInt8] ( repeating: 0x00 , count: tagLen)
104
139
CCHmac ( CCHmacAlgorithm ( kCCHmacAlgSHA256) , macKey, macKey. count, data, data. count, & mac)
105
140
return mac
0 commit comments