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

CRYSTALS Dilithium #149

Draft
wants to merge 11 commits 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
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cesride"
version = "0.6.1"
version = "0.7.0"
edition = "2021"
description = "Cryptographic primitives for use with Composable Event Streaming Representation (CESR)"
license = "Apache-2.0"
Expand All @@ -9,14 +9,15 @@ keywords = ["cesr", "keri", "acdc"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "~1"
anyhow = { version = "~1", features = ["backtrace"] }
argon2 = "~0.5"
base64 = "~0.21"
blake2 = "~0.10"
blake3 = "~1"
chrono = { version = "~0.4", default-features = false, features = ["clock"] }
crystals-dilithium = "~1"
ed25519-dalek = { version = "2.0.0-rc.2", features = ["rand_core"] }
indexmap = "~1"
indexmap = "~2"
k256 = "~0.13"
lazy_static = "~1"
num-rational = "~0.4"
Expand All @@ -32,4 +33,4 @@ zeroize = { version = "~1", features = ["derive"] }
[dev-dependencies]
hex-literal = "0.4.0"
hex = "0.4.3"
rstest = "0.17.0"
rstest = "0.18.2"
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ and terminology was carried along with the code. The basics:

- `Diger` - a primitive that represents a **digest**. It has the ability to verify that an input hashes to its raw value.
- `Verfer` - a primitive that represents a **public key**. It has the ability to verify signatures on data.
- `Signer` - a primitive that represents a **private key**. It has the ability to create `Sigers` and `Cigars` (signatures).
- `Siger` - an **_indexed_ signature**. This is used within KERI when there are multiple current keys associated with an identifier.
- `Signer` - a primitive that represents a **private key** seed (which in some cases is the private key itself). It has the ability to create `Sigers` and `Cigars` (signatures).
- `Siger` - an **_indexed_ signature**. This is used within KERI when there are multiple keys associated with an identifier.
- `Cigar` - an **_unindexed_ signature**.
- `Salter` - a primitive that represents a **seed**. It has the ability to generate new `Signers`.
- `Salter` - a primitive for creating entropy. It has the ability to generate new `Signers`.

Each primitive will have methods attached to it that permit one to generate and parse the qualified
base2 or base64 representation. Common methods you'll find:
Expand Down Expand Up @@ -224,14 +224,16 @@ Blake3 is recommended for most applications since it outperforms the other algor
- Ed25519 ([ed25519-dalek](https://docs.rs/ed25519-dalek))
- Secp256k1 ([k256](https://docs.rs/k256))
- Secp256r1 ([p256](https://docs.rs/p256))

We have planned support for Ed448.
- CRYSTALS-Dilithium3/5 ([crystals-dilithium](https://docs.rs/crystals-dilithium))

The ECDSA curves (Secp256k1 and Secp256r1) use randomized signatures. Ed25519 is always deterministic.
This means that if you need to avoid correlation and want to use Ed25519, you'll need to salt your data
for every use case that you do not want correlated. ACDC, for example, takes this into account, allowing for
configurable use of Ed25519 by injecting salty nonces in the data to be signed where privacy is a concern.

The `crystals-dilithium` implementation was chosen over `pqcrypto` due to superior documentation and
the fact that it was stated to be a direct port of the C code.

## Community

### Bi-weekly Meeting
Expand Down
2 changes: 2 additions & 0 deletions src/core/cigar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ fn validate_code(code: &str) -> Result<()> {
matter::Codex::Ed25519_Sig,
matter::Codex::ECDSA_256k1_Sig,
matter::Codex::ECDSA_256r1_Sig,
matter::Codex::CRYSTALS_Dilithium3_Sig,
matter::Codex::CRYSTALS_Dilithium5_Sig,
// matter::Codex::Ed448_Sig,
];

Expand Down
45 changes: 44 additions & 1 deletion src/core/indexer/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ pub mod Codex {
pub const ECDSA_256k1_Crt: &str = "D"; // ECDSA secp256k1 sig appears in current list.
pub const ECDSA_256r1: &str = "E"; // ECDSA secp256r1 sig appears same in both lists if any.
pub const ECDSA_256r1_Crt: &str = "F"; // ECDSA secp256r1 sig appears in current list.
pub const CRYSTALS_Dilithium3_Big: &str = "G"; // CRYSTALS Dilithium sig appears in both lists.
pub const CRYSTALS_Dilithium3_Big_Crt: &str = "H"; // CRYSTALS Dilithium sig appears in current list only.
pub const CRYSTALS_Dilithium5_Big: &str = "I"; // CRYSTALS Dilithium sig appears in both lists.
pub const CRYSTALS_Dilithium5_Big_Crt: &str = "J"; // CRYSTALS Dilithium sig appears in current list only.
pub const Ed448: &str = "0A"; // Ed448 signature appears in both lists.
pub const Ed448_Crt: &str = "0B"; // Ed448 signature appears in current list only.
pub const Ed25519_Big: &str = "2A"; // Ed25519 sig appears in both lists.
Expand All @@ -41,6 +45,10 @@ pub mod Codex {
pub const TBD0: &str = "0z"; // Test of Var len label L=N*4 <= 4095 char quadlets includes code
pub const TBD1: &str = "1z"; // Test of index sig lead 1
pub const TBD4: &str = "4z"; // Test of index sig lead 1 big
pub const CRYSTALS_Dilithium3: &str = "5AAA"; // CRYSTALS Dilithium sig appears same in both lists if any.
pub const CRYSTALS_Dilithium3_Crt: &str = "5AAB"; // CRYSTALS Dilithium sig appears in current list.
pub const CRYSTALS_Dilithium5: &str = "5AAC"; // CRYSTALS Dilithium sig appears same in both lists if any.
pub const CRYSTALS_Dilithium5_Crt: &str = "5AAD"; // CRYSTALS Dilithium sig appears in current list.
}

/// SigCodex is all indexed signature derivation codes
Expand All @@ -53,6 +61,10 @@ pub mod SigCodex {
pub const ECDSA_256k1_Crt: &str = "D"; // ECDSA secp256k1 sig appears in current list.
pub const ECDSA_256r1: &str = "E"; // ECDSA secp256r1 sig appears same in both lists if any.
pub const ECDSA_256r1_Crt: &str = "F"; // ECDSA secp256r1 sig appears in current list.
pub const CRYSTALS_Dilithium3_Big: &str = "G"; // CRYSTALS Dilithium sig appears in both lists.
pub const CRYSTALS_Dilithium3_Big_Crt: &str = "H"; // CRYSTALS Dilithium sig appears in current list only.
pub const CRYSTALS_Dilithium5_Big: &str = "I"; // CRYSTALS Dilithium sig appears in both lists.
pub const CRYSTALS_Dilithium5_Big_Crt: &str = "J"; // CRYSTALS Dilithium sig appears in current list only.
pub const Ed448: &str = "0A"; // Ed448 signature appears in both lists.
pub const Ed448_Crt: &str = "0B"; // Ed448 signature appears in current list only.
pub const Ed25519_Big: &str = "2A"; // Ed25519 sig appears in both lists.
Expand All @@ -63,6 +75,10 @@ pub mod SigCodex {
pub const ECDSA_256r1_Big_Crt: &str = "2F"; // ECDSA secp256r1 sig appears in current list only.
pub const Ed448_Big: &str = "3A"; // Ed448 signature appears in both lists.
pub const Ed448_Big_Crt: &str = "3B"; // Ed448 signature appears in current list only.
pub const CRYSTALS_Dilithium3: &str = "5AAA"; // CRYSTALS Dilithium sig appears same in both lists if any.
pub const CRYSTALS_Dilithium3_Crt: &str = "5AAB"; // CRYSTALS Dilithium sig appears in current list.
pub const CRYSTALS_Dilithium5: &str = "5AAC"; // CRYSTALS Dilithium sig appears same in both lists if any.
pub const CRYSTALS_Dilithium5_Crt: &str = "5AAD"; // CRYSTALS Dilithium sig appears in current list.
}

/// CurrentSigCodex is codex indexed signature codes for current list.
Expand All @@ -72,22 +88,30 @@ pub mod CurrentSigCodex {
pub const Ed25519_Crt: &str = "B"; // Ed25519 sig appears in current list only.
pub const ECDSA_256k1_Crt: &str = "D"; // ECDSA secp256k1 sig appears in current list only.
pub const ECDSA_256r1_Crt: &str = "F"; // ECDSA secp256r1 sig appears in current list.
pub const CRYSTALS_Dilithium3_Big_Crt: &str = "H"; // CRYSTALS Dilithium sig appears in current list only.
pub const CRYSTALS_Dilithium5_Big_Crt: &str = "J"; // CRYSTALS Dilithium sig appears in current list only.
pub const Ed448_Crt: &str = "0B"; // Ed448 signature appears in current list only.
pub const Ed25519_Big_Crt: &str = "2B"; // Ed25519 sig appears in current list only.
pub const ECDSA_256k1_Big_Crt: &str = "2D"; // ECDSA secp256k1 sig appears in current list only.
pub const ECDSA_256r1_Big_Crt: &str = "2F"; // ECDSA secp256r1 sig appears in current list only.
pub const Ed448_Big_Crt: &str = "3B"; // Ed448 signature appears in current list only.
pub const CRYSTALS_Dilithium3_Crt: &str = "5AAB"; // CRYSTALS Dilithium sig appears in current list.
pub const CRYSTALS_Dilithium5_Crt: &str = "5AAD"; // CRYSTALS Dilithium sig appears in current list.

pub(crate) fn has_code(code: &str) -> bool {
const CODES: &[&str] = &[
Ed25519_Crt,
ECDSA_256k1_Crt,
ECDSA_256r1_Crt,
CRYSTALS_Dilithium3_Crt,
CRYSTALS_Dilithium5_Crt,
Ed448_Crt,
Ed25519_Big_Crt,
ECDSA_256k1_Big_Crt,
ECDSA_256r1_Big_Crt,
Ed448_Big_Crt,
CRYSTALS_Dilithium3_Big_Crt,
CRYSTALS_Dilithium5_Big_Crt,
];

CODES.contains(&code)
Expand All @@ -100,21 +124,29 @@ pub mod BothSigCodex {
pub const Ed25519: &str = "A"; // Ed25519 sig appears same in both lists if any.
pub const ECDSA_256k1: &str = "C"; // ECDSA secp256k1 sig appears same in both lists if any.
pub const ECDSA_256r1: &str = "E"; // ECDSA secp256r1 sig appears same in both lists if any.
pub const CRYSTALS_Dilithium3_Big: &str = "G"; // CRYSTALS Dilithium sig appears in both lists.
pub const CRYSTALS_Dilithium5_Big: &str = "I"; // CRYSTALS Dilithium sig appears in both lists.
pub const Ed448: &str = "0A"; // Ed448 signature appears in both lists.
pub const Ed25519_Big: &str = "2A"; // Ed25519 sig appears in both lists.
pub const ECDSA_256k1_Big: &str = "2C"; // ECDSA secp256k1 sig appears in both lists.
pub const ECDSA_256r1_Big: &str = "2E"; // ECDSA secp256r1 sig appears in both lists.
pub const Ed448_Big: &str = "3A"; // Ed448 signature appears in both lists.
pub const CRYSTALS_Dilithium3: &str = "5AAA"; // CRYSTALS Dilithium sig appears same in both lists if any.
pub const CRYSTALS_Dilithium5: &str = "5AAC"; // CRYSTALS Dilithium sig appears same in both lists if any.

pub(crate) fn has_code(code: &str) -> bool {
const CODES: &[&str] = &[
Ed25519,
ECDSA_256k1,
ECDSA_256r1,
CRYSTALS_Dilithium3,
CRYSTALS_Dilithium5,
Ed448,
Ed25519_Big,
ECDSA_256k1_Big,
ECDSA_256r1_Big,
CRYSTALS_Dilithium3_Big,
CRYSTALS_Dilithium5_Big,
Ed448_Big,
];

Expand Down Expand Up @@ -144,6 +176,10 @@ pub(crate) fn sizage(s: &str) -> Result<Sizage> {
"D" => Sizage { hs: 1, ss: 1, os: 0, fs: 88, ls: 0 },
"E" => Sizage { hs: 1, ss: 1, os: 0, fs: 88, ls: 0 },
"F" => Sizage { hs: 1, ss: 1, os: 0, fs: 88, ls: 0 },
"G" => Sizage { hs: 1, ss: 4, os: 2, fs: 4396, ls: 0 },
"H" => Sizage { hs: 1, ss: 4, os: 2, fs: 4396, ls: 0 },
"I" => Sizage { hs: 1, ss: 4, os: 2, fs: 6132, ls: 0 },
"J" => Sizage { hs: 1, ss: 4, os: 2, fs: 6132, ls: 0 },
"0A" => Sizage { hs: 2, ss: 2, os: 1, fs: 156, ls: 0 },
"0B" => Sizage { hs: 2, ss: 2, os: 1, fs: 156, ls: 0 },
"2A" => Sizage { hs: 2, ss: 4, os: 2, fs: 92, ls: 0 },
Expand All @@ -157,6 +193,10 @@ pub(crate) fn sizage(s: &str) -> Result<Sizage> {
"0z" => Sizage { hs: 2, ss: 2, os: 0, fs: u32::MAX, ls: 0 },
"1z" => Sizage { hs: 2, ss: 2, os: 1, fs: 76, ls: 1 },
"4z" => Sizage { hs: 2, ss: 6, os: 3, fs: 80, ls: 1 },
"5AAA" => Sizage { hs: 4, ss: 1, os: 0, fs: 4396, ls: 0 },
"5AAB" => Sizage { hs: 4, ss: 1, os: 0, fs: 4396, ls: 0 },
"5AAC" => Sizage { hs: 4, ss: 1, os: 0, fs: 6132, ls: 0 },
"5AAD" => Sizage { hs: 4, ss: 1, os: 0, fs: 6132, ls: 0 },
_ => return err!(Error::UnknownSizage(s.to_string())),
})
}
Expand All @@ -165,6 +205,7 @@ pub(crate) fn hardage(c: char) -> Result<u32> {
match c {
'A'..='Z' | 'a'..='z' => Ok(1),
'0'..='4' => Ok(2),
'5' => Ok(4),
'-' => err!(Error::UnexpectedCode("count code start".to_owned())),
'_' => err!(Error::UnexpectedCode("op code start".to_owned())),
_ => err!(Error::UnknownHardage(c.to_string())),
Expand All @@ -175,6 +216,7 @@ pub(crate) fn bardage(b: u8) -> Result<u32> {
match b {
b'\x00'..=b'\x33' => Ok(1),
b'\x34'..=b'\x38' => Ok(2),
b'\x39' => Ok(4),
b'\x3e' => err!(Error::UnexpectedCode("count code start".to_owned())),
b'\x3f' => err!(Error::UnexpectedCode("op code start".to_owned())),
_ => err!(Error::UnknownBardage(b.to_string())),
Expand Down Expand Up @@ -387,6 +429,7 @@ mod test {
#[case(0x36, 2)]
#[case(0x37, 2)]
#[case(0x38, 2)]
#[case(0x39, 4)]
fn bardage(#[case] code: u8, #[case] bdg: u32) {
assert_eq!(indexer::bardage(code).unwrap(), bdg);
}
Expand All @@ -403,6 +446,6 @@ mod test {

#[test]
fn unknown_bardage() {
assert!(indexer::bardage(0x39).is_err());
assert!(indexer::bardage(0x40).is_err());
}
}
8 changes: 8 additions & 0 deletions src/core/matter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ pub trait Matter: Default {
tables::Codex::ECDSA_256k1N,
tables::Codex::Ed448N,
tables::Codex::ECDSA_256r1N,
tables::Codex::CRYSTALS_Dilithium3N,
tables::Codex::CRYSTALS_Dilithium5N,
];

!CODES.contains(&self.code().as_str())
Expand Down Expand Up @@ -822,9 +824,15 @@ mod test {
#[case(TestMatter::new_with_code_and_raw(matter::Codex::Ed25519, b"00000000000000000000000000000000").unwrap(), true)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::ECDSA_256k1, b"000000000000000000000000000000000").unwrap(), true)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::ECDSA_256r1, b"000000000000000000000000000000000").unwrap(), true)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::Ed448, &[0u8; 57]).unwrap(), true)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::CRYSTALS_Dilithium3, &[0u8; 1952]).unwrap(), true)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::CRYSTALS_Dilithium5, &[0u8; 2592]).unwrap(), true)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::Ed25519N, b"00000000000000000000000000000000").unwrap(), false)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::ECDSA_256k1N, b"000000000000000000000000000000000").unwrap(), false)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::ECDSA_256r1N, b"000000000000000000000000000000000").unwrap(), false)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::Ed448N, &[0u8; 57]).unwrap(), false)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::CRYSTALS_Dilithium3N, &[0u8; 1952]).unwrap(), false)]
#[case(TestMatter::new_with_code_and_raw(matter::Codex::CRYSTALS_Dilithium5N, &[0u8; 2592]).unwrap(), false)]
fn transferable(#[case] matter: TestMatter, #[case] result: bool) {
assert_eq!(matter.transferable(), result);
}
Expand Down
18 changes: 18 additions & 0 deletions src/core/matter/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ pub(crate) fn sizage(s: &str) -> Result<Sizage> {
"O" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
"P" => Sizage { hs: 1, ss: 0, fs: 124, ls: 0 },
"Q" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
"R" => Sizage { hs: 1, ss: 0, fs: 2604, ls: 0 },
"S" => Sizage { hs: 1, ss: 0, fs: 2604, ls: 0 },
"T" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
"U" => Sizage { hs: 1, ss: 0, fs: 4392, ls: 0 },
"V" => Sizage { hs: 1, ss: 0, fs: 44, ls: 0 },
"W" => Sizage { hs: 1, ss: 0, fs: 6128, ls: 0 },
"0A" => Sizage { hs: 2, ss: 0, fs: 24, ls: 0 },
"0B" => Sizage { hs: 2, ss: 0, fs: 88, ls: 0 },
"0C" => Sizage { hs: 2, ss: 0, fs: 88, ls: 0 },
Expand All @@ -51,6 +57,8 @@ pub(crate) fn sizage(s: &str) -> Result<Sizage> {
"1AAH" => Sizage { hs: 4, ss: 0, fs: 100, ls: 0 },
"1AAI" => Sizage { hs: 4, ss: 0, fs: 48, ls: 0 },
"1AAJ" => Sizage { hs: 4, ss: 0, fs: 48, ls: 0 },
"1AAK" => Sizage { hs: 4, ss: 0, fs: 3460, ls: 0 },
"1AAL" => Sizage { hs: 4, ss: 0, fs: 3460, 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: u32::MAX, ls: 0 },
Expand Down Expand Up @@ -121,6 +129,12 @@ pub mod Codex {
pub const X25519_Private: &str = "O"; // X25519 private decryption key converted from Ed25519
pub const X25519_Cipher_Seed: &str = "P"; // X25519 124 char b64 Cipher of 44 char qb64 Seed
pub const ECDSA_256r1_Seed: &str = "Q"; // ECDSA secp256r1 256 bit random Seed for private key
pub const CRYSTALS_Dilithium3N: &str = "R"; // CRYSTALS Dilithium verficiation key, non-transferable.
pub const CRYSTALS_Dilithium3: &str = "S"; // CRYSTALS Dilithium verficiation key.
pub const CRYSTALS_Dilithium3_Seed: &str = "T"; // CRYSTALS Dilithium private key seed. This one is actually a seed.
pub const CRYSTALS_Dilithium3_Sig: &str = "U"; // CRYSTALS Dilithium signature.
pub const CRYSTALS_Dilithium5_Seed: &str = "V"; // CRYSTALS Dilithium5 verification key, non-transferable.
pub const CRYSTALS_Dilithium5_Sig: &str = "W"; // CRYSTALS Dilithium5 signature.
pub const Salt_128: &str = "0A"; // 128 bit random salt or 128 bit number (see Huge)
pub const Ed25519_Sig: &str = "0B"; // Ed25519 signature.
pub const ECDSA_256k1_Sig: &str = "0C"; // ECDSA secp256k1 signature.
Expand All @@ -140,6 +154,8 @@ pub mod Codex {
pub const X25519_Cipher_Salt: &str = "1AAH"; // X25519 100 char b64 Cipher of 24 char qb64 Salt
pub const ECDSA_256r1N: &str = "1AAI"; // ECDSA secp256r1 verification key non-transferable, basic derivation.
pub const ECDSA_256r1: &str = "1AAJ"; // ECDSA secp256r1 verification or encryption key, basic derivation
pub const CRYSTALS_Dilithium5N: &str = "1AAK"; // CRYSTALS Dilithium5 verification key, non-transferable.
pub const CRYSTALS_Dilithium5: &str = "1AAL"; // CRYSTALS Dilithium5 verification key, non-transferable.
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 @@ -198,6 +214,8 @@ mod test {
#[case("1AAH", 4, 0, 100, 0)]
#[case("1AAI", 4, 0, 48, 0)]
#[case("1AAJ", 4, 0, 48, 0)]
#[case("1AAK", 4, 0, 3460, 0)]
#[case("1AAL", 4, 0, 3460, 0)]
#[case("2AAA", 4, 0, 8, 1)]
#[case("3AAA", 4, 0, 8, 2)]
#[case("4A", 2, 2, u32::MAX, 0)]
Expand Down
4 changes: 4 additions & 0 deletions src/core/prefixer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ fn validate_code(code: &str) -> Result<()> {
matter::Codex::Ed25519N,
matter::Codex::ECDSA_256k1N,
matter::Codex::ECDSA_256r1N,
matter::Codex::CRYSTALS_Dilithium3N,
matter::Codex::CRYSTALS_Dilithium5N,
// transferable
matter::Codex::Ed25519,
matter::Codex::ECDSA_256k1,
matter::Codex::ECDSA_256r1,
matter::Codex::CRYSTALS_Dilithium3,
matter::Codex::CRYSTALS_Dilithium5,
// digests
matter::Codex::Blake3_256,
matter::Codex::Blake3_512,
Expand Down
Loading