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 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.
|
||||
//! - **Nonce** (24 bytes): randomly generated per encryption via [`OsRng`].
|
||||
//! 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};
|
||||
|
||||
/// 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.
|
||||
const NONCE_LEN: usize = 24;
|
||||
@@ -123,12 +123,12 @@ pub fn decrypt(key: &[u8; 32], data: &[u8]) -> Result<Vec<u8>> {
|
||||
));
|
||||
}
|
||||
|
||||
let version = data[0];
|
||||
if version != VERSION_BYTE {
|
||||
return Err(IdfotoError::Format(format!(
|
||||
"unknown version byte: 0x{:02x}",
|
||||
version
|
||||
)));
|
||||
let found = data[0];
|
||||
if found != VERSION_BYTE {
|
||||
return Err(IdfotoError::UnsupportedFormatVersion {
|
||||
found,
|
||||
expected: VERSION_BYTE,
|
||||
});
|
||||
}
|
||||
|
||||
let nonce = XNonce::from_slice(&data[1..1 + NONCE_LEN]);
|
||||
@@ -344,8 +344,8 @@ mod tests {
|
||||
let expected_len = 1 + 24 + plaintext.len() + 16;
|
||||
assert_eq!(ciphertext.len(), expected_len);
|
||||
|
||||
// Version byte must be 0x01
|
||||
assert_eq!(ciphertext[0], 0x01);
|
||||
// Version byte must be 0x02
|
||||
assert_eq!(ciphertext[0], 0x02);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -394,4 +394,27 @@ mod tests {
|
||||
let key: zeroize::Zeroizing<[u8; 32]> = derive_master_key(b"x", &img, &salt, ¶ms).unwrap();
|
||||
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