Skip to content

Commit

Permalink
Add support for EC_MONTGOMERY keys
Browse files Browse the repository at this point in the history
This refactors the ECDH out of the generic ECC module, as the same
code is used also for derivation operation with Montgomery EC keys.

Signed-off-by: Jakub Jelen <[email protected]>
  • Loading branch information
Jakuje committed Nov 19, 2024
1 parent e6623a5 commit 4de3062
Show file tree
Hide file tree
Showing 11 changed files with 1,169 additions and 261 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ zeroize = "1.6.0"
aes = []
ecc = []
eddsa = []
ec_montgomery = []
hash = []
hkdf = []
hmac = ["hash"]
Expand All @@ -69,7 +70,7 @@ basic = [ "aes", "hmac", "pbkdf2", "sqlitedb" ]

#select everything by default
# Use --no-default-features --features basic, xxx for custom selections
default = [ "basic", "ecc", "eddsa", "hash", "hkdf", "rsa", "sp800_108", "sshkdf", "tlskdf"]
default = [ "basic", "ecc", "ec_montgomery", "eddsa", "hash", "hkdf", "rsa", "sp800_108", "sshkdf", "tlskdf"]

fips = [ "rusqlite/bundled", "basic", "ecc", "hash", "hkdf", "rsa", "sp800_108", "sshkdf", "tlskdf"]

Expand Down
244 changes: 244 additions & 0 deletions src/ec_montgomery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Copyright 2024 Jakub Jelen
// See LICENSE.txt file for terms

use std::fmt::Debug;

use crate::attribute::Attribute;
use crate::ecc_misc::*;
use crate::error::Result;
use crate::interface::*;
use crate::mechanism::*;
use crate::object::*;
use crate::ossl::ec_montgomery::*;
use crate::{attr_element, bytes_attr_not_empty};

use once_cell::sync::Lazy;

pub const MIN_EC_MONTGOMERY_SIZE_BITS: usize = BITS_CURVE25519;
pub const MAX_EC_MONTGOMERY_SIZE_BITS: usize = BITS_CURVE448;

#[derive(Debug)]
pub struct ECMontgomeryPubFactory {
attributes: Vec<ObjectAttr>,
}

impl ECMontgomeryPubFactory {
pub fn new() -> ECMontgomeryPubFactory {
let mut data: ECMontgomeryPubFactory = ECMontgomeryPubFactory {
attributes: Vec::new(),
};
data.attributes.append(&mut data.init_common_object_attrs());
data.attributes
.append(&mut data.init_common_storage_attrs());
data.attributes.append(&mut data.init_common_key_attrs());
data.attributes
.append(&mut data.init_common_public_key_attrs());
data.attributes.push(attr_element!(
CKA_EC_PARAMS; OAFlags::AlwaysRequired | OAFlags::Unchangeable;
Attribute::from_bytes; val Vec::new()));
data.attributes.push(attr_element!(
CKA_EC_POINT; OAFlags::RequiredOnCreate
| OAFlags::SettableOnlyOnCreate | OAFlags::Unchangeable;
Attribute::from_bytes; val Vec::new()));
data
}
}

impl ObjectFactory for ECMontgomeryPubFactory {
fn create(&self, template: &[CK_ATTRIBUTE]) -> Result<Object> {
let obj = self.default_object_create(template)?;

bytes_attr_not_empty!(obj; CKA_EC_PARAMS);
bytes_attr_not_empty!(obj; CKA_EC_POINT);

Ok(obj)
}

fn get_attributes(&self) -> &Vec<ObjectAttr> {
&self.attributes
}
}

impl CommonKeyFactory for ECMontgomeryPubFactory {}

impl PubKeyFactory for ECMontgomeryPubFactory {}

#[derive(Debug)]
pub struct ECMontgomeryPrivFactory {
attributes: Vec<ObjectAttr>,
}

impl ECMontgomeryPrivFactory {
pub fn new() -> ECMontgomeryPrivFactory {
let mut data: ECMontgomeryPrivFactory = ECMontgomeryPrivFactory {
attributes: Vec::new(),
};
data.attributes.append(&mut data.init_common_object_attrs());
data.attributes
.append(&mut data.init_common_storage_attrs());
data.attributes.append(&mut data.init_common_key_attrs());
data.attributes
.append(&mut data.init_common_private_key_attrs());
data.attributes.push(attr_element!(
CKA_EC_PARAMS; OAFlags::RequiredOnCreate | OAFlags::Unchangeable;
Attribute::from_bytes; val Vec::new()));
data.attributes.push(attr_element!(
CKA_VALUE; OAFlags::Sensitive | OAFlags::RequiredOnCreate
| OAFlags::SettableOnlyOnCreate | OAFlags::Unchangeable;
Attribute::from_bytes; val Vec::new()));

/* default to private */
let private = attr_element!(
CKA_PRIVATE; OAFlags::Defval | OAFlags::ChangeOnCopy;
Attribute::from_bool; val true);
match data
.attributes
.iter()
.position(|x| x.get_type() == CKA_PRIVATE)
{
Some(idx) => data.attributes[idx] = private,
None => data.attributes.push(private),
}

data
}
}

