lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

wire_vectors.rs (5897B)


      1 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
      2 use chacha20poly1305::{Key, XChaCha20Poly1305, XNonce};
      3 use radroots_secrets::context::{
      4     EnvelopeContext, EnvelopePurpose, EnvelopeSubject, PayloadSchemaId,
      5 };
      6 
      7 const V1_ENVELOPE_HEX: &str = "52525331000101010100000007000c656e76656c6f70652d6b6579222222222222222222222222222222222222222222222222000000204b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b00000028f106837e33d690e7c5287abdd815ce9257b7b5b176ea9596abf3b7fe745aec5a8c2487a553d4659d";
      8 const V2_CONTEXT_HEX: &str = "0001726164726f6f74732e656e76656c6f70655f636f6e746578742e76310019726164726f6f74732e707269766174655f61727469666163740010707269766174655f617274696661637400203031303130313031303130313031303130313031303130313031303130313031001674726164652e707269766174655f7465726d732e7631";
      9 const V2_HEADER_HEX: &str = "52525331000201010100000007000c656e76656c6f70652d6b65790001726164726f6f74732e656e76656c6f70655f636f6e746578742e76310019726164726f6f74732e707269766174655f61727469666163740010707269766174655f617274696661637400203031303130313031303130313031303130313031303130313031303130313031001674726164652e707269766174655f7465726d732e7631222222222222222222222222222222222222222222222222000000204b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b00000028";
     10 const V2_CIPHERTEXT_HEX: &str =
     11     "f106837e33d690e7c5287abdd815ce9257b7b5b176ea9596f762615d1c221c5c10964c4f799d5d59";
     12 const V2_ENVELOPE_HEX: &str = "52525331000201010100000007000c656e76656c6f70652d6b65790001726164726f6f74732e656e76656c6f70655f636f6e746578742e76310019726164726f6f74732e707269766174655f61727469666163740010707269766174655f617274696661637400203031303130313031303130313031303130313031303130313031303130313031001674726164652e707269766174655f7465726d732e7631222222222222222222222222222222222222222222222222000000204b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b00000028f106837e33d690e7c5287abdd815ce9257b7b5b176ea9596f762615d1c221c5c10964c4f799d5d59";
     13 
     14 fn context() -> EnvelopeContext {
     15     EnvelopeContext::new(
     16         EnvelopePurpose::parse("radroots.private_artifact").expect("purpose"),
     17         EnvelopeSubject::parse("private_artifact", "01010101010101010101010101010101")
     18             .expect("subject"),
     19         PayloadSchemaId::parse("trade.private_terms.v1").expect("schema"),
     20     )
     21 }
     22 
     23 fn independent_v2_vector() -> (Vec<u8>, Vec<u8>, Vec<u8>) {
     24     let context = context().to_canonical_bytes();
     25     let mut header = Vec::new();
     26     header.extend_from_slice(b"RRS1");
     27     header.extend_from_slice(&2_u16.to_be_bytes());
     28     header.extend_from_slice(&[1, 1, 1]);
     29     header.extend_from_slice(&7_u32.to_be_bytes());
     30     header.extend_from_slice(&12_u16.to_be_bytes());
     31     header.extend_from_slice(b"envelope-key");
     32     header.extend_from_slice(&context);
     33     header.extend_from_slice(&[0x22; 24]);
     34     header.extend_from_slice(&32_u32.to_be_bytes());
     35     header.extend_from_slice(&[0x4b; 32]);
     36     header.extend_from_slice(&40_u32.to_be_bytes());
     37 
     38     let cipher = XChaCha20Poly1305::new(Key::from_slice(&[0x11; 32]));
     39     let ciphertext = cipher
     40         .encrypt(
     41             XNonce::from_slice(&[0x22; 24]),
     42             Payload {
     43                 msg: b"radroots envelope vector",
     44                 aad: &header,
     45             },
     46         )
     47         .expect("independent encryption");
     48     let mut envelope = header.clone();
     49     envelope.extend_from_slice(&ciphertext);
     50     (header, ciphertext, envelope)
     51 }
     52 
     53 #[test]
     54 fn v1_corpus_is_stable_and_strictly_bounded() {
     55     let bytes = hex::decode(V1_ENVELOPE_HEX).expect("v1 vector hex");
     56     assert_eq!(&bytes[..4], b"RRS1");
     57     assert_eq!(u16::from_be_bytes([bytes[4], bytes[5]]), 1);
     58     assert!(bytes.len() < radroots_secrets::envelope::ENVELOPE_MAX_BYTES);
     59     let decoded = radroots_secrets::EncryptedEnvelope::decode(&bytes).expect("v1 corpus decode");
     60     assert_eq!(decoded.encode().expect("v1 re-encode"), bytes);
     61 }
     62 
     63 #[test]
     64 fn v2_vector_freezes_context_header_ciphertext_and_envelope() {
     65     let (header, ciphertext, envelope) = independent_v2_vector();
     66     assert_eq!(hex::encode(context().to_canonical_bytes()), V2_CONTEXT_HEX);
     67     assert_eq!(hex::encode(header), V2_HEADER_HEX);
     68     assert_eq!(hex::encode(ciphertext), V2_CIPHERTEXT_HEX);
     69     assert_eq!(hex::encode(envelope), V2_ENVELOPE_HEX);
     70 }
     71 
     72 #[test]
     73 fn v2_header_positions_and_length_prefixes_are_exact() {
     74     let (header, ciphertext, envelope) = independent_v2_vector();
     75     let context_bytes = context().to_canonical_bytes();
     76     assert_eq!(&header[..4], b"RRS1");
     77     assert_eq!(&header[4..6], &2_u16.to_be_bytes());
     78     assert_eq!(&header[6..9], &[1, 1, 1]);
     79     assert_eq!(&header[9..13], &7_u32.to_be_bytes());
     80     assert_eq!(&header[13..15], &12_u16.to_be_bytes());
     81     assert_eq!(&header[15..27], b"envelope-key");
     82     assert_eq!(&header[27..27 + context_bytes.len()], &context_bytes);
     83     let nonce_offset = 27 + context_bytes.len();
     84     assert_eq!(&header[nonce_offset..nonce_offset + 24], &[0x22; 24]);
     85     let wrapped_length_offset = nonce_offset + 24;
     86     assert_eq!(
     87         &header[wrapped_length_offset..wrapped_length_offset + 4],
     88         &32_u32.to_be_bytes()
     89     );
     90     assert_eq!(
     91         &header[wrapped_length_offset + 4..wrapped_length_offset + 36],
     92         &[0x4b; 32]
     93     );
     94     assert_eq!(&header[header.len() - 4..], &40_u32.to_be_bytes());
     95     assert_eq!(ciphertext.len(), 40);
     96     assert_eq!(envelope.len(), header.len() + ciphertext.len());
     97 }
     98 
     99 #[test]
    100 fn vector_corruption_and_truncation_change_authenticated_material() {
    101     let (_, _, envelope) = independent_v2_vector();
    102     for offset in [0, 4, 6, 8, 9, 13, 15, 27, envelope.len() - 1] {
    103         let mut corrupted = envelope.clone();
    104         corrupted[offset] ^= 1;
    105         assert_ne!(hex::encode(corrupted), V2_ENVELOPE_HEX);
    106     }
    107     for length in [0, 1, 4, 6, 27, envelope.len() - 1] {
    108         assert_ne!(hex::encode(&envelope[..length]), V2_ENVELOPE_HEX);
    109     }
    110 }