@@ -23,7 +23,7 @@ protocol ContentCryptor {
2323 - Parameter ad: Associated data, which needs to be authenticated during decryption.
2424 - Returns: Nonce/IV + ciphertext + MAC/tag, as a concatenated byte array.
2525 */
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 ]
2727
2828 /**
2929 Decrypts one single chunk of encrypted data.
@@ -33,29 +33,60 @@ protocol ContentCryptor {
3333 - Parameter ad: Associated data, which needs to be authenticated during decryption.
3434 - Returns: The original cleartext.
3535 */
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+ }
3766}
3867
3968class GcmContentCryptor : ContentCryptor {
4069 let nonceLen = 12 // 96 bit
4170 let tagLen = 16 // 128 bit
4271
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 ] {
4577 let key = SymmetricKey ( data: keyBytes)
4678 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 )
4880
4981 return [ UInt8] ( encrypted. nonce + encrypted. ciphertext + encrypted. tag)
5082 }
5183
52- func decrypt( _ chunk: [ UInt8 ] , key keyBytes: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ] {
84+ func decrypt( _ chunk: [ UInt8 ] , key keyBytes: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ] {
5385 assert ( chunk. count >= nonceLen + tagLen, " ciphertext chunk must at least contain nonce + tag " )
5486
55- let concatAd = ad. reduce ( [ ] , + )
5687 let key = SymmetricKey ( data: keyBytes)
5788 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 )
5990
6091 return [ UInt8] ( decrypted)
6192 }
@@ -73,13 +104,17 @@ class CtrThenHmacContentCryptor: ContentCryptor {
73104 self . cryptoSupport = cryptoSupport
74105 }
75106
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 ] {
77112 let ciphertext = try AesCtr . compute ( key: key, iv: nonce, data: chunk)
78113 let mac = computeHmac ( ciphertext, nonce: nonce, ad: ad)
79114 return nonce + ciphertext + mac
80115 }
81116
82- func decrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , ad: [ UInt8 ] ... ) throws -> [ UInt8 ] {
117+ func decrypt( _ chunk: [ UInt8 ] , key: [ UInt8 ] , ad: [ UInt8 ] ) throws -> [ UInt8 ] {
83118 assert ( chunk. count >= nonceLen + tagLen, " ciphertext chunk must at least contain nonce + tag " )
84119
85120 // decompose chunk:
@@ -98,8 +133,8 @@ class CtrThenHmacContentCryptor: ContentCryptor {
98133 return try AesCtr . compute ( key: key, iv: chunkNonce, data: ciphertext)
99134 }
100135
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
103138 var mac = [ UInt8] ( repeating: 0x00 , count: tagLen)
104139 CCHmac ( CCHmacAlgorithm ( kCCHmacAlgSHA256) , macKey, macKey. count, data, data. count, & mac)
105140 return mac
0 commit comments