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 {
94 } 94 }
95 95
96 static func deriveKey(passphrase: String, kdf: KDFParams) throws -> SymmetricKey { 96 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 }
97 let normalized = Array(passphrase.precomposedStringWithCanonicalMapping.utf8) 100 let normalized = Array(passphrase.precomposedStringWithCanonicalMapping.utf8)
98 do { 101 do {
99 return try KDF.Insecure.PBKDF2.deriveKey( 102 return try KDF.Insecure.PBKDF2.deriveKey(
Tests/KeycaskCoreTests/EnvelopeTests.swift +17
@@ -93,6 +93,23 @@ import Testing
93 } 93 }
94 } 94 }
95 95
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
96 @Test func passphraseIsNFCNormalized() throws { 113 @Test func passphraseIsNFCNormalized() throws {
97 let composed = "caf\u{00E9}" 114 let composed = "caf\u{00E9}"
98 let decomposed = "cafe\u{0301}" 115 let decomposed = "cafe\u{0301}"