mirror of
https://github.com/mikedilger/nip44.git
synced 2026-07-04 05:57:29 +00:00
Initial code
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target
|
||||
Generated
+264
@@ -0,0 +1,264 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin-private"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501"
|
||||
dependencies = [
|
||||
"bitcoin-private",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||
|
||||
[[package]]
|
||||
name = "nip44"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chacha20",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"rand_core",
|
||||
"secp256k1",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
|
||||
dependencies = [
|
||||
"bitcoin_hashes",
|
||||
"rand",
|
||||
"secp256k1-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1-sys"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "nip44"
|
||||
version = "0.1.0"
|
||||
description = "nostr NIP-44 encryption code"
|
||||
authors = ["Mike Dilger <mike@mikedilger.com>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/mikedilger/nip44"
|
||||
homepage = "https://github.com/mikedilger/nip44"
|
||||
readme = "README.md"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.21"
|
||||
chacha20 = "0.9"
|
||||
hkdf = "0.12"
|
||||
hmac = "0.12"
|
||||
hex = "*"
|
||||
rand_core = "0.6"
|
||||
secp256k1 = { version = "0.27", features = [ "bitcoin-hashes", "global-context", "rand-std" ] }
|
||||
sha2 = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4"
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
use base64::Engine;
|
||||
use chacha20::cipher::{KeyIvInit, StreamCipher};
|
||||
use chacha20::ChaCha20;
|
||||
use hkdf::Hkdf;
|
||||
use hmac::{Hmac, Mac};
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use secp256k1::ecdh::shared_secret_point;
|
||||
use secp256k1::{Parity, PublicKey, SecretKey, XOnlyPublicKey};
|
||||
use sha2::Sha256;
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
struct MessageKeys([u8; 76]);
|
||||
|
||||
impl MessageKeys {
|
||||
#[inline]
|
||||
pub fn zero() -> MessageKeys {
|
||||
MessageKeys([0; 76])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn encryption(&self) -> [u8; 32] {
|
||||
self.0[0..32].try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn nonce(&self) -> [u8; 12] {
|
||||
self.0[32..44].try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn auth(&self) -> [u8; 32] {
|
||||
self.0[44..76].try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A conversation key is the long-term secret that two nostr identities share.
|
||||
pub fn get_conversation_key(
|
||||
private_key_a: SecretKey,
|
||||
x_only_public_key_b: XOnlyPublicKey,
|
||||
) -> [u8; 32] {
|
||||
let pubkey = PublicKey::from_x_only_public_key(x_only_public_key_b, Parity::Even);
|
||||
let mut ssp = shared_secret_point(&pubkey, &private_key_a)
|
||||
.as_slice()
|
||||
.to_owned();
|
||||
ssp.resize(32, 0); // toss the Y part
|
||||
ssp.try_into().unwrap()
|
||||
}
|
||||
|
||||
fn get_message_keys(
|
||||
conversation_key: &[u8; 32],
|
||||
salt: &[u8; 32],
|
||||
) -> Result<MessageKeys, Box<dyn Error>> {
|
||||
let hk = Hkdf::<Sha256>::new(Some(&salt[..]), conversation_key);
|
||||
let mut message_keys: MessageKeys = MessageKeys::zero();
|
||||
hk.expand("nip44-v2".as_bytes(), &mut message_keys.0)
|
||||
.map_err(|_| {
|
||||
Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Invalid Length for HKDF",
|
||||
))
|
||||
})?;
|
||||
Ok(message_keys)
|
||||
}
|
||||
|
||||
fn calc_padding(len: usize) -> usize {
|
||||
if len < 32 {
|
||||
return 32;
|
||||
}
|
||||
let nextpower = 1 << ((len - 1).ilog2() + 1);
|
||||
let chunk = if nextpower <= 256 { 32 } else { nextpower / 8 };
|
||||
if len <= 32 {
|
||||
32
|
||||
} else {
|
||||
chunk * (((len - 1) / chunk) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn pad(unpadded: &str) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let len: usize = unpadded.len();
|
||||
if len < 1 {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Message is empty",
|
||||
)));
|
||||
}
|
||||
if len > 65536 - 128 {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Message too long",
|
||||
)));
|
||||
}
|
||||
|
||||
let mut padded: Vec<u8> = Vec::new();
|
||||
padded.extend_from_slice(&(len as u16).to_be_bytes());
|
||||
padded.extend_from_slice(unpadded.as_bytes());
|
||||
padded.extend(std::iter::repeat(0).take(calc_padding(len) - len));
|
||||
Ok(padded)
|
||||
}
|
||||
|
||||
/// Encrypt a plaintext message with a conversation key.
|
||||
/// The output is a base64 encoded string that can be placed into message contents.
|
||||
#[inline]
|
||||
pub fn encrypt(conversation_key: &[u8; 32], plaintext: &str) -> Result<String, Box<dyn Error>> {
|
||||
encrypt_inner(conversation_key, plaintext, None)
|
||||
}
|
||||
|
||||
fn encrypt_inner(
|
||||
conversation_key: &[u8; 32],
|
||||
plaintext: &str,
|
||||
override_random_salt: Option<&[u8; 32]>,
|
||||
) -> Result<String, Box<dyn Error>> {
|
||||
let salt = match override_random_salt {
|
||||
Some(salt) => salt.to_owned(),
|
||||
None => {
|
||||
let mut salt: [u8; 32] = [0; 32];
|
||||
OsRng.fill_bytes(&mut salt);
|
||||
salt
|
||||
}
|
||||
};
|
||||
|
||||
let keys = get_message_keys(conversation_key, &salt)?;
|
||||
let mut buffer = pad(plaintext)?;
|
||||
let mut cipher = ChaCha20::new(&keys.encryption().into(), &keys.nonce().into());
|
||||
cipher.apply_keystream(&mut buffer);
|
||||
let mut mac = Hmac::<Sha256>::new_from_slice(&keys.auth())?;
|
||||
mac.update(&buffer);
|
||||
let mac_bytes = mac.finalize().into_bytes();
|
||||
|
||||
let mut pre_base64: Vec<u8> = vec![2];
|
||||
pre_base64.extend_from_slice(&salt);
|
||||
pre_base64.extend_from_slice(&buffer);
|
||||
pre_base64.extend_from_slice(&mac_bytes);
|
||||
|
||||
Ok(base64::engine::general_purpose::STANDARD.encode(&pre_base64))
|
||||
}
|
||||
|
||||
/// Decrypt the base64 encrypted contents with a conversation key
|
||||
pub fn decrypt(
|
||||
conversation_key: &[u8; 32],
|
||||
base64_ciphertext: &str,
|
||||
) -> Result<String, Box<dyn Error>> {
|
||||
if base64_ciphertext.as_bytes()[0] == b'#' {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Encryption version not yet supported",
|
||||
)));
|
||||
}
|
||||
let binary_ciphertext: Vec<u8> =
|
||||
base64::engine::general_purpose::STANDARD.decode(base64_ciphertext)?;
|
||||
let version = binary_ciphertext[0];
|
||||
if version != 2 {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Unknown encryption version",
|
||||
)));
|
||||
}
|
||||
let dlen = binary_ciphertext.len();
|
||||
let salt = &binary_ciphertext[1..33];
|
||||
let mut buffer = binary_ciphertext[33..dlen - 32].to_owned();
|
||||
let mac = &binary_ciphertext[dlen - 32..dlen];
|
||||
let keys = get_message_keys(conversation_key, &salt.try_into().unwrap())?;
|
||||
let mut calculated_mac = Hmac::<Sha256>::new_from_slice(&keys.auth())?;
|
||||
calculated_mac.update(&buffer);
|
||||
let calculated_mac_bytes = calculated_mac.finalize().into_bytes();
|
||||
if mac != calculated_mac_bytes.as_slice() {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Invalid Message Authentication Code",
|
||||
)));
|
||||
}
|
||||
let mut cipher = ChaCha20::new(&keys.encryption().into(), &keys.nonce().into());
|
||||
cipher.apply_keystream(&mut buffer);
|
||||
let unpadded_len = u16::from_be_bytes(buffer[0..2].try_into().unwrap()) as usize;
|
||||
let unpadded = &buffer[2..2 + unpadded_len];
|
||||
if unpadded.is_empty() {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Empty message",
|
||||
)));
|
||||
}
|
||||
if unpadded.len() != unpadded_len {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Logic error 1",
|
||||
)));
|
||||
}
|
||||
if buffer.len() != 2 + calc_padding(unpadded_len) {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Logic error 2",
|
||||
)));
|
||||
}
|
||||
Ok(String::from_utf8(unpadded.to_vec())?)
|
||||
}
|
||||
+492
@@ -0,0 +1,492 @@
|
||||
use crate::*;
|
||||
use secp256k1::{SecretKey, XOnlyPublicKey, SECP256K1};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ValidSec {
|
||||
sec1: &'static str,
|
||||
sec2: &'static str,
|
||||
shared: &'static str,
|
||||
salt: &'static str,
|
||||
plaintext: &'static str,
|
||||
ciphertext: &'static str,
|
||||
note: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ValidSecParsed {
|
||||
sec1: SecretKey,
|
||||
sec2: SecretKey,
|
||||
shared: [u8; 32],
|
||||
salt: [u8; 32],
|
||||
plaintext: &'static str,
|
||||
ciphertext: &'static str,
|
||||
note: &'static str,
|
||||
}
|
||||
|
||||
impl ValidSec {
|
||||
fn parsed(self) -> ValidSecParsed {
|
||||
let sec1hex = hex::decode(self.sec1).unwrap();
|
||||
let sec1 = SecretKey::from_slice(&sec1hex).unwrap();
|
||||
let sec2hex = hex::decode(self.sec2).unwrap();
|
||||
let sec2 = SecretKey::from_slice(&sec2hex).unwrap();
|
||||
ValidSecParsed {
|
||||
sec1,
|
||||
sec2,
|
||||
shared: hex::decode(self.shared).unwrap().try_into().unwrap(),
|
||||
salt: hex::decode(self.salt).unwrap().try_into().unwrap(),
|
||||
plaintext: self.plaintext,
|
||||
ciphertext: self.ciphertext,
|
||||
note: self.note,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_valid_sec_test_vectors() {
|
||||
for vec in &VALID_SEC {
|
||||
let vector = vec.clone().parsed();
|
||||
|
||||
// Test conversation key
|
||||
let conversation_key =
|
||||
get_conversation_key(vector.sec1, vector.sec2.x_only_public_key(&SECP256K1).0);
|
||||
assert_eq!(
|
||||
conversation_key, vector.shared,
|
||||
"Conversation key failure on {}",
|
||||
vector.note
|
||||
);
|
||||
|
||||
// Test encryption with an overridden salt
|
||||
let ciphertext =
|
||||
encrypt_inner(&conversation_key, &vector.plaintext, Some(&vector.salt)).unwrap();
|
||||
assert_eq!(
|
||||
ciphertext, vector.ciphertext,
|
||||
"Encryption does not match on {}",
|
||||
vector.note
|
||||
);
|
||||
|
||||
// Test decryption
|
||||
let plaintext = decrypt(&conversation_key, &vector.ciphertext).unwrap();
|
||||
assert_eq!(
|
||||
plaintext, vector.plaintext,
|
||||
"Decryption does not match on {}",
|
||||
vector.note
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const VALID_SEC: [ValidSec; 10] = [
|
||||
ValidSec {
|
||||
sec1: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
sec2: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
shared: "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
||||
salt: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
plaintext: "a",
|
||||
ciphertext: "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYNpT9ESckRbRUY7bUF5P+1rObpA4BNoksAUQ8myMDd9/37W/J2YHvBpRjvy9uC0+ovbpLc0WLaMFieqAMdIYqR14",
|
||||
note: "sk1 = 1, sk2 = random, 0x02",
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
sec2: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
shared: "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
||||
salt: "f00000000000000000000000000000f00000000000000000000000000000000f",
|
||||
plaintext: "🍕🫃",
|
||||
ciphertext: "AvAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAPKY68BwdF7PIT205jBoaZHSs7OMpKsULW5F5ClOJWiy6XjZy7s2v85KugYmbBKgEC2LytbXbxkr7Jpgfk529K3/pP",
|
||||
note: "sk1 = 1, sk2 = random, 0x02"
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a",
|
||||
sec2: "4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d",
|
||||
shared: "94da47d851b9c1ed33b3b72f35434f56aa608d60e573e9c295f568011f4f50a4",
|
||||
salt: "b635236c42db20f021bb8d1cdff5ca75dd1a0cc72ea742ad750f33010b24f73b",
|
||||
plaintext: "表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀",
|
||||
ciphertext: "ArY1I2xC2yDwIbuNHN/1ynXdGgzHLqdCrXUPMwELJPc7yuU7XwJ8wCYUrq4aXX86HLnkMx7fPFvNeMk0uek9ma01magfEBIf+vJvZdWKiv48eUu9Cv31plAJsH6kSIsGc5TVYBYipkrQUNRxxJA15QT+uCURF96v3XuSS0k2Pf108AI=",
|
||||
note: "hard-unicode string"
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "8f40e50a84a7462e2b8d24c28898ef1f23359fff50d8c509e6fb7ce06e142f9c",
|
||||
sec2: "b9b0a1e9cc20100c5faa3bbe2777303d25950616c4c6a3fa2e3e046f936ec2ba",
|
||||
shared: "ab99c122d4586cdd5c813058aa543d0e7233545dbf6874fc34a3d8d9a18fbbc3",
|
||||
salt: "b20989adc3ddc41cd2c435952c0d59a91315d8c5218d5040573fc3749543acaf",
|
||||
plaintext: "ability🤝的 ȺȾ",
|
||||
ciphertext: "ArIJia3D3cQc0sQ1lSwNWakTFdjFIY1QQFc/w3SVQ6yvPSc+7YCIFTmGk5OLuh1nhl6TvID7sGKLFUCWRW1eRfV/0a7sT46N3nTQzD7IE67zLWrYqGnE+0DDNz6sJ4hAaFrT",
|
||||
note: "",
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "875adb475056aec0b4809bd2db9aa00cff53a649e7b59d8edcbf4e6330b0995c",
|
||||
sec2: "9c05781112d5b0a2a7148a222e50e0bd891d6b60c5483f03456e982185944aae",
|
||||
shared: "a449f2a85c6d3db0f44c64554a05d11a3c0988d645e4b4b2592072f63662f422",
|
||||
salt: "8d4442713eb9d4791175cb040d98d6fc5be8864d6ec2f89cf0895a2b2b72d1b1",
|
||||
plaintext: "pepper👀їжак",
|
||||
ciphertext: "Ao1EQnE+udR5EXXLBA2Y1vxb6IZNbsL4nPCJWisrctGx1TkkMfiHJxEeSdQ/4Rlaghn0okDCNYLihBsHrDzBsNRC27APmH9mmZcpcg66Mb0exH9V5/lLBWdQW+fcY9GpvXv0",
|
||||
note: "",
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "eba1687cab6a3101bfc68fd70f214aa4cc059e9ec1b79fdb9ad0a0a4e259829f",
|
||||
sec2: "dff20d262bef9dfd94666548f556393085e6ea421c8af86e9d333fa8747e94b3",
|
||||
shared: "decde9938ffcb14fa7ff300105eb1bf239469af9baf376e69755b9070ae48c47",
|
||||
salt: "2180b52ae645fcf9f5080d81b1f0b5d6f2cd77ff3c986882bb549158462f3407",
|
||||
plaintext: "( ͡° ͜ʖ ͡°)",
|
||||
ciphertext: "AiGAtSrmRfz59QgNgbHwtdbyzXf/PJhogrtUkVhGLzQHiR8Hljs6Nl/XsNDAmCz6U1Z3NUGhbCtczc3wXXxDzFkjjMimxsf/74OEzu7LphUadM9iSWvVKPrNXY7lTD0B2muz",
|
||||
note: "",
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
|
||||
sec2: "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
|
||||
shared: "c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
salt: "e4cd5f7ce4eea024bc71b17ad456a986a74ac426c2c62b0a15eb5c5c8f888b68",
|
||||
plaintext: "مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،",
|
||||
ciphertext: "AuTNX3zk7qAkvHGxetRWqYanSsQmwsYrChXrXFyPiItohfde4vHVRHUupr+Glh9JW4f9EY+w795hvRZbixs0EQgDZ7zwLlymVQI3NNvMqvemQzHUA1I5+9gSu8XSMwX9gDCUAjUJtntCkRt9+tjdy2Wa2ZrDYqCvgirvzbJTIC69Ve3YbKuiTQCKtVi0PA5ZLqVmnkHPIqfPqDOGj/a3dvJVzGSgeijcIpjuEgFF54uirrWvIWmTBDeTA+tlQzJHpB2wQnUndd2gLDb8+eKFUZPBifshD3WmgWxv8wRv6k3DeWuWEZQ70Z+YDpgpeOzuzHj0MDBwMAlY8Qq86Rx6pxY76PLDDfHh3rE2CHJEKl2MhDj7pGXao2o633vSRd9ueG8W",
|
||||
note: "",
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
|
||||
sec2: "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
|
||||
shared: "c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
salt: "38d1ca0abef9e5f564e89761a86cee04574b6825d3ef2063b10ad75899e4b023",
|
||||
plaintext: "الكل في المجمو عة (5)",
|
||||
ciphertext: "AjjRygq++eX1ZOiXYahs7gRXS2gl0+8gY7EK11iZ5LAjTHmhdBC3meTY4A7Lv8s8B86MnmlUBJ8ebzwxFQzDyVCcdSbWFaKe0gigEBdXew7TjrjH8BCpAbtYjoa4YHa8GNjj7zH314ApVnwoByHdLHLB9Vr6VdzkxcJgA6oL4MAsRLg=",
|
||||
note: "",
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
|
||||
sec2: "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
|
||||
shared: "c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
salt: "4f1a31909f3483a9e69c8549a55bbc9af25fa5bbecf7bd32d9896f83ef2e12e0",
|
||||
plaintext: "𝖑𝖆𝖟𝖞 社會科學院語學研究所",
|
||||
ciphertext: "Ak8aMZCfNIOp5pyFSaVbvJryX6W77Pe9MtmJb4PvLhLg/25Q5uBC88jl5ghtEREXX6o4QijPzM0uwmkeQ54/6aIqUyzGNVdryWKZ0mee2lmVVWhU+26X6XGFQ5DGRn+1v0POsFUCZ/REh35+beBNHnyvjxD/rbrMfhP2Blc8X5m8Xvk=",
|
||||
note: "",
|
||||
},
|
||||
ValidSec {
|
||||
sec1: "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
|
||||
sec2: "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
|
||||
shared: "c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
salt: "a3e219242d85465e70adcd640b564b3feff57d2ef8745d5e7a0663b2dccceb54",
|
||||
plaintext: "🙈 🙉 🙊 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗",
|
||||
ciphertext: "AqPiGSQthUZecK3NZAtWSz/v9X0u+HRdXnoGY7LczOtU9bUC2ji2A2udRI2VCEQZ7IAmYRRgxodBtd5Yi/5htCUczf1jLHxIt9AhVAZLKuRgbWOuEMq5RBybkxPsSeAkxzXVOlWHZ1Febq5ogkjqY/6Xj8CwwmaZxfbx+d1BKKO3Wa+IFuXwuVAZa1Xo+fan+skyf+2R5QSj10QGAnGO7odAu/iZ9A28eMoSNeXsdxqy1+PRt5Zk4i019xmf7C4PDGSzgFZSvQ2EzusJN5WcsnRFmF1L5rXpX1AYo8HusOpWcGf9PjmFbO+8spUkX1W/T21GRm4o7dro1Y6ycgGOA9BsiQ==",
|
||||
note: "emoji and lang 7"
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ValidPub {
|
||||
sec1: &'static str,
|
||||
pub2: &'static str,
|
||||
shared: &'static str,
|
||||
salt: &'static str,
|
||||
plaintext: &'static str,
|
||||
ciphertext: &'static str,
|
||||
note: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ValidPubParsed {
|
||||
sec1: SecretKey,
|
||||
pub2: XOnlyPublicKey,
|
||||
shared: [u8; 32],
|
||||
salt: [u8; 32],
|
||||
plaintext: &'static str,
|
||||
ciphertext: &'static str,
|
||||
note: &'static str,
|
||||
}
|
||||
|
||||
impl ValidPub {
|
||||
fn parsed(self) -> ValidPubParsed {
|
||||
let sec1hex = hex::decode(self.sec1).unwrap();
|
||||
let sec1 = SecretKey::from_slice(&sec1hex).unwrap();
|
||||
let pub2hex = hex::decode(self.pub2).unwrap();
|
||||
let pub2 = XOnlyPublicKey::from_slice(&pub2hex).unwrap();
|
||||
ValidPubParsed {
|
||||
sec1,
|
||||
pub2,
|
||||
shared: hex::decode(self.shared).unwrap().try_into().unwrap(),
|
||||
salt: hex::decode(self.salt).unwrap().try_into().unwrap(),
|
||||
plaintext: self.plaintext,
|
||||
ciphertext: self.ciphertext,
|
||||
note: self.note,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_valid_pub_test_vectors() {
|
||||
for vec in &VALID_PUB {
|
||||
let vector = vec.clone().parsed();
|
||||
|
||||
// Test conversation key
|
||||
let conversation_key = get_conversation_key(vector.sec1, vector.pub2);
|
||||
assert_eq!(
|
||||
conversation_key, vector.shared,
|
||||
"Conversation key failure on {}",
|
||||
vector.note
|
||||
);
|
||||
|
||||
// Test encryption with an overridden salt
|
||||
let ciphertext =
|
||||
encrypt_inner(&conversation_key, &vector.plaintext, Some(&vector.salt)).unwrap();
|
||||
assert_eq!(
|
||||
ciphertext, vector.ciphertext,
|
||||
"Encryption does not match on {}",
|
||||
vector.note
|
||||
);
|
||||
|
||||
// Test decryption
|
||||
let plaintext = decrypt(&conversation_key, &vector.ciphertext).unwrap();
|
||||
assert_eq!(
|
||||
plaintext, vector.plaintext,
|
||||
"Decryption does not match on {}",
|
||||
vector.note
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const VALID_PUB: [ValidPub; 3] = [
|
||||
ValidPub {
|
||||
sec1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139",
|
||||
pub2: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
shared: "7a1ccf5ce5a08e380f590de0c02776623b85a61ae67cfb6a017317e505b7cb51",
|
||||
salt: "a000000000000000000000000000000000000000000000000000000000000001",
|
||||
plaintext: "⁰⁴⁵₀₁₂",
|
||||
ciphertext: "AqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2+xmGnjIMPMqqJGmjdYAYZUDUyEEUO3/evHUaO40LePeR91VlMVZ7I+nKJPkaUiKZ3cQiQnA86Uwti2IxepmzOFN",
|
||||
note: "sec1 = n-2, pub2: random, 0x02"
|
||||
},
|
||||
ValidPub {
|
||||
sec1: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
pub2: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdeb",
|
||||
shared: "aa971537d741089885a0b48f2730a125e15b36033d089d4537a4e1204e76b39e",
|
||||
salt: "b000000000000000000000000000000000000000000000000000000000000002",
|
||||
plaintext: "A Peer-to-Peer Electronic Cash System",
|
||||
ciphertext: "ArAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACyuqG6RycuPyDPtwxzTcuMQu+is3N5XuWTlvCjligVaVBRydexaylXbsX592MEd3/Jt13BNL/GlpYpGDvLS4Tt/+2s9FX/16e/RDc+czdwXglc4DdSHiq+O06BvvXYfEQOPw=",
|
||||
note: "sec1 = 2, pub2: "
|
||||
},
|
||||
ValidPub {
|
||||
sec1: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
pub2: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
shared: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
salt: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
plaintext: "A purely peer-to-peer version of electronic cash would allow online payments to be sent directly from one party to another without going through a financial institution. Digital signatures provide part of the solution, but the main benefits are lost if a trusted third party is still required to prevent double-spending.",
|
||||
ciphertext: "Anm+Zn753LusVaBilc6HCwcCm/zbLc4o2VnygVsW+BeYb9wHyKevpe7ohJ6OkpceFcb0pySY8TLGwT7Q3zWNDKxc9blXanxKborEXkQH8xNaB2ViJfgxpkutbwbYd0Grix34xzaZBASufdsNm7R768t51tI6sdS0nms6kWLVJpEGu6Ke4Bldv4StJtWBLaTcgsgN+4WxDbBhC/nhwjEQiBBbbmUrPWjaVZXjl8dzzPrYtkSoeBNJs/UNvDwym4+qrmhv4ASTvVflpZgLlSe4seqeu6dWoRqn8uRHZQnPs+XhqwbdCHpeKGB3AfGBykZY0RIr0tjarWdXNasGbIhGM3GiLasioJeabAZw0plCevDkKpZYDaNfMJdzqFVJ8UXRIpvDpQad0SOm8lLum/aBzUpLqTjr3RvSlhYdbuODpd9pR5K60k4L2N8nrPtBv08wlilQg2ymwQgKVE6ipxIzzKMetn8+f0nQ9bHjWFJqxetSuMzzArTUQl9c4q/DwZmCBhI2",
|
||||
note: "sec1 == pub2 == salt"
|
||||
}
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct InvalidPub {
|
||||
sec1: &'static str,
|
||||
pub2: &'static str,
|
||||
shared: &'static str,
|
||||
salt: &'static str,
|
||||
plaintext: &'static str,
|
||||
ciphertext: &'static str,
|
||||
note: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct InvalidPubParsed {
|
||||
sec1: SecretKey,
|
||||
pub2: XOnlyPublicKey,
|
||||
shared: [u8; 32],
|
||||
//salt: [u8; 32],
|
||||
//plaintext: &'static str,
|
||||
ciphertext: &'static str,
|
||||
note: &'static str,
|
||||
}
|
||||
|
||||
impl InvalidPub {
|
||||
fn parsed(self) -> InvalidPubParsed {
|
||||
let sec1hex = hex::decode(self.sec1).unwrap();
|
||||
let sec1 = SecretKey::from_slice(&sec1hex).unwrap();
|
||||
let pub2hex = hex::decode(self.pub2).unwrap();
|
||||
let pub2 = XOnlyPublicKey::from_slice(&pub2hex).unwrap();
|
||||
InvalidPubParsed {
|
||||
sec1,
|
||||
pub2,
|
||||
shared: hex::decode(self.shared).unwrap().try_into().unwrap(),
|
||||
//salt: hex::decode(self.salt).unwrap().try_into().unwrap(),
|
||||
//plaintext: self.plaintext,
|
||||
ciphertext: self.ciphertext,
|
||||
note: self.note,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_invalid_pub_test_vectors() {
|
||||
for vec in &INVALID_PUB {
|
||||
let vector = vec.clone().parsed();
|
||||
|
||||
// Test conversation key
|
||||
let conversation_key = get_conversation_key(vector.sec1, vector.pub2);
|
||||
assert_eq!(
|
||||
conversation_key, vector.shared,
|
||||
"Conversation key failure on {}",
|
||||
vector.note
|
||||
);
|
||||
|
||||
// Test decryption fails
|
||||
assert!(
|
||||
decrypt(&conversation_key, &vector.ciphertext).is_err(),
|
||||
"Should not have decrypted: {}",
|
||||
vector.note
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const INVALID_PUB: [InvalidPub; 8] = [
|
||||
InvalidPub {
|
||||
sec1: "2573d1e9b9ac5de5d570f652cbb9e8d4f235e3d3d334181448e87c417f374e83",
|
||||
pub2: "8348c2d35549098706e5bab7966d9a9c72fbf6554e918f41c2b6cb275f79ec13",
|
||||
shared: "8673ec68393a997bfad7eab8661461daf8b3931b7e885d78312a3fb7fe17f41a",
|
||||
salt: "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db",
|
||||
plaintext: "n o s t r",
|
||||
ciphertext: "##Atqupco0WyaOW2IGDKcshwxI9xO8HgD/P8Ddt46CbxDbOsrsqIEybscEwg5rnI/Cx03mDSmeweOLKD7dw5BDZQDxXSlCwX1LIcTJEZaJPTz98Ftu0zSE0d93ED7OtdlvNeZx",
|
||||
note: "unknown encryption version"
|
||||
},
|
||||
InvalidPub {
|
||||
sec1: "11063318c5cb3cd9cafcced42b4db5ea02ec976ed995962d2bc1fa1e9b52e29f",
|
||||
pub2: "5c49873b6eac3dd363325250cc55d5dd4c7ce9a885134580405736d83506bb74",
|
||||
shared: "e2aad10de00913088e5cb0f73fa526a6a17e95763cc5b2a127022f5ea5a73445",
|
||||
salt: "ad408d4be8616dc84bb0bf046454a2a102edac937c35209c43cd7964c5feb781",
|
||||
plaintext: "⚠️",
|
||||
ciphertext: "AK1AjUvoYW3IS7C/BGRUoqEC7ayTfDUgnEPNeWTF/reBA4fZmoHrtrz5I5pCHuwWZ22qqL/Xt1VidEZGMLds0yaJ5VwUbeEifEJlPICOFt1ssZJxCUf43HvRwCVTFskbhSMh",
|
||||
note: "unknown encryption version: 0"
|
||||
},
|
||||
InvalidPub {
|
||||
sec1: "2573d1e9b9ac5de5d570f652cbb9e8d4f235e3d3d334181448e87c417f374e83",
|
||||
pub2: "8348c2d35549098706e5bab7966d9a9c72fbf6554e918f41c2b6cb275f79ec13",
|
||||
shared: "8673ec68393a997bfad7eab8661461daf8b3931b7e885d78312a3fb7fe17f41a",
|
||||
salt: "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db",
|
||||
plaintext: "n o s t r",
|
||||
ciphertext: "Atqupco0WyaOW2IGDKcshwxI9xO8HgD/P8Ddt46CbxDbOsrsqIEybscEwg5rnI/Cx03mDSmeweOLKD,7dw5BDZQDxXSlCwX1LIcTJEZaJPTz98Ftu0zSE0d93ED7OtdlvNeZx",
|
||||
note: "invalid base64"
|
||||
},
|
||||
InvalidPub {
|
||||
sec1: "5a2f39347fed3883c9fe05868a8f6156a292c45f606bc610495fcc020ed158f7",
|
||||
pub2: "775bbfeba58d07f9d1fbb862e306ac780f39e5418043dadb547c7b5900245e71",
|
||||
shared: "2e70c0a1cde884b88392458ca86148d859b273a5695ede5bbe41f731d7d88ffd",
|
||||
salt: "09ff97750b084012e15ecb84614ce88180d7b8ec0d468508a86b6d70c0361a25",
|
||||
plaintext: "¯\\_(ツ)_/¯",
|
||||
ciphertext: "Agn/l3ULCEAS4V7LhGFM6IGA17jsDUaFCKhrbXDANholdUejFZPARM22IvOqp1U/UmFSkeSyTBYbbwy5ykmi+mKiEcWL+nVmTOf28MMiC+rTpZys/8p1hqQFpn+XWZRPrVay",
|
||||
note: "invalid MAC"
|
||||
},
|
||||
InvalidPub {
|
||||
sec1: "067eda13c4a36090ad28a7a183e9df611186ca01f63cb30fcdfa615ebfd6fb6d",
|
||||
pub2: "32c1ece2c5dd2160ad03b243f50eff12db605b86ac92da47eacc78144bf0cdd3",
|
||||
shared: "a808915e31afc5b853d654d2519632dac7298ee2ecddc11695b8eba925935c2a",
|
||||
salt: "65b14b0b949aaa7d52c417eb753b390e8ad6d84b23af4bec6d9bfa3e03a08af4",
|
||||
plaintext: "🥎",
|
||||
ciphertext: "AmWxSwuUmqp9UsQX63U7OQ6K1thLI69L7G2b+j4DoIr0U0P/M1/oKm95z8qz6Kg0zQawLzwk3DskvWA2drXP4zK+tzHpKvWq0KOdx5MdypboSQsP4NXfhh2KoUffjkyIOiMA",
|
||||
note: "invalid MAC"
|
||||
},
|
||||
InvalidPub {
|
||||
sec1: "3e7be560fb9f8c965c48953dbd00411d48577e200cf00d7cc427e49d0e8d9c01",
|
||||
pub2: "e539e5fee58a337307e2a937ee9a7561b45876fb5df405c5e7be3ee564b239cc",
|
||||
shared: "6ee3efc4255e3b8270e5dd3f7dc7f6b60878cda6218c8df34a3261cd48744931",
|
||||
salt: "7ab65dbb8bbc2b8e35cafb5745314e1f050325a864d11d0475ef75b3660d91c1",
|
||||
plaintext: "elliptic-curve cryptography",
|
||||
ciphertext: "Anq2XbuLvCuONcr7V0UxTh8FAyWoZNEdBHXvdbNmDZHBu7F9m36yBd58mVUBB5ktBTOJREDaQT1KAyPmZidP+IRea1lNw5YAEK7+pbnpfCw8CD0i2n8Pf2IDWlKDhLiVvatw",
|
||||
note: "invalid padding"
|
||||
},
|
||||
InvalidPub {
|
||||
sec1: "c22e1d4de967aa39dc143354d8f596cec1d7c912c3140831fff2976ce3e387c1",
|
||||
pub2: "4e405be192677a2da95ffc733950777213bf880cf7c3b084eeb6f3fe5bd43705",
|
||||
shared: "1675a773dbf6fbcbef6a293004a4504b6c856978be738b10584b0269d437c8d1",
|
||||
salt: "7d4283e3b54c885d6afee881f48e62f0a3f5d7a9e1cb71ccab594a7882c39330",
|
||||
plaintext: "Peer-to-Peer",
|
||||
ciphertext: "An1Cg+O1TIhdav7ogfSOYvCj9dep4ctxzKtZSniCw5MwhT0hvSnF9Xjp9Lml792qtNbmAVvR6laukTe9eYEjeWPpZFxtkVpYTbbL9wDKFeplDMKsUKVa+roSeSvv0ela9seDVl2Sfso=",
|
||||
note: "invalid padding"
|
||||
},
|
||||
InvalidPub {
|
||||
sec1: "be1edab14c5912e5c59084f197f0945242e969c363096cccb59af8898815096f",
|
||||
pub2: "9eaf0775d971e4941c97189232542e1daefcdb7dddafc39bcea2520217710ba2",
|
||||
shared: "1741a44c052d5ae363c7845441f73d2b6c28d9bfb3006190012bba12eb4c774b",
|
||||
salt: "6f9fd72667c273acd23ca6653711a708434474dd9eb15c3edb01ce9a95743e9b",
|
||||
plaintext: "censorship-resistant and global social network",
|
||||
ciphertext: "Am+f1yZnwnOs0jymZTcRpwhDRHTdnrFcPtsBzpqVdD6bL9HUMo3Mjkz4bjQo/FJF2LWHmaCr9Byc3hU9D7we+EkNBWenBHasT1G52fZk9r3NKeOC1hLezNwBLr7XXiULh+NbMBDtJh9/aQh1uZ9EpAfeISOzbZXwYwf0P5M85g9XER8hZ2fgJDLb4qMOuQRG6CrPezhr357nS3UHwPC2qHo3uKACxhE+2td+965yDcvMTx4KYTQg1zNhd7PA5v/WPnWeq2B623yLxlevUuo/OvXplFho3QVy7s5QZVop6qV2g2/l/SIsvD0HIcv3V35sywOCBR0K4VHgduFqkx/LEF3NGgAbjONXQHX8ZKushsEeR4TxlFoRSovAyYjhWolz+Ok3KJL2Ertds3H+M/Bdl2WnZGT0IbjZjn3DS+b1Ke0R0X4Onww2ZG3+7o6ncIwTc+lh1O7YQn00V0HJ+EIp03heKV2zWdVSC615By/+Yt9KAiV56n5+02GAuNqA",
|
||||
note: "invalid padding"
|
||||
}
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct InvalidKeys {
|
||||
sec1: &'static str,
|
||||
pub2: &'static str,
|
||||
note: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_invalid_keys() {
|
||||
for vector in &INVALID_KEYS {
|
||||
let sec1hex = hex::decode(vector.sec1).unwrap();
|
||||
let sec1 = SecretKey::from_slice(&sec1hex);
|
||||
let pub2hex = hex::decode(vector.pub2).unwrap();
|
||||
let pub2 = XOnlyPublicKey::from_slice(&pub2hex);
|
||||
assert!(
|
||||
sec1.is_err() || pub2.is_err(),
|
||||
"One of the keys should have failed: {}",
|
||||
vector.note
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const INVALID_KEYS: [InvalidKeys; 5] = [
|
||||
InvalidKeys {
|
||||
sec1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
pub2: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||
note: "sec1 higher than curve.n",
|
||||
},
|
||||
InvalidKeys {
|
||||
sec1: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
pub2: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||
note: "sec1 is 0",
|
||||
},
|
||||
InvalidKeys {
|
||||
sec1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139",
|
||||
pub2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
note: "pub2 is invalid, no sqrt, all-ff",
|
||||
},
|
||||
InvalidKeys {
|
||||
sec1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
|
||||
pub2: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||
note: "sec1 == curve.n",
|
||||
},
|
||||
InvalidKeys {
|
||||
sec1: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
pub2: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||
note: "pub2 is invalid, no sqrt",
|
||||
},
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_padding_length() {
|
||||
for (len, pad) in PADDING {
|
||||
assert_eq!(calc_padding(len), pad);
|
||||
}
|
||||
}
|
||||
|
||||
const PADDING: [(usize, usize); 24] = [
|
||||
(16, 32),
|
||||
(32, 32),
|
||||
(33, 64),
|
||||
(37, 64),
|
||||
(45, 64),
|
||||
(49, 64),
|
||||
(64, 64),
|
||||
(65, 96),
|
||||
(100, 128),
|
||||
(111, 128),
|
||||
(200, 224),
|
||||
(250, 256),
|
||||
(320, 320),
|
||||
(383, 384),
|
||||
(384, 384),
|
||||
(400, 448),
|
||||
(500, 512),
|
||||
(512, 512),
|
||||
(515, 640),
|
||||
(700, 768),
|
||||
(800, 896),
|
||||
(900, 1024),
|
||||
(1020, 1024),
|
||||
(74123, 81920),
|
||||
];
|
||||
Reference in New Issue
Block a user