Skip to content

Commit 6fb3d94

Browse files
committed
ECDSA support through OpenSSL 3.* API
Signed-off-by: Jakub Jelen <[email protected]>
1 parent b0afadd commit 6fb3d94

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

openssl/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ pub mod pkcs7;
186186
pub mod pkey;
187187
pub mod pkey_ctx;
188188
#[cfg(ossl300)]
189+
pub mod pkey_ecdsa;
190+
#[cfg(ossl300)]
189191
pub mod pkey_rsa;
190192
#[cfg(ossl300)]
191193
pub mod provider;

openssl/src/pkey_ecdsa.rs

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
//! Elliptic Curve using OpenSSL 3.* API
2+
//!
3+
//! Cryptography relies on the difficulty of solving mathematical problems, such as the factor
4+
//! of large integers composed of two large prime numbers and the discrete logarithm of a
5+
//! random elliptic curve. This module provides low-level features of the latter.
6+
//! Elliptic Curve protocols can provide the same security with smaller keys.
7+
8+
use foreign_types::ForeignType;
9+
use libc::c_int;
10+
use std::ptr;
11+
12+
use crate::bn::{BigNum, BigNumRef};
13+
use crate::error::ErrorStack;
14+
use crate::ossl_param::{OsslParam, OsslParamBuilder};
15+
use crate::pkey::{PKey, Private, Public};
16+
use crate::pkey_ctx::PkeyCtx;
17+
use crate::{cvt, cvt_p};
18+
use openssl_macros::corresponds;
19+
20+
const OSSL_PKEY_PARAM_GROUP_NAME: &[u8; 6] = b"group\0";
21+
const OSSL_PKEY_PARAM_PUB_KEY: &[u8; 4] = b"pub\0";
22+
const OSSL_PKEY_PARAM_PRIV_KEY: &[u8; 5] = b"priv\0";
23+
24+
pub struct PKeyEcdsaBuilder<T> {
25+
bld: OsslParamBuilder,
26+
_m: ::std::marker::PhantomData<T>,
27+
}
28+
29+
impl<T> PKeyEcdsaBuilder<T> {
30+
/// Creates a new `PKeyEcdsaBuilder` to build ECDSA private or public keys.
31+
///
32+
/// `n` is the modulus common to both public and private key.
33+
/// `e` is the public exponent and `d` is the private exponent.
34+
///
35+
pub fn new(
36+
group: &str,
37+
point: &[u8],
38+
private: Option<&BigNumRef>,
39+
) -> Result<PKeyEcdsaBuilder<T>, ErrorStack> {
40+
let bld = OsslParamBuilder::new()?;
41+
bld.add_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, group)?;
42+
bld.add_octet_string(OSSL_PKEY_PARAM_PUB_KEY, point)?;
43+
if let Some(private) = private {
44+
bld.add_bn(OSSL_PKEY_PARAM_PRIV_KEY, private)?
45+
};
46+
Ok(PKeyEcdsaBuilder::<T> {
47+
bld,
48+
_m: ::std::marker::PhantomData,
49+
})
50+
}
51+
52+
/// Build PKey. Internal.
53+
#[corresponds(EVP_PKEY_fromdata)]
54+
fn build_internal(self, selection: c_int) -> Result<PKey<T>, ErrorStack> {
55+
let mut ctx = PkeyCtx::new_from_name(None, "EC", None)?;
56+
ctx.fromdata_init()?;
57+
let params = self.bld.to_param()?;
58+
unsafe {
59+
let evp = cvt_p(ffi::EVP_PKEY_new())?;
60+
let pkey = PKey::from_ptr(evp);
61+
cvt(ffi::EVP_PKEY_fromdata(
62+
ctx.as_ptr(),
63+
&mut pkey.as_ptr(),
64+
selection,
65+
params.as_ptr(),
66+
))?;
67+
Ok(pkey)
68+
}
69+
}
70+
}
71+
72+
impl PKeyEcdsaBuilder<Private> {
73+
/// Returns the Private ECDSA PKey from the provided parameters.
74+
#[corresponds(EVP_PKEY_fromdata)]
75+
pub fn build(self) -> Result<PKey<Private>, ErrorStack> {
76+
/* The ECDSA requires here a keypair as the private key does not work without public point! */
77+
self.build_internal(ffi::EVP_PKEY_KEYPAIR)
78+
}
79+
}
80+
81+
impl PKeyEcdsaBuilder<Public> {
82+
/// Returns the Public ECDSA PKey from the provided parameters.
83+
#[corresponds(EVP_PKEY_fromdata)]
84+
pub fn build(self) -> Result<PKey<Public>, ErrorStack> {
85+
self.build_internal(ffi::EVP_PKEY_PUBLIC_KEY)
86+
}
87+
}
88+
89+
pub struct PKeyEcdsaParams<T> {
90+
params: OsslParam,
91+
_m: ::std::marker::PhantomData<T>,
92+
}
93+
94+
impl<T> PKeyEcdsaParams<T> {
95+
/// Creates a new `PKeyEcdsaParams` from existing ECDSA PKey. Internal.
96+
#[corresponds(EVP_PKEY_todata)]
97+
fn _new_from_pkey(pkey: &PKey<T>, selection: c_int) -> Result<PKeyEcdsaParams<T>, ErrorStack> {
98+
unsafe {
99+
let mut params: *mut ffi::OSSL_PARAM = ptr::null_mut();
100+
cvt(ffi::EVP_PKEY_todata(pkey.as_ptr(), selection, &mut params))?;
101+
Ok(PKeyEcdsaParams::<T> {
102+
params: OsslParam::from_ptr(params),
103+
_m: ::std::marker::PhantomData,
104+
})
105+
}
106+
}
107+
108+
/// Returns a reference to the public key.
109+
pub fn public_key(&self) -> Result<&[u8], ErrorStack> {
110+
self.params
111+
.locate(OSSL_PKEY_PARAM_PUB_KEY)?
112+
.get_octet_string()
113+
}
114+
115+
/// Returns a reference to a group name
116+
pub fn group(&self) -> Result<&str, ErrorStack> {
117+
self.params
118+
.locate(OSSL_PKEY_PARAM_GROUP_NAME)?
119+
.get_utf8_string()
120+
}
121+
}
122+
123+
impl PKeyEcdsaParams<Public> {
124+
/// Creates a new `PKeyEcdsaParams` from existing Public ECDSA PKey.
125+
#[corresponds(EVP_PKEY_todata)]
126+
pub fn from_pkey(pkey: &PKey<Public>) -> Result<PKeyEcdsaParams<Public>, ErrorStack> {
127+
Self::_new_from_pkey(pkey, ffi::EVP_PKEY_PUBLIC_KEY)
128+
}
129+
}
130+
131+
impl PKeyEcdsaParams<Private> {
132+
/// Creates a new `PKeyEcdsaParams` from existing Private ECDSA PKey.
133+
#[corresponds(EVP_PKEY_todata)]
134+
pub fn from_pkey(pkey: &PKey<Private>) -> Result<PKeyEcdsaParams<Private>, ErrorStack> {
135+
Self::_new_from_pkey(pkey, ffi::EVP_PKEY_KEYPAIR)
136+
}
137+
138+
/// Returns the private key.
139+
pub fn private_key(&self) -> Result<BigNum, ErrorStack> {
140+
self.params.locate(OSSL_PKEY_PARAM_PRIV_KEY)?.get_bn()
141+
}
142+
}
143+
144+
#[cfg(test)]
145+
mod tests {
146+
147+
use crate::bn::BigNumContext;
148+
use crate::ec::{EcKey, PointConversionForm};
149+
use crate::error::Error;
150+
use crate::nid::Nid;
151+
152+
use super::*;
153+
154+
#[test]
155+
fn test_build_pkey_ecdsa_private() {
156+
/* First, generate the key with old API */
157+
let nid: Nid = Nid::SECP256K1;
158+
let curve_name = nid.short_name().unwrap();
159+
let group = crate::ec::EcGroup::from_curve_name(nid).unwrap();
160+
let ec_key = EcKey::generate(&group).unwrap();
161+
let pkey1 = PKey::from_ec_key(ec_key.clone()).unwrap();
162+
163+
/* Now, build the new PKey from the old key with new API */
164+
let mut ctx = BigNumContext::new().unwrap();
165+
let pubkey = ec_key
166+
.public_key()
167+
.to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx)
168+
.unwrap();
169+
let bld = PKeyEcdsaBuilder::<Private>::new(curve_name, &pubkey, Some(ec_key.private_key()))
170+
.unwrap();
171+
let pkey2 = bld.build().unwrap();
172+
173+
/* Verify it works the same way as the old one */
174+
assert!(pkey1.public_eq(&pkey2));
175+
assert!(Error::get().is_none());
176+
177+
let params = PKeyEcdsaParams::<Private>::from_pkey(&pkey2).unwrap();
178+
assert_eq!(params.group().unwrap(), curve_name);
179+
assert_eq!(&params.private_key().unwrap(), ec_key.private_key());
180+
assert_eq!(params.public_key().unwrap(), pubkey);
181+
}
182+
183+
#[test]
184+
fn test_build_pkey_ecdsa_public() {
185+
/* First, generate the key with old API */
186+
let nid: Nid = Nid::SECP256K1;
187+
let curve_name = nid.short_name().unwrap();
188+
let group = crate::ec::EcGroup::from_curve_name(nid).unwrap();
189+
let ec_key = EcKey::generate(&group).unwrap();
190+
let pkey1 = PKey::from_ec_key(ec_key.clone()).unwrap();
191+
192+
/* Now, build the new PKey from the old key with new API */
193+
let mut ctx = BigNumContext::new().unwrap();
194+
let pubkey = ec_key
195+
.public_key()
196+
.to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx)
197+
.unwrap();
198+
let bld = PKeyEcdsaBuilder::<Public>::new(curve_name, &pubkey, None).unwrap();
199+
let pkey2 = bld.build().unwrap();
200+
201+
/* Verify it works the same way as the old one */
202+
assert!(pkey1.public_eq(&pkey2));
203+
assert!(Error::get().is_none());
204+
205+
let params = PKeyEcdsaParams::<Public>::from_pkey(&pkey2).unwrap();
206+
assert_eq!(params.group().unwrap(), curve_name);
207+
assert_eq!(params.public_key().unwrap(), pubkey);
208+
}
209+
}

0 commit comments

Comments
 (0)