lernie 0.1.60

lernie: the operator seat — the window and wire client for a yog server
Documentation
//! Signing, verifying, the target, and the exact bytes a signature covers.

use super::*;

fn keypair() -> Keypair {
    Keypair::from_seed([7u8; 32]).unwrap()
}

#[test]
fn the_signed_bytes_are_the_beps_spelling() {
    assert_eq!(
        signed_bytes(b"foobar", 1, b"Hello World!"),
        b"4:salt6:foobar3:seqi1e1:v12:Hello World!"
    );
    assert_eq!(
        signed_bytes(b"", 1, b"Hello World!"),
        b"3:seqi1e1:v12:Hello World!"
    );
}

#[test]
fn an_item_signs_verifies_and_fails_when_touched() {
    let kp = keypair();
    let item = kp.sign(b"salt".to_vec(), 3, b"value".to_vec()).unwrap();
    assert_eq!(item.key, kp.public());
    assert!(item.verify());
    let mut forged = item.clone();
    forged.seq += 1;
    assert!(!forged.verify());
    let mut other_key = item.clone();
    other_key.key = Keypair::from_seed([8u8; 32]).unwrap().public();
    assert!(!other_key.verify());
    // The same seed is the same key, so a stored seed comes back whole.
    assert_eq!(keypair().public(), kp.public());
    assert_ne!(Keypair::generate().unwrap().public(), kp.public());
}

#[test]
fn a_value_over_the_beps_cap_is_refused_before_it_leaves() {
    let kp = keypair();
    assert!(kp.sign(vec![], 1, vec![0u8; 996]).is_ok());
    let e = kp.sign(vec![], 1, vec![0u8; 997]).unwrap_err();
    assert_eq!(e, "value bencodes to 1001 bytes; BEP 44 allows 1000");
}

#[test]
fn the_target_is_sha1_of_key_and_salt() {
    let key = [1u8; 32];
    let plain = target_of(&key, b"");
    let salted = target_of(&key, b"s");
    assert_ne!(plain, salted);
    let expect = ring::digest::digest(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY, &key);
    assert_eq!(plain.0.as_slice(), expect.as_ref());
    let item = keypair().sign(b"s".to_vec(), 1, vec![]).unwrap();
    assert_eq!(item.target(), target_of(&keypair().public(), b"s"));
}

#[test]
fn a_reply_yields_its_item_only_when_it_verifies() {
    let kp = keypair();
    let item = kp.sign(b"s".to_vec(), 5, b"v".to_vec()).unwrap();
    let mut r = Dict::from([
        entry("seq", Value::Int(5)),
        entry("sig", bytes(&item.sig)),
        entry("v", bytes(b"v")),
    ]);
    assert_eq!(
        Mutable::from_reply(&r, kp.public(), b"s"),
        Some(item.clone())
    );
    // Under another salt the same bytes verify nothing.
    assert_eq!(Mutable::from_reply(&r, kp.public(), b"t"), None);
    // A reply that spells no item, or a non-string value, is nothing.
    assert_eq!(Mutable::from_reply(&Dict::new(), kp.public(), b"s"), None);
    r.insert(b"v".to_vec(), Value::Int(1));
    assert_eq!(Mutable::from_reply(&r, kp.public(), b"s"), None);
    r.insert(b"sig".to_vec(), bytes(b"short"));
    assert_eq!(Mutable::from_reply(&r, kp.public(), b"s"), None);
}

#[test]
fn put_arguments_carry_the_salt_only_when_there_is_one() {
    let kp = keypair();
    let salted = kp.sign(b"s".to_vec(), 1, b"v".to_vec()).unwrap();
    let args = salted.put_args(b"tok");
    assert_eq!(args.get(b"salt".as_slice()), Some(&bytes(b"s")));
    assert_eq!(args.get(b"token".as_slice()), Some(&bytes(b"tok")));
    assert_eq!(args.get(b"k".as_slice()), Some(&bytes(&kp.public())));
    assert_eq!(args.get(b"seq".as_slice()), Some(&Value::Int(1)));
    assert_eq!(args.get(b"v".as_slice()), Some(&bytes(b"v")));
    assert_eq!(args.get(b"sig".as_slice()), Some(&bytes(&salted.sig)));
    let plain = kp.sign(vec![], 1, b"v".to_vec()).unwrap();
    assert_eq!(plain.put_args(b"tok").get(b"salt".as_slice()), None);
}