-
Notifications
You must be signed in to change notification settings - Fork 3
Post-Quantum Cryptography #14
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
base: main
Are you sure you want to change the base?
Conversation
--------- Co-authored-by: Kyle Kotowick <[email protected]>
Cargo.toml
Outdated
cfg-if = "1.0.0" | ||
heapless-bytes = "0.3.0" | ||
pqcrypto-mldsa = { version = "0.1.0", optional = true} | ||
serde-big-array = "0.5.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused dependency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serde-big-array
is unused, yes.
pqcrypto-mldsa
is used with dynamic crate selection in the macro, here (and other places).
|
||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] | ||
#[serde(try_from = "RawPublicKey")] | ||
#[serde(try_from = "RawEcPublicKey")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn’t this mean that the MLDSA variants cannot be deserialized?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We renamed RawPublicKey
to RawEcPublicKey
(RawElipticCurvePublicKey) to differentiate it from the new RawMldsaPublicKey
.
Deserialization for MLDSA is done in the macro, here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two ways to serialize and deserialize data – using the specific structs like Mldsa44PublicKey
, or using the PublicKey
enum. With this implementation, you could serialize a PublicKey::Mldsa44(_)
or a Mldsa44PublicKey
, but you could only deserialize the result into a Mldsa44PublicKey
, not into a PublicKey
.
#[cfg(feature = "backend-dilithium2")] | ||
use cosey::Dilithium2PublicKey; | ||
#[cfg(feature = "backend-dilithium3")] | ||
use cosey::Dilithium3PublicKey; | ||
#[cfg(feature = "backend-dilithium5")] | ||
use cosey::Dilithium5PublicKey; | ||
#[cfg(feature = "backend-dilithium2")] | ||
use pqcrypto_dilithium::dilithium2; | ||
#[cfg(feature = "backend-dilithium3")] | ||
use pqcrypto_dilithium::dilithium3; | ||
#[cfg(feature = "backend-dilithium5")] | ||
use pqcrypto_dilithium::dilithium5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These features are missing from Cargo.toml
heapless-bytes = "0.3.0" | ||
pqcrypto-mldsa = { version = "0.1.0", optional = true} | ||
serde_repr = "0.1" | ||
paste = "1.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Paste is unmaintained. Please remove it.
#[cfg(feature = "mldsa")] | ||
macro_rules! mldsa_public_key { | ||
($mldsa_number: tt) => { | ||
paste! { | ||
with_eager_expansions! { | ||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)] | ||
#[serde(into = #{ concat!("RawMldsa", stringify!($mldsa_number), "PublicKey") })] | ||
pub struct [<Mldsa $mldsa_number PublicKey>] { | ||
pub pk: Bytes<{ [<mldsa $mldsa_number>]::public_key_bytes() }>, | ||
} | ||
|
||
impl PublicKeyConstants for [<Mldsa $mldsa_number PublicKey>] { | ||
const KTY: Kty = Kty::Pqc; | ||
const ALG: Alg = Alg::[<Mldsa $mldsa_number>]; | ||
const CRV: Crv = Crv::None; | ||
} | ||
|
||
impl From<[<Mldsa $mldsa_number PublicKey>]> for PublicKey { | ||
fn from(key: [<Mldsa $mldsa_number PublicKey>]) -> Self { | ||
PublicKey::[<Mldsa $mldsa_number>](key) | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, Default)] | ||
struct [<RawMldsa $mldsa_number PublicKey>] { | ||
kty: Option<Kty>, | ||
alg: Option<Alg>, | ||
pk: Option<Bytes<{ [<mldsa $mldsa_number>]::public_key_bytes() }>>, | ||
} | ||
|
||
impl From<[<Mldsa $mldsa_number PublicKey>]> for [<RawMldsa $mldsa_number PublicKey>] { | ||
fn from(key: [<Mldsa $mldsa_number PublicKey>]) -> Self { | ||
Self { | ||
kty: Some([<Mldsa $mldsa_number PublicKey>]::KTY), | ||
alg: Some([<Mldsa $mldsa_number PublicKey>]::ALG), | ||
pk: Some(key.pk), | ||
} | ||
} | ||
} | ||
|
||
impl<'de> serde::Deserialize<'de> for [<Mldsa $mldsa_number PublicKey>] { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: serde::Deserializer<'de>, | ||
{ | ||
let [<RawMldsa $mldsa_number PublicKey>] { kty, alg, pk, .. } = | ||
[<RawMldsa $mldsa_number PublicKey>]::deserialize(deserializer)?; | ||
check_key_constants::<[<Mldsa $mldsa_number PublicKey>], D::Error>(kty, alg, Some(Crv::None))?; | ||
let pk = pk.ok_or_else(|| D::Error::missing_field("pk"))?; | ||
Ok(Self { pk }) | ||
} | ||
} | ||
|
||
impl<'de> Deserialize<'de> for [<RawMldsa $mldsa_number PublicKey>] { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: serde::Deserializer<'de>, | ||
{ | ||
struct IndexedVisitor; | ||
impl<'de> serde::de::Visitor<'de> for IndexedVisitor { | ||
type Value = [<RawMldsa $mldsa_number PublicKey>]; | ||
|
||
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
formatter.write_str(concat!("RawMldsa", stringify!($mldsa_number), "PublicKey")) | ||
} | ||
|
||
fn visit_map<V>(self, mut map: V) -> Result<[<RawMldsa $mldsa_number PublicKey>], V::Error> | ||
where | ||
V: MapAccess<'de>, | ||
{ | ||
#[derive(PartialEq)] | ||
enum Key { | ||
Label(Label), | ||
Unknown(i8), | ||
None, | ||
} | ||
|
||
fn next_key<'a, V: MapAccess<'a>>(map: &mut V) -> Result<Key, V::Error> { | ||
let key: Option<i8> = map.next_key()?; | ||
let key = match key { | ||
Some(key) => match Label::try_from(key) { | ||
Ok(label) => Key::Label(label), | ||
Err(_) => Key::Unknown(key), | ||
}, | ||
None => Key::None, | ||
}; | ||
Ok(key) | ||
} | ||
|
||
let mut public_key = [<RawMldsa $mldsa_number PublicKey>]::default(); | ||
|
||
// As we cannot deserialize arbitrary values with cbor-smol, we do not support | ||
// unknown keys before a known key. If there are unknown keys, they must be at the | ||
// end. | ||
|
||
// only deserialize in canonical order | ||
|
||
let mut key = next_key(&mut map)?; | ||
|
||
if key == Key::Label(Label::Kty) { | ||
public_key.kty = Some(map.next_value()?); | ||
key = next_key(&mut map)?; | ||
} | ||
|
||
if key == Key::Label(Label::Alg) { | ||
public_key.alg = Some(map.next_value()?); | ||
key = next_key(&mut map)?; | ||
} | ||
|
||
if key == Key::Label(Label::CrvOrPk) { | ||
public_key.pk = Some(map.next_value()?); | ||
key = next_key(&mut map)?; | ||
} | ||
|
||
// if there is another key, it should be an unknown one | ||
if matches!(key, Key::Label(_)) { | ||
Err(serde::de::Error::custom( | ||
"public key data in wrong order or with duplicates", | ||
)) | ||
} else { | ||
Ok(public_key) | ||
} | ||
} | ||
} | ||
deserializer.deserialize_map(IndexedVisitor {}) | ||
} | ||
} | ||
|
||
impl Serialize for [<RawMldsa $mldsa_number PublicKey>] { | ||
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> | ||
where | ||
S: serde::Serializer, | ||
{ | ||
let is_set = [self.kty.is_some(), self.alg.is_some(), self.pk.is_some()]; | ||
let fields = is_set.into_iter().map(usize::from).sum(); | ||
use serde::ser::SerializeMap; | ||
let mut map = serializer.serialize_map(Some(fields))?; | ||
|
||
// 1: kty | ||
if let Some(kty) = &self.kty { | ||
map.serialize_entry(&(Label::Kty as i8), &(*kty as i8))?; | ||
} | ||
// 3: alg | ||
if let Some(alg) = &self.alg { | ||
map.serialize_entry(&(Label::Alg as i8), &(*alg as i8))?; | ||
} | ||
// -1: pk | ||
if let Some(pk) = &self.pk { | ||
map.serialize_entry(&(Label::CrvOrPk as i8), pk)?; | ||
} | ||
|
||
map.end() | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
#[cfg(feature = "mldsa44")] | ||
mldsa_public_key!(44); | ||
#[cfg(feature = "mldsa65")] | ||
mldsa_public_key!(65); | ||
#[cfg(feature = "mldsa87")] | ||
mldsa_public_key!(87); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Paste
really necessary?
You should be able to work around the need for paste by making the macro_rules
require the identifier:
mldsa_public_key!(44, MlDsa44PublicKey, RawMlDsa44PublicKey);
#[test] | ||
#[cfg(feature = "backend-dilithium2")] | ||
fn de_dilithium2() { | ||
const DILITHIUM2_KAT_PK: &str = "1c0ee1111b08003f28e65e8b3bdeb037cf8f221dfcdaf5950edb38d506d85bef6177e3de0d4f1ef5847735947b56d08e841db2444fa2b729adeb1417ca7adf42a1490c5a097f002760c1fc419be8325aad0197c52ced80d3df18e7774265b289912ceca1be3a90d8a4fde65c84c610864e47deecae3eea4430b9909559408d11a6abdb7db9336df7f96eab4864a6579791265fa56c348cb7d2ddc90e133a95c3f6b13601429f5408bd999aa479c1018159550ec55a113c493be648f4e036dd4f8c809e036b4fbb918c2c484ad8e1747ae05585ab433fdf461af03c25a773700721aa05f7379fe7f5ed96175d4021076e7f52b60308eff5d42ba6e093b3d0815eb3496646e49230a9b35c8d41900c2bb8d3b446a23127f7e096d85a1c794ad4c89277904fc6bfec57b1cdd80df9955030fdca741afbdac827b13ccd5403588af4644003c2265dfa4d419dbccd2064892386518be9d51c16498275ebecf5cdc7a820f2c29314ac4a6f08b2252ad3cfb199aa42fe0b4fb571975c1020d949e194ee1ead937bfb550bb3ba8e357a029c29f077554602e1ca2f2289cb9169941c3aafdb8e58c7f2ac77291fb4147c65f6b031d3eba42f2acfd9448a5bc22b476e07ccceda2306c554ec9b7ab655f1d7318c2b7e67d5f69bedf56000fda98986b5ab1b3a22d8dfd6681697b23a55c96e8710f3f98c044fb15f606313ee56c0f1f5ca0f512e08484fcb358e6e528ffa89f8a866ccff3c0c5813147ec59af0470c4aad0141d34f101da2e5e1bd52d0d4c9b13b3e3d87d1586105796754e7978ca1c68a7d85df112b7ab921b359a9f03cbd27a7eac87a9a80b0b26b4c9657ed85ad7fa2616ab345eb8226f69fc0f48183ff574bcd767b5676413adb12ea2150a0e97683ee54243c25b7ea8a718606f86993d8d0dace834ed341eeb724fe3d5ff0bc8b8a7b8104ba269d34133a4cf8300a2d688496b59b6fcbc61ae96062ea1d8e5b410c5671f424417ed693329cd983001ffcd10023d598859fb7ad5fd263547117100690c6ce7438956e6cc57f1b5de53bb0dc72ce9b6deaa85789599a70f0051f1a0e25e86d888b00df36bdbc93ef7217c45ace11c0790d70e9953e5b417ba2fd9a4caf82f1fce6f45f53e215b8355ef61d891df1c794231c162dd24164b534a9d48467cdc323624c2f95d4402ff9d66ab1191a8124144afa35d4e31dc86caa797c31f68b85854cd959c4fac5ec53b3b56d374b888a9e979a6576b6345ec8522c9606990281bf3ef7c5945d10fd21a2a1d2e5404c5cf21220641391b98bcf825398305b56e58b611fe5253203e3df0d22466a73b3f0fbe43b9a62928091898b8a0e5b269db586b0e4ddef50d682a12d2c1be824149aa254c6381bb412d77c3f9aa902b688c81715a59c839558556d35ed4fc83b4ab18181f40f73dcd76860d8d8bf94520237c2ac0e463ba09e3c9782380dc07fe4fcba340cc2003439fd2314610638070d6c9eea0a70bae83b5d5d3c5d3fde26dd01606c8c520158e7e5104020f248ceaa666457c10aebf068f8a3bd5ce7b52c6af0abd5944af1ad4752c9113976083c03b6c34e1d47ed69644cad782c2f7d05f8a148961d965fa2e1723a8ddebc22a90cd783dd1f4db38fb9ae5a6714b3d946781643d317b7dd79381cf789a9588bb3e193b92a0b60d6b07d047f6984b0609ec57543c394ca8d5e5bcc2a731a79618bd1e2e0da8704af98f20f5f8f5452ddf646b95b341dd7f0d2cc1fa15bd9895cd5b65aa1cb94b5e2e788fda9825b656639193d98328154a4f2c35495a38b6ea0d2ffaaa35df92c203c7f31cbbca7bd03c3c2302190cecd161fd49237e4f839e3f3"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please document the source of these keys. Are they test vectors ? Please document their source.
This PR adds support for ML-DSA assertions.
Recreate of #11 after changing the source branch name.