|
@@ -24,15 +24,6 @@ public final class GCM: BlockMode {
|
|
case combined
|
|
case combined
|
|
/// Some applications may need to store the authentication tag and the encrypted message at different locations.
|
|
/// Some applications may need to store the authentication tag and the encrypted message at different locations.
|
|
case detached
|
|
case detached
|
|
-
|
|
|
|
- var additionalBufferSize: Int {
|
|
|
|
- switch self {
|
|
|
|
- case .combined:
|
|
|
|
- return GCMModeWorker.tagLength
|
|
|
|
- case .detached:
|
|
|
|
- return 0
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
|
|
public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
|
|
@@ -47,6 +38,11 @@ public final class GCM: BlockMode {
|
|
private let iv: Array<UInt8>
|
|
private let iv: Array<UInt8>
|
|
private let additionalAuthenticatedData: Array<UInt8>?
|
|
private let additionalAuthenticatedData: Array<UInt8>?
|
|
private let mode: Mode
|
|
private let mode: Mode
|
|
|
|
+
|
|
|
|
+ /// Length of authentication tag, in bytes.
|
|
|
|
+ /// For encryption, the value is given as init parameter.
|
|
|
|
+ /// For decryption, the lenght of given authentication tag is used.
|
|
|
|
+ private let tagLength: Int
|
|
|
|
|
|
// `authenticationTag` nil for encryption, known tag for decryption
|
|
// `authenticationTag` nil for encryption, known tag for decryption
|
|
/// For encryption, the value is set at the end of the encryption.
|
|
/// For encryption, the value is set at the end of the encryption.
|
|
@@ -54,15 +50,17 @@ public final class GCM: BlockMode {
|
|
public var authenticationTag: Array<UInt8>?
|
|
public var authenticationTag: Array<UInt8>?
|
|
|
|
|
|
// encrypt
|
|
// encrypt
|
|
- public init(iv: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, mode: Mode = .detached) {
|
|
|
|
|
|
+ /// Possible tag lengths: 4,8,12,13,14,15,16
|
|
|
|
+ public init(iv: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, tagLength: Int = 16, mode: Mode = .detached) {
|
|
self.iv = iv
|
|
self.iv = iv
|
|
self.additionalAuthenticatedData = additionalAuthenticatedData
|
|
self.additionalAuthenticatedData = additionalAuthenticatedData
|
|
self.mode = mode
|
|
self.mode = mode
|
|
|
|
+ self.tagLength = tagLength
|
|
}
|
|
}
|
|
|
|
|
|
// decrypt
|
|
// decrypt
|
|
public convenience init(iv: Array<UInt8>, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, mode: Mode = .detached) {
|
|
public convenience init(iv: Array<UInt8>, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, mode: Mode = .detached) {
|
|
- self.init(iv: iv, additionalAuthenticatedData: additionalAuthenticatedData, mode: mode)
|
|
|
|
|
|
+ self.init(iv: iv, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode)
|
|
self.authenticationTag = authenticationTag
|
|
self.authenticationTag = authenticationTag
|
|
}
|
|
}
|
|
|
|
|
|
@@ -71,7 +69,7 @@ public final class GCM: BlockMode {
|
|
throw Error.invalidInitializationVector
|
|
throw Error.invalidInitializationVector
|
|
}
|
|
}
|
|
|
|
|
|
- let worker = GCMModeWorker(iv: iv.slice, aad: additionalAuthenticatedData?.slice, expectedTag: authenticationTag, mode: mode, cipherOperation: cipherOperation)
|
|
|
|
|
|
+ let worker = GCMModeWorker(iv: iv.slice, aad: additionalAuthenticatedData?.slice, expectedTag: authenticationTag, tagLength: tagLength, mode: mode, cipherOperation: cipherOperation)
|
|
worker.didCalculateTag = { [weak self] tag in
|
|
worker.didCalculateTag = { [weak self] tag in
|
|
self?.authenticationTag = tag
|
|
self?.authenticationTag = tag
|
|
}
|
|
}
|
|
@@ -87,8 +85,7 @@ final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, Finaliz
|
|
// Callback called when authenticationTag is ready
|
|
// Callback called when authenticationTag is ready
|
|
var didCalculateTag: ((Array<UInt8>) -> Void)?
|
|
var didCalculateTag: ((Array<UInt8>) -> Void)?
|
|
|
|
|
|
- // 128 bit tag. Other possible tags 4,8,12,13,14,15,16
|
|
|
|
- fileprivate static let tagLength = 16
|
|
|
|
|
|
+ private let tagLength: Int
|
|
// GCM nonce is 96-bits by default. It's the most effective length for the IV
|
|
// GCM nonce is 96-bits by default. It's the most effective length for the IV
|
|
private static let nonceSize = 12
|
|
private static let nonceSize = 12
|
|
|
|
|
|
@@ -115,15 +112,21 @@ final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, Finaliz
|
|
return GF(aad: [UInt8](), h: h, blockSize: blockSize)
|
|
return GF(aad: [UInt8](), h: h, blockSize: blockSize)
|
|
}()
|
|
}()
|
|
|
|
|
|
- init(iv: ArraySlice<UInt8>, aad: ArraySlice<UInt8>? = nil, expectedTag: Array<UInt8>? = nil, mode: GCM.Mode, cipherOperation: @escaping CipherOperationOnBlock) {
|
|
|
|
|
|
+ init(iv: ArraySlice<UInt8>, aad: ArraySlice<UInt8>? = nil, expectedTag: Array<UInt8>? = nil, tagLength: Int, mode: GCM.Mode, cipherOperation: @escaping CipherOperationOnBlock) {
|
|
self.cipherOperation = cipherOperation
|
|
self.cipherOperation = cipherOperation
|
|
self.iv = iv
|
|
self.iv = iv
|
|
self.mode = mode
|
|
self.mode = mode
|
|
- self.additionalBufferSize = mode.additionalBufferSize
|
|
|
|
self.aad = aad
|
|
self.aad = aad
|
|
self.expectedTag = expectedTag
|
|
self.expectedTag = expectedTag
|
|
|
|
+ self.tagLength = tagLength
|
|
h = UInt128(cipherOperation(Array<UInt8>(repeating: 0, count: blockSize).slice)!) // empty block
|
|
h = UInt128(cipherOperation(Array<UInt8>(repeating: 0, count: blockSize).slice)!) // empty block
|
|
|
|
|
|
|
|
+ if mode == .combined {
|
|
|
|
+ self.additionalBufferSize = tagLength
|
|
|
|
+ } else {
|
|
|
|
+ self.additionalBufferSize = 0
|
|
|
|
+ }
|
|
|
|
+
|
|
// Assume nonce is 12 bytes long, otherwise initial counter would be calulated from GHASH
|
|
// Assume nonce is 12 bytes long, otherwise initial counter would be calulated from GHASH
|
|
// counter = GF.ghash(aad: [UInt8](), ciphertext: nonce)
|
|
// counter = GF.ghash(aad: [UInt8](), ciphertext: nonce)
|
|
if iv.count == GCMModeWorker.nonceSize {
|
|
if iv.count == GCMModeWorker.nonceSize {
|
|
@@ -155,7 +158,7 @@ final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, Finaliz
|
|
func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
|
func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
|
// Calculate MAC tag.
|
|
// Calculate MAC tag.
|
|
let ghash = gf.ghashFinish()
|
|
let ghash = gf.ghashFinish()
|
|
- let tag = Array((ghash ^ eky0).bytes.prefix(GCMModeWorker.tagLength))
|
|
|
|
|
|
+ let tag = Array((ghash ^ eky0).bytes.prefix(tagLength))
|
|
|
|
|
|
// Notify handler
|
|
// Notify handler
|
|
didCalculateTag?(tag)
|
|
didCalculateTag?(tag)
|
|
@@ -186,13 +189,14 @@ final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, Finaliz
|
|
// The authenticated decryption operation has five inputs: K, IV , C, A, and T. It has only a single
|
|
// The authenticated decryption operation has five inputs: K, IV , C, A, and T. It has only a single
|
|
// output, either the plaintext value P or a special symbol FAIL that indicates that the inputs are not
|
|
// output, either the plaintext value P or a special symbol FAIL that indicates that the inputs are not
|
|
// authentic.
|
|
// authentic.
|
|
|
|
+ @discardableResult
|
|
func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
|
func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
|
// Validate tag
|
|
// Validate tag
|
|
switch mode {
|
|
switch mode {
|
|
case .combined:
|
|
case .combined:
|
|
// overwrite expectedTag property used later for verification
|
|
// overwrite expectedTag property used later for verification
|
|
- self.expectedTag = Array(ciphertext.suffix(GCMModeWorker.tagLength))
|
|
|
|
- return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(GCMModeWorker.tagLength,ciphertext.count))]
|
|
|
|
|
|
+ self.expectedTag = Array(ciphertext.suffix(tagLength))
|
|
|
|
+ return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength,ciphertext.count))]
|
|
case .detached:
|
|
case .detached:
|
|
return ciphertext
|
|
return ciphertext
|
|
}
|
|
}
|
|
@@ -201,7 +205,7 @@ final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, Finaliz
|
|
func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
|
func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
|
// Calculate MAC tag.
|
|
// Calculate MAC tag.
|
|
let ghash = gf.ghashFinish()
|
|
let ghash = gf.ghashFinish()
|
|
- let computedTag = Array((ghash ^ eky0).bytes.prefix(GCMModeWorker.tagLength))
|
|
|
|
|
|
+ let computedTag = Array((ghash ^ eky0).bytes.prefix(tagLength))
|
|
|
|
|
|
// Validate tag
|
|
// Validate tag
|
|
guard let expectedTag = self.expectedTag, computedTag == expectedTag else {
|
|
guard let expectedTag = self.expectedTag, computedTag == expectedTag else {
|