62 lines
2.7 KiB
Swift
62 lines
2.7 KiB
Swift
|
|
import XCTest
|
||
|
|
import CryptoKit
|
||
|
|
@testable import Closer
|
||
|
|
|
||
|
|
final class FieldEncryptorTests: XCTestCase {
|
||
|
|
private let key = SymmetricKey(size: .bits256)
|
||
|
|
|
||
|
|
func testStringRoundTripWithoutAAD() throws {
|
||
|
|
let plaintext = "hello, closer"
|
||
|
|
let blob = try FieldEncryptor.encryptString(plaintext, key: key, aad: nil)
|
||
|
|
XCTAssertTrue(blob.hasPrefix(FieldEncryptor.prefix))
|
||
|
|
let recovered = try FieldEncryptor.decryptString(blob, key: key, aad: nil)
|
||
|
|
XCTAssertEqual(recovered, plaintext)
|
||
|
|
}
|
||
|
|
|
||
|
|
func testStringRoundTripWithAAD() throws {
|
||
|
|
let plaintext = "secret payload"
|
||
|
|
let aad = "couple-123".data(using: .utf8)!
|
||
|
|
let blob = try FieldEncryptor.encryptString(plaintext, key: key, aad: aad)
|
||
|
|
let recovered = try FieldEncryptor.decryptString(blob, key: key, aad: aad)
|
||
|
|
XCTAssertEqual(recovered, plaintext)
|
||
|
|
}
|
||
|
|
|
||
|
|
func testBinaryRoundTrip() throws {
|
||
|
|
let plaintext = Data((0..<64).map { $0 })
|
||
|
|
let aad = Data([0x00, 0x01, 0x02])
|
||
|
|
let ct = try FieldEncryptor.encrypt(plaintext, key: key, aad: aad)
|
||
|
|
let recovered = try FieldEncryptor.decrypt(ct, key: key, aad: aad)
|
||
|
|
XCTAssertEqual(recovered, plaintext)
|
||
|
|
}
|
||
|
|
|
||
|
|
func testTamperedCiphertextThrows() throws {
|
||
|
|
let plaintext = "tamper me"
|
||
|
|
let blob = try FieldEncryptor.encryptString(plaintext, key: key, aad: nil)
|
||
|
|
var bytes = Array(blob.utf8)
|
||
|
|
// Flip a bit in the base64 payload portion.
|
||
|
|
let prefixEnd = FieldEncryptor.prefix.count
|
||
|
|
bytes[prefixEnd + 5] ^= 0x01
|
||
|
|
let tampered = String(bytes: bytes, encoding: .utf8)!
|
||
|
|
XCTAssertThrowsError(try FieldEncryptor.decryptString(tampered, key: key, aad: nil))
|
||
|
|
}
|
||
|
|
|
||
|
|
func testWrongAADThrows() throws {
|
||
|
|
let plaintext = "aad bound"
|
||
|
|
let blob = try FieldEncryptor.encryptString(plaintext, key: key, aad: Data([0xAB]))
|
||
|
|
XCTAssertThrowsError(try FieldEncryptor.decryptString(blob, key: key, aad: Data([0xBA])))
|
||
|
|
}
|
||
|
|
|
||
|
|
func testKnownVectorCryptoKitRoundTrip() throws {
|
||
|
|
// NIST-style AES-256-GCM test vector derived from CryptoKit itself:
|
||
|
|
// fixed key + fixed nonce + fixed plaintext should round-trip deterministically.
|
||
|
|
let keyBytes = Data(repeating: 0xAB, count: 32)
|
||
|
|
let fixedKey = SymmetricKey(data: keyBytes)
|
||
|
|
let nonce = AES.GCM.Nonce(data: Data(repeating: 0xCD, count: 12))!
|
||
|
|
let plaintext = Data("known vector plaintext".utf8)
|
||
|
|
let sealed = try AES.GCM.seal(plaintext, using: fixedKey, nonce: nonce)
|
||
|
|
let ct = sealed.combined!
|
||
|
|
let recovered = try AES.GCM.open(try AES.GCM.SealedBox(combined: ct), using: fixedKey)
|
||
|
|
XCTAssertEqual(recovered, plaintext)
|
||
|
|
}
|
||
|
|
}
|