Skip to content

Commit eb95f9c

Browse files
committed
implement an OpenSSL provider -- P256
1 parent d225901 commit eb95f9c

9 files changed

Lines changed: 757 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
[workspace]
2-
members = ["crates/core", "crates/rust_nist_ec", "crates/rust_curve25519"]
2+
members = [
3+
"crates/core",
4+
"crates/openssl_provider",
5+
"crates/rust_nist_ec",
6+
"crates/rust_curve25519"
7+
]
38
resolver = "2"
49

510
[workspace.package]

crates/core/src/error.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ pub enum CryptoCoreError {
2222
given: usize,
2323
expected: usize,
2424
},
25-
#[cfg(any(feature = "nist_curves", feature = "curve25519"))]
2625
EllipticCurveError(String),
2726
EncryptionError,
2827
GenericDeserializationError(String),
@@ -85,7 +84,6 @@ impl Display for CryptoCoreError {
8584
f,
8685
"wrong size when parsing bytes: {given} given should be {expected}"
8786
),
88-
#[cfg(any(feature = "nist_curves", feature = "curve25519"))]
8987
CryptoCoreError::EllipticCurveError(e) => write!(f, "NIST elliptic curve error: {e}"),
9088
CryptoCoreError::EncryptionError => write!(f, "error during encryption"),
9189
CryptoCoreError::GenericDeserializationError(err) => {

crates/openssl_provider/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "cosmian_openssl_provider"
3+
version = "1.0.0"
4+
description = "OpenSSL cryptographic provider."
5+
6+
authors = { workspace = true }
7+
categories = { workspace = true }
8+
edition = { workspace = true }
9+
keywords = { workspace = true }
10+
license = { workspace = true }
11+
repository = { workspace = true }
12+
13+
[lib]
14+
crate-type = ["lib", "staticlib"]
15+
name = "cosmian_openssl_provider"
16+
path = "src/lib.rs"
17+
18+
[dependencies]
19+
cosmian_crypto_core = { path = "../core", version = "10.4", default-features = false }
20+
openssl = { version = "0.10", default-features = false }
21+
zeroize = { workspace = true }
22+
23+
[dev-dependencies]
24+
cosmian_crypto_core = { path = "../core", version = "10.4", default-features = false, features = ["sha3"] }

crates/openssl_provider/src/kem.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use crate::FFIMonad;
2+
use cosmian_crypto_core::{
3+
bytes_ser_de::Serializable,
4+
reexport::rand_core::CryptoRngCore,
5+
traits::{CyclicGroup, Field, KDF, KEM, NIKE},
6+
CryptoCoreError, Sampling, SymmetricKey,
7+
};
8+
use std::{
9+
marker::PhantomData,
10+
ops::{Add, Div, Mul, Neg, Sub},
11+
};
12+
use zeroize::ZeroizeOnDrop;
13+
14+
#[derive(Debug, Clone, Copy, Default)]
15+
pub struct MonadicKEM<const KEY_LENGTH: usize, Group: CyclicGroup, Kdf: KDF<KEY_LENGTH>>
16+
where
17+
Group::Element: FFIMonad,
18+
Group::Multiplicity: FFIMonad,
19+
for<'a> &'a Group::Element: Neg<Output = Group::Element>,
20+
for<'a, 'b> &'a Group::Element: Add<&'b Group::Element, Output = Group::Element>,
21+
for<'a, 'b> &'a Group::Element: Sub<&'b Group::Element, Output = Group::Element>,
22+
for<'a, 'b> &'a Group::Element: Mul<Group::Multiplicity, Output = Group::Element>,
23+
for<'a, 'b> &'a Group::Element: Mul<&'b Group::Multiplicity, Output = Group::Element>,
24+
for<'a> &'a Group::Multiplicity: Neg<Output = Group::Multiplicity>,
25+
for<'a, 'b> &'a Group::Multiplicity: Add<&'b Group::Multiplicity, Output = Group::Multiplicity>,
26+
for<'a, 'b> &'a Group::Multiplicity: Sub<&'b Group::Multiplicity, Output = Group::Multiplicity>,
27+
for<'a, 'b> &'a Group::Multiplicity: Mul<&'b Group::Multiplicity, Output = Group::Multiplicity>,
28+
for<'a, 'b> &'a Group::Multiplicity: Div<
29+
&'b Group::Multiplicity,
30+
Output = Result<Group::Multiplicity, <Group::Multiplicity as Field>::InvError>,
31+
>,
32+
{
33+
data: PhantomData<(Group, Kdf)>,
34+
}
35+
36+
impl<const KEY_LENGTH: usize, Group: CyclicGroup, Kdf: KDF<KEY_LENGTH>> KEM<KEY_LENGTH>
37+
for MonadicKEM<KEY_LENGTH, Group, Kdf>
38+
where
39+
Group::Element: FFIMonad + Serializable + ZeroizeOnDrop,
40+
Group::Multiplicity: FFIMonad + Sampling + ZeroizeOnDrop,
41+
for<'a> &'a Group::Element: Neg<Output = Group::Element>,
42+
for<'a, 'b> &'a Group::Element: Add<&'b Group::Element, Output = Group::Element>,
43+
for<'a, 'b> &'a Group::Element: Sub<&'b Group::Element, Output = Group::Element>,
44+
for<'a, 'b> &'a Group::Element: Mul<Group::Multiplicity, Output = Group::Element>,
45+
for<'a, 'b> &'a Group::Element: Mul<&'b Group::Multiplicity, Output = Group::Element>,
46+
for<'a> &'a Group::Multiplicity: Neg<Output = Group::Multiplicity>,
47+
for<'a, 'b> &'a Group::Multiplicity: Add<&'b Group::Multiplicity, Output = Group::Multiplicity>,
48+
for<'a, 'b> &'a Group::Multiplicity: Sub<&'b Group::Multiplicity, Output = Group::Multiplicity>,
49+
for<'a, 'b> &'a Group::Multiplicity: Mul<&'b Group::Multiplicity, Output = Group::Multiplicity>,
50+
for<'a, 'b> &'a Group::Multiplicity: Div<
51+
&'b Group::Multiplicity,
52+
Output = Result<Group::Multiplicity, <Group::Multiplicity as Field>::InvError>,
53+
>,
54+
{
55+
type Encapsulation = Group::Element;
56+
57+
type EncapsulationKey = Group::Element;
58+
59+
type DecapsulationKey = Group::Multiplicity;
60+
61+
type Error = CryptoCoreError;
62+
63+
fn keygen(
64+
rng: &mut impl CryptoRngCore,
65+
) -> Result<(Self::DecapsulationKey, Self::EncapsulationKey), Self::Error> {
66+
let (sk, pk) = <Group as NIKE>::keygen(rng)?;
67+
let sk = sk.manage_error(|e| {
68+
CryptoCoreError::EllipticCurveError(format!("secret key is in error state: {e}"))
69+
})?;
70+
let pk = pk.manage_error(|e| {
71+
CryptoCoreError::EllipticCurveError(format!("public key is in error state: {e}"))
72+
})?;
73+
Ok((sk, pk))
74+
}
75+
76+
fn enc(
77+
ek: &Self::EncapsulationKey,
78+
rng: &mut impl CryptoRngCore,
79+
) -> Result<(SymmetricKey<KEY_LENGTH>, Self::Encapsulation), Self::Error> {
80+
let (sk, pk) = <Group as NIKE>::keygen(rng)?;
81+
let ss = (ek * sk).manage_error(|e| {
82+
CryptoCoreError::EllipticCurveError(format!("shared secret is in error state: {e}"))
83+
})?;
84+
let pk = pk.manage_error(|e| {
85+
CryptoCoreError::EllipticCurveError(format!("public key is in error state: {e}"))
86+
})?;
87+
let key = Kdf::derive(
88+
&ss.serialize()
89+
.map_err(|e| CryptoCoreError::GenericDeserializationError(e.to_string()))?,
90+
&[],
91+
);
92+
Ok((key, pk))
93+
}
94+
95+
fn dec(
96+
dk: &Self::DecapsulationKey,
97+
enc: &Self::Encapsulation,
98+
) -> Result<SymmetricKey<KEY_LENGTH>, Self::Error> {
99+
let ss = <Group as NIKE>::shared_secret(dk, enc)?.manage_error(|e| {
100+
CryptoCoreError::EllipticCurveError(format!("shared secret is in error state: {e}"))
101+
})?;
102+
let key = Kdf::derive(
103+
&ss.serialize()
104+
.map_err(|e| CryptoCoreError::GenericDeserializationError(e.to_string()))?,
105+
&[],
106+
);
107+
Ok(key)
108+
}
109+
}

crates/openssl_provider/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//! This crate implements a cryptographic provider on top of the OpenSSL
2+
//! bindings. Those bindings do not fit well with the arithmetic traits defined
3+
//! in CryptoCore: since all operations are performed through FFI, the all --
4+
//! even those as simple as returning the 0 -- are fallible.
5+
//!
6+
//! Instead of modifying all arithmetic traits, which would imply the need to
7+
//! unwrap upon every single arithmetic operation, this provider has been design
8+
//! in a monadic style: elements are FFI monads that can either be a valid
9+
//! object or in an error state (the `openssl::ErrorStack` error type) and
10+
//! comply with arithmetic traits. The only quirk is that the caller is then
11+
//! required to check the monad state at some point, which leaks an internal
12+
//! detail.
13+
//!
14+
//! To ease its usage, a dedicated KEM is implemented which performs the check
15+
//! of the monad state when necessary (using the `GenericKEM` from CryptoCore
16+
//! would return a monad instead of the proper keys or encapsulation).
17+
18+
pub mod kem;
19+
pub mod p256;
20+
21+
/// An FFI monad is either in an OK or error state.
22+
pub trait FFIMonad: Sized {
23+
/// Type of the error state.
24+
type Error: std::error::Error;
25+
26+
/// Return true if this monad is in its OK state.
27+
fn is_ok(&self) -> bool;
28+
29+
/// Returns true if this monad is in its error state.
30+
fn is_err(&self) -> bool {
31+
!self.is_ok()
32+
}
33+
34+
/// Return this monad unchanged if it is in its OK state, or apply the given
35+
/// function on its error state.
36+
fn manage_error<E: std::error::Error>(self, f: fn(Self::Error) -> E) -> Result<Self, E>;
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
mod point;
2+
mod scalar;
3+
4+
pub use point::P256Point;
5+
pub use scalar::P256Scalar;
6+
7+
use cosmian_crypto_core::traits::CyclicGroup;
8+
use openssl::nid::Nid;
9+
10+
const NID: Nid = Nid::X9_62_PRIME256V1;
11+
12+
#[derive(Debug, Clone, Copy, Default)]
13+
pub struct P256;
14+
15+
impl CyclicGroup for P256 {
16+
type Element = P256Point;
17+
18+
type Multiplicity = P256Scalar;
19+
}
20+
21+
#[cfg(test)]
22+
mod tests {
23+
use super::*;
24+
use crate::kem::MonadicKEM;
25+
use cosmian_crypto_core::{
26+
kdf::Kdf256,
27+
traits::tests::{test_cyclic_group, test_kem, test_nike},
28+
};
29+
30+
#[test]
31+
fn test_p256() {
32+
test_cyclic_group::<P256>();
33+
test_nike::<P256>();
34+
test_kem::<32, MonadicKEM<32, P256, Kdf256>>();
35+
}
36+
}

0 commit comments

Comments
 (0)