浏览代码

Rework Poly1305. Fixes #183

Marcin Krzyżanowski 8 年之前
父节点
当前提交
339eb8cf88
共有 2 个文件被更改,包括 107 次插入239 次删除
  1. 96 235
      Sources/CryptoSwift/Poly1305.swift
  2. 11 4
      Tests/CryptoSwiftTests/Poly1305Tests.swift

+ 96 - 235
Sources/CryptoSwift/Poly1305.swift

@@ -6,269 +6,140 @@
 //  Copyright (c) 2014 Marcin Krzyzanowski. All rights reserved.
 //  Copyright (c) 2014 Marcin Krzyzanowski. All rights reserved.
 //
 //
 //  http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-4
 //  http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-4
+//  nacl/crypto_onetimeauth/poly1305/ref/auth.c
 //
 //
 ///  Poly1305 takes a 32-byte, one-time key and a message and produces a 16-byte tag that authenticates the
 ///  Poly1305 takes a 32-byte, one-time key and a message and produces a 16-byte tag that authenticates the
 ///  message such that an attacker has a negligible chance of producing a valid tag for an inauthentic message.
 ///  message such that an attacker has a negligible chance of producing a valid tag for an inauthentic message.
+
 public final class Poly1305: Authenticator {
 public final class Poly1305: Authenticator {
 
 
     public enum Error: Swift.Error {
     public enum Error: Swift.Error {
         case authenticateError
         case authenticateError
     }
     }
 
 
-    let blockSize = 16
-    private var ctx: Context?
-
-    private final class Context {
-        var r = Array<UInt8>(repeating: 0, count: 17)
-        var h = Array<UInt8>(repeating: 0, count: 17)
-        var pad = Array<UInt8>(repeating: 0, count: 17)
-        var buffer = Array<UInt8>(repeating: 0, count: 16)
-
-        var final: UInt8 = 0
-        var leftover: Int = 0
-
-        init(_ key: Array<UInt8>) {
-            precondition(key.count == 32, "Invalid key length")
-
-            for i in 0 ..< 17 {
-                h[i] = 0
-            }
-
-            r[0] = key[0] & 0xff
-            r[1] = key[1] & 0xff
-            r[2] = key[2] & 0xff
-            r[3] = key[3] & 0x0f
-            r[4] = key[4] & 0xfc
-            r[5] = key[5] & 0xff
-            r[6] = key[6] & 0xff
-            r[7] = key[7] & 0x0f
-            r[8] = key[8] & 0xfc
-            r[9] = key[9] & 0xff
-            r[10] = key[10] & 0xff
-            r[11] = key[11] & 0x0f
-            r[12] = key[12] & 0xfc
-            r[13] = key[13] & 0xff
-            r[14] = key[14] & 0xff
-            r[15] = key[15] & 0x0f
-            r[16] = 0
-
-            for i in 0 ..< 16 {
-                pad[i] = key[i + 16]
-            }
-            pad[16] = 0
-
-            leftover = 0
-            final = 0
-        }
+    public static let blockSize: Int = 16
 
 
-        deinit {
-            for i in 0 ..< buffer.count {
-                buffer[i] = 0
-            }
-
-            for i in 0 ..< r.count {
-                r[i] = 0
-                h[i] = 0
-                pad[i] = 0
-                final = 0
-                leftover = 0
-            }
-        }
-    }
+    private let key: SecureBytes
 
 
     /// - parameter key: 32-byte key
     /// - parameter key: 32-byte key
     public init(key: Array<UInt8>) {
     public init(key: Array<UInt8>) {
-        ctx = Context(key)
-    }
-
-    // MARK: - Private
-
-    /**
-     Add message to be processed
-
-     - parameter context: Context
-     - parameter message: message
-     - parameter bytes:   length of the message fragment to be processed
-     */
-    private func update(_ context: Context, message: Array<UInt8>, bytes: Int? = nil) {
-        var bytes = bytes ?? message.count
-        var mPos = 0
-
-        /* handle leftover */
-        if (context.leftover > 0) {
-            var want = blockSize - context.leftover
-            if (want > bytes) {
-                want = bytes
-            }
-
-            for i in 0 ..< want {
-                context.buffer[context.leftover + i] = message[mPos + i]
-            }
-
-            bytes -= want
-            mPos += want
-            context.leftover += want
-
-            if (context.leftover < blockSize) {
-                return
-            }
-
-            blocks(context, m: context.buffer)
-            context.leftover = 0
-        }
-
-        /* process full blocks */
-        if (bytes >= blockSize) {
-            let want = bytes & ~(blockSize - 1)
-            blocks(context, m: message, startPos: mPos)
-            mPos += want
-            bytes -= want
-        }
-
-        /* store leftover */
-        if (bytes > 0) {
-            for i in 0 ..< bytes {
-                context.buffer[context.leftover + i] = message[mPos + i]
-            }
-
-            context.leftover += bytes
-        }
+        self.key = SecureBytes(bytes: key)
     }
     }
 
 
-    private func finish(_ context: Context) -> Array<UInt8>? {
-        var mac = Array<UInt8>(repeating: 0, count: 16)
-
-        /* process the remaining block */
-        if (context.leftover > 0) {
-
-            context.buffer[context.leftover] = 1
-            for i in (context.leftover + 1) ..< blockSize {
-                context.buffer[i] = 0
-            }
-            context.final = 1
-
-            blocks(context, m: context.buffer)
+    private func squeeze(h: inout Array<UInt32>) {
+        assert(h.count == 17)
+        var u: UInt32 = 0
+        for j in 0..<16 {
+            u = u &+ h[j]
+            h[j] = u & 255
+            u = u >> 8
         }
         }
 
 
-        /* fully reduce h */
-        freeze(context)
+        u = u &+ h[16]
+        h[16] = u & 3
+        u = 5 * (u >> 2)
 
 
-        /* h = (h + pad) % (1 << 128) */
-        add(context, c: context.pad)
-        for i in 0 ..< mac.count {
-            mac[i] = context.h[i]
+        for j in 0..<16 {
+            u = u &+ h[j]
+            h[j] = u & 255
+            u = u >> 8
         }
         }
 
 
-        return mac
+        u = u &+ h[16]
+        h[16] = u
     }
     }
 
 
-    // MARK: - Utils
-
-    private func add(_ context: Context, c: Array<UInt8>) {
-        if (context.h.count != 17 && c.count != 17) {
-            assertionFailure()
-            return
-        }
+    private func add(h: inout Array<UInt32>, c: Array<UInt32>) {
+        assert(h.count == 17 && c.count == 17)
 
 
-        var u: UInt16 = 0
-        for i in 0 ..< 17 {
-            u += UInt16(context.h[i]) + UInt16(c[i])
-            context.h[i] = UInt8.with(value: u)
+        var u: UInt32 = 0
+        for j in 0..<17 {
+            u = u &+ (h[j] &+ c[j])
+            h[j] = u & 255
             u = u >> 8
             u = u >> 8
         }
         }
-        return
     }
     }
 
 
-    private func squeeze(_ context: Context, hr: Array<UInt32>) {
-        if (context.h.count != 17 && hr.count != 17) {
-            assertionFailure()
-            return
-        }
-
+    private func mulmod(h: inout Array<UInt32>, r: Array<UInt32>) {
+        var hr = Array<UInt32>(repeating: 0, count: 17)
         var u: UInt32 = 0
         var u: UInt32 = 0
-
-        for i in 0 ..< 16 {
-            u += hr[i]
-            context.h[i] = UInt8.with(value: u) // crash! h[i] = UInt8(u) & 0xff
-            u >>= 8
-        }
-
-        u += hr[16]
-        context.h[16] = UInt8.with(value: u) & 0x03
-        u >>= 2
-        u += (u << 2) /* u *= 5; */
-        for i in 0 ..< 16 {
-            u += UInt32(context.h[i])
-            context.h[i] = UInt8.with(value: u) // crash! h[i] = UInt8(u) & 0xff
-            u >>= 8
+        for i in 0..<17 {
+            u = 0
+            for j in 0...i {
+                u = u &+ (h[j] * r[i &- j])
+            }
+            for j in (i + 1)..<17 {
+                u = u &+ (320 * h[j] * r[i &+ 17 &- j])
+            }
+            hr[i] = u
         }
         }
-        context.h[16] += UInt8.with(value: u)
+        h = hr
+        self.squeeze(h: &h)
     }
     }
 
 
-    private func freeze(_ context: Context) {
-        assert(context.h.count == 17, "Invalid length")
-        if (context.h.count != 17) {
-            return
-        }
-
-        let minusp: Array<UInt8> = [0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc]
-        var horig: Array<UInt8> = Array<UInt8>(repeating: 0, count: 17)
-
-        /* compute h + -p */
-        for i in 0 ..< 17 {
-            horig[i] = context.h[i]
-        }
-
-        add(context, c: minusp)
-
-        /* select h if h < p, or h + -p if h >= p */
-        let bits: [Bit] = (context.h[16] >> 7).bits()
-        let invertedBits = bits.map({ (bit) -> Bit in
-            return bit.inverted()
-        })
-
-        let negative = UInt8(bits: invertedBits)
-        for i in 0 ..< 17 {
-            context.h[i] ^= negative & (horig[i] ^ context.h[i])
+    private func freeze(h: inout Array<UInt32>) {
+        let horig = h
+        add(h: &h, c: [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252])
+        let negative = UInt32(bitPattern: -Int32(h[16] >> 7))
+        for j in 0..<17 {
+            h[j] ^= negative & (horig[j] ^ h[j])
         }
         }
     }
     }
 
 
-    private func blocks(_ context: Context, m: Array<UInt8>, startPos: Int = 0) {
-        var bytes = m.count
-        let hibit = context.final ^ 1 // 1 <<128
-        var mPos = startPos
-
-        while (bytes >= Int(blockSize)) {
-            var hr: Array<UInt32> = Array<UInt32>(repeating: 0, count: 17)
-            var u: UInt32 = 0
-            var c: Array<UInt8> = Array<UInt8>(repeating: 0, count: 17)
-
-            /* h += m */
-            for i in 0 ..< 16 {
-                c[i] = m[mPos + i]
+    /// the key is partitioned into two parts, called "r" and "s"
+    fileprivate func onetimeauth(message input: Array<UInt8>, key k: Array<UInt8>) -> Array<UInt8> {
+        // clamp
+        var r = Array<UInt32>(repeating: 0, count: 17)
+        var h = Array<UInt32>(repeating: 0, count: 17)
+        var c = Array<UInt32>(repeating: 0, count: 17)
+
+        r[0] = UInt32(k[0])
+        r[1] = UInt32(k[1])
+        r[2] = UInt32(k[2])
+        r[3] = UInt32(k[3] & 15)
+        r[4] = UInt32(k[4] & 252)
+        r[5] = UInt32(k[5])
+        r[6] = UInt32(k[6])
+        r[7] = UInt32(k[7] & 15)
+        r[8] = UInt32(k[8] & 252)
+        r[9] = UInt32(k[9])
+        r[10] = UInt32(k[10])
+        r[11] = UInt32(k[11] & 15)
+        r[12] = UInt32(k[12] & 252)
+        r[13] = UInt32(k[13])
+        r[14] = UInt32(k[14])
+        r[15] = UInt32(k[15] & 15)
+        r[16] = 0
+
+
+        var inlen = input.count
+        var inpos = 0
+        while inlen > 0 {
+            for j in 0..<c.count {
+                c[j] = 0
             }
             }
-            c[16] = hibit
 
 
-            add(context, c: c)
-
-            /* h *= r */
-            for i in 0 ..< 17 {
-                u = 0
-                for j in 0 ... i {
-                    u = u + UInt32(UInt16(context.h[j])) * UInt32(context.r[i - j]) // u += (unsigned short)st->h[j] * st->r[i - j];
-                }
-                for j in (i + 1) ..< 17 {
-                    var v: UInt32 = UInt32(UInt16(context.h[j])) * UInt32(context.r[i + 17 - j]) // unsigned long v = (unsigned short)st->h[j] * st->r[i + 17 - j];
-                    v = ((v << 8) &+ (v << 6))
-                    u = u &+ v
-                }
-                hr[i] = u
+            let maxj = min(inlen, 16)
+            for j in 0..<maxj {
+                c[j] = UInt32(input[inpos + j])
             }
             }
+            c[maxj] = 1
+            inpos = inpos + maxj
+            inlen = inlen - maxj
+            self.add(h: &h, c: c)
+            self.mulmod(h: &h, r: r)
+        }
 
 
-            squeeze(context, hr: hr)
+        self.freeze(h: &h)
 
 
-            mPos += blockSize
-            bytes -= blockSize
+        for j in 0..<16 {
+            c[j] = UInt32(k[j + 16])
+        }
+        c[16] = 0
+        add(h: &h, c: c)
+
+        return h[0..<16].map {
+            UInt8($0 & 0xFF)
         }
         }
     }
     }
 
 
@@ -283,16 +154,6 @@ public final class Poly1305: Authenticator {
      - returns: 16-byte tag that authenticates the message
      - returns: 16-byte tag that authenticates the message
      */
      */
     public func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
     public func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
-        guard let ctx = self.ctx else {
-            throw Error.authenticateError
-        }
-
-        update(ctx, message: bytes)
-
-        guard let result = finish(ctx) else {
-            throw Error.authenticateError
-        }
-
-        return result
+        return onetimeauth(message: bytes, key: Array(self.key))
     }
     }
 }
 }

