use super::{
AlgorithmIdentifierOwned, CryptoRng, RecipientInfoBuilder, RecipientInfoType, Result,
UserKeyingMaterial,
utils::kw::{KeyWrapAlgorithm, WrappedKey},
};
#[cfg(doc)]
use crate::enveloped_data::EnvelopedData;
use crate::{
content_info::CmsVersion,
enveloped_data::{
EncryptedKey, KeyAgreeRecipientIdentifier, KeyAgreeRecipientInfo,
OriginatorIdentifierOrKey, OriginatorPublicKey, RecipientEncryptedKey, RecipientInfo,
},
};
use const_oid::{AssociatedOid, ObjectIdentifier, db::rfc5753};
use der::{
Any, Decode, Encode, Sequence,
asn1::{BitString, OctetString},
};
use core::{marker::PhantomData, ops::Add};
use alloc::{string::String, vec, vec::Vec};
use aes::cipher::{
KeySizeUser,
array::ArraySize,
typenum::{Sum, U8},
};
use digest::{Digest, FixedOutputReset};
use elliptic_curve::{
AffinePoint, Curve, CurveArithmetic, FieldBytesSize, Generate, PublicKey,
ecdh::{EphemeralSecret, SharedSecret},
point::PointCompression,
sec1::{FromSec1Point, ModulusSize, ToSec1Point},
};
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct EccCmsSharedInfo {
pub key_info: AlgorithmIdentifierOwned,
#[asn1(
context_specific = "0",
tag_mode = "EXPLICIT",
constructed = "true",
optional = "true"
)]
pub entity_u_info: Option<UserKeyingMaterial>,
#[asn1(context_specific = "2", tag_mode = "EXPLICIT", constructed = "true")]
pub supp_pub_info: OctetString,
}
pub trait KeyAgreementAlgorithm {
fn kdf<C, Enc>(
secret: &SharedSecret<C>,
shared_info: &EccCmsSharedInfo,
key_wrapper: &mut WrappedKey<Enc>,
) -> Result<()>
where
C: Curve,
Enc: KeySizeUser,
Sum<Enc::KeySize, U8>: ArraySize,
<Enc as KeySizeUser>::KeySize: Add<U8>;
}
pub struct DhSinglePassStdDhKdf<D> {
digest: PhantomData<D>,
}
impl<D> KeyAgreementAlgorithm for DhSinglePassStdDhKdf<D>
where
D: Digest + FixedOutputReset,
{
fn kdf<C, Enc>(
secret: &SharedSecret<C>,
shared_info: &EccCmsSharedInfo,
key_wrapper: &mut WrappedKey<Enc>,
) -> Result<()>
where
C: Curve,
Enc: KeySizeUser,
Sum<Enc::KeySize, U8>: ArraySize,
<Enc as KeySizeUser>::KeySize: Add<U8>,
{
let shared_info_der = shared_info.to_der()?;
let secret_bytes = secret.raw_secret_bytes();
ansi_x963_kdf::derive_key_into::<D>(secret_bytes, &shared_info_der, key_wrapper.as_mut())
.map_err(|_| {
super::Error::Builder(String::from(
"Could not generate a shared secret via ansi-x9.63-kdf",
))
})?;
Ok(())
}
}
impl AssociatedOid for DhSinglePassStdDhKdf<sha2::Sha224> {
const OID: ObjectIdentifier = rfc5753::DH_SINGLE_PASS_STD_DH_SHA_224_KDF_SCHEME;
}
impl AssociatedOid for DhSinglePassStdDhKdf<sha2::Sha256> {
const OID: ObjectIdentifier = rfc5753::DH_SINGLE_PASS_STD_DH_SHA_256_KDF_SCHEME;
}
impl AssociatedOid for DhSinglePassStdDhKdf<sha2::Sha384> {
const OID: ObjectIdentifier = rfc5753::DH_SINGLE_PASS_STD_DH_SHA_384_KDF_SCHEME;
}
impl AssociatedOid for DhSinglePassStdDhKdf<sha2::Sha512> {
const OID: ObjectIdentifier = rfc5753::DH_SINGLE_PASS_STD_DH_SHA_512_KDF_SCHEME;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EcKeyEncryptionInfo<C>
where
C: CurveArithmetic,
{
Ec(PublicKey<C>),
}
impl<C> EcKeyEncryptionInfo<C>
where
C: CurveArithmetic + AssociatedOid,
{
pub fn get_oid(&self) -> ObjectIdentifier {
C::OID
}
}
impl<C> From<EcKeyEncryptionInfo<C>> for AlgorithmIdentifierOwned
where
C: CurveArithmetic + AssociatedOid,
{
fn from(ec_key_encryption_info: EcKeyEncryptionInfo<C>) -> Self {
let parameters = Some(Any::from(&ec_key_encryption_info.get_oid()));
AlgorithmIdentifierOwned {
oid: elliptic_curve::ALGORITHM_OID, parameters, }
}
}
pub struct KeyAgreeRecipientInfoBuilder<R: ?Sized, C, KA, KW, Enc>
where
C: CurveArithmetic,
KA: KeyAgreementAlgorithm,
KW: KeyWrapAlgorithm,
Enc: KeySizeUser,
{
pub ukm: Option<UserKeyingMaterial>,
pub rid: KeyAgreeRecipientIdentifier,
pub eckey_encryption_info: EcKeyEncryptionInfo<C>,
key_agreement_algorithm: PhantomData<KA>,
key_wrap_algorithm: PhantomData<KW>,
enc_cipher: PhantomData<Enc>,
_rng: PhantomData<R>,
}
impl<R: ?Sized, C, KA, KW, Enc> KeyAgreeRecipientInfoBuilder<R, C, KA, KW, Enc>
where
C: CurveArithmetic,
KA: KeyAgreementAlgorithm,
KW: KeyWrapAlgorithm,
Enc: KeySizeUser,
{
pub fn new(
ukm: Option<UserKeyingMaterial>,
rid: KeyAgreeRecipientIdentifier,
eckey_encryption_info: EcKeyEncryptionInfo<C>,
) -> Result<Self> {
Ok(KeyAgreeRecipientInfoBuilder {
ukm,
eckey_encryption_info,
rid,
key_agreement_algorithm: PhantomData,
key_wrap_algorithm: PhantomData,
enc_cipher: PhantomData,
_rng: PhantomData,
})
}
}
impl<R: ?Sized, C, KA, KW, Enc> RecipientInfoBuilder
for KeyAgreeRecipientInfoBuilder<R, C, KA, KW, Enc>
where
R: CryptoRng,
KA: KeyAgreementAlgorithm + AssociatedOid,
C: CurveArithmetic + AssociatedOid + PointCompression,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
KW: KeyWrapAlgorithm,
Enc: KeySizeUser,
Sum<Enc::KeySize, U8>: ArraySize,
<Enc as KeySizeUser>::KeySize: Add<U8>,
{
type Rng = R;
fn recipient_info_type(&self) -> RecipientInfoType {
RecipientInfoType::Kari
}
fn recipient_info_version(&self) -> CmsVersion {
CmsVersion::V3
}
fn build_with_rng(
&mut self,
content_encryption_key: &[u8],
rng: &mut Self::Rng,
) -> Result<RecipientInfo> {
let (
encrypted_key,
ephemeral_pubkey_encoded_point,
originator_algorithm_identifier,
key_encryption_algorithm_identifier,
) = match self.eckey_encryption_info {
EcKeyEncryptionInfo::Ec(recipient_public_key) => {
let ephemeral_secret = EphemeralSecret::generate_from_rng(rng);
let ephemeral_public_key_encoded_point =
ephemeral_secret.public_key().to_sec1_point(false);
let non_uniformly_random_shared_secret =
ephemeral_secret.diffie_hellman(&recipient_public_key);
let key_wrap_algorithm_identifier = KW::algorithm_identifier();
let key_wrap_algorithm_der = key_wrap_algorithm_identifier.to_der()?;
let entity_u_info = self.ukm.clone();
let key_wrap_algo_keysize_bits_in_be_bytes: [u8; 4] =
KW::key_size_in_bits().to_be_bytes();
let shared_info = EccCmsSharedInfo {
key_info: key_wrap_algorithm_identifier,
entity_u_info,
supp_pub_info: OctetString::new(key_wrap_algo_keysize_bits_in_be_bytes)?,
};
let kek = KW::init_kek();
let mut wrapped: WrappedKey<Enc> = KW::init_wrapped();
KA::kdf(
&non_uniformly_random_shared_secret,
&shared_info,
&mut wrapped,
)?;
KW::try_wrap(&kek, content_encryption_key, wrapped.as_mut())?;
(
Vec::from(wrapped),
ephemeral_public_key_encoded_point,
self.eckey_encryption_info.into(),
AlgorithmIdentifierOwned {
oid: KA::OID,
parameters: Some(Any::from_der(&key_wrap_algorithm_der)?),
},
)
}
};
Ok(RecipientInfo::Kari(KeyAgreeRecipientInfo {
originator: OriginatorIdentifierOrKey::OriginatorKey(OriginatorPublicKey {
algorithm: originator_algorithm_identifier,
public_key: BitString::from_bytes(ephemeral_pubkey_encoded_point.as_bytes())?,
}),
version: self.recipient_info_version(),
ukm: self.ukm.clone(),
key_enc_alg: key_encryption_algorithm_identifier,
recipient_enc_keys: vec![RecipientEncryptedKey {
rid: self.rid.clone(),
enc_key: EncryptedKey::new(encrypted_key)?,
}],
}))
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::eprintln;
use super::*;
use p256::{NistP256, PublicKey, pkcs8::DecodePublicKey};
fn get_test_ec_key_info() -> EcKeyEncryptionInfo<NistP256> {
let public_key_der_bytes: &[u8] = &[
48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7,
3, 66, 0, 4, 28, 172, 255, 181, 95, 47, 44, 239, 216, 157, 137, 235, 55, 75, 38, 129,
21, 36, 82, 128, 45, 238, 160, 153, 22, 6, 129, 55, 216, 57, 207, 127, 196, 129, 164,
68, 146, 48, 77, 126, 246, 106, 193, 23, 190, 254, 131, 168, 208, 143, 21, 95, 43, 82,
249, 246, 24, 221, 68, 112, 41, 4, 142, 15,
];
let p256_public_key = PublicKey::from_public_key_der(public_key_der_bytes)
.map_err(|e| eprintln!("{e}"))
.expect("Getting PublicKey failed");
EcKeyEncryptionInfo::Ec(p256_public_key)
}
#[test]
fn test_keyagreementalgorithm_oid() {
assert_eq!(
DhSinglePassStdDhKdf::<sha2::Sha224>::OID,
rfc5753::DH_SINGLE_PASS_STD_DH_SHA_224_KDF_SCHEME
);
assert_eq!(
DhSinglePassStdDhKdf::<sha2::Sha256>::OID,
rfc5753::DH_SINGLE_PASS_STD_DH_SHA_256_KDF_SCHEME
);
assert_eq!(
DhSinglePassStdDhKdf::<sha2::Sha384>::OID,
rfc5753::DH_SINGLE_PASS_STD_DH_SHA_384_KDF_SCHEME
);
assert_eq!(
DhSinglePassStdDhKdf::<sha2::Sha512>::OID,
rfc5753::DH_SINGLE_PASS_STD_DH_SHA_512_KDF_SCHEME
);
}
#[test]
fn test_eckeyencryptioninfo_get_oid() {
let ec_key_encryption_info = get_test_ec_key_info();
assert_eq!(
ec_key_encryption_info.get_oid(),
ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7")
);
}
#[test]
fn test_algorithmidentifierowned_from_eckeyencryptioninfo() {
let ec_key_encryption_info = get_test_ec_key_info();
assert_eq!(
AlgorithmIdentifierOwned {
oid: ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"),
parameters: Some(ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7").into()),
},
AlgorithmIdentifierOwned::from(ec_key_encryption_info)
)
}
}