M1: persistent vault, local unlock key, sync client, test vectors
kv-store: SQLite (bundled) and in-memory Store holding only ciphertext envelopes, with base copies for three-way merges and a 10-version history. kv-ffi: VaultCore::open(path); persisted account record; local unlock key (enable/unlock_with_local_key/disable); async sync API (configure_server, set_device_signer, register_account, login, sync_now, logout, devices); DeviceSigner foreign trait; IntegrityAlarm/PinMismatch/... error variants. kv-sync: reqwest + rustls (TLS 1.3 only, ring) with SPKI pinning; OPAQUE client on kv-proto's OpaqueSuite; RFC 9421 signing per kv_proto::httpsig; pull/push with If-Match, field-level three-way merge, edit-beats-delete; signed manifest (canonical CBOR, Merkle root, prev chain, Ed25519) with rollback/fork/Merkle checks as hard integrity alarms. Tested against a wiremock fake server, plus an #[ignore] test against kestrove-server. kv-crypto: gen-vectors example and tests reproducing kestrove-spec/vectors. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
//! The code must reproduce the published test vectors byte for byte, and the vectors must decode.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use kv_crypto::{aead::SymKey, seal::EncKeypair, secret_key::SecretKey};
|
||||
use kv_proto::Envelope;
|
||||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[path = "../vectors/gen.rs"]
|
||||
mod gen;
|
||||
|
||||
fn dir() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/vectors")
|
||||
}
|
||||
|
||||
fn load(name: &str) -> Value {
|
||||
let text = std::fs::read_to_string(dir().join(format!("{name}.json"))).expect("vector file (run gen-vectors)");
|
||||
serde_json::from_str(&text).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_reproduces_vector_files() {
|
||||
let spec = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../kestrove-spec/vectors");
|
||||
for (name, doc) in gen::generate() {
|
||||
let rendered = gen::render(&doc);
|
||||
let on_disk = std::fs::read_to_string(dir().join(format!("{name}.json"))).unwrap();
|
||||
assert_eq!(rendered, on_disk, "{name}.json differs from what the code produces");
|
||||
// When the spec repo is checked out next to this one, it must carry the same files.
|
||||
if let Ok(published) = std::fs::read_to_string(spec.join(format!("{name}.json"))) {
|
||||
assert_eq!(published, on_disk, "kestrove-spec/vectors/{name}.json is stale");
|
||||
}
|
||||
}
|
||||
assert_eq!(gen::FILES.len(), gen::generate().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn envelopes_open() {
|
||||
for v in load("envelope")["vectors"].as_array().unwrap() {
|
||||
let key: [u8; 32] = gen::unhex(v["key"].as_str().unwrap()).try_into().unwrap();
|
||||
let key = SymKey::from_bytes(Uuid::parse_str(v["kid"].as_str().unwrap()).unwrap(), key);
|
||||
let env: Envelope = serde_json::from_value(v["envelope"].clone()).unwrap();
|
||||
let pt = key.open(&env, &gen::unhex(v["aad"].as_str().unwrap())).unwrap();
|
||||
assert_eq!(gen::hex(&pt), v["plaintext"].as_str().unwrap());
|
||||
assert_eq!(gen::hex(&env.ct), v["ciphertext"].as_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hpke_opens() {
|
||||
for v in load("hpke")["vectors"].as_array().unwrap() {
|
||||
let kp = EncKeypair::from_secret_bytes(
|
||||
Uuid::parse_str(v["recipient_kid"].as_str().unwrap()).unwrap(),
|
||||
&gen::unhex(v["recipient_sk"].as_str().unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(gen::hex(&kp.public_bytes()), v["recipient_pk"].as_str().unwrap());
|
||||
let env: Envelope = serde_json::from_value(v["envelope"].clone()).unwrap();
|
||||
let pt = kp.open(&env, &gen::unhex(v["aad"].as_str().unwrap())).unwrap();
|
||||
assert_eq!(gen::hex(&pt), v["plaintext"].as_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_keys_parse() {
|
||||
let doc = load("secret_key_format");
|
||||
for v in doc["valid"].as_array().unwrap() {
|
||||
let k = SecretKey::parse(v["display"].as_str().unwrap()).unwrap();
|
||||
assert_eq!(gen::hex(k.expose()), v["raw"].as_str().unwrap());
|
||||
}
|
||||
for v in doc["alternate_inputs"].as_array().unwrap() {
|
||||
let k = SecretKey::parse(v["input"].as_str().unwrap()).unwrap();
|
||||
assert_eq!(gen::hex(k.expose()), v["raw"].as_str().unwrap());
|
||||
}
|
||||
for v in doc["invalid"].as_array().unwrap() {
|
||||
assert!(SecretKey::parse(v["input"].as_str().unwrap()).is_err(), "{v}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rfc6238_codes_are_the_published_ones() {
|
||||
// Spot-check against the RFC 6238 Appendix B table itself, not only our own output.
|
||||
let doc = load("totp");
|
||||
let find = |t: u64, alg: &str| {
|
||||
doc["vectors"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|v| v["time"] == t && v["uri"].as_str().unwrap().contains(&format!("algorithm={alg}&")))
|
||||
.unwrap()["code"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
};
|
||||
assert_eq!(find(59, "SHA1"), "94287082");
|
||||
assert_eq!(find(59, "SHA256"), "46119246");
|
||||
assert_eq!(find(59, "SHA512"), "90693936");
|
||||
assert_eq!(find(20_000_000_000, "SHA1"), "65353130");
|
||||
assert_eq!(find(20_000_000_000, "SHA512"), "47863826");
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"aad_layout": "\"kestrove/v1/\" || type || 0x00 || account || 0x00 || vault || 0x00 || item (absent ids = nil UUID)",
|
||||
"account_id": "01010101-0101-4101-8101-010101010101",
|
||||
"description": "XChaCha20-Poly1305 envelopes with location-binding AAD and 256-byte padding (docs/02 'Envelope format')",
|
||||
"item_id": "03030303-0303-4303-8303-030303030303",
|
||||
"vault_id": "02020202-0202-4202-8202-020202020202",
|
||||
"vectors": [
|
||||
{
|
||||
"aad": "6b657374726f76652f76312f6974656d000101010101014101810101010101010100020202020202420282020202020202020003030303030343038303030303030303",
|
||||
"ciphertext": "0ae824b61b2253f40015d22f52ee7412e5be95cf17185bd8490e0e77200d50b5f629c6dfd2e2bccc1d86a06c6453150a96e1c8fc81f1a82b4830f511e831aa66438d35ed9c7d6d11d904b3d53641f05175066f63c0d985ea91738efcd7afb6a205b4a8471fc827a11c51cb598e429227ef2c3020ccdd749f254538ca5429e95f70b7258d87a6b52018976695a15b6e77e6c47a49609fa691f602dbeb4a15ea73c87909c7267d7bf84de31762480feab0b96553c2d4046b5ed00c899e30175fbd7333e9da01329d89a3158a715a85a83d81b8a0105e3c2cdd0ffb2f52e1971d0c7ac1c57504fea274b883fd4b65dae757d758cb3ecd7828e7edd44b35cbba452261039e436beeb3aeba178d379a54abf9",
|
||||
"envelope": {
|
||||
"alg": "xc20p",
|
||||
"ct": "CugkthsiU_QAFdIvUu50EuW-lc8XGFvYSQ4OdyANULX2Kcbf0uK8zB2GoGxkUxUKluHI_IHxqCtIMPUR6DGqZkONNe2cfW0R2QSz1TZB8FF1Bm9jwNmF6pFzjvzXr7aiBbSoRx_IJ6EcUctZjkKSJ-8sMCDM3XSfJUU4ylQp6V9wtyWNh6a1IBiXZpWhW2535sR6SWCfppH2AtvrShXqc8h5CccmfXv4TeMXYkgP6rC5ZVPC1ARrXtAMiZ4wF1-9czPp2gEynYmjFYpxWoWoPYG4oBBePCzdD_svUuGXHQx6wcV1BP6idLiD_Utl2udX11jLPs14KOft1Es1y7pFImEDnkNr7rOuuheNN5pUq_k",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"n": "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB",
|
||||
"v": 1
|
||||
},
|
||||
"key": "4242424242424242424242424242424242424242424242424242424242424242",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"name": "item payload (padded)",
|
||||
"nonce": "010101010101010101010101010101010101010101010101",
|
||||
"plaintext": "6d0000007b226964223a2230333033303330332d303330332d343330332d383330332d303330333033303330333033222c226b696e64223a226c6f67696e222c227469746c65223a224578616d706c65222c22637265617465645f6174223a312c226d6f6469666965645f6174223a317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
"aad": "6b657374726f76652f76312f6974656d2d6b6579000101010101014101810101010101010100020202020202420282020202020202020003030303030343038303030303030303",
|
||||
"ciphertext": "89b9440109da7c2a9fa4c5d03a25c7a047b11f9133e7e8ddcff986029675c5106f9240f5ca69911f73dc1106e3f3d32bcc8470e999d6818689daee36907279ec",
|
||||
"envelope": {
|
||||
"alg": "xc20p",
|
||||
"ct": "iblEAQnafCqfpMXQOiXHoEexH5Ez5-jdz_mGApZ1xRBvkkD1ymmRH3PcEQbj89MrzIRw6ZnWgYaJ2u42kHJ57A",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"n": "AgICAgICAgICAgICAgICAgICAgICAgIC",
|
||||
"v": 1
|
||||
},
|
||||
"key": "4242424242424242424242424242424242424242424242424242424242424242",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"name": "item key: kid || key",
|
||||
"nonce": "020202020202020202020202020202020202020202020202",
|
||||
"plaintext": "050505050505450585050505050505055555555555555555555555555555555555555555555555555555555555555555"
|
||||
},
|
||||
{
|
||||
"aad": "6b657374726f76652f76312f7661756c742d6d657461000101010101014101810101010101010100020202020202420282020202020202020000000000000000000000000000000000",
|
||||
"ciphertext": "0ee485ea37c4e9b6f4ea55e7381f12dbd4390a34b4d1342f9043da961bdcd3b47d1986",
|
||||
"envelope": {
|
||||
"alg": "xc20p",
|
||||
"ct": "DuSF6jfE6bb06lXnOB8S29Q5CjS00TQvkEPalhvc07R9GYY",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"n": "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMD",
|
||||
"v": 1
|
||||
},
|
||||
"key": "4242424242424242424242424242424242424242424242424242424242424242",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"name": "vault metadata",
|
||||
"nonce": "030303030303030303030303030303030303030303030303",
|
||||
"plaintext": "7b226e616d65223a22506572736f6e616c227d"
|
||||
},
|
||||
{
|
||||
"aad": "6b657374726f76652f76312f6b6579736574000101010101014101810101010101010100000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"ciphertext": "e7b3287dd61ee4062f51d81ed4bc13841733671cfd05429e42f7c820906a73e8d8ca3f083b0713fa560a597c65c18cceccbf90ae2a17e711f8d3a9b7c94ba4561d2e0f5d63bbd4bb4f6d5c81b0d903cad7f19e5a89abb43973269f37f8aad8e5",
|
||||
"envelope": {
|
||||
"alg": "xc20p",
|
||||
"ct": "57MofdYe5AYvUdge1LwThBczZxz9BUKeQvfIIJBqc-jYyj8IOwcT-lYKWXxlwYzOzL-QrioX5xH406m3yUukVh0uD11ju9S7T21cgbDZA8rX8Z5aiau0OXMmnzf4qtjl",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"n": "BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE",
|
||||
"v": 1
|
||||
},
|
||||
"key": "4242424242424242424242424242424242424242424242424242424242424242",
|
||||
"kid": "04040404-0404-4404-8404-040404040404",
|
||||
"name": "keyset (80 bytes)",
|
||||
"nonce": "040404040404040404040404040404040404040404040404",
|
||||
"plaintext": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"account_id": "01010101-0101-4101-8101-010101010101",
|
||||
"description": "HPKE base mode, DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20-Poly1305 sealing a vault key (vault key id || key) to the account. ikm_e is the RFC 9180 DeriveKeyPair input for the ephemeral key.",
|
||||
"vectors": [
|
||||
{
|
||||
"aad": "6b657374726f76652f76312f7661756c742d6b6579000101010101014101810101010101010100020202020202420282020202020202020000000000000000000000000000000000",
|
||||
"ciphertext": "41e270319f576cf6e10bd2981b866b9a296b42b810e5b10f617f2b03fabe54e1dbbc434382b79b007dff7734de75e03f2a99cb54ec0d4be64c0972211fa65442",
|
||||
"enc": "e62492d40ab10d0f43fdaa89e1f2107e1e2d059d23a1f00c487a7e62b5c6e67d",
|
||||
"envelope": {
|
||||
"alg": "hpke-x25519-chacha",
|
||||
"ct": "QeJwMZ9XbPbhC9KYG4ZrmilrQrgQ5bEPYX8rA_q-VOHbvENDgrebAH3_dzTedeA_KpnLVOwNS-ZMCXIhH6ZUQg",
|
||||
"enc": "5iSS1AqxDQ9D_aqJ4fIQfh4tBZ0jofAMSHp-YrXG5n0",
|
||||
"kid": "0e0e0e0e-0e0e-4e0e-8e0e-0e0e0e0e0e0e",
|
||||
"v": 1
|
||||
},
|
||||
"ikm_e": "7777777777777777777777777777777777777777777777777777777777777777",
|
||||
"info": "6b657374726f76652f76312f68706b65",
|
||||
"plaintext": "0a0a0a0a0a0a4a0a8a0a0a0a0a0a0a0aabababababababababababababababababababababababababababababababab",
|
||||
"recipient_kid": "0e0e0e0e-0e0e-4e0e-8e0e-0e0e0e0e0e0e",
|
||||
"recipient_pk": "7b4e909bbe7ffe44c465a220037d608ee35897d31ef972f07f74892cb0f73f13",
|
||||
"recipient_sk": "1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"vault_id": "02020202-0202-4202-8202-020202020202"
|
||||
},
|
||||
{
|
||||
"aad": "6b657374726f76652f76312f7661756c742d6b6579000101010101014101810101010101010100090909090909490989090909090909090000000000000000000000000000000000",
|
||||
"ciphertext": "da381938bfd0c81106206bca02b17c5bade147d88e21e6627aeac640c232e489c5a54cf659e27c6b272bbce1df895f998a3ed250b4b299a3d2485a0f05519a4b",
|
||||
"enc": "90ab790ecbf0704232ffe9436faeccc913b66a60e99c688a40bba4d5e2e61450",
|
||||
"envelope": {
|
||||
"alg": "hpke-x25519-chacha",
|
||||
"ct": "2jgZOL_QyBEGIGvKArF8W63hR9iOIeZieurGQMIy5InFpUz2WeJ8aycrvOHfiV-Zij7SULSymaPSSFoPBVGaSw",
|
||||
"enc": "kKt5DsvwcEIy_-lDb67MyRO2amDpnGiKQLuk1eLmFFA",
|
||||
"kid": "0e0e0e0e-0e0e-4e0e-8e0e-0e0e0e0e0e0e",
|
||||
"v": 1
|
||||
},
|
||||
"ikm_e": "0303030303030303030303030303030303030303030303030303030303030303",
|
||||
"info": "6b657374726f76652f76312f68706b65",
|
||||
"plaintext": "0a0a0a0a0a0a4a0a8a0a0a0a0a0a0a0aabababababababababababababababababababababababababababababababab",
|
||||
"recipient_kid": "0e0e0e0e-0e0e-4e0e-8e0e-0e0e0e0e0e0e",
|
||||
"recipient_pk": "07aaff3e9fc167275544f4c3a6a17cd837f2ec6e78cd8a57b1e3dfb3cc035a76",
|
||||
"recipient_sk": "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a",
|
||||
"vault_id": "09090909-0909-4909-8909-090909090909"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"description": "2SKD (docs/02 'Derivation'): Argon2id(NFKD(trim(password))) + HKDF(secret_key) -> root -> AUK, AUTH_INPUT",
|
||||
"vectors": [
|
||||
{
|
||||
"account_id": "11111111-1111-4111-9111-111111111111",
|
||||
"auk": "33e9bfb720e3cb5092677bf71d58781f7c032840299d5cc0f10c59b6d0b868d6",
|
||||
"auth_input": "0eeae51fc4c439082b91211477c5e6fd792c98f1781a7b9808dbb39b54483096",
|
||||
"params": {
|
||||
"alg": "argon2id",
|
||||
"m_kib": 65536,
|
||||
"p": 4,
|
||||
"salt": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
|
||||
"t": 3
|
||||
},
|
||||
"password": "correct horse battery staple",
|
||||
"root": "c6b0513bc1244cdabe9b2ec388b5cd441b8d25a3ba0ab8834f9fb231ad89a5e7",
|
||||
"secret_key": "01010101010101010101010101010101",
|
||||
"secret_key_display": "K1-01040G-20810-40G20-81040-G2081-C"
|
||||
},
|
||||
{
|
||||
"account_id": "22222222-2222-4222-a222-222222222222",
|
||||
"auk": "fc443a82cb371b0a33672509e61b64c08cd26bdc4ce0cdc726e98adc25906068",
|
||||
"auth_input": "77a83f16adf68bae8fe9ee448e7b2ab60ff2ea5234fba69816d5b560700c18a1",
|
||||
"params": {
|
||||
"alg": "argon2id",
|
||||
"m_kib": 65536,
|
||||
"p": 4,
|
||||
"salt": "00000000000000000000000000000000",
|
||||
"t": 3
|
||||
},
|
||||
"password": " café fish ",
|
||||
"root": "da0438d0cb15483dff9f4f569741911fdde9dee37b116c11402480c8f4d9b406",
|
||||
"secret_key": "ffffffffffffffffffffffffffffffff",
|
||||
"secret_key_display": "K1-7ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-T"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"description": "Autofill URL matching (docs/05): exact > host > domain (eTLD+1 via the PSL) > none. http items may match https pages, never the reverse.",
|
||||
"vectors": [
|
||||
{
|
||||
"item_url": "https://example.com",
|
||||
"level": "exact",
|
||||
"page_url": "https://example.com/login"
|
||||
},
|
||||
{
|
||||
"item_url": "example.com",
|
||||
"level": "exact",
|
||||
"page_url": "https://example.com/"
|
||||
},
|
||||
{
|
||||
"item_url": "https://example.com:8443",
|
||||
"level": "host",
|
||||
"page_url": "https://example.com"
|
||||
},
|
||||
{
|
||||
"item_url": "https://example.com",
|
||||
"level": "domain",
|
||||
"page_url": "https://login.example.com"
|
||||
},
|
||||
{
|
||||
"item_url": "https://login.example.com",
|
||||
"level": "domain",
|
||||
"page_url": "https://example.com"
|
||||
},
|
||||
{
|
||||
"item_url": "https://a.github.io",
|
||||
"level": "none",
|
||||
"page_url": "https://b.github.io"
|
||||
},
|
||||
{
|
||||
"item_url": "https://example.com",
|
||||
"level": "none",
|
||||
"page_url": "https://example.com.evil.net"
|
||||
},
|
||||
{
|
||||
"item_url": "https://example.com",
|
||||
"level": "none",
|
||||
"page_url": "http://example.com"
|
||||
},
|
||||
{
|
||||
"item_url": "http://example.com",
|
||||
"level": "host",
|
||||
"page_url": "https://example.com"
|
||||
},
|
||||
{
|
||||
"item_url": "https://example.co.uk",
|
||||
"level": "none",
|
||||
"page_url": "https://other.co.uk"
|
||||
},
|
||||
{
|
||||
"item_url": "https://shop.example.co.uk",
|
||||
"level": "domain",
|
||||
"page_url": "https://www.example.co.uk/cart"
|
||||
},
|
||||
{
|
||||
"item_url": "https://EXAMPLE.com",
|
||||
"level": "exact",
|
||||
"page_url": "https://example.COM/"
|
||||
},
|
||||
{
|
||||
"item_url": "https://192.168.1.10:8080",
|
||||
"level": "exact",
|
||||
"page_url": "https://192.168.1.10:8080/admin"
|
||||
},
|
||||
{
|
||||
"item_url": "not a url",
|
||||
"level": "none",
|
||||
"page_url": "https://example.com"
|
||||
},
|
||||
{
|
||||
"item_url": "ftp://example.com",
|
||||
"level": "none",
|
||||
"page_url": "https://example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"alphabet": "0123456789ABCDEFGHJKMNPQRSTVWXYZ",
|
||||
"alternate_inputs": [
|
||||
{
|
||||
"input": "k1-014d2p-f2dbs-qqzxq-5tk1v-58cgg-1",
|
||||
"raw": "0123456789abcdeffedcba9876543210"
|
||||
},
|
||||
{
|
||||
"input": "K1 014D2P F2DBS QQZXQ 5TK1V 58CGG 1",
|
||||
"raw": "0123456789abcdeffedcba9876543210"
|
||||
},
|
||||
{
|
||||
"input": "K1014D2PF2DBSQQZXQ5TK1V58CGG1",
|
||||
"raw": "0123456789abcdeffedcba9876543210"
|
||||
},
|
||||
{
|
||||
"input": "K1-OI4D2P-F2DBS-QQZXQ-5TKIV-58CGG-I",
|
||||
"raw": "0123456789abcdeffedcba9876543210"
|
||||
}
|
||||
],
|
||||
"description": "Secret Key display format K1-XXXXXX-XXXXX-XXXXX-XXXXX-XXXXX-C: 26 Crockford base32 symbols of the 128-bit key (MSB first, 2 zero pad bits at the top), check symbol = SHA-256(raw)[0] mod 32",
|
||||
"invalid": [
|
||||
{
|
||||
"input": "K1-014D2P-F2DBS-QQZXQ-5TK1V-58CGG-0",
|
||||
"reason": "checksum"
|
||||
},
|
||||
{
|
||||
"input": "K2-014D2P-F2DBS-QQZXQ-5TK1V-58CGG-1",
|
||||
"reason": "prefix"
|
||||
},
|
||||
{
|
||||
"input": "K1-014D2P-F2DBS-QQZXQ-5TK1V-58CGG",
|
||||
"reason": "length"
|
||||
},
|
||||
{
|
||||
"input": "K1-8ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-T",
|
||||
"reason": "overflow: first symbol carries only 3 bits"
|
||||
},
|
||||
{
|
||||
"input": "K1-U014D2P-F2DBS-QQZXQ-5TK1V-58CGG-",
|
||||
"reason": "invalid symbol U"
|
||||
}
|
||||
],
|
||||
"valid": [
|
||||
{
|
||||
"display": "K1-000000-00000-00000-00000-00000-Q",
|
||||
"raw": "00000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
"display": "K1-7ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-T",
|
||||
"raw": "ffffffffffffffffffffffffffffffff"
|
||||
},
|
||||
{
|
||||
"display": "K1-40G208-1040G-20810-40G20-81040-E",
|
||||
"raw": "80808080808080808080808080808080"
|
||||
},
|
||||
{
|
||||
"display": "K1-5BNENT-QAXBN-ENTQA-XBNEN-TQAXB-T",
|
||||
"raw": "abababababababababababababababab"
|
||||
},
|
||||
{
|
||||
"display": "K1-014D2P-F2DBS-QQZXQ-5TK1V-58CGG-1",
|
||||
"raw": "0123456789abcdeffedcba9876543210"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"description": "RFC 6238 TOTP (HMAC-SHA1/256/512). `uri` is what Totp::parse accepts.",
|
||||
"vectors": [
|
||||
{
|
||||
"code": "94287082",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 59,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "46119246",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 59,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&algorithm=SHA256&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "90693936",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 59,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA&algorithm=SHA512&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "07081804",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1111111109,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "68084774",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1111111109,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&algorithm=SHA256&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "25091201",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1111111109,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA&algorithm=SHA512&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "14050471",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1111111111,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "67062674",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1111111111,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&algorithm=SHA256&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "99943326",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1111111111,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA&algorithm=SHA512&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "89005924",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1234567890,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "91819424",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1234567890,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&algorithm=SHA256&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "93441116",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 1234567890,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA&algorithm=SHA512&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "69279037",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 2000000000,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "90698825",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 2000000000,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&algorithm=SHA256&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "38618901",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 2000000000,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA&algorithm=SHA512&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "65353130",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 20000000000,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "77737706",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 20000000000,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&algorithm=SHA256&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "47863826",
|
||||
"source": "RFC 6238 Appendix B",
|
||||
"time": 20000000000,
|
||||
"uri": "otpauth://totp/RFC6238?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA&algorithm=SHA512&digits=8&period=30"
|
||||
},
|
||||
{
|
||||
"code": "287082",
|
||||
"source": "bare base32 secret, defaults (SHA1, 6 digits, 30 s)",
|
||||
"time": 59,
|
||||
"uri": "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"
|
||||
},
|
||||
{
|
||||
"code": "081804",
|
||||
"source": "bare base32 secret, defaults (SHA1, 6 digits, 30 s)",
|
||||
"time": 1111111109,
|
||||
"uri": "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"
|
||||
},
|
||||
{
|
||||
"code": "857319",
|
||||
"source": "60 s period",
|
||||
"time": 1111111109,
|
||||
"uri": "otpauth://totp/x?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&digits=6&period=60&algorithm=SHA256"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user