Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions e2e/generator/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion e2e/generator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
publish = false

[dependencies]
samp = { path = "../../rust" }
samp = { package = "samp-core", path = "../../rust" }
schnorrkel = "0.11"
curve25519-dalek = { version = "4", features = ["digest"] }
hkdf = "0.12"
Expand Down
167 changes: 122 additions & 45 deletions e2e/generator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use samp::encryption;
use samp::extrinsic::{build_signed_extrinsic, ChainParams};
use samp::scale::{decode_compact, encode_compact};
use samp::wire::*;
use samp::{BlockRef, GenesisHash, Nonce, Pubkey, Seed, Signature};
use samp::{
BlockRef, CallArgs, CallIdx, ContentKey, EphPubkey, ExtrinsicNonce, GenesisHash, Nonce,
PalletIdx, Pubkey, Seed, Signature, SpecVersion, Ss58Prefix, TxVersion,
};

fn h(bytes: &[u8]) -> String {
format!("0x{}", hex::encode(bytes))
Expand Down Expand Up @@ -112,6 +115,18 @@ struct NegativeCases {
truncated_encrypted: String,
}

#[derive(Serialize)]
struct Ss58CaseVec {
prefix: u16,
address: String,
}

#[derive(Serialize)]
struct Ss58Vec {
pubkey: String,
cases: Vec<Ss58CaseVec>,
}

#[derive(Serialize)]
struct TestVectors {
alice: KeypairVec,
Expand All @@ -126,6 +141,7 @@ struct TestVectors {
group_message: GroupMsgVec,
edge_cases: EdgeCases,
negative_cases: NegativeCases,
ss58: Ss58Vec,
}

fn make_keypair_vec(seed: &[u8; 32]) -> KeypairVec {
Expand All @@ -135,7 +151,7 @@ fn make_keypair_vec(seed: &[u8; 32]) -> KeypairVec {
KeypairVec {
seed: h(seed),
sr25519_public: h(&kp.public.to_bytes()),
signing_scalar: h(&scalar.to_bytes()),
signing_scalar: h(scalar.expose_secret()),
}
}

Expand Down Expand Up @@ -234,10 +250,15 @@ fn main() {
&encrypted_content,
);

let samp::Remark::Encrypted(parsed) = decode_remark(&enc_remark).unwrap() else {
let samp::Remark::Encrypted {
nonce: parsed_nonce,
ciphertext: parsed_ciphertext,
..
} = decode_remark(&enc_remark).unwrap()
else {
panic!("expected Encrypted");
};
let decrypted = encryption::decrypt(&parsed, &bob_scalar).unwrap();
let decrypted = encryption::decrypt(&parsed_ciphertext, &parsed_nonce, &bob_scalar).unwrap();
assert_eq!(decrypted.as_bytes(), plaintext);

// === Thread message ===
Expand All @@ -254,9 +275,13 @@ fn main() {
let thread_nonce = Nonce::from_bytes(thread_nonce_bytes);

let thread_plaintext_typed = samp::Plaintext::from_bytes(thread_plaintext.clone());
let thread_encrypted =
encryption::encrypt(&thread_plaintext_typed, &bob_pubkey, &thread_nonce, &alice_seed)
.unwrap();
let thread_encrypted = encryption::encrypt(
&thread_plaintext_typed,
&bob_pubkey,
&thread_nonce,
&alice_seed,
)
.unwrap();
let thread_view_tag =
encryption::compute_view_tag(&alice_seed, &bob_pubkey, &thread_nonce).unwrap();
let thread_remark = encode_encrypted(
Expand All @@ -266,10 +291,16 @@ fn main() {
&thread_encrypted,
);

let samp::Remark::Thread(thread_parsed) = decode_remark(&thread_remark).unwrap() else {
let samp::Remark::Thread {
nonce: thread_parsed_nonce,
ciphertext: thread_parsed_ciphertext,
..
} = decode_remark(&thread_remark).unwrap()
else {
panic!("expected Thread");
};
let thread_decrypted = encryption::decrypt(&thread_parsed, &bob_scalar).unwrap();
let thread_decrypted =
encryption::decrypt(&thread_parsed_ciphertext, &thread_parsed_nonce, &bob_scalar).unwrap();
assert_eq!(thread_decrypted.as_bytes(), thread_plaintext.as_slice());

// === Sender self-decryption intermediates ===
Expand All @@ -295,7 +326,8 @@ fn main() {
.unwrap();
let sd_shared = (sd_eph_scalar * sd_recip_point).compress().to_bytes();

let sd_decrypted = encryption::decrypt_as_sender(&parsed, &alice_seed).unwrap();
let sd_decrypted =
encryption::decrypt_as_sender(&parsed_ciphertext, &parsed_nonce, &alice_seed).unwrap();
assert_eq!(sd_decrypted.as_bytes(), plaintext);

// === Channel message ===
Expand Down Expand Up @@ -327,52 +359,73 @@ fn main() {
let mut root_plaintext = member_list_encoded.clone();
root_plaintext.extend_from_slice(group_body);

let group_inner =
encode_thread_content(BlockRef::ZERO, BlockRef::ZERO, BlockRef::ZERO, &root_plaintext);
let group_inner = encode_thread_content(
BlockRef::ZERO,
BlockRef::ZERO,
BlockRef::ZERO,
&root_plaintext,
);

let content_key: [u8; 32] = [0xDD; 32];
let group_eph_scalar = encryption::derive_group_ephemeral(&alice_seed, &group_nonce);
let group_eph_pubkey = (group_eph_scalar * RISTRETTO_BASEPOINT_POINT).compress();
let group_content_key = ContentKey::from_bytes(content_key);
let group_capsules = encryption::build_capsules(
&content_key,
&group_content_key,
&group_members,
&group_eph_scalar,
&group_nonce,
);

let group_cipher = ChaCha20Poly1305::new((&content_key).into());
let group_ciphertext_raw = group_cipher
.encrypt(ChaChaNonce::from_slice(&group_nonce_bytes), group_inner.as_slice())
.encrypt(
ChaChaNonce::from_slice(&group_nonce_bytes),
group_inner.as_slice(),
)
.expect("group encryption");
let group_ciphertext = samp::Ciphertext::from_bytes(group_ciphertext_raw);

let group_eph_pubkey_pk = Pubkey::from_bytes(group_eph_pubkey.to_bytes());
let group_eph_pubkey_pk = EphPubkey::from_bytes(group_eph_pubkey.to_bytes());
let group_remark = encode_group(
&group_nonce,
&group_eph_pubkey_pk,
&group_capsules,
&group_ciphertext,
);

let group_payload_for_decode = match decode_remark(&group_remark).unwrap() {
samp::Remark::Group(p) => p,
_ => panic!("expected Group"),
};
let bob_decrypted =
encryption::decrypt_from_group(&group_payload_for_decode, &bob_scalar, Some(3)).unwrap();
let (group_nonce_for_decode, group_payload_for_decode) =
match decode_remark(&group_remark).unwrap() {
samp::Remark::Group { nonce, content } => (nonce, content),
_ => panic!("expected Group"),
};
let bob_decrypted = encryption::decrypt_from_group(
&group_payload_for_decode,
&group_nonce_for_decode,
&bob_scalar,
Some(3),
)
.unwrap();
assert_eq!(bob_decrypted.as_bytes(), group_inner.as_slice());

let charlie_decrypted =
encryption::decrypt_from_group(&group_payload_for_decode, &charlie_scalar, Some(3))
.unwrap();
let charlie_decrypted = encryption::decrypt_from_group(
&group_payload_for_decode,
&group_nonce_for_decode,
&charlie_scalar,
Some(3),
)
.unwrap();
assert_eq!(charlie_decrypted.as_bytes(), group_inner.as_slice());

let random_seed = Seed::from_bytes([0xEE; 32]);
let random_scalar = encryption::sr25519_signing_scalar(&random_seed);
assert!(
encryption::decrypt_from_group(&group_payload_for_decode, &random_scalar, Some(3))
.is_err()
);
assert!(encryption::decrypt_from_group(
&group_payload_for_decode,
&group_nonce_for_decode,
&random_scalar,
Some(3),
)
.is_err());

// === Edge cases ===
let empty_body_public = encode_public(&bob_pubkey, "");
Expand All @@ -396,6 +449,16 @@ fn main() {

let scale_vectors = build_scale_vectors();
let extrinsic_vectors = build_extrinsic_vectors(&alice_kp, &alice_seed_bytes);
let ss58_cases = [0u16, 42, 63, 64, 255, 16_383]
.into_iter()
.map(|prefix| {
let prefix_typed = Ss58Prefix::new(prefix).unwrap();
Ss58CaseVec {
prefix,
address: bob_pubkey.to_ss58(prefix_typed).as_str().to_string(),
}
})
.collect();

let out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
Expand Down Expand Up @@ -486,9 +549,19 @@ fn main() {
reserved_type,
truncated_encrypted,
},
ss58: Ss58Vec {
pubkey: h(bob_pubkey.as_bytes()),
cases: ss58_cases,
},
};

println!("{}", serde_json::to_string_pretty(&vectors).unwrap());
let vectors_json = serde_json::to_string_pretty(&vectors).unwrap();
std::fs::write(
out_dir.join("test-vectors.json"),
format!("{vectors_json}\n"),
)
.expect("write test-vectors.json");
println!("{vectors_json}");
}

#[derive(Serialize)]
Expand Down Expand Up @@ -560,13 +633,16 @@ struct ExtrinsicVectors {
cases: Vec<ExtrinsicCaseVec>,
}

fn build_extrinsic_vectors(alice_kp: &schnorrkel::Keypair, _alice_seed: &[u8; 32]) -> ExtrinsicVectors {
fn build_extrinsic_vectors(
alice_kp: &schnorrkel::Keypair,
_alice_seed: &[u8; 32],
) -> ExtrinsicVectors {
let public_key = Pubkey::from_bytes(alice_kp.public.to_bytes());
let chain = ChainParams {
genesis_hash: GenesisHash::from_bytes([0x11; 32]),
spec_version: 100,
tx_version: 1,
};
let chain = ChainParams::new(
GenesisHash::from_bytes([0x11; 32]),
SpecVersion::new(100),
TxVersion::new(1),
);
let fixed_signature = Signature::from_bytes([0xAB; 64]);

let long_payload = vec![0xCD; 1024];
Expand Down Expand Up @@ -618,17 +694,18 @@ struct CaseInputs<'a> {
}

fn build_case(c: CaseInputs<'_>) -> ExtrinsicCaseVec {
let mut call_args = Vec::new();
encode_compact(c.remark.len() as u64, &mut call_args);
call_args.extend_from_slice(c.remark);
let mut call_args_raw = Vec::new();
encode_compact(c.remark.len() as u64, &mut call_args_raw);
call_args_raw.extend_from_slice(c.remark);
let call_args = CallArgs::from_bytes(call_args_raw);

let extrinsic = build_signed_extrinsic(
c.pallet_idx,
c.call_idx,
PalletIdx::new(c.pallet_idx),
CallIdx::new(c.call_idx),
&call_args,
c.public_key,
|_msg| *c.fixed_signature,
c.nonce,
ExtrinsicNonce::new(c.nonce),
c.chain,
)
.expect("build_signed_extrinsic");
Expand All @@ -637,14 +714,14 @@ fn build_case(c: CaseInputs<'_>) -> ExtrinsicCaseVec {
label: c.label,
pallet_idx: c.pallet_idx,
call_idx: c.call_idx,
call_args: h(&call_args),
call_args: h(call_args.as_bytes()),
public_key: h(c.public_key.as_bytes()),
fixed_signature: h(c.fixed_signature.as_bytes()),
nonce: c.nonce,
chain_params: ChainParamsVec {
genesis_hash: h(c.chain.genesis_hash.as_bytes()),
spec_version: c.chain.spec_version,
tx_version: c.chain.tx_version,
genesis_hash: h(c.chain.genesis_hash().as_bytes()),
spec_version: c.chain.spec_version().get(),
tx_version: c.chain.tx_version().get(),
},
expected_extrinsic: h(extrinsic.as_bytes()),
}
Expand Down
29 changes: 29 additions & 0 deletions e2e/test-vectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,34 @@
"non_samp_version": "0x2100",
"reserved_type": "0x17",
"truncated_encrypted": "0x12000102"
},
"ss58": {
"pubkey": "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48",
"cases": [
{
"prefix": 0,
"address": "14E5nqKAp3oAJcmzgZhUD2RcptBeUBScxKHgJKU4HPNcKVf3"
},
{
"prefix": 42,
"address": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
},
{
"prefix": 63,
"address": "7Lpe5LRa2Ntx9KGDk77xzoBPYTCAvj7QqaBx4Nz2TFqL3sLw"
},
{
"prefix": 64,
"address": "cEYoHYutFpXcexmxW4JMuaK2JzGqnVYxL7WxeGScJuR4t4bFg"
},
{
"prefix": 255,
"address": "yGFxbGGNhpzjSAKB7iU4FRoxjKetFPqxqwEucsa8nqudSJANV"
},
{
"prefix": 16383,
"address": "yNYZ9YmV73KqE11fLn1UkDaiW3DtmrPddpVotddem1n6A2RWL"
}
]
}
}
Loading
Loading