krz/keycask

Password manager: Swift core library, CLI for macOS/Linux/Windows, iOS/macOS app. cli password-manager swift

Commit 24ff5dde9c

24ff5dde9c4efe0377fed272e89a586bac97e2b5

parent: 6821fa25ee

Verified · cmc ci/build: failure ci/test: failure

cmc <hello@cleberg.net> · 2026-09-17 15:39 UTC

Reject out-of-range KDF iteration counts

Layout: unified · split

Sources/KeycaskCore/Envelope.swift +3
@@ -94,6 +94,9 @@ public struct Envelope: Codable, Equatable, Sendable {
9494 }
9595
9696 static func deriveKey(passphrase: String, kdf: KDFParams) throws -> SymmetricKey {
97 guard (1...Int(UInt32.max)).contains(kdf.iterations) else {
98 throw KeycaskError.corrupt("bad iteration count \(kdf.iterations)")
99 }
97100 let normalized = Array(passphrase.precomposedStringWithCanonicalMapping.utf8)
98101 do {
99102 return try KDF.Insecure.PBKDF2.deriveKey(
Tests/KeycaskCoreTests/EnvelopeTests.swift +17
@@ -93,6 +93,23 @@ import Testing
9393 }
9494 }
9595
96 @Test func outOfRangeIterationsAreCorrupt() throws {
97 var env = try Envelope.seal(Data("x".utf8), passphrase: "pw", kdf: kdf)
98 env.kdf.iterations = -1
99 #expect(throws: KeycaskError.corrupt("bad iteration count -1")) {
100 try env.open(passphrase: "pw")
101 }
102 env.kdf.iterations = 0
103 #expect(throws: KeycaskError.corrupt("bad iteration count 0")) {
104 try env.open(passphrase: "pw")
105 }
106 let tooMany = Envelope.KDFParams(
107 name: Envelope.kdfName, iterations: Int(UInt32.max) + 1, salt: kdf.salt)
108 #expect(throws: KeycaskError.corrupt("bad iteration count \(Int(UInt32.max) + 1)")) {
109 try Envelope.seal(Data("x".utf8), passphrase: "pw", kdf: tooMany)
110 }
111 }
112
96113 @Test func passphraseIsNFCNormalized() throws {
97114 let composed = "caf\u{00E9}"
98115 let decomposed = "cafe\u{0301}"