#![cfg(not(target_arch = "wasm32"))]
use aethel_sdk::{verify, Identity};
const KEY: &[u8] = b"a sealing key of thirty-two byte";
const OTHER_KEY: &[u8] = b"a different sealing key, 32 byte";
const FIXTURE: &[u8] = include_bytes!("fixtures/sealed-identity.bin");
const FIXTURE_KEY: &[u8] = b"aethel-sdk sealing fixture key!!";
const FIXTURE_PUBLIC_KEY_PREFIX: &str =
"604db5834fe60b750f0267c8e4fa76ef4be1884ac129f12cd83b854ceb20e271";
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
#[test]
fn an_identity_round_trips_through_a_sealed_blob() {
let mut original = Identity::generate().expect("generate");
let public_key = original.public_key().to_vec();
let sealed = original.export_sealed(KEY).expect("seal");
let reopened = Identity::open_sealed(&sealed, KEY).expect("open");
assert_eq!(
reopened.public_key(),
public_key.as_slice(),
"the reopened identity is a different one"
);
}
#[test]
fn a_reopened_identity_can_still_sign() {
let mut original = Identity::generate().expect("generate");
let public_key = original.public_key().to_vec();
let sealed = original.export_sealed(KEY).expect("seal");
let mut reopened = Identity::open_sealed(&sealed, KEY).expect("open");
let message = b"signed after a round trip through disk";
let signature = reopened.sign(message).expect("sign");
assert!(
verify(&public_key, message, &signature).expect("verify"),
"a signature from the reopened identity did not verify under the original key"
);
}
#[test]
fn a_fixture_sealed_on_another_platform_opens_here() {
let identity = Identity::open_sealed(FIXTURE, FIXTURE_KEY)
.expect("the fixture sealed on another platform did not open");
assert_eq!(
&hex(identity.public_key())[..64],
FIXTURE_PUBLIC_KEY_PREFIX,
"the fixture opened but produced a different identity"
);
}
#[test]
fn the_fixture_check_distinguishes_identities() {
let from_fixture = Identity::open_sealed(FIXTURE, FIXTURE_KEY).expect("open");
let mut other = Identity::generate().expect("generate");
let other_sealed = other.export_sealed(KEY).expect("seal");
let from_other = Identity::open_sealed(&other_sealed, KEY).expect("open");
assert_ne!(
from_fixture.public_key(),
from_other.public_key(),
"two different sealed blobs opened as the same identity"
);
}
#[test]
fn the_wrong_key_does_not_open_it() {
let mut identity = Identity::generate().expect("generate");
let sealed = identity.export_sealed(KEY).expect("seal");
assert!(
Identity::open_sealed(&sealed, OTHER_KEY).is_err(),
"a sealed identity opened under the wrong key"
);
}
#[test]
fn a_tampered_blob_does_not_open() {
let mut identity = Identity::generate().expect("generate");
let sealed = identity.export_sealed(KEY).expect("seal");
for index in [0usize, 5, sealed.len() - 1] {
let mut tampered = sealed.clone();
tampered[index] ^= 0x01;
assert!(
Identity::open_sealed(&tampered, KEY).is_err(),
"a blob with byte {index} flipped still opened"
);
}
}
#[test]
fn a_truncated_blob_does_not_open() {
let mut identity = Identity::generate().expect("generate");
let sealed = identity.export_sealed(KEY).expect("seal");
assert!(Identity::open_sealed(&sealed[..sealed.len() - 1], KEY).is_err());
assert!(Identity::open_sealed(&[], KEY).is_err());
}
#[test]
fn a_short_key_is_refused() {
let mut identity = Identity::generate().expect("generate");
assert!(
identity.export_sealed(b"too short").is_err(),
"a key shorter than the minimum was accepted for sealing"
);
}
#[test]
fn the_sealed_blob_does_not_contain_the_public_key() {
let mut identity = Identity::generate().expect("generate");
let public_key = identity.public_key().to_vec();
let sealed = identity.export_sealed(KEY).expect("seal");
assert!(
!sealed.windows(32).any(|w| w == &public_key[..32]),
"the public key appears in the sealed blob, making it identifiable at rest"
);
}
#[test]
fn the_at_rest_leak_check_can_detect_a_leak() {
let identity = Identity::generate().expect("generate");
let public_key = identity.public_key().to_vec();
let mut leaky = vec![0u8; 8];
leaky.extend_from_slice(&public_key);
assert!(
leaky.windows(32).any(|w| w == &public_key[..32]),
"the leak check cannot see the public key even when it is stored in the clear"
);
}
#[cfg(feature = "experimental-credentials")]
#[test]
fn a_reopened_identity_can_issue_and_present() {
let mut original = Identity::generate().expect("generate");
let sealed = original.export_sealed(KEY).expect("seal");
let mut reopened = Identity::open_sealed(&sealed, KEY).expect("open");
let credential = reopened
.issue_credential(b"issuer seed for the sdk tests!!!", &[("tier", 3)])
.expect("issue");
let presentation = reopened
.present(&credential, b"after-reload", &["tier"])
.expect("present");
assert_eq!(presentation.disclosed().get("tier"), Some(&3));
assert!(
aethel_sdk::verify_presentation(
&aethel_sdk::IssuerPublicParameters::derive(b"issuer seed for the sdk tests!!!")
.expect("derive issuer parameters"),
&presentation,
b"after-reload"
)
.expect("verify"),
"a credential issued by a reopened identity did not verify"
);
}