#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::sync::Arc as ArcAlloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
pub use std::sync::Arc;
#[cfg(not(feature = "std"))]
pub use ArcAlloc as Arc;
mod attributes;
mod common;
mod error;
mod utils;
#[cfg(test)]
mod tests;
pub mod client;
pub mod negotiation;
pub mod server;
pub mod state;
pub mod primitives;
#[cfg(feature = "transport-cms")]
pub mod builders;
#[cfg(feature = "transport-cms")]
pub mod kari;
#[cfg(feature = "transport-cms")]
pub mod processors;
pub use attributes::*;
pub use common::{HandshakeAlertHandler, HandshakeFinalization, HandshakeNegotiation};
pub use error::HandshakeError;
pub use utils::{aes_256_gcm_algorithm, aes_gcm_decrypt, aes_gcm_encrypt, generate_cek};
#[cfg(feature = "transport-cms")]
pub use builders::{KariBuilderError, TightBeamKariBuilder};
#[cfg(feature = "transport-cms")]
pub use kari::{kari_unwrap, kari_wrap};
#[cfg(all(feature = "transport-cms", feature = "kem"))]
pub use kari::{kari_unwrap_hybrid, kari_wrap_hybrid};
#[cfg(feature = "transport-cms")]
pub use processors::{TightBeamEnvelopedDataProcessor, TightBeamKariRecipient};
use core::marker::PhantomData;
use crate::asn1::OctetString;
use crate::cms::content_info::CmsVersion;
use crate::cms::enveloped_data::{EncryptedContentInfo, EnvelopedData, RecipientInfos};
use crate::cms::signed_data::SignedData;
use crate::cms::signed_data::{EncapsulatedContentInfo, SignerInfos};
use crate::crypto::aead::{KeyInit, RuntimeAead};
use crate::crypto::key::{Secp256k1KeyProvider, SigningKeyProvider};
use crate::crypto::profiles::{CryptoProvider, DefaultCryptoProvider, SecurityProfileDesc};
use crate::crypto::sign::elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use crate::crypto::sign::elliptic_curve::{AffinePoint, Curve, CurveArithmetic, PublicKey};
use crate::crypto::sign::{SignatureEncoding, Verifier};
use crate::crypto::x509::policy::CertificateValidation;
use crate::crypto::x509::store::CertificateTrust;
use crate::der::asn1::SetOfVec;
use crate::der::{Decode, Encode, Enumerated, Sequence};
use crate::spki::EncodePublicKey;
use crate::transport::error::TransportError;
use crate::transport::handshake::error::Result;
use crate::transport::handshake::negotiation::{SecurityAccept, SecurityOffer};
use crate::Beamable;
#[cfg(feature = "transport-ecies")]
use crate::crypto::ecies::{EciesEphemeral, EciesMessageOps, EciesPublicKeyOps};
#[cfg(feature = "transport-ecies")]
use crate::transport::handshake::client::EciesHandshakeClient;
#[cfg(feature = "transport-ecies")]
use crate::transport::handshake::client::ExtractVerifyingKey;
#[cfg(feature = "transport-ecies")]
use crate::transport::handshake::server::EciesHandshakeServer;
#[cfg(feature = "std")]
use std::time::Instant;
#[cfg(all(feature = "x509", feature = "secp256k1"))]
use crate::crypto::sign::ecdsa::Secp256k1SigningKey;
#[cfg(feature = "x509")]
use crate::crypto::x509::attr::{Attribute, AttributeValue, Attributes};
#[cfg(feature = "x509")]
use crate::x509::Certificate;
#[cfg(feature = "x509")]
pub trait ServerHandshakeKey: Send + Sync {
fn create_ecies_server(
&self,
server_cert: Arc<Certificate>,
aad_domain_tag: Option<&'static [u8]>,
supported_profiles: Vec<SecurityProfileDesc>,
client_validators: Option<Arc<Vec<Arc<dyn CertificateValidation>>>>,
) -> Result<Box<dyn ServerHandshakeProtocol<Error = HandshakeError> + Send + Sync + 'static>>;
fn create_ecies_client(
&self,
server_cert: Option<Arc<Certificate>>,
client_cert: Option<Arc<Certificate>>,
aad_domain_tag: Option<&'static [u8]>,
validator: Option<Arc<dyn CertificateValidation>>,
) -> Result<Box<dyn ClientHandshakeProtocol<Error = HandshakeError> + Send + 'static>>;
#[cfg(feature = "transport-cms")]
fn create_cms_client(
&self,
server_cert: Arc<Certificate>,
validators: Option<Arc<Vec<Arc<dyn CertificateValidation>>>>,
) -> Result<Box<dyn ClientHandshakeProtocol<Error = HandshakeError> + Send + 'static>>;
#[cfg(feature = "transport-cms")]
fn create_cms_server(
&self,
client_validators: Option<Arc<Vec<Arc<dyn CertificateValidation>>>>,
) -> Result<Box<dyn ServerHandshakeProtocol<Error = HandshakeError> + Send + Sync + 'static>>;
}
#[cfg(feature = "x509")]
pub struct HandshakeKeyManager<P: CryptoProvider> {
provider: Arc<dyn SigningKeyProvider>,
_phantom: PhantomData<P>,
}
#[cfg(feature = "x509")]
impl<P: CryptoProvider> Clone for HandshakeKeyManager<P> {
fn clone(&self) -> Self {
Self { provider: Arc::clone(&self.provider), _phantom: PhantomData }
}
}
#[cfg(feature = "x509")]
impl From<Secp256k1SigningKey> for HandshakeKeyManager<DefaultCryptoProvider> {
fn from(signing_key: Secp256k1SigningKey) -> Self {
let provider = Secp256k1KeyProvider::from(signing_key);
Self { provider: Arc::new(provider), _phantom: PhantomData }
}
}
#[cfg(feature = "x509")]
impl From<Secp256k1KeyProvider> for HandshakeKeyManager<DefaultCryptoProvider> {
fn from(provider: Secp256k1KeyProvider) -> Self {
Self { provider: Arc::new(provider), _phantom: PhantomData }
}
}
#[cfg(feature = "x509")]
impl From<Arc<dyn SigningKeyProvider>> for HandshakeKeyManager<DefaultCryptoProvider> {
fn from(provider: Arc<dyn SigningKeyProvider>) -> Self {
Self { provider, _phantom: PhantomData }
}
}
#[cfg(feature = "x509")]
impl<P: CryptoProvider + Send + Sync + 'static> HandshakeKeyManager<P> {
pub fn new(provider: Arc<dyn SigningKeyProvider>) -> Self {
Self { provider, _phantom: PhantomData }
}
#[cfg(feature = "transport-ecies")]
pub fn create_ecies_server<'a>(
&'a self,
server_cert: Arc<Certificate>,
aad_domain_tag: Option<&'static [u8]>,
supported_profiles: Vec<SecurityProfileDesc>,
client_validators: Option<Arc<Vec<Arc<dyn CertificateValidation>>>>,
) -> Result<Box<dyn ServerHandshakeProtocol<Error = HandshakeError> + Send + Sync + 'static>>
where
P::Curve: Curve + CurveArithmetic,
<P::Curve as Curve>::FieldBytesSize: ModulusSize,
AffinePoint<P::Curve>: FromEncodedPoint<P::Curve> + ToEncodedPoint<P::Curve>,
for<'b> P::Signature: TryFrom<&'b [u8]>,
P::VerifyingKey: Verifier<P::Signature> + for<'b> From<&'b PublicKey<P::Curve>>,
P::AeadCipher: KeyInit + Send + Sync + 'static,
P::Signature: SignatureEncoding,
{
let server =
EciesHandshakeServer::<P>::new(Arc::clone(&self.provider), server_cert, aad_domain_tag, client_validators)
.with_supported_profiles(supported_profiles);
Ok(Box::new(server))
}
#[cfg(feature = "transport-ecies")]
pub fn create_ecies_client<'a, M>(
&'a self,
_server_cert: Option<Arc<Certificate>>,
client_cert: Option<Arc<Certificate>>,
aad_domain_tag: Option<&'static [u8]>,
validator: Option<Arc<dyn CertificateValidation>>,
) -> Result<Box<dyn ClientHandshakeProtocol<Error = HandshakeError> + Send + 'static>>
where
M: EciesMessageOps + Send + Sync + 'static,
P::Curve: Curve + CurveArithmetic,
<P::Curve as Curve>::FieldBytesSize: ModulusSize,
AffinePoint<P::Curve>: FromEncodedPoint<P::Curve> + ToEncodedPoint<P::Curve>,
PublicKey<P::Curve>: EciesPublicKeyOps,
<PublicKey<P::Curve> as EciesPublicKeyOps>::SecretKey: EciesEphemeral<PublicKey = PublicKey<P::Curve>>,
P::Signature: SignatureEncoding + Send + Sync + 'static,
for<'b> P::Signature: TryFrom<&'b [u8]>,
for<'b> <P::Signature as TryFrom<&'b [u8]>>::Error: Into<HandshakeError>,
P::VerifyingKey: Verifier<P::Signature> + ExtractVerifyingKey + Send + Sync + 'static,
P::AeadCipher: KeyInit + Send + Sync + 'static,
{
let provider_opt = client_cert.as_ref().map(|_| Arc::clone(&self.provider));
let client = EciesHandshakeClient::<P, M>::new_with_identity(aad_domain_tag, client_cert, provider_opt);
Ok(Box::new(if let Some(val) = validator {
client.with_certificate_validator(val)
} else {
client
}))
}
#[cfg(feature = "transport-cms")]
pub fn create_cms_client<'a>(
&'a self,
server_cert: Arc<Certificate>,
trust_store: Option<Arc<dyn CertificateTrust>>,
) -> Result<Box<dyn ClientHandshakeProtocol<Error = HandshakeError> + Send + 'static>>
where
P: Default + 'static,
P::Curve: elliptic_curve::Curve + elliptic_curve::CurveArithmetic,
<P::Curve as elliptic_curve::Curve>::FieldBytesSize: ModulusSize,
AffinePoint<P::Curve>: FromEncodedPoint<P::Curve> + ToEncodedPoint<P::Curve>,
PublicKey<P::Curve>: EncodePublicKey,
P::VerifyingKey: From<PublicKey<P::Curve>> + EncodePublicKey + signature::Verifier<P::Signature> + 'static,
P::Signature: 'static,
P::Digest: Send + 'static,
P::AeadCipher: Send + Sync + KeyInit,
{
let provider = P::default();
let mut client = crate::transport::handshake::client::CmsHandshakeClient::<P>::new(
provider,
Arc::clone(&self.provider),
server_cert,
);
if let Some(store) = trust_store {
client = client.with_trust_store(store);
}
Ok(Box::new(client))
}
#[cfg(feature = "transport-cms")]
pub fn create_cms_server<'a>(
&'a self,
client_validators: Option<Arc<Vec<Arc<dyn CertificateValidation>>>>,
supported_profiles: Vec<SecurityProfileDesc>,
) -> Result<Box<dyn ServerHandshakeProtocol<Error = HandshakeError> + Send + Sync + 'static>>
where
P::Curve: Curve + CurveArithmetic,
<P::Curve as Curve>::FieldBytesSize: ModulusSize,
AffinePoint<P::Curve>: FromEncodedPoint<P::Curve> + ToEncodedPoint<P::Curve>,
P::VerifyingKey: From<PublicKey<P::Curve>> + EncodePublicKey + Verifier<P::Signature> + 'static,
P::Signature: 'static,
P::Digest: Send + 'static,
P::AeadCipher: Send + Sync + KeyInit + 'static,
{
let server = crate::transport::handshake::server::CmsHandshakeServer::<P>::new(
Arc::clone(&self.provider),
client_validators,
)
.with_supported_profiles(supported_profiles);
Ok(Box::new(server))
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum TcpHandshakeState {
#[default]
None,
#[cfg(feature = "std")]
AwaitingServerResponse {
initiated_at: Instant,
},
#[cfg(not(feature = "std"))]
AwaitingServerResponse {
initiated_at: u64,
},
#[cfg(feature = "std")]
AwaitingClientFinish {
initiated_at: Instant,
},
#[cfg(not(feature = "std"))]
AwaitingClientFinish {
initiated_at: u64,
},
Complete,
}
#[derive(Enumerated, Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum HandshakeAlert {
AuthRequired = 1,
VersionMismatch = 2,
AlgorithmMismatch = 3,
DecryptFail = 4,
FinishedIntegrityFail = 5,
}
pub trait ClientHandshakeProtocol: Send {
type Error: Into<TransportError> + Send;
#[allow(clippy::type_complexity)]
fn start<'a>(
&'a mut self,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = ::core::result::Result<Vec<u8>, Self::Error>> + Send + 'a>>;
#[allow(clippy::type_complexity)]
fn handle_response<'a, 'b>(
&'a mut self,
msg: &'b [u8],
) -> core::pin::Pin<
Box<dyn core::future::Future<Output = ::core::result::Result<Option<Vec<u8>>, Self::Error>> + Send + 'a>,
>
where
'b: 'a;
#[cfg(feature = "aead")]
fn complete<'a>(
&'a mut self,
) -> core::pin::Pin<
Box<dyn core::future::Future<Output = ::core::result::Result<RuntimeAead, Self::Error>> + Send + 'a>,
>;
fn is_complete(&self) -> bool;
fn selected_profile(&self) -> Option<SecurityProfileDesc>;
}
pub trait ServerHandshakeProtocol: Send {
type Error: Into<TransportError> + Send;
#[allow(clippy::type_complexity)]
fn handle_request<'a, 'b>(
&'a mut self,
msg: &'b [u8],
) -> core::pin::Pin<
Box<dyn core::future::Future<Output = ::core::result::Result<Option<Vec<u8>>, Self::Error>> + Send + 'a>,
>
where
'b: 'a;
#[cfg(feature = "aead")]
fn complete<'a>(
&'a mut self,
) -> core::pin::Pin<
Box<dyn core::future::Future<Output = ::core::result::Result<RuntimeAead, Self::Error>> + Send + 'a>,
>;
fn is_complete(&self) -> bool;
#[cfg(feature = "x509")]
fn peer_certificate(&self) -> Option<&Certificate>;
fn selected_profile(&self) -> Option<SecurityProfileDesc>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HandshakeProtocolKind {
#[default]
Ecies,
Cms,
}
#[derive(Beamable, Sequence, Debug, Clone, PartialEq)]
pub struct ClientHello {
pub client_random: OctetString,
#[asn1(optional = "true")]
pub security_offer: Option<SecurityOffer>,
}
#[derive(Beamable, Sequence, Debug, Clone, PartialEq)]
pub struct ServerHandshake {
#[cfg(feature = "x509")]
pub certificate: Certificate,
pub server_random: OctetString,
pub signature: OctetString,
#[asn1(optional = "true")]
pub security_accept: Option<SecurityAccept>,
pub client_cert_required: bool,
}
#[derive(Beamable, Sequence, Debug, Clone, PartialEq)]
pub struct ClientKeyExchange {
pub encrypted_data: OctetString,
#[cfg(feature = "x509")]
#[asn1(optional = "true")]
pub client_certificate: Option<Certificate>,
#[cfg(feature = "x509")]
#[asn1(optional = "true")]
pub client_signature: Option<OctetString>,
}
fn encodable_to_signed_data<T: Encode>(message: &T) -> Result<SignedData> {
let message_der = message.to_der()?;
let octet_string = OctetString::new(message_der)?;
let econtent = crate::der::Any::new(crate::der::Tag::OctetString, octet_string.to_der()?)?;
Ok(SignedData {
version: CmsVersion::V1,
digest_algorithms: Default::default(),
encap_content_info: EncapsulatedContentInfo { econtent_type: crate::oids::DATA, econtent: Some(econtent) },
certificates: None,
crls: None,
signer_infos: SignerInfos::try_from(Vec::new())?,
})
}
fn signed_data_to_decodable<T: for<'a> Decode<'a>>(signed_data: &SignedData) -> Result<T> {
let econtent_any = signed_data
.encap_content_info
.econtent
.as_ref()
.ok_or(HandshakeError::InvalidServerKeyExchange)?;
let octet_string_der = econtent_any.value();
let octet_string = OctetString::from_der(octet_string_der)?;
Ok(T::from_der(octet_string.as_bytes())?)
}
impl TryFrom<&ClientHello> for SignedData {
type Error = HandshakeError;
fn try_from(hello: &ClientHello) -> ::core::result::Result<Self, Self::Error> {
encodable_to_signed_data(hello)
}
}
impl TryFrom<&SignedData> for ClientHello {
type Error = HandshakeError;
fn try_from(signed_data: &SignedData) -> ::core::result::Result<Self, Self::Error> {
signed_data_to_decodable(signed_data)
}
}
impl TryFrom<&ServerHandshake> for SignedData {
type Error = HandshakeError;
fn try_from(handshake: &ServerHandshake) -> ::core::result::Result<Self, Self::Error> {
encodable_to_signed_data(handshake)
}
}
impl TryFrom<&SignedData> for ServerHandshake {
type Error = HandshakeError;
fn try_from(signed_data: &SignedData) -> ::core::result::Result<Self, Self::Error> {
signed_data_to_decodable(signed_data)
}
}
#[cfg(feature = "x509")]
fn build_client_key_exchange_attrs(kex: &ClientKeyExchange) -> Result<Option<x509_cert::attr::Attributes>> {
let mut attrs = Vec::new();
if let Some(cert) = &kex.client_certificate {
let cert_der = cert.to_der()?;
let cert_octet = OctetString::new(cert_der)?;
let cert_der_wrapped = cert_octet.to_der()?;
let cert_any = crate::der::Any::new(crate::der::Tag::OctetString, cert_der_wrapped)?;
let cert_values = SetOfVec::try_from(vec![AttributeValue::from(cert_any)])?;
attrs.push(Attribute { oid: crate::oids::CLIENT_CERTIFICATE, values: cert_values });
}
if let Some(sig) = &kex.client_signature {
let sig_der = sig.to_der()?;
let sig_any = crate::der::Any::new(crate::der::Tag::OctetString, sig_der)?;
let sig_values = SetOfVec::try_from(vec![AttributeValue::from(sig_any)])?;
attrs.push(Attribute { oid: crate::oids::CLIENT_SIGNATURE, values: sig_values });
}
if attrs.is_empty() {
Ok(None)
} else {
Ok(Some(Attributes::try_from(attrs)?))
}
}
#[cfg(feature = "x509")]
fn parse_client_key_exchange_attrs(
enveloped_data: &crate::cms::enveloped_data::EnvelopedData,
) -> Result<(Option<Certificate>, Option<OctetString>)> {
let mut cert = None;
let mut sig = None;
if let Some(attrs) = &enveloped_data.unprotected_attrs {
for attr in attrs.iter() {
if attr.oid == crate::oids::CLIENT_CERTIFICATE {
if let Some(value) = attr.values.iter().next() {
let octet_bytes = value.value();
let cert_octet = OctetString::from_der(octet_bytes)?;
cert = Some(Certificate::from_der(cert_octet.as_bytes())?);
}
} else if attr.oid == crate::oids::CLIENT_SIGNATURE {
if let Some(value) = attr.values.iter().next() {
let octet_bytes = value.value();
sig = Some(OctetString::from_der(octet_bytes)?);
}
}
}
}
Ok((cert, sig))
}
impl TryFrom<&ClientKeyExchange> for crate::cms::enveloped_data::EnvelopedData {
type Error = HandshakeError;
fn try_from(kex: &ClientKeyExchange) -> ::core::result::Result<Self, Self::Error> {
#[cfg(feature = "x509")]
let unprotected_attrs = build_client_key_exchange_attrs(kex)?;
#[cfg(not(feature = "x509"))]
let unprotected_attrs = None;
Ok(EnvelopedData {
version: CmsVersion::V0,
originator_info: None,
recip_infos: RecipientInfos::try_from(Vec::new())?,
encrypted_content: EncryptedContentInfo {
content_type: crate::oids::DATA,
content_enc_alg: crate::transport::handshake::utils::aes_256_gcm_algorithm(),
encrypted_content: Some(OctetString::new(kex.encrypted_data.as_bytes())?),
},
unprotected_attrs,
})
}
}
impl TryFrom<&crate::cms::enveloped_data::EnvelopedData> for ClientKeyExchange {
type Error = HandshakeError;
fn try_from(
enveloped_data: &crate::cms::enveloped_data::EnvelopedData,
) -> ::core::result::Result<Self, Self::Error> {
let encrypted_bytes = enveloped_data
.encrypted_content
.encrypted_content
.as_ref()
.ok_or(HandshakeError::InvalidClientKeyExchange)?
.as_bytes();
#[cfg(feature = "x509")]
let (client_certificate, client_signature) = parse_client_key_exchange_attrs(enveloped_data)?;
Ok(ClientKeyExchange {
encrypted_data: OctetString::new(encrypted_bytes)?,
#[cfg(feature = "x509")]
client_certificate,
#[cfg(feature = "x509")]
client_signature,
})
}
}