ソースを参照

CCM base on CBC. formatNonce

Marcin Krzyzanowski 7 年 前
コミット
2019b23517
1 ファイル変更78 行追加16 行削除
  1. 78 16
      Sources/CryptoSwift/BlockMode/CCM.swift

+ 78 - 16
Sources/CryptoSwift/BlockMode/CCM.swift

@@ -13,40 +13,102 @@
 //
 
 // CCM mode combines the well known CBC-MAC with the well known counter mode of encryption.
+// https://tools.ietf.org/html/rfc3610
+// https://csrc.nist.gov/publications/detail/sp/800-38c/final
 
-public final class CCM: BlockMode {
+
+public struct CCM: BlockMode {
     public enum Error: Swift.Error {
         /// Invalid IV
         case invalidInitializationVector
+        case invalidParameter
     }
 
-    public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
+    public let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired]
+    private let iv: Array<UInt8>
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
-        fatalError("Not implemented")
+    public init(iv: Array<UInt8>) {
+        self.iv = iv
     }
-}
 
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+        if iv.count != blockSize {
+            throw Error.invalidInitializationVector
+        }
+
+        return CBCModeWorker(blockSize: blockSize, iv: iv.slice, cipherOperation: cipherOperation)
+    }
 
-struct CCMModeWorker: StreamModeWorker, CounterModeWorker {
-    var counter: CTRModeWorker.CTRCounter
 
-    func seek(to position: Int) throws {
-        fatalError("Unimplemented")
+    // Apply the formatting function to (N, A, P) to produce blocks [B0, ..., Br]
+    private func format(N: [UInt8], A: [UInt8], P: [UInt8]) throws -> [UInt8] {
+        let block0 = try formatNonce(N: N, Q: 512, q: 3, t: 12, hasAssociatedData: false) // mock
+        return []
     }
 
-    typealias Counter = CTRModeWorker.CTRCounter
+    // Q - octet length of P
+    // q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
+    // t - octet length of T (MAC length). An element of {4,6,8,10,12,14,16}
+    private func formatNonce(N: [UInt8], Q: UInt32, q: UInt8, t: UInt8, hasAssociatedData: Bool) throws -> [UInt8] {
+        var flags0: UInt8 = 0
+
+        if hasAssociatedData {
+            // 6 bit
+            flags0 |= (1 << 6)
+        }
+
+        // 5,4,3 is t in 3 bits
+        flags0 |= (((t-2)/2) & 0x07) << 3
 
-    var cipherOperation: CipherOperationOnBlock
+        // 2,1,0 is q in 3 bits
+        flags0 |= ((q-1) & 0x07) << 0
 
-    var additionalBufferSize: Int
+        var block0: [UInt8] = Array<UInt8>(repeating: 0, count: 16) // block[0]
+        block0[0] = flags0
 
-    func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
-        fatalError("Unimplemented")
+        // N in 1...(15-q) octets, n = 15-q
+        // n is an element of {7,8,9,10,11,12,13}
+        let n = 15-Int(q)
+        guard (n + Int(q)) == 15 else {
+            // n+q == 15
+            throw Error.invalidParameter
+        }
+        block0[1...n] = N[0...(n-1)]
+
+        // Q in (16-q)...15 octets
+        block0[(16-Int(q))...15] = Q.bytes(totalBytes: Int(q)).slice
+
+        return block0
     }
+}
+
+struct CCMModeWorker: BlockModeWorker {
+    let cipherOperation: CipherOperationOnBlock
+    var blockSize: Int
+    let additionalBufferSize: Int = 0
+    private let iv: ArraySlice<UInt8>
+    private var prev: ArraySlice<UInt8>?
 
-    func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
-        fatalError("Unimplemented")
+    init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
+        self.blockSize = blockSize
+        self.iv = iv
+        self.cipherOperation = cipherOperation
     }
 
+    mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+        guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
+            return Array(plaintext)
+        }
+        prev = ciphertext.slice
+        return ciphertext
+    }
+
+    mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+        guard let plaintext = cipherOperation(ciphertext) else {
+            return Array(ciphertext)
+        }
+        let result: Array<UInt8> = xor(prev ?? iv, plaintext)
+        prev = ciphertext
+        return result
+    }
 }