Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ed448 poc #119

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ blake2 = "~0.10"
blake3 = "~1"
chrono = { version = "~0.4", default-features = false, features = ["clock"] }
ed25519-dalek = "~1"
ed448-rust = { git = "https://github.com/jasoncolburne/ed448-rust.git" }
indexmap = "~1"
k256 = "~0.12"
lazy_static = "~1"
Expand Down
7 changes: 2 additions & 5 deletions src/core/cigar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ impl Default for Cigar {
}

fn validate_code(code: &str) -> Result<()> {
const CODES: &[&str] = &[
matter::Codex::Ed25519_Sig,
matter::Codex::ECDSA_256k1_Sig,
// matter::Codex::Ed448_Sig,
];
const CODES: &[&str] =
&[matter::Codex::Ed25519_Sig, matter::Codex::ECDSA_256k1_Sig, matter::Codex::Ed448_Sig];

if !CODES.contains(&code) {
return err!(Error::UnexpectedCode(code.to_string()));
Expand Down
20 changes: 10 additions & 10 deletions src/core/matter/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ pub(crate) fn sizage(s: &str) -> Result<Sizage> {
"H" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
"I" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
"J" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
"K" => Sizage { hs: 1, ss: 0, fs: 76, ls: 0 },
"L" => Sizage { hs: 1, ss: 0, fs: 76, ls: 0 },
"M" => Sizage { hs: 1, ss: 0, fs: 4, ls: 0 },
"N" => Sizage { hs: 1, ss: 0, fs: 12, ls: 0 },
"O" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
Expand All @@ -43,10 +41,12 @@ pub(crate) fn sizage(s: &str) -> Result<Sizage> {
"1AAB" => Sizage { hs: 4, ss: 0, fs: 48, ls: 0 },
"1AAC" => Sizage { hs: 4, ss: 0, fs: 80, ls: 0 },
"1AAD" => Sizage { hs: 4, ss: 0, fs: 80, ls: 0 },
"1AAE" => Sizage { hs: 4, ss: 0, fs: 56, ls: 0 },
"1AAE" => Sizage { hs: 4, ss: 0, fs: 156, ls: 0 },
"1AAF" => Sizage { hs: 4, ss: 0, fs: 8, ls: 0 },
"1AAG" => Sizage { hs: 4, ss: 0, fs: 36, ls: 0 },
"1AAH" => Sizage { hs: 4, ss: 0, fs: 100, ls: 0 },
"1AAI" => Sizage { hs: 4, ss: 0, fs: 80, ls: 0 },
"1AAJ" => Sizage { hs: 4, ss: 0, fs: 80, ls: 0 },
"2AAA" => Sizage { hs: 4, ss: 0, fs: 8, ls: 1 },
"3AAA" => Sizage { hs: 4, ss: 0, fs: 8, ls: 2 },
"4A" => Sizage { hs: 2, ss: 2, fs: 0, ls: 0 },
Expand Down Expand Up @@ -110,8 +110,6 @@ pub mod Codex {
pub const SHA3_256: &str = "H"; // SHA3 256 bit digest self-addressing derivation.
pub const SHA2_256: &str = "I"; // SHA2 256 bit digest self-addressing derivation.
pub const ECDSA_256k1_Seed: &str = "J"; // ECDSA secp256k1 256 bit random Seed for private key
pub const Ed448_Seed: &str = "K"; // Ed448 448 bit random Seed for private key
pub const X448: &str = "L"; // X448 public encryption key, converted from Ed448
pub const Short: &str = "M"; // Short 2 byte b2 number
pub const Big: &str = "N"; // Big 8 byte b2 number
pub const X25519_Private: &str = "O"; // X25519 private decryption key converted from Ed25519
Expand All @@ -132,6 +130,8 @@ pub mod Codex {
pub const Tern: &str = "1AAF"; // 3 byte b2 number or 4 char B64 str.
pub const DateTime: &str = "1AAG"; // Base64 custom encoded 32 char ISO-8601 DateTime
pub const X25519_Cipher_Salt: &str = "1AAH"; // X25519 100 char b64 Cipher of 24 char qb64 Salt
pub const Ed448_Seed: &str = "1AAI"; // Ed448 448 bit random Seed for private key
pub const X448: &str = "1AAJ"; // X448 public encryption key, converted from Ed448
pub const TBD1: &str = "2AAA"; // Testing purposes only fixed with lead size 1
pub const TBD2: &str = "3AAA"; // Testing purposes only of fixed with lead size 2
pub const StrB64_L0: &str = "4A"; // String Base64 Only Lead Size 0 (4095 * 3 | 4)
Expand Down Expand Up @@ -164,8 +164,6 @@ mod test {
#[case("H", 1, 0, 44, 0)]
#[case("I", 1, 0, 44, 0)]
#[case("J", 1, 0, 44, 0)]
#[case("K", 1, 0, 76, 0)]
#[case("L", 1, 0, 76, 0)]
#[case("M", 1, 0, 4, 0)]
#[case("N", 1, 0, 12, 0)]
#[case("O", 1, 0, 44, 0)]
Expand All @@ -182,10 +180,12 @@ mod test {
#[case("1AAB", 4, 0, 48, 0)]
#[case("1AAC", 4, 0, 80, 0)]
#[case("1AAD", 4, 0, 80, 0)]
#[case("1AAE", 4, 0, 56, 0)]
#[case("1AAE", 4, 0, 156, 0)]
#[case("1AAF", 4, 0, 8, 0)]
#[case("1AAG", 4, 0, 36, 0)]
#[case("1AAH", 4, 0, 100, 0)]
#[case("1AAI", 4, 0, 80, 0)]
#[case("1AAJ", 4, 0, 80, 0)]
#[case("2AAA", 4, 0, 8, 1)]
#[case("3AAA", 4, 0, 8, 2)]
#[case("4A", 2, 2, 0, 0)]
Expand Down Expand Up @@ -292,8 +292,6 @@ mod test {
#[case(Codex::SHA3_256, "H")]
#[case(Codex::SHA2_256, "I")]
#[case(Codex::ECDSA_256k1_Seed, "J")]
#[case(Codex::Ed448_Seed, "K")]
#[case(Codex::X448, "L")]
#[case(Codex::Short, "M")]
#[case(Codex::Big, "N")]
#[case(Codex::X25519_Private, "O")]
Expand All @@ -314,6 +312,8 @@ mod test {
#[case(Codex::Tern, "1AAF")]
#[case(Codex::DateTime, "1AAG")]
#[case(Codex::X25519_Cipher_Salt, "1AAH")]
#[case(Codex::Ed448_Seed, "1AAI")]
#[case(Codex::X448, "1AAJ")]
#[case(Codex::TBD1, "2AAA")]
#[case(Codex::TBD2, "3AAA")]
#[case(Codex::StrB64_L0, "4A")]
Expand Down
29 changes: 24 additions & 5 deletions src/core/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,8 @@ impl Default for Signer {
}

fn validate_code(code: &str) -> Result<()> {
const CODES: &[&str] = &[
matter::Codex::Ed25519_Seed,
matter::Codex::ECDSA_256k1_Seed,
// matter::Codex::Ed448_Seed,
];
const CODES: &[&str] =
&[matter::Codex::Ed25519_Seed, matter::Codex::ECDSA_256k1_Seed, matter::Codex::Ed448_Seed];

if !CODES.contains(&code) {
return err!(Error::UnexpectedCode(code.to_string()));
Expand All @@ -70,11 +67,13 @@ fn derive_verfer(code: &str, private_key: &[u8], transferable: bool) -> Result<V
true => match code {
matter::Codex::Ed25519_Seed => matter::Codex::Ed25519,
matter::Codex::ECDSA_256k1_Seed => matter::Codex::ECDSA_256k1,
matter::Codex::Ed448_Seed => matter::Codex::Ed448,
_ => return err!(Error::UnexpectedCode(code.to_string())),
},
false => match code {
matter::Codex::Ed25519_Seed => matter::Codex::Ed25519N,
matter::Codex::ECDSA_256k1_Seed => matter::Codex::ECDSA_256k1N,
matter::Codex::Ed448_Seed => matter::Codex::Ed448N,
_ => return err!(Error::UnexpectedCode(code.to_string())),
},
};
Expand Down Expand Up @@ -137,6 +136,7 @@ impl Signer {
let code = match self.code().as_str() {
matter::Codex::Ed25519_Seed => matter::Codex::Ed25519_Sig,
matter::Codex::ECDSA_256k1_Seed => matter::Codex::ECDSA_256k1_Sig,
matter::Codex::Ed448_Seed => matter::Codex::Ed448_Sig,
_ => return err!(Error::UnexpectedCode(self.code())),
};

Expand All @@ -157,12 +157,14 @@ impl Signer {
match self.code().as_str() {
matter::Codex::Ed25519_Seed => indexer::Codex::Ed25519_Crt,
matter::Codex::ECDSA_256k1_Seed => indexer::Codex::ECDSA_256k1_Crt,
matter::Codex::Ed448_Seed => indexer::Codex::Ed448_Crt,
_ => return err!(Error::UnexpectedCode(self.code())),
}
} else {
match self.code().as_str() {
matter::Codex::Ed25519_Seed => indexer::Codex::Ed25519_Big_Crt,
matter::Codex::ECDSA_256k1_Seed => indexer::Codex::ECDSA_256k1_Big_Crt,
matter::Codex::Ed448_Seed => indexer::Codex::Ed448_Big_Crt,
_ => return err!(Error::UnexpectedCode(self.code())),
}
};
Expand All @@ -175,12 +177,14 @@ impl Signer {
match self.code().as_str() {
matter::Codex::Ed25519_Seed => indexer::Codex::Ed25519,
matter::Codex::ECDSA_256k1_Seed => indexer::Codex::ECDSA_256k1,
matter::Codex::Ed448_Seed => indexer::Codex::Ed448,
_ => return err!(Error::UnexpectedCode(self.code())),
}
} else {
match self.code().as_str() {
matter::Codex::Ed25519_Seed => indexer::Codex::Ed25519_Big,
matter::Codex::ECDSA_256k1_Seed => indexer::Codex::ECDSA_256k1_Big,
matter::Codex::Ed448_Seed => indexer::Codex::Ed448_Big,
_ => return err!(Error::UnexpectedCode(self.code())),
}
};
Expand Down Expand Up @@ -325,6 +329,21 @@ mod test {
assert!(!signer.verfer().verify(&cigar.raw(), bad_ser).unwrap());
}

#[test]
fn sign_ed448_unindexed() {
let ser = b"abcdefghijklmnopqrstuvwxyz0123456789";
let bad_ser = b"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";

let signer =
Signer::new(Some(true), Some(matter::Codex::Ed448_Seed), None, None, None, None)
.unwrap();

let cigar = signer.sign_unindexed(ser).unwrap();
assert_eq!(cigar.code(), matter::Codex::Ed448_Sig);
assert!(signer.verfer().verify(&cigar.raw(), ser).unwrap());
assert!(!signer.verfer().verify(&cigar.raw(), bad_ser).unwrap());
}

#[rstest]
#[case(false, 0, None, 0, indexer::Codex::Ed25519)]
#[case(false, 1, None, 1, indexer::Codex::Ed25519)]
Expand Down
4 changes: 2 additions & 2 deletions src/core/verfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ fn validate_code(code: &str) -> Result<()> {
matter::Codex::Ed25519,
matter::Codex::ECDSA_256k1N,
matter::Codex::ECDSA_256k1,
// matter::Codex::Ed448N,
// matter::Codex::Ed448,
matter::Codex::Ed448N,
matter::Codex::Ed448,
];

if !CODES.contains(&code) {
Expand Down
103 changes: 102 additions & 1 deletion src/crypto/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ pub(crate) fn generate(code: &str) -> Result<Vec<u8>> {
| matter::Codex::ECDSA_256k1N
| matter::Codex::ECDSA_256k1_Seed
| matter::Codex::ECDSA_256k1_Sig => ecdsa_256k1::generate(),
matter::Codex::Ed448
| matter::Codex::Ed448N
| matter::Codex::Ed448_Seed
| matter::Codex::Ed448_Sig => ed448::generate(),
_ => err!(Error::UnexpectedCode(code.to_string())),
}
}
Expand All @@ -25,6 +29,10 @@ pub(crate) fn public_key(code: &str, private_key: &[u8]) -> Result<Vec<u8>> {
| matter::Codex::ECDSA_256k1N
| matter::Codex::ECDSA_256k1_Seed
| matter::Codex::ECDSA_256k1_Sig => ecdsa_256k1::public_key(private_key),
matter::Codex::Ed448
| matter::Codex::Ed448N
| matter::Codex::Ed448_Seed
| matter::Codex::Ed448_Sig => ed448::public_key(private_key),
_ => err!(Error::UnexpectedCode(code.to_string())),
}
}
Expand All @@ -39,6 +47,10 @@ pub(crate) fn sign(code: &str, private_key: &[u8], ser: &[u8]) -> Result<Vec<u8>
| matter::Codex::ECDSA_256k1N
| matter::Codex::ECDSA_256k1_Seed
| matter::Codex::ECDSA_256k1_Sig => ecdsa_256k1::sign(private_key, ser),
matter::Codex::Ed448
| matter::Codex::Ed448N
| matter::Codex::Ed448_Seed
| matter::Codex::Ed448_Sig => ed448::sign(private_key, ser),
_ => err!(Error::UnexpectedCode(code.to_string())),
}
}
Expand All @@ -53,6 +65,10 @@ pub(crate) fn verify(code: &str, public_key: &[u8], sig: &[u8], ser: &[u8]) -> R
| matter::Codex::ECDSA_256k1N
| matter::Codex::ECDSA_256k1_Seed
| matter::Codex::ECDSA_256k1_Sig => ecdsa_256k1::verify(public_key, sig, ser),
matter::Codex::Ed448
| matter::Codex::Ed448N
| matter::Codex::Ed448_Seed
| matter::Codex::Ed448_Sig => ed448::verify(public_key, sig, ser),
_ => err!(Error::UnexpectedCode(code.to_string())),
}
}
Expand Down Expand Up @@ -134,21 +150,106 @@ mod ecdsa_256k1 {
}
}

mod ed448 {
use crate::error::{err, Error, Result};

use ed448_rust::{PrivateKey, PublicKey, KEY_LENGTH};
use rand_core::OsRng;

pub(crate) fn generate() -> Result<Vec<u8>> {
let key = PrivateKey::new(&mut OsRng);
Ok(key.as_bytes().to_vec())
}

pub(crate) fn public_key(private_key: &[u8]) -> Result<Vec<u8>> {
if private_key.len() < KEY_LENGTH {
return err!(Error::Conversion(
"not enough private key material submitted to generate public key".to_string()
));
}

let mut key = [0u8; KEY_LENGTH];
key.copy_from_slice(private_key);
Ok(PublicKey::from(&PrivateKey::from(key)).as_bytes().to_vec())
}

pub(crate) fn sign(private_key: &[u8], ser: &[u8]) -> Result<Vec<u8>> {
if private_key.len() < KEY_LENGTH {
return err!(Error::Conversion(
"not enough private key material submitted to generate public key".to_string()
));
}

let mut key = [0u8; KEY_LENGTH];
key.copy_from_slice(private_key);

let result = match PrivateKey::from(key).sign(ser, None) {
Ok(s) => s,
Err(_) => return err!(Error::Derivation("could not sign data".to_string())),
};

Ok(result.to_vec())
}

pub(crate) fn verify(public_key: &[u8], sig: &[u8], ser: &[u8]) -> Result<bool> {
if public_key.len() < KEY_LENGTH {
return err!(Error::Conversion(
"not enough private key material submitted to generate public key".to_string()
));
}

let mut key = [0u8; KEY_LENGTH];
key.copy_from_slice(public_key);

match PublicKey::try_from(&key) {
Ok(public_key) => match public_key.verify(ser, sig, None) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
},
Err(_) => Ok(false),
}
}
}

#[cfg(test)]
mod test {
use crate::core::matter::tables as matter;
use crate::crypto::sign;
use hex_literal::hex;
use rstest::rstest;

#[rstest]
fn end_to_end(#[values(matter::Codex::Ed25519, matter::Codex::ECDSA_256k1)] code: &str) {
fn end_to_end(
#[values(matter::Codex::Ed25519, matter::Codex::ECDSA_256k1, matter::Codex::Ed448)]
code: &str,
) {
let ser = b"abcdefghijklmnopqrstuvwxyz";
let private_key = sign::generate(code).unwrap();
let signature = sign::sign(code, &private_key, ser).unwrap();
let public_key = sign::public_key(code, &private_key).unwrap();
assert!(sign::verify(code, &public_key, &signature, ser).unwrap());
}

#[test]
// rfc 8032
fn ed448() {
let code = matter::Codex::Ed448;

assert_eq!(sign::generate(code).unwrap().len(), 57);

let private_key = hex!("6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6"
"e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b");
let public_key = hex!("5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80"
"e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180");
let message = b"";
let signature = hex!("533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980"
"ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600");

assert_eq!(sign::public_key(code, &private_key).unwrap(), public_key);
assert_eq!(sign::sign(code, &private_key, message).unwrap(), signature);
assert!(sign::verify(code, &public_key, &signature, message).unwrap());
}

#[test]
fn unhappy_paths() {
let code = matter::Codex::SHA3_256;
Expand Down