impl ObjectFactory for ECMontgomeryPrivFactory {
fn create(&self, template: &[CK_ATTRIBUTE]) -> Result<Object> {
let mut obj = self.default_object_create(template)?;

ec_key_check_import(&mut obj)?;

Ok(obj)
}

fn get_attributes(&self) -> &Vec<ObjectAttr> {
&self.attributes
}

fn export_for_wrapping(&self, key: &Object) -> Result<Vec<u8>> {
PrivKeyFactory::export_for_wrapping(self, key)
}

fn import_from_wrapped(
&self,
data: Vec<u8>,
template: &[CK_ATTRIBUTE],
) -> Result<Object> {
PrivKeyFactory::import_from_wrapped(self, data, template)
}
}

impl CommonKeyFactory for ECMontgomeryPrivFactory {}

impl PrivKeyFactory for ECMontgomeryPrivFactory {}

static PUBLIC_KEY_FACTORY: Lazy<Box<dyn ObjectFactory>> =
Lazy::new(|| Box::new(ECMontgomeryPubFactory::new()));

static PRIVATE_KEY_FACTORY: Lazy<Box<dyn ObjectFactory>> =
Lazy::new(|| Box::new(ECMontgomeryPrivFactory::new()));

#[derive(Debug)]
struct ECMontgomeryMechanism {
info: CK_MECHANISM_INFO,
}

impl ECMontgomeryMechanism {
fn register_mechanisms(mechs: &mut Mechanisms) {
/* TODO PKCS #11 defines also CKM_XEDDSA for signatures, but it is not implemented by
* OpenSSL */
mechs.add_mechanism(
CKM_EC_MONTGOMERY_KEY_PAIR_GEN,
Box::new(ECMontgomeryMechanism {
info: CK_MECHANISM_INFO {
ulMinKeySize: CK_ULONG::try_from(
MIN_EC_MONTGOMERY_SIZE_BITS,
)
.unwrap(),
ulMaxKeySize: CK_ULONG::try_from(
MAX_EC_MONTGOMERY_SIZE_BITS,
)
.unwrap(),
flags: CKF_GENERATE_KEY_PAIR,
},
}),
);
}
}

impl Mechanism for ECMontgomeryMechanism {
fn info(&self) -> &CK_MECHANISM_INFO {
&self.info
}

fn generate_keypair(
&self,
mech: &CK_MECHANISM,
pubkey_template: &[CK_ATTRIBUTE],
prikey_template: &[CK_ATTRIBUTE],
) -> Result<(Object, Object)> {
let mut pubkey =
PUBLIC_KEY_FACTORY.default_object_generate(pubkey_template)?;
if !pubkey.check_or_set_attr(Attribute::from_ulong(
CKA_CLASS,
CKO_PUBLIC_KEY,
))? {
return Err(CKR_TEMPLATE_INCONSISTENT)?;
}
if !pubkey.check_or_set_attr(Attribute::from_ulong(
CKA_KEY_TYPE,
CKK_EC_EDWARDS,
))? {
return Err(CKR_TEMPLATE_INCONSISTENT)?;
}

let mut privkey =
PRIVATE_KEY_FACTORY.default_object_generate(prikey_template)?;
if !privkey.check_or_set_attr(Attribute::from_ulong(
CKA_CLASS,
CKO_PRIVATE_KEY,
))? {
return Err(CKR_TEMPLATE_INCONSISTENT)?;
}
if !privkey.check_or_set_attr(Attribute::from_ulong(
CKA_KEY_TYPE,
CKK_EC_EDWARDS,
))? {
return Err(CKR_TEMPLATE_INCONSISTENT)?;
}

let ec_params = match pubkey.get_attr_as_bytes(CKA_EC_PARAMS) {
Ok(a) => a.clone(),
Err(_) => {
return Err(CKR_ATTRIBUTE_VALUE_INVALID)?;
}
};
if !privkey.check_or_set_attr(Attribute::from_bytes(
CKA_EC_PARAMS,
ec_params,
))? {
return Err(CKR_TEMPLATE_INCONSISTENT)?;
}

ECMontgomeryOperation::generate_keypair(&mut pubkey, &mut privkey)?;
default_key_attributes(&mut privkey, mech.mechanism)?;
default_key_attributes(&mut pubkey, mech.mechanism)?;

Ok((pubkey, privkey))
}
}

pub fn register(mechs: &mut Mechanisms, ot: &mut ObjectFactories) {
ECMontgomeryMechanism::register_mechanisms(mechs);

ot.add_factory(
ObjectType::new(CKO_PUBLIC_KEY, CKK_EC_MONTGOMERY),
&PUBLIC_KEY_FACTORY,
);
ot.add_factory(
ObjectType::new(CKO_PRIVATE_KEY, CKK_EC_MONTGOMERY),
&PRIVATE_KEY_FACTORY,
);
}
19 changes: 2 additions & 17 deletions src/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use crate::interface::*;
use crate::kasn1::PrivateKeyInfo;
use crate::mechanism::*;
use crate::object::*;
use crate::ossl::ecc::{ECDHOperation, EccOperation};
use crate::{attr_element, bytes_attr_not_empty, cast_params};
use crate::ossl::ecc::EccOperation;
use crate::{attr_element, bytes_attr_not_empty};

use asn1;
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -398,20 +398,6 @@ impl Mechanism for EccMechanism {
Ok(Box::new(EccOperation::verify_new(mech, key, &self.info)?))
}

fn derive_operation(&self, mech: &CK_MECHANISM) -> Result<Operation> {
if self.info.flags & CKF_DERIVE != CKF_DERIVE {
return Err(CKR_MECHANISM_INVALID)?;
}
let kdf = match mech.mechanism {
CKM_ECDH1_DERIVE | CKM_ECDH1_COFACTOR_DERIVE => {
let params = cast_params!(mech, CK_ECDH1_DERIVE_PARAMS);
ECDHOperation::derive_new(mech.mechanism, params)?
}
_ => return Err(CKR_MECHANISM_INVALID)?,
};
Ok(Operation::Derive(Box::new(kdf)))
}

fn generate_keypair(
&self,
mech: &CK_MECHANISM,
Expand Down Expand Up @@ -469,7 +455,6 @@ impl Mechanism for EccMechanism {

pub fn register(mechs: &mut Mechanisms, ot: &mut ObjectFactories) {
EccOperation::register_mechanisms(mechs);
ECDHOperation::register_mechanisms(mechs);

ot.add_factory(
ObjectType::new(CKO_PUBLIC_KEY, CKK_EC),
Expand Down
60 changes: 60 additions & 0 deletions src/ecdh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2024 Simo Sorce, Jakub Jelen
// See LICENSE.txt file for terms

use std::fmt::Debug;

use crate::ecc::*;
use crate::error::Result;
use crate::interface::*;
use crate::mechanism::{Mechanism, Mechanisms, Operation};
use crate::object::ObjectFactories;
use crate::ossl::ecdh::ECDHOperation;

use crate::cast_params;

pub fn register(mechs: &mut Mechanisms, _: &mut ObjectFactories) {
ECDHMechanism::register_mechanisms(mechs);
}

#[derive(Debug)]
struct ECDHMechanism {
info: CK_MECHANISM_INFO,
}

impl ECDHMechanism {
fn new_mechanism() -> Box<dyn Mechanism> {
Box::new(ECDHMechanism {
info: CK_MECHANISM_INFO {
ulMinKeySize: CK_ULONG::try_from(MIN_EC_SIZE_BITS).unwrap(),
ulMaxKeySize: CK_ULONG::try_from(MAX_EC_SIZE_BITS).unwrap(),
flags: CKF_DERIVE,
},
})
}

pub fn register_mechanisms(mechs: &mut Mechanisms) {
for ckm in &[CKM_ECDH1_DERIVE, CKM_ECDH1_COFACTOR_DERIVE] {
mechs.add_mechanism(*ckm, Self::new_mechanism());
}
}
}

impl Mechanism for ECDHMechanism {
fn info(&self) -> &CK_MECHANISM_INFO {
&self.info
}

fn derive_operation(&self, mech: &CK_MECHANISM) -> Result<Operation> {
if self.info.flags & CKF_DERIVE != CKF_DERIVE {
return Err(CKR_MECHANISM_INVALID)?;
}
let kdf = match mech.mechanism {
CKM_ECDH1_DERIVE | CKM_ECDH1_COFACTOR_DERIVE => {
let params = cast_params!(mech, CK_ECDH1_DERIVE_PARAMS);
ECDHOperation::derive_new(mech.mechanism, params)?
}
_ => return Err(CKR_MECHANISM_INVALID)?,
};
Ok(Operation::Derive(Box::new(kdf)))
}
}
12 changes: 12 additions & 0 deletions src/enabled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ mod ecc;
#[cfg(any(feature = "ecc", feature = "eddsa"))]
mod ecc_misc;

#[cfg(all(feature = "ec_montgomery", not(feature = "fips")))]
mod ec_montgomery;

#[cfg(any(feature = "ec_montgomery", feature = "ecc"))]
mod ecdh;

#[cfg(all(feature = "eddsa", not(feature = "fips")))]
mod eddsa;

Expand Down Expand Up @@ -52,6 +58,12 @@ pub fn register_all(mechs: &mut Mechanisms, ot: &mut ObjectFactories) {
#[cfg(feature = "ecc")]
ecc::register(mechs, ot);

#[cfg(any(feature = "ec_montgomery", feature = "ecc"))]
ecdh::register(mechs, ot);

#[cfg(all(feature = "ec_montgomery", not(feature = "fips")))]
ec_montgomery::register(mechs, ot);

#[cfg(all(feature = "eddsa", not(feature = "fips")))]
eddsa::register(mechs, ot);

Expand Down
Loading

0 comments on commit 4de3062

Please sign in to comment.