+ 11 - 4
Tests/CryptoSwiftTests/Poly1305Tests.swift

@@ -16,16 +16,23 @@ final class Poly1305Tests: XCTestCase {
         let msg: Array<UInt8> = [0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1]
         let msg: Array<UInt8> = [0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1]
         let expectedMac: Array<UInt8> = [0xdd, 0xb9, 0xda, 0x7d, 0xdd, 0x5e, 0x52, 0x79, 0x27, 0x30, 0xed, 0x5c, 0xda, 0x5f, 0x90, 0xa4]
         let expectedMac: Array<UInt8> = [0xdd, 0xb9, 0xda, 0x7d, 0xdd, 0x5e, 0x52, 0x79, 0x27, 0x30, 0xed, 0x5c, 0xda, 0x5f, 0x90, 0xa4]
 
 
-        let mac = try! Poly1305(key: key).authenticate(msg)
-        XCTAssertEqual(mac, expectedMac, "Invalid authentication result")
+        XCTAssertEqual(try Poly1305(key: key).authenticate(msg), expectedMac)
 
 
         // extensions
         // extensions
         let msgData = Data(bytes: msg)
         let msgData = Data(bytes: msg)
-        let mac2 = try! msgData.authenticate(with: Poly1305(key: key))
-        XCTAssertEqual(mac2, Data(bytes: expectedMac), "Invalid authentication result")
+        XCTAssertEqual(try msgData.authenticate(with: Poly1305(key: key)), Data(bytes: expectedMac), "Invalid authentication result")
+    }
+
+    // https://github.com/krzyzanowskim/CryptoSwift/issues/183
+    func testIssue183() {
+        let key:[UInt8] = [111, 53, 197, 181, 1, 92, 67, 199, 37, 92, 76, 167, 12, 35, 75, 226, 198, 34, 107, 84, 79, 6, 231, 10, 25, 221, 14, 155, 81, 244, 15, 203]
+        let message:[UInt8] = [208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 210, 40, 78, 3, 161, 87, 187, 96, 253, 104, 187, 87, 87, 249, 56, 5, 156, 122, 121, 196, 192, 254, 58, 98, 22, 47, 151, 205, 201, 108, 143, 197, 99, 182, 109, 59, 63, 172, 111, 120, 185, 175, 129, 59, 126, 68, 140, 237, 126, 175, 49, 224, 249, 245, 37, 75, 252, 69, 215, 171, 27, 163, 16, 185, 239, 184, 144, 37, 131, 242, 12, 90, 134, 24, 237, 209, 127, 71, 86, 122, 173, 238, 73, 186, 58, 102, 112, 90, 217, 243, 251, 110, 85, 106, 18, 172, 167, 179, 173, 73, 125, 9, 129, 132, 80, 70, 4, 254, 178, 211, 200, 207, 231, 232, 17, 176, 127, 153, 120, 71, 164, 139, 56, 106, 71, 96, 79, 11, 213, 243, 66, 53, 167, 108, 233, 250, 136, 69, 190, 191, 12, 136, 24, 157, 202, 49, 158, 152, 150, 34, 88, 132, 112, 74, 168, 153, 116, 31, 7, 61, 60, 22, 199, 108, 187, 209, 114, 234, 185, 247, 41, 68, 184, 95, 169, 60, 126, 73, 59, 54, 126, 162, 90, 18, 32, 230, 123, 2, 40, 74, 177, 127, 219, 93, 186, 22, 75, 251, 101, 95, 160, 68, 235, 77, 2, 10, 202, 2, 0, 0, 0, 0, 0, 0, 0, 208, 0, 0, 0, 0, 0, 0, 0]
+        let expectedMac:[UInt8] = [68, 216, 92, 163, 164, 144, 55, 43, 185, 18, 83, 92, 41, 133, 72, 168]
+        XCTAssertEqual(try message.authenticate(with: Poly1305(key: key)), expectedMac)
     }
     }
 
 
     static let allTests = [
     static let allTests = [
         ("testPoly1305", testPoly1305),
         ("testPoly1305", testPoly1305),
+        ("testIssue183", testIssue183)
     ]
     ]
 }
 }