feat(core): bump VERSION_BYTE to 0x02 with typed UnsupportedFormatVersion
Clean break from v1 — no migration. Decrypting a v1 blob now returns
IdfotoError::UnsupportedFormatVersion { found: 0x01, expected: 0x02 }.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@
|
|||||||
//! [version: 1 byte] [nonce: 24 bytes] [ciphertext + Poly1305 tag: variable]
|
//! [version: 1 byte] [nonce: 24 bytes] [ciphertext + Poly1305 tag: variable]
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! - **Version byte** (`0x01`): allows future format changes without ambiguity.
|
//! - **Version byte** (`0x02`): allows future format changes without ambiguity.
|
||||||
//! Decryption rejects any version it does not recognize.
|
//! Decryption rejects any version it does not recognize.
|
||||||
//! - **Nonce** (24 bytes): randomly generated per encryption via [`OsRng`].
|
//! - **Nonce** (24 bytes): randomly generated per encryption via [`OsRng`].
|
||||||
//! Stored alongside the ciphertext so the decryptor does not need out-of-band
|
//! Stored alongside the ciphertext so the decryptor does not need out-of-band
|
||||||
@@ -56,7 +56,7 @@ use zeroize::Zeroizing;
|
|||||||
use crate::error::{IdfotoError, Result};
|
use crate::error::{IdfotoError, Result};
|
||||||
|
|
||||||
/// Current binary format version. Increment this if the ciphertext layout changes.
|
/// Current binary format version. Increment this if the ciphertext layout changes.
|
||||||
const VERSION_BYTE: u8 = 0x01;
|
pub const VERSION_BYTE: u8 = 0x02;
|
||||||
|
|
||||||
/// XChaCha20-Poly1305 nonce length: 192 bits = 24 bytes.
|
/// XChaCha20-Poly1305 nonce length: 192 bits = 24 bytes.
|
||||||
const NONCE_LEN: usize = 24;
|
const NONCE_LEN: usize = 24;
|
||||||
@@ -123,12 +123,12 @@ pub fn decrypt(key: &[u8; 32], data: &[u8]) -> Result<Vec<u8>> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = data[0];
|
let found = data[0];
|
||||||
if version != VERSION_BYTE {
|
if found != VERSION_BYTE {
|
||||||
return Err(IdfotoError::Format(format!(
|
return Err(IdfotoError::UnsupportedFormatVersion {
|
||||||
"unknown version byte: 0x{:02x}",
|
found,
|
||||||
version
|
expected: VERSION_BYTE,
|
||||||
)));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let nonce = XNonce::from_slice(&data[1..1 + NONCE_LEN]);
|
let nonce = XNonce::from_slice(&data[1..1 + NONCE_LEN]);
|
||||||
@@ -344,8 +344,8 @@ mod tests {
|
|||||||
let expected_len = 1 + 24 + plaintext.len() + 16;
|
let expected_len = 1 + 24 + plaintext.len() + 16;
|
||||||
assert_eq!(ciphertext.len(), expected_len);
|
assert_eq!(ciphertext.len(), expected_len);
|
||||||
|
|
||||||
// Version byte must be 0x01
|
// Version byte must be 0x02
|
||||||
assert_eq!(ciphertext[0], 0x01);
|
assert_eq!(ciphertext[0], 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -394,4 +394,27 @@ mod tests {
|
|||||||
let key: zeroize::Zeroizing<[u8; 32]> = derive_master_key(b"x", &img, &salt, ¶ms).unwrap();
|
let key: zeroize::Zeroizing<[u8; 32]> = derive_master_key(b"x", &img, &salt, ¶ms).unwrap();
|
||||||
assert_eq!(key.len(), 32);
|
assert_eq!(key.len(), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_byte_is_0x02() {
|
||||||
|
assert_eq!(VERSION_BYTE, 0x02);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decrypt_rejects_v1_blob_with_typed_error() {
|
||||||
|
// Construct a v1-style blob: [0x01][24 nonce bytes][16 tag bytes].
|
||||||
|
let mut blob = vec![0x01u8];
|
||||||
|
blob.extend_from_slice(&[0u8; 24]);
|
||||||
|
blob.extend_from_slice(&[0u8; 16]);
|
||||||
|
|
||||||
|
let key = Zeroizing::new([0u8; 32]);
|
||||||
|
let err = decrypt(&*key, &blob).expect_err("v1 blob should fail decrypt");
|
||||||
|
match err {
|
||||||
|
IdfotoError::UnsupportedFormatVersion { found, expected } => {
|
||||||
|
assert_eq!(found, 0x01);
|
||||||
|
assert_eq!(expected, 0x02);
|
||||||
|
}
|
||||||
|
other => panic!("expected UnsupportedFormatVersion, got {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user