2026-07-01 20:48:44 +02:00
|
|
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
|
// Copyright (c) VexaHub and contributors.
|
2025-04-28 21:46:58 +02:00
|
|
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
#![cfg(test_hsm)]
|
|
|
|
|
#![allow(type_alias_bounds)]
|
|
|
|
|
|
2025-04-22 09:08:17 +02:00
|
|
|
use std::env;
|
|
|
|
|
use std::sync::{LazyLock, Mutex};
|
|
|
|
|
use std::vec::Vec;
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
use ::ecdsa::SignatureSize;
|
2025-04-22 09:08:17 +02:00
|
|
|
use cryptoki::context::{CInitializeArgs, Pkcs11};
|
|
|
|
|
use cryptoki::mechanism::Mechanism;
|
2025-07-10 21:55:41 +02:00
|
|
|
use cryptoki::mechanism::elliptic_curve::{EcKdf, Ecdh1DeriveParams};
|
2025-04-22 09:08:17 +02:00
|
|
|
use cryptoki::object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle};
|
|
|
|
|
use cryptoki::session::{Session, UserType};
|
|
|
|
|
use cryptoki::types::AuthPin;
|
2025-05-19 22:56:25 +02:00
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
use digest::Digest;
|
2025-05-05 13:12:54 +02:00
|
|
|
use digest::OutputSizeUser;
|
2025-07-10 21:55:41 +02:00
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
use elliptic_curve::PrimeCurve;
|
2025-04-22 09:08:17 +02:00
|
|
|
use elliptic_curve::group::Curve;
|
2025-07-10 21:55:41 +02:00
|
|
|
use elliptic_curve::group::prime::PrimeCurveAffine;
|
2025-04-22 09:08:17 +02:00
|
|
|
use elliptic_curve::pkcs8::der::asn1::{OctetString, OctetStringRef};
|
|
|
|
|
use elliptic_curve::pkcs8::der::{Decode, Encode};
|
|
|
|
|
use elliptic_curve::pkcs8::{AssociatedOid, ObjectIdentifier};
|
|
|
|
|
use elliptic_curve::point::{AffineCoordinates, DecompressPoint};
|
2025-05-19 22:56:25 +02:00
|
|
|
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, Tag, ToEncodedPoint};
|
|
|
|
|
use elliptic_curve::{AffinePoint, CurveArithmetic, FieldBytesSize, Group as _, ProjectivePoint};
|
|
|
|
|
use generic_array::typenum::Unsigned;
|
2025-04-22 09:08:17 +02:00
|
|
|
use generic_array::{ArrayLength, GenericArray};
|
2026-07-01 11:52:12 +02:00
|
|
|
use opaque_vx::key_exchange::KeyExchange;
|
|
|
|
|
use opaque_vx::key_exchange::group::Group;
|
2025-05-19 22:56:25 +02:00
|
|
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
2026-07-01 11:52:12 +02:00
|
|
|
use opaque_vx::key_exchange::group::ed25519::{self, Ed25519};
|
|
|
|
|
use opaque_vx::key_exchange::group::elliptic_curve::NonIdentity;
|
2025-05-19 22:56:25 +02:00
|
|
|
#[cfg(feature = "ecdsa")]
|
2026-07-01 11:52:12 +02:00
|
|
|
use opaque_vx::key_exchange::sigma_i::ecdsa::{self, Ecdsa, PreHash};
|
2025-05-19 22:56:25 +02:00
|
|
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
2026-07-01 11:52:12 +02:00
|
|
|
use opaque_vx::key_exchange::sigma_i::pure_eddsa::PureEddsa;
|
2025-05-19 22:56:25 +02:00
|
|
|
#[cfg(feature = "ecdsa")]
|
2026-07-01 11:52:12 +02:00
|
|
|
use opaque_vx::key_exchange::sigma_i::{CachedMessage, HashOutput, Message, SigmaI};
|
|
|
|
|
use opaque_vx::key_exchange::tripledh::TripleDh;
|
|
|
|
|
use opaque_vx::keypair::{KeyPair, PublicKey};
|
|
|
|
|
use opaque_vx::ksf::Identity;
|
|
|
|
|
use opaque_vx::{
|
2025-05-19 22:56:25 +02:00
|
|
|
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartResult,
|
|
|
|
|
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationStartResult,
|
|
|
|
|
ServerLogin, ServerLoginParameters, ServerLoginStartResult, ServerRegistration, ServerSetup,
|
|
|
|
|
};
|
|
|
|
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
2026-07-01 11:52:12 +02:00
|
|
|
use opaque_vx::{Curve25519, Ristretto255};
|
2025-04-22 09:08:17 +02:00
|
|
|
use p256::NistP256;
|
|
|
|
|
use p384::NistP384;
|
|
|
|
|
use p521::NistP521;
|
|
|
|
|
use rand::rngs::OsRng;
|
2025-05-19 22:56:25 +02:00
|
|
|
use sha2::{Sha256, Sha384, Sha512};
|
2025-04-22 09:08:17 +02:00
|
|
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
type OprfGroup<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Group;
|
|
|
|
|
type OprfHash<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Hash;
|
|
|
|
|
type KeGroup<CS: CipherSuite> = <CS::KeyExchange as KeyExchange>::Group;
|
2025-04-22 09:08:17 +02:00
|
|
|
|
|
|
|
|
#[test]
|
2025-05-19 22:56:25 +02:00
|
|
|
fn triple_dh_p256() {
|
2025-04-22 09:08:17 +02:00
|
|
|
struct Suite;
|
|
|
|
|
|
|
|
|
|
impl CipherSuite for Suite {
|
|
|
|
|
type OprfCs = NistP256;
|
2025-05-19 22:56:25 +02:00
|
|
|
type KeyExchange = TripleDh<NistP256, Sha256>;
|
2025-04-22 09:08:17 +02:00
|
|
|
type Ksf = Identity;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-05 13:12:54 +02:00
|
|
|
test::<Suite>(
|
|
|
|
|
Mechanism::EccKeyPairGen,
|
|
|
|
|
NistP256::OID,
|
2025-05-19 22:56:25 +02:00
|
|
|
Attribute::Derive(true),
|
2025-05-05 13:12:54 +02:00
|
|
|
Mechanism::Sha256Hmac,
|
|
|
|
|
);
|
2025-04-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-05-19 22:56:25 +02:00
|
|
|
fn triple_dh_p384() {
|
2025-04-22 09:08:17 +02:00
|
|
|
struct Suite;
|
|
|
|
|
|
|
|
|
|
impl CipherSuite for Suite {
|
|
|
|
|
type OprfCs = NistP384;
|
2025-05-19 22:56:25 +02:00
|
|
|
type KeyExchange = TripleDh<NistP384, Sha384>;
|
2025-04-22 09:08:17 +02:00
|
|
|
type Ksf = Identity;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-05 13:12:54 +02:00
|
|
|
test::<Suite>(
|
|
|
|
|
Mechanism::EccKeyPairGen,
|
|
|
|
|
NistP384::OID,
|
2025-05-19 22:56:25 +02:00
|
|
|
Attribute::Derive(true),
|
2025-05-05 13:12:54 +02:00
|
|
|
Mechanism::Sha384Hmac,
|
|
|
|
|
);
|
2025-04-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-05-19 22:56:25 +02:00
|
|
|
fn triple_dh_p521() {
|
2025-04-22 09:08:17 +02:00
|
|
|
struct Suite;
|
|
|
|
|
|
|
|
|
|
impl CipherSuite for Suite {
|
|
|
|
|
type OprfCs = NistP521;
|
2025-05-19 22:56:25 +02:00
|
|
|
type KeyExchange = TripleDh<NistP521, Sha512>;
|
2025-04-22 09:08:17 +02:00
|
|
|
type Ksf = Identity;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-05 13:12:54 +02:00
|
|
|
test::<Suite>(
|
|
|
|
|
Mechanism::EccKeyPairGen,
|
|
|
|
|
NistP521::OID,
|
2025-05-19 22:56:25 +02:00
|
|
|
Attribute::Derive(true),
|
2025-05-05 13:12:54 +02:00
|
|
|
Mechanism::Sha512Hmac,
|
|
|
|
|
);
|
2025-04-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
2025-05-19 22:56:25 +02:00
|
|
|
fn triple_dh_curve25519() {
|
2025-04-22 09:08:17 +02:00
|
|
|
struct Suite;
|
|
|
|
|
|
|
|
|
|
impl CipherSuite for Suite {
|
|
|
|
|
type OprfCs = Ristretto255;
|
2025-05-19 22:56:25 +02:00
|
|
|
type KeyExchange = TripleDh<Curve25519, Sha512>;
|
2025-04-22 09:08:17 +02:00
|
|
|
type Ksf = Identity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test::<Suite>(
|
|
|
|
|
// This should be [`Mechanism::EccMontgomeryKeyPairGen`], but SoftHSM has an incorrect
|
|
|
|
|
// implementation. See https://github.com/softhsm/SoftHSMv2/issues/647.
|
|
|
|
|
Mechanism::EccEdwardsKeyPairGen,
|
|
|
|
|
ObjectIdentifier::new("1.3.101.110").unwrap(),
|
2025-05-19 22:56:25 +02:00
|
|
|
Attribute::Derive(true),
|
|
|
|
|
Mechanism::Sha512Hmac,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
fn sigma_i_p256() {
|
|
|
|
|
struct Suite;
|
|
|
|
|
|
|
|
|
|
impl CipherSuite for Suite {
|
|
|
|
|
type OprfCs = NistP256;
|
|
|
|
|
type KeyExchange = SigmaI<Ecdsa<NistP256, Sha256>, NistP256, Sha256>;
|
|
|
|
|
type Ksf = Identity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test::<Suite>(
|
|
|
|
|
Mechanism::EccKeyPairGen,
|
|
|
|
|
NistP256::OID,
|
|
|
|
|
Attribute::Sign(true),
|
|
|
|
|
Mechanism::Sha256Hmac,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
fn sigma_i_p384() {
|
|
|
|
|
struct Suite;
|
|
|
|
|
|
|
|
|
|
impl CipherSuite for Suite {
|
|
|
|
|
type OprfCs = NistP384;
|
|
|
|
|
type KeyExchange = SigmaI<Ecdsa<NistP384, Sha384>, NistP384, Sha384>;
|
|
|
|
|
type Ksf = Identity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test::<Suite>(
|
|
|
|
|
Mechanism::EccKeyPairGen,
|
|
|
|
|
NistP384::OID,
|
|
|
|
|
Attribute::Sign(true),
|
|
|
|
|
Mechanism::Sha384Hmac,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
|
|
|
|
fn sigma_i_ed25519() {
|
|
|
|
|
struct Suite;
|
|
|
|
|
|
|
|
|
|
impl CipherSuite for Suite {
|
|
|
|
|
type OprfCs = Ristretto255;
|
|
|
|
|
type KeyExchange = SigmaI<PureEddsa<Ed25519>, Ristretto255, Sha512>;
|
|
|
|
|
type Ksf = Identity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test::<Suite>(
|
|
|
|
|
Mechanism::EccEdwardsKeyPairGen,
|
|
|
|
|
ObjectIdentifier::new_unwrap("1.3.101.112"),
|
|
|
|
|
Attribute::Sign(true),
|
2025-05-05 13:12:54 +02:00
|
|
|
Mechanism::Sha512Hmac,
|
2025-04-22 09:08:17 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
struct RemoteKey(ObjectHandle);
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
trait Pkcs11PublicKey
|
|
|
|
|
where
|
|
|
|
|
Self: Group,
|
|
|
|
|
{
|
|
|
|
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<Self>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trait Pkcs11KeyExchange<KE: KeyExchange> {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
2025-04-22 09:08:17 +02:00
|
|
|
&self,
|
2025-05-19 22:56:25 +02:00
|
|
|
server_pk: &PublicKey<KE::Group>,
|
|
|
|
|
data: KE::KE2BuilderData<'_, CS>,
|
|
|
|
|
) -> KE::KE2BuilderInput<CS>;
|
2025-04-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
fn test<CS: 'static + CipherSuite>(
|
2025-05-05 13:12:54 +02:00
|
|
|
dh_mechanism: Mechanism,
|
|
|
|
|
oid: ObjectIdentifier,
|
2025-05-19 22:56:25 +02:00
|
|
|
attribute: Attribute,
|
2025-05-05 13:12:54 +02:00
|
|
|
hmac_mechanism: Mechanism,
|
|
|
|
|
) where
|
2025-05-19 22:56:25 +02:00
|
|
|
KeGroup<CS>: Pkcs11PublicKey,
|
|
|
|
|
RemoteKey: Pkcs11KeyExchange<CS::KeyExchange>,
|
2025-04-22 09:08:17 +02:00
|
|
|
{
|
2025-05-19 22:56:25 +02:00
|
|
|
let (remote_key, pk) = pkcs11_generate_key_pair(dh_mechanism, oid, attribute);
|
2025-04-22 09:08:17 +02:00
|
|
|
|
|
|
|
|
let keypair = KeyPair::new(RemoteKey(remote_key), pk);
|
2025-05-05 13:12:54 +02:00
|
|
|
let oprf_seed = pkcs11_generate_oprf_seed(<OprfHash<CS> as OutputSizeUser>::OutputSize::U64);
|
|
|
|
|
let server_setup = ServerSetup::new_with_key_pair_and_seed(&mut OsRng, keypair, oprf_seed);
|
2025-04-22 09:08:17 +02:00
|
|
|
|
|
|
|
|
const PASSWORD: &str = "password";
|
|
|
|
|
|
|
|
|
|
let ClientRegistrationStartResult {
|
|
|
|
|
message,
|
|
|
|
|
state: client,
|
|
|
|
|
} = ClientRegistration::<CS>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
2025-05-05 13:12:54 +02:00
|
|
|
let key_material_info = server_setup.key_material_info(&[]);
|
|
|
|
|
let key_material = pkcs11_hkdf::<CS>(
|
|
|
|
|
key_material_info.ikm,
|
|
|
|
|
hmac_mechanism,
|
|
|
|
|
Vec::from_iter(key_material_info.info.into_iter().flatten().copied()),
|
|
|
|
|
);
|
|
|
|
|
let message = ServerRegistration::start_with_key_material(&server_setup, key_material, message)
|
2025-04-22 09:08:17 +02:00
|
|
|
.unwrap()
|
|
|
|
|
.message;
|
|
|
|
|
let message = client
|
|
|
|
|
.finish(
|
|
|
|
|
&mut OsRng,
|
|
|
|
|
PASSWORD.as_bytes(),
|
|
|
|
|
message,
|
|
|
|
|
ClientRegistrationFinishParameters::default(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.message;
|
|
|
|
|
let file = ServerRegistration::finish(message);
|
|
|
|
|
|
|
|
|
|
let ClientLoginStartResult {
|
|
|
|
|
message,
|
|
|
|
|
state: client,
|
|
|
|
|
} = ClientLogin::<CS>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
2025-05-05 13:12:54 +02:00
|
|
|
let key_material_info = server_setup.key_material_info(&[]);
|
|
|
|
|
let key_material = pkcs11_hkdf::<CS>(
|
|
|
|
|
key_material_info.ikm,
|
|
|
|
|
hmac_mechanism,
|
|
|
|
|
Vec::from_iter(key_material_info.info.into_iter().flatten().copied()),
|
|
|
|
|
);
|
|
|
|
|
let builder = ServerLogin::builder_with_key_material(
|
2025-04-22 09:08:17 +02:00
|
|
|
&mut OsRng,
|
|
|
|
|
&server_setup,
|
2025-05-05 13:12:54 +02:00
|
|
|
key_material,
|
2025-04-22 09:08:17 +02:00
|
|
|
Some(file),
|
|
|
|
|
message,
|
2025-05-19 22:56:25 +02:00
|
|
|
ServerLoginParameters::default(),
|
2025-04-22 09:08:17 +02:00
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let shared_secret = builder
|
|
|
|
|
.private_key()
|
2025-05-19 22:56:25 +02:00
|
|
|
.pkcs11_key_exchange(server_setup.keypair().public(), builder.data());
|
2025-04-22 09:08:17 +02:00
|
|
|
|
|
|
|
|
let ServerLoginStartResult {
|
|
|
|
|
message,
|
|
|
|
|
state: server,
|
|
|
|
|
..
|
|
|
|
|
} = builder.clone().build(shared_secret).unwrap();
|
|
|
|
|
|
|
|
|
|
let message = client
|
|
|
|
|
.clone()
|
|
|
|
|
.finish(
|
2025-05-19 22:56:25 +02:00
|
|
|
&mut OsRng,
|
2025-04-22 09:08:17 +02:00
|
|
|
PASSWORD.as_bytes(),
|
|
|
|
|
message,
|
|
|
|
|
ClientLoginFinishParameters::default(),
|
|
|
|
|
)
|
|
|
|
|
.map(|result| result.message);
|
|
|
|
|
|
|
|
|
|
message
|
2025-05-19 22:56:25 +02:00
|
|
|
.map(|message| {
|
|
|
|
|
server
|
|
|
|
|
.finish(message, ServerLoginParameters::default())
|
|
|
|
|
.unwrap()
|
|
|
|
|
})
|
2025-04-22 09:08:17 +02:00
|
|
|
.unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SESSION: LazyLock<Mutex<Session>> = LazyLock::new(|| {
|
|
|
|
|
let module = env::var("PKCS11_MODULE").expect("`PKCS11_MODULE` environment variable");
|
|
|
|
|
let pkcs11 = Pkcs11::new(module).unwrap();
|
|
|
|
|
pkcs11.initialize(CInitializeArgs::OsThreads).unwrap();
|
|
|
|
|
|
|
|
|
|
let slot = pkcs11.get_slots_with_token().unwrap()[0];
|
|
|
|
|
|
|
|
|
|
let so_pin = AuthPin::new("abcdef".into());
|
|
|
|
|
pkcs11.init_token(slot, &so_pin, "Test Token").unwrap();
|
|
|
|
|
|
|
|
|
|
let user_pin = AuthPin::new("fedcba".into());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let session = pkcs11.open_rw_session(slot).unwrap();
|
|
|
|
|
session.login(UserType::So, Some(&so_pin)).unwrap();
|
|
|
|
|
session.init_pin(&user_pin).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let session = pkcs11.open_rw_session(slot).unwrap();
|
|
|
|
|
session.login(UserType::User, Some(&user_pin)).unwrap();
|
|
|
|
|
|
|
|
|
|
Mutex::new(session)
|
|
|
|
|
});
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
fn pkcs11_generate_key_pair<G: Group + Pkcs11PublicKey>(
|
2025-04-22 09:08:17 +02:00
|
|
|
mechanism: Mechanism,
|
|
|
|
|
oid: ObjectIdentifier,
|
2025-05-19 22:56:25 +02:00
|
|
|
attribute: Attribute,
|
|
|
|
|
) -> (ObjectHandle, PublicKey<G>) {
|
2025-04-22 09:08:17 +02:00
|
|
|
let session = SESSION.lock().unwrap();
|
|
|
|
|
let (pk, remote_key) = session
|
|
|
|
|
.generate_key_pair(
|
|
|
|
|
&mechanism,
|
|
|
|
|
&[
|
|
|
|
|
Attribute::Token(false),
|
|
|
|
|
Attribute::EcParams(oid.to_der().unwrap()),
|
|
|
|
|
],
|
2025-05-19 22:56:25 +02:00
|
|
|
&[Attribute::Token(false), attribute],
|
2025-04-22 09:08:17 +02:00
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let Attribute::EcPoint(pk) = session
|
|
|
|
|
.get_attributes(pk, &[AttributeType::EcPoint])
|
|
|
|
|
.unwrap()
|
|
|
|
|
.pop()
|
|
|
|
|
.unwrap()
|
|
|
|
|
else {
|
|
|
|
|
unreachable!()
|
|
|
|
|
};
|
|
|
|
|
drop(session);
|
|
|
|
|
|
|
|
|
|
let pk = OctetString::from_der(&pk).unwrap();
|
2025-05-19 22:56:25 +02:00
|
|
|
let pk = G::pkcs11_public_key(pk.as_bytes());
|
2025-04-22 09:08:17 +02:00
|
|
|
|
|
|
|
|
(remote_key, pk)
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-05 13:12:54 +02:00
|
|
|
fn pkcs11_generate_oprf_seed(length: u64) -> ObjectHandle {
|
|
|
|
|
SESSION
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.generate_key(
|
|
|
|
|
&Mechanism::GenericSecretKeyGen,
|
|
|
|
|
&[Attribute::Token(false), Attribute::ValueLen(length.into())],
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SoftHSM, nor any other popular HSM at the time of writing, supports HKDF. So
|
|
|
|
|
// we instead implement HKDF by hand on top of the HSMs HMAC, which is supported
|
|
|
|
|
// by almost all HSMs and still protects the OPRF seed.
|
|
|
|
|
fn pkcs11_hkdf<CS: CipherSuite>(
|
|
|
|
|
hmac: ObjectHandle,
|
|
|
|
|
mechanism: Mechanism,
|
|
|
|
|
info: Vec<u8>,
|
|
|
|
|
) -> GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen> {
|
|
|
|
|
let mut okm = GenericArray::default();
|
|
|
|
|
let mut prev: Option<Vec<u8>> = None;
|
|
|
|
|
let chunk_len = <OprfHash<CS> as OutputSizeUser>::OutputSize::USIZE;
|
|
|
|
|
|
|
|
|
|
if okm.len() > chunk_len * 255 {
|
|
|
|
|
panic!("invalid length");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let session = SESSION.lock().unwrap();
|
|
|
|
|
|
|
|
|
|
for (block_n, block) in (0..).zip(okm.chunks_mut(chunk_len)) {
|
|
|
|
|
let mut data = Vec::new();
|
|
|
|
|
|
|
|
|
|
if let Some(ref prev) = prev {
|
|
|
|
|
data.extend(prev.as_slice())
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
data.extend(&info);
|
|
|
|
|
data.extend(&[block_n + 1]);
|
|
|
|
|
|
|
|
|
|
let output = session.sign(&mechanism, hmac, &data).unwrap();
|
|
|
|
|
block.copy_from_slice(&output[..block.len()]);
|
|
|
|
|
prev = Some(output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
okm
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
impl Pkcs11PublicKey for NistP256 {
|
|
|
|
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<NistP256> {
|
|
|
|
|
pkcs11_ec_public_key(data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pkcs11PublicKey for NistP384 {
|
|
|
|
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<NistP384> {
|
|
|
|
|
pkcs11_ec_public_key(data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pkcs11PublicKey for NistP521 {
|
|
|
|
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<NistP521> {
|
|
|
|
|
pkcs11_ec_public_key(data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
|
|
|
|
impl Pkcs11PublicKey for Curve25519 {
|
|
|
|
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<Curve25519> {
|
|
|
|
|
PublicKey::deserialize(data).unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
|
|
|
|
impl Pkcs11PublicKey for Ed25519 {
|
|
|
|
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<Ed25519> {
|
|
|
|
|
PublicKey::deserialize(data).unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pkcs11KeyExchange<TripleDh<NistP256, Sha256>> for RemoteKey {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
2025-04-22 09:08:17 +02:00
|
|
|
&self,
|
|
|
|
|
server_pk: &PublicKey<NistP256>,
|
|
|
|
|
client_pk: &PublicKey<NistP256>,
|
2025-05-19 22:56:25 +02:00
|
|
|
) -> GenericArray<u8, <NistP256 as Group>::PkLen> {
|
|
|
|
|
pkcs_11_ecdsa_derive_secret::<NistP256>(self.0, server_pk, client_pk)
|
2025-04-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
impl Pkcs11KeyExchange<TripleDh<NistP384, Sha384>> for RemoteKey {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
2025-04-22 09:08:17 +02:00
|
|
|
&self,
|
|
|
|
|
server_pk: &PublicKey<NistP384>,
|
|
|
|
|
client_pk: &PublicKey<NistP384>,
|
2025-05-19 22:56:25 +02:00
|
|
|
) -> GenericArray<u8, <NistP384 as Group>::PkLen> {
|
|
|
|
|
pkcs_11_ecdsa_derive_secret::<NistP384>(self.0, server_pk, client_pk)
|
2025-04-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
impl Pkcs11KeyExchange<TripleDh<NistP521, Sha512>> for RemoteKey {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
2025-04-22 09:08:17 +02:00
|
|
|
&self,
|
|
|
|
|
server_pk: &PublicKey<NistP521>,
|
|
|
|
|
client_pk: &PublicKey<NistP521>,
|
2025-05-19 22:56:25 +02:00
|
|
|
) -> GenericArray<u8, <NistP521 as Group>::PkLen> {
|
|
|
|
|
pkcs_11_ecdsa_derive_secret::<NistP521>(self.0, server_pk, client_pk)
|
2025-04-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
2025-05-19 22:56:25 +02:00
|
|
|
impl Pkcs11KeyExchange<TripleDh<Curve25519, Sha512>> for RemoteKey {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
2025-04-22 09:08:17 +02:00
|
|
|
&self,
|
|
|
|
|
_: &PublicKey<Curve25519>,
|
|
|
|
|
pk: &PublicKey<Curve25519>,
|
2025-05-19 22:56:25 +02:00
|
|
|
) -> GenericArray<u8, <Curve25519 as Group>::PkLen> {
|
|
|
|
|
let shared_secret = pkcs_11_dh_derive_secret(self.0, &pk.serialize());
|
2025-04-22 09:08:17 +02:00
|
|
|
|
|
|
|
|
GenericArray::clone_from_slice(&shared_secret)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
impl Pkcs11KeyExchange<SigmaI<Ecdsa<NistP256, Sha256>, NistP256, Sha256>> for RemoteKey {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
|
|
|
|
&self,
|
|
|
|
|
_: &PublicKey<NistP256>,
|
|
|
|
|
message: &Message<CS, NistP256>,
|
|
|
|
|
) -> (ecdsa::Signature<NistP256>, PreHash<Sha256>) {
|
|
|
|
|
pkcs_11_ecdsa_sign::<NistP256, Sha256>(self.0, message.hash())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
impl Pkcs11KeyExchange<SigmaI<Ecdsa<NistP384, Sha384>, NistP384, Sha384>> for RemoteKey {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
|
|
|
|
&self,
|
|
|
|
|
_: &PublicKey<NistP384>,
|
|
|
|
|
message: &Message<CS, NistP384>,
|
|
|
|
|
) -> (ecdsa::Signature<NistP384>, PreHash<Sha384>) {
|
|
|
|
|
pkcs_11_ecdsa_sign::<NistP384, Sha384>(self.0, message.hash())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
|
|
|
|
impl Pkcs11KeyExchange<SigmaI<PureEddsa<Ed25519>, Ristretto255, Sha512>> for RemoteKey {
|
|
|
|
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
|
|
|
|
&self,
|
|
|
|
|
_: &PublicKey<Ed25519>,
|
|
|
|
|
message: &Message<CS, Ristretto255>,
|
|
|
|
|
) -> (ed25519::Signature, CachedMessage<CS, Ristretto255>) {
|
|
|
|
|
pkcs_11_eddsa_sign(self.0, message)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pkcs11_ec_public_key<G>(data: &[u8]) -> PublicKey<G>
|
|
|
|
|
where
|
|
|
|
|
G: Group<Pk = NonIdentity<G>> + CurveArithmetic,
|
|
|
|
|
FieldBytesSize<G>: ModulusSize,
|
|
|
|
|
AffinePoint<G>:
|
|
|
|
|
FromEncodedPoint<G> + ToEncodedPoint<G> + PrimeCurveAffine<Curve = ProjectivePoint<G>>,
|
|
|
|
|
{
|
|
|
|
|
PublicKey::deserialize(
|
|
|
|
|
elliptic_curve::PublicKey::<G>::from_sec1_bytes(data)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.to_encoded_point(true)
|
|
|
|
|
.as_bytes(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pkcs_11_dh_derive_secret(sk: ObjectHandle, pk: &[u8]) -> Vec<u8> {
|
2025-04-22 09:08:17 +02:00
|
|
|
let session = SESSION.lock().unwrap();
|
|
|
|
|
let shared_secret = session
|
|
|
|
|
.derive_key(
|
|
|
|
|
&Mechanism::Ecdh1Derive(Ecdh1DeriveParams::new(EcKdf::null(), pk)),
|
|
|
|
|
sk,
|
|
|
|
|
&[
|
|
|
|
|
Attribute::Token(false),
|
|
|
|
|
Attribute::KeyType(KeyType::GENERIC_SECRET),
|
|
|
|
|
Attribute::Class(ObjectClass::SECRET_KEY),
|
|
|
|
|
Attribute::Extractable(true),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let Attribute::Value(shared_secret) = session
|
|
|
|
|
.get_attributes(shared_secret, &[AttributeType::Value])
|
|
|
|
|
.unwrap()
|
|
|
|
|
.pop()
|
|
|
|
|
.unwrap()
|
|
|
|
|
else {
|
|
|
|
|
unreachable!()
|
|
|
|
|
};
|
|
|
|
|
drop(session);
|
|
|
|
|
|
|
|
|
|
shared_secret
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
fn pkcs_11_ecdsa_derive_secret<G>(
|
2025-04-22 09:08:17 +02:00
|
|
|
server_sk: ObjectHandle,
|
2025-05-19 22:56:25 +02:00
|
|
|
server_pk: &PublicKey<G>,
|
|
|
|
|
client_pk: &PublicKey<G>,
|
|
|
|
|
) -> GenericArray<u8, <G as Group>::PkLen>
|
2025-04-22 09:08:17 +02:00
|
|
|
where
|
2025-05-19 22:56:25 +02:00
|
|
|
G: Group<Pk = NonIdentity<G>> + CurveArithmetic,
|
|
|
|
|
AffinePoint<G>: DecompressPoint<G> + ToEncodedPoint<G>,
|
|
|
|
|
FieldBytesSize<G>: ModulusSize,
|
2025-04-22 09:08:17 +02:00
|
|
|
{
|
|
|
|
|
let client_pk_point = client_pk.to_group_type();
|
|
|
|
|
let client_pk = client_pk.serialize();
|
|
|
|
|
let client_pk = OctetStringRef::new(&client_pk).unwrap();
|
|
|
|
|
let client_pk = client_pk.to_der().unwrap();
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
let shared_secret_bytes = pkcs_11_dh_derive_secret(server_sk, &client_pk);
|
|
|
|
|
let shared_secret_point = AffinePoint::<G>::decompress(
|
2025-04-22 09:08:17 +02:00
|
|
|
&GenericArray::clone_from_slice(&shared_secret_bytes),
|
|
|
|
|
Choice::from(0),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let mut shared_secret = GenericArray::default();
|
|
|
|
|
shared_secret[1..].copy_from_slice(&shared_secret_bytes);
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
let shifted_client_pk = client_pk_point.0.to_point() + ProjectivePoint::<G>::generator();
|
2025-04-22 09:08:17 +02:00
|
|
|
let shifted_client_pk = shifted_client_pk.to_affine().to_encoded_point(true);
|
|
|
|
|
let shifted_client_pk = OctetStringRef::new(shifted_client_pk.as_bytes()).unwrap();
|
|
|
|
|
let shifted_client_pk = shifted_client_pk.to_der().unwrap();
|
|
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
let check_point = pkcs_11_dh_derive_secret(server_sk, &shifted_client_pk);
|
2025-04-22 09:08:17 +02:00
|
|
|
|
2025-05-19 22:56:25 +02:00
|
|
|
let shifted_server_pk = server_pk.to_group_type().0.to_point() + shared_secret_point;
|
2025-04-22 09:08:17 +02:00
|
|
|
let shifted_server_pk = shifted_server_pk.to_affine();
|
|
|
|
|
|
|
|
|
|
let tag = u8::conditional_select(
|
|
|
|
|
&(Tag::CompressedEvenY as u8),
|
|
|
|
|
&(Tag::CompressedOddY as u8),
|
|
|
|
|
check_point.ct_ne(&shifted_server_pk.x()),
|
|
|
|
|
);
|
|
|
|
|
shared_secret[0] = tag;
|
|
|
|
|
|
|
|
|
|
shared_secret
|
|
|
|
|
}
|
2025-05-19 22:56:25 +02:00
|
|
|
|
|
|
|
|
#[cfg(feature = "ecdsa")]
|
|
|
|
|
fn pkcs_11_ecdsa_sign<G: CurveArithmetic + PrimeCurve, H: Clone + Digest>(
|
|
|
|
|
sk: ObjectHandle,
|
|
|
|
|
hashes: HashOutput<H>,
|
|
|
|
|
) -> (ecdsa::Signature<G>, PreHash<H>)
|
|
|
|
|
where
|
|
|
|
|
SignatureSize<G>: ArrayLength<u8>,
|
|
|
|
|
{
|
|
|
|
|
let sign_pre_hash = hashes.sign.finalize();
|
|
|
|
|
|
|
|
|
|
let session = SESSION.lock().unwrap();
|
|
|
|
|
let signature = session.sign(&Mechanism::Ecdsa, sk, &sign_pre_hash).unwrap();
|
|
|
|
|
drop(session);
|
|
|
|
|
|
|
|
|
|
let signature = ::ecdsa::Signature::from_slice(&signature).unwrap();
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
ecdsa::Signature(signature),
|
|
|
|
|
PreHash(hashes.verify.finalize()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
|
|
|
|
fn pkcs_11_eddsa_sign<CS: CipherSuite>(
|
|
|
|
|
sk: ObjectHandle,
|
|
|
|
|
message: &Message<CS, Ristretto255>,
|
|
|
|
|
) -> (ed25519::Signature, CachedMessage<CS, Ristretto255>) {
|
|
|
|
|
use cryptoki::mechanism::eddsa::{EddsaParams, EddsaSignatureScheme};
|
|
|
|
|
|
|
|
|
|
let mut message_bytes = Vec::new();
|
|
|
|
|
message
|
|
|
|
|
.sign_message()
|
|
|
|
|
.for_each(|bytes| message_bytes.extend_from_slice(bytes));
|
|
|
|
|
|
|
|
|
|
let session = SESSION.lock().unwrap();
|
|
|
|
|
let signature = session
|
|
|
|
|
.sign(
|
|
|
|
|
&Mechanism::Eddsa(EddsaParams::new(EddsaSignatureScheme::Pure)),
|
|
|
|
|
sk,
|
|
|
|
|
&message_bytes,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
drop(session);
|
|
|
|
|
|
|
|
|
|
let signature = ed25519::Signature::from_slice(&signature).unwrap();
|
|
|
|
|
|
|
|
|
|
(signature, message.to_cached())
|
|
|
|
|
}
|