Skip to content

Commit 26e0a36

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

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-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

+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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+
#[corresponds(OSSL_PARAM_BLD_new)]
36+
#[corresponds(OSSL_PARAM_BLD_push_BN)]
37+
#[corresponds(OSSL_PARAM_BLD_push_utf8_string)]
38+
#[corresponds(OSSL_PARAM_BLD_push_octet_string)]
39+
pub fn new(
40+
group: &str,
41+
point: &[u8],
42+
private: Option<&BigNumRef>,
43+
) -> Result<PKeyEcdsaBuilder<T>, ErrorStack> {
44+
let bld = OsslParamBuilder::new()?;
45+
bld.add_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, group)?;
46+
bld.add_octet_string(OSSL_PKEY_PARAM_PUB_KEY, point)?;
47+
if let Some(private) = private {
48+
bld.add_bn(OSSL_PKEY_PARAM_PRIV_KEY, private)?
49+
};
50+
Ok(PKeyEcdsaBuilder::<T> {
51+
bld,
52+
_m: ::std::marker::PhantomData,
53+
})
54+
}
55+
56+
/// Build PKey. Internal.
57+
#[corresponds(OSSL_PARAM_BLD_to_param)]
58+
#[corresponds(EVP_PKEY_fromdata)]
59+
fn build_internal(self, selection: c_int) -> Result<PKey<T>, ErrorStack> {
60+
let mut ctx = PkeyCtx::new_from_name(None, "EC", None)?;
61+
ctx.fromdata_init()?;
62+
let params = self.bld.to_param()?;
63+
unsafe {
64+
let evp = cvt_p(ffi::EVP_PKEY_new())?;
65+
let pkey = PKey::from_ptr(evp);
66+
cvt(ffi::EVP_PKEY_fromdata(
67+
ctx.as_ptr(),
68+
&mut pkey.as_ptr(),
69+
selection,
70+
params.as_ptr(),
71+
))?;
72+
Ok(pkey)
73+
}
74+
}
75+
}
76+
77+
impl PKeyEcdsaBuilder<Private> {
78+
/// Returns the Private ECDSA PKey from the provided parameters.
79+
#[corresponds(OSSL_PARAM_BLD_to_param)]
80+
#[corresponds(EVP_PKEY_fromdata)]
81+
pub fn build(self) -> Result<PKey<Private>, ErrorStack> {
82+
/* The ECDSA requires here a keypair as the private key does not work without public point! */
83+
self.build_internal(ffi::EVP_PKEY_KEYPAIR)
84+
}
85+
}
86+
87+
impl PKeyEcdsaBuilder<Public> {
88+
/// Returns the Public ECDSA PKey from the provided parameters.
89+
#[corresponds(OSSL_PARAM_BLD_to_param)]
90+
#[corresponds(EVP_PKEY_fromdata)]
91+
pub fn build(self) -> Result<PKey<Public>, ErrorStack> {
92+
self.build_internal(ffi::EVP_PKEY_PUBLIC_KEY)
93+
}
94+
}
95+
96+
pub struct PKeyEcdsaParams<T> {
97+
params: OsslParam,
98+
_m: ::std::marker::PhantomData<T>,
99+
}
100+
101+
impl<T> PKeyEcdsaParams<T> {
102+
/// Creates a new `PKeyEcdsaParams` from existing ECDSA PKey. Internal.
103+
#[corresponds(EVP_PKEY_todata)]
104+
fn _new_from_pkey(pkey: &PKey<T>, selection: c_int) -> Result<PKeyEcdsaParams<T>, ErrorStack> {
105+
unsafe {
106+
let mut params: *mut ffi::OSSL_PARAM = ptr::null_mut();
107+
cvt(ffi::EVP_PKEY_todata(pkey.as_ptr(), selection, &mut params))?;
108+
Ok(PKeyEcdsaParams::<T> {
109+
params: OsslParam::from_ptr(params),
110+
_m: ::std::marker::PhantomData,
111+
})
112+
}
113+
}
114+
115+
/// Returns a reference to the public key.
116+
#[corresponds(OSSL_PARAM_locate)]
117+
#[corresponds(OSSL_PARAM_get_BN)]
118+
pub fn public_key(&self) -> Result<&[u8], ErrorStack> {
119+
self.params
120+
.locate(OSSL_PKEY_PARAM_PUB_KEY)?
121+
.get_octet_string()
122+
}
123+
124+
/// Returns a reference to a group name
125+
#[corresponds(OSSL_PARAM_locate)]
126+
#[corresponds(OSSL_PARAM_get_BN)]
127+
pub fn group(&self) -> Result<&str, ErrorStack> {
128+
self.params
129+
.locate(OSSL_PKEY_PARAM_GROUP_NAME)?
130+
.get_utf8_string()
131+
}
132+
}
133+
134+
impl PKeyEcdsaParams<Public> {
135+
/// Creates a new `PKeyEcdsaParams` from existing Public ECDSA PKey.
136+
#[corresponds(EVP_PKEY_todata)]
137+
pub fn from_pkey(pkey: &PKey<Public>) -> Result<PKeyEcdsaParams<Public>, ErrorStack> {
138+
Self::_new_from_pkey(pkey, ffi::EVP_PKEY_PUBLIC_KEY)
139+
}
140+
}
141+
142+
impl PKeyEcdsaParams<Private> {
143+
/// Creates a new `PKeyEcdsaParams` from existing Private ECDSA PKey.
144+
#[corresponds(EVP_PKEY_todata)]
145+
pub fn from_pkey(pkey: &PKey<Private>) -> Result<PKeyEcdsaParams<Private>, ErrorStack> {
146+
Self::_new_from_pkey(pkey, ffi::EVP_PKEY_KEYPAIR)
147+
}
148+
149+
/// Returns the private key.
150+
#[corresponds(OSSL_PARAM_locate)]
151+
#[corresponds(OSSL_PARAM_get_BN)]
152+
pub fn private_key(&self) -> Result<BigNum, ErrorStack> {
153+
self.params.locate(OSSL_PKEY_PARAM_PRIV_KEY)?.get_bn()
154+
}
155+
}
156+
157+
#[cfg(test)]
158+
mod tests {
159+
160+
use crate::bn::BigNumContext;
161+
use crate::ec::{EcKey, PointConversionForm};
162+
use crate::error::Error;
163+
use crate::nid::Nid;
164+
165+
use super::*;
166+
167+
#[test]
168+
fn test_build_pkey_ecdsa_private() {
169+
/* First, generate the key with old API */
170+
let nid: Nid = Nid::SECP256K1;
171+
let curve_name = nid.short_name().unwrap();
172+
let group = crate::ec::EcGroup::from_curve_name(nid).unwrap();
173+
let ec_key = EcKey::generate(&group).unwrap();
174+
let pkey1 = PKey::from_ec_key(ec_key.clone()).unwrap();
175+
176+
/* Now, build the new PKey from the old key with new API */
177+
let mut ctx = BigNumContext::new().unwrap();
178+
let pubkey = ec_key
179+
.public_key()
180+
.to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx)
181+
.unwrap();
182+
let bld = PKeyEcdsaBuilder::<Private>::new(curve_name, &pubkey, Some(ec_key.private_key()))
183+
.unwrap();
184+
let pkey2 = bld.build().unwrap();
185+
186+
/* Verify it works the same way as the old one */
187+
assert!(pkey1.public_eq(&pkey2));
188+
assert!(Error::get().is_none());
189+
190+
let params = PKeyEcdsaParams::<Private>::from_pkey(&pkey2).unwrap();
191+
assert_eq!(params.group().unwrap(), curve_name);
192+
assert_eq!(&params.private_key().unwrap(), ec_key.private_key());
193+
assert_eq!(params.public_key().unwrap(), pubkey);
194+
}
195+
196+
#[test]
197+
fn test_build_pkey_ecdsa_public() {
198+
/* First, generate the key with old API */
199+
let nid: Nid = Nid::SECP256K1;
200+
let curve_name = nid.short_name().unwrap();
201+
let group = crate::ec::EcGroup::from_curve_name(nid).unwrap();
202+
let ec_key = EcKey::generate(&group).unwrap();
203+
let pkey1 = PKey::from_ec_key(ec_key.clone()).unwrap();
204+
205+
/* Now, build the new PKey from the old key with new API */
206+
let mut ctx = BigNumContext::new().unwrap();
207+
let pubkey = ec_key
208+
.public_key()
209+
.to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx)
210+
.unwrap();
211+
let bld = PKeyEcdsaBuilder::<Public>::new(curve_name, &pubkey, None).unwrap();
212+
let pkey2 = bld.build().unwrap();
213+
214+
/* Verify it works the same way as the old one */
215+
assert!(pkey1.public_eq(&pkey2));
216+
assert!(Error::get().is_none());
217+
218+
let params = PKeyEcdsaParams::<Public>::from_pkey(&pkey2).unwrap();
219+
assert_eq!(params.group().unwrap(), curve_name);
220+
assert_eq!(params.public_key().unwrap(), pubkey);
221+
}
222+
}

0 commit comments

Comments
 (0)