[
{"fn": "hashPassword", "name": "a passphrase with a 16-byte salt at 1,000 iterations", "args": ["correct horse battery staple", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 1000], "expect": "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="},
{"fn": "hashPassword", "name": "the same password with another salt gives a different string", "args": ["correct horse battery staple", [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131], 1000], "expect": "pbkdf2_sha256$1000$ZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoM=$Ex4eaoxau7xpouluxG9E/RjMZTpG1gfchDgUwL5k4nE="},
{"fn": "hashPassword", "name": "the iteration count is part of the result", "args": ["correct horse battery staple", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 1001], "expect": "pbkdf2_sha256$1001$AAECAwQFBgcICQoLDA0ODw==$OG6DHYhanszHUwoT+w6oqeOWctyHfPmQrhnxZi4B+Mc="},
{"fn": "hashPassword", "name": "non-ASCII is hashed as UTF-8", "args": ["pässwörd 🔑", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 1000], "expect": "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$3NkHSPhtBEJ7V71tycgH0BEWOfIITqysFEFL5iBfsgk="},
{"fn": "hashPassword", "name": "a trailing space is part of the password, not trimmed", "args": ["correct horse battery staple ", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 1000], "expect": "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$oUKJ6EHOjzE04WJBnhA+dla9KZq/IWZZswmcGWhbSNI="},
{"fn": "hashPassword", "name": "the empty password still hashes; refusing it is the password policy's job", "args": ["", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 1000], "expect": "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$xbMBsf1hvO1j8AZCojBOxnRRn7182DxLyD2v4XQ/mFU="},
{"fn": "hashPassword", "name": "a salt of high bytes, whose base64 uses + and /", "args": ["correct horse battery staple", [251, 255, 191, 251, 255, 191, 251, 255, 191, 251, 255, 191, 251, 255, 191, 251, 255, 191], 1000], "expect": "pbkdf2_sha256$1000$+/+/+/+/+/+/+/+/+/+/+/+/$zbEcnYXrJwbG3QbB8OYQTmndOkFcnEtJtHURfCSVTMc="},
{"fn": "hashPassword", "name": "a 15-byte salt is refused", "args": ["correct horse battery staple", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 1000], "expectError": "salt must be at least 16 bytes, received 15"},
{"fn": "hashPassword", "name": "999 iterations is under NIST SP 800-132's minimum", "args": ["correct horse battery staple", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 999], "expectError": "iterations must be a whole number of at least 1000"},
{"fn": "hashPassword", "name": "a fractional iteration count", "args": ["correct horse battery staple", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 1000.5], "expectError": "iterations must be a whole number of at least 1000"},
{"fn": "hashPassword", "name": "a salt byte above 255", "args": ["correct horse battery staple", [256, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 1000], "expectError": "salt must be a list of integers from 0 to 255"},
{"fn": "verifyPassword", "name": "the right password", "args": ["correct horse battery staple", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="], "expect": true},
{"fn": "verifyPassword", "name": "a wrong password", "args": ["correct horse battery stapler", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="], "expect": false},
{"fn": "verifyPassword", "name": "case matters", "args": ["Correct horse battery staple", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="], "expect": false},
{"fn": "verifyPassword", "name": "a trailing space makes it a different password", "args": ["correct horse battery staple ", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="], "expect": false},
{"fn": "verifyPassword", "name": "non-ASCII, right", "args": ["pässwörd 🔑", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$3NkHSPhtBEJ7V71tycgH0BEWOfIITqysFEFL5iBfsgk="], "expect": true},
{"fn": "verifyPassword", "name": "the same letters with a combining accent are different bytes, so wrong", "args": ["pässwörd 🔑", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$3NkHSPhtBEJ7V71tycgH0BEWOfIITqysFEFL5iBfsgk="], "expect": false},
{"fn": "verifyPassword", "name": "the stored count is used, whatever today's setting", "args": ["correct horse battery staple", "pbkdf2_sha256$5000$ZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoM=$u/ryxpXZ/oGk/lfmNp6APHuNO8PyBzG5QsqV6CyK6qE="], "expect": true},
{"fn": "verifyPassword", "name": "the empty password against its own hash", "args": ["", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$xbMBsf1hvO1j8AZCojBOxnRRn7182DxLyD2v4XQ/mFU="], "expect": true},
{"fn": "verifyPassword", "name": "a hash of 16 bytes, not 32, is checked at its own length", "args": ["correct horse battery staple", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKg=="], "expect": true},
{"fn": "verifyPassword", "name": "another algorithm's hash", "args": ["correct horse battery staple", "bcrypt$2b$12$abcdefghijklmnopqrstuv"], "expectError": "stored password hash is not a pbkdf2_sha256 hash"},
{"fn": "verifyPassword", "name": "a missing field", "args": ["correct horse battery staple", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw=="], "expectError": "stored password hash is malformed"},
{"fn": "verifyPassword", "name": "an iteration count with a leading zero", "args": ["correct horse battery staple", "pbkdf2_sha256$01000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="], "expectError": "stored password hash is malformed"},
{"fn": "verifyPassword", "name": "a zero iteration count", "args": ["correct horse battery staple", "pbkdf2_sha256$0$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="], "expectError": "stored password hash is malformed"},
{"fn": "verifyPassword", "name": "base64 with a character outside the alphabet", "args": ["correct horse battery staple", "pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8c*=="], "expectError": "stored password hash is malformed"},
{"fn": "verifyPassword", "name": "an empty salt field", "args": ["correct horse battery staple", "pbkdf2_sha256$1000$$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4="], "expectError": "stored password hash is malformed"},
{"fn": "passwordNeedsRehash", "name": "1,000 iterations when new hashes use 600,000", "args": ["pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4=", 600000], "expect": true},
{"fn": "passwordNeedsRehash", "name": "the same count as today", "args": ["pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4=", 1000], "expect": false},
{"fn": "passwordNeedsRehash", "name": "more iterations than today is not a reason to re-hash", "args": ["pbkdf2_sha256$5000$ZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoM=$u/ryxpXZ/oGk/lfmNp6APHuNO8PyBzG5QsqV6CyK6qE=", 1000], "expect": false},
{"fn": "passwordNeedsRehash", "name": "an 8-byte salt from an older scheme", "args": ["pbkdf2_sha256$5000$AAECAwQFBgc=$2FZ3ntoESozfKw6vOmuoLOSWdzwww4+R0vJBF5xQLtY=", 1000], "expect": true},
{"fn": "passwordNeedsRehash", "name": "a 16-byte hash from an older scheme", "args": ["pbkdf2_sha256$5000$AAECAwQFBgcICQoLDA0ODw==$AhEiPny5jm3U51k/u5lqxw==", 1000], "expect": true},
{"fn": "passwordNeedsRehash", "name": "a 32-byte salt is fine", "args": ["pbkdf2_sha256$1000$ZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoM=$Ex4eaoxau7xpouluxG9E/RjMZTpG1gfchDgUwL5k4nE=", 1000], "expect": false},
{"fn": "passwordNeedsRehash", "name": "one iteration short", "args": ["pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4=", 1001], "expect": true},
{"fn": "passwordNeedsRehash", "name": "today's count must itself be at least 1,000", "args": ["pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODw==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4=", 10], "expectError": "iterations must be a whole number of at least 1000"},
{"fn": "passwordNeedsRehash", "name": "a stored string from another algorithm", "args": ["sha1$abc$def", 1000], "expectError": "stored password hash is not a pbkdf2_sha256 hash"},
{"fn": "passwordNeedsRehash", "name": "a stored string with non-canonical base64", "args": ["pbkdf2_sha256$1000$AAECAwQFBgcICQoLDA0ODx==$ppsXnjrdPB4KryJ6DrOqKqhkWrhv7PbKAMF1Eml8cZ4=", 1000], "expectError": "stored password hash is malformed"}
]