pub mod ecdsa {
pub use ecdsa::der;
pub use ecdsa::hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive};
pub use ecdsa::{Error, Signature, SignatureSize, SigningKey, VerifyingKey};
#[cfg(feature = "secp256k1")]
pub use k256;
#[cfg(feature = "secp256k1")]
pub use k256::{
ecdsa::{
Signature as Secp256k1Signature, SigningKey as Secp256k1SigningKey, VerifyingKey as Secp256k1VerifyingKey,
},
schnorr, Secp256k1,
};
}
pub use elliptic_curve;
pub use signature::hazmat::{PrehashSigner, PrehashVerifier};
pub use signature::{Error, Keypair, SignatureEncoding, Signer, Verifier};
use crate::cms::content_info::CmsVersion;
use crate::cms::signed_data::{SignatureValue, SignerIdentifier, SignerInfo};
use crate::crypto::hash::Digest;
use crate::der::asn1::{ObjectIdentifier, OctetString};
use crate::der::oid::AssociatedOid;
use crate::error::{Result, TightBeamError};
use crate::oids::SIGNER_ECDSA_WITH_SHA3_256;
use crate::spki::{AlgorithmIdentifierOwned, EncodePublicKey};
use crate::x509::ext::pkix::SubjectKeyIdentifier;
pub trait SignatureAlgorithmIdentifier {
const ALGORITHM_OID: ObjectIdentifier;
}
pub fn sign_canonical<D, S>(signer: &impl PrehashSigner<S>, content: impl AsRef<[u8]>) -> core::result::Result<S, Error>
where
D: Digest,
{
let mut hasher = D::new();
hasher.update(content.as_ref());
signer.sign_prehash(&hasher.finalize())
}
pub fn verify_canonical<D, S>(
verifier: &impl PrehashVerifier<S>,
content: impl AsRef<[u8]>,
signature: &S,
) -> core::result::Result<(), Error>
where
D: Digest,
{
let mut hasher = D::new();
hasher.update(content.as_ref());
verifier.verify_prehash(&hasher.finalize(), signature)
}
pub trait Signatory<S>: PrehashSigner<S> + Keypair
where
S: SignatureEncoding,
{
type DigestAlgorithm: Digest + AssociatedOid;
fn to_signer_info(&self, data: impl AsRef<[u8]>) -> Result<SignerInfo>
where
Self: Sized,
{
let signature: S = sign_canonical::<Self::DigestAlgorithm, S>(self, data)?;
let digest_alg = AlgorithmIdentifierOwned { oid: Self::DigestAlgorithm::OID, parameters: None };
let signature_algorithm = self.signature_algorithm();
let sid = self.signer_identifier()?;
SignerInfo::from_parts(signature.to_bytes(), signature_algorithm, digest_alg, sid)
}
fn signature_algorithm(&self) -> AlgorithmIdentifierOwned;
fn signer_identifier(&self) -> Result<SignerIdentifier>;
}
pub trait SignerInfoExt: Sized {
fn from_parts(
signature: impl AsRef<[u8]>,
signature_algorithm: AlgorithmIdentifierOwned,
digest_alg: AlgorithmIdentifierOwned,
sid: SignerIdentifier,
) -> Result<Self>;
}
impl SignerInfoExt for SignerInfo {
fn from_parts(
signature: impl AsRef<[u8]>,
signature_algorithm: AlgorithmIdentifierOwned,
digest_alg: AlgorithmIdentifierOwned,
sid: SignerIdentifier,
) -> Result<Self> {
let signature = SignatureValue::new(signature.as_ref())?;
Ok(SignerInfo {
version: CmsVersion::V1,
sid,
digest_alg,
signed_attrs: None,
signature_algorithm,
signature,
unsigned_attrs: None,
})
}
}
#[cfg(feature = "secp256k1")]
impl Signatory<ecdsa::Signature<ecdsa::Secp256k1>> for ecdsa::SigningKey<ecdsa::Secp256k1> {
type DigestAlgorithm = sha3::Sha3_256;
fn signature_algorithm(&self) -> AlgorithmIdentifierOwned {
AlgorithmIdentifierOwned { oid: SIGNER_ECDSA_WITH_SHA3_256, parameters: None }
}
fn signer_identifier(&self) -> Result<SignerIdentifier> {
let verifying_key = self.verifying_key();
let public_key_der = verifying_key
.to_public_key_der()
.map_err(|_| TightBeamError::SignatureEncodingError)?;
let mut hasher = Self::DigestAlgorithm::new();
hasher.update(public_key_der.as_bytes());
let skid_bytes = hasher.finalize();
let octet_string = OctetString::new(&skid_bytes[..20])?;
Ok(SignerIdentifier::SubjectKeyIdentifier(SubjectKeyIdentifier::from(octet_string)))
}
}
pub struct Sha3Signer<'a, S>(&'a S);
impl<'a, S> crate::spki::DynSignatureAlgorithmIdentifier for Sha3Signer<'a, S> {
fn signature_algorithm_identifier(&self) -> crate::spki::Result<crate::spki::AlgorithmIdentifierOwned> {
Ok(crate::spki::AlgorithmIdentifierOwned { oid: SIGNER_ECDSA_WITH_SHA3_256, parameters: None })
}
}
impl<'a, S> signature::Keypair for Sha3Signer<'a, S>
where
S: signature::Keypair,
{
type VerifyingKey = <S as signature::Keypair>::VerifyingKey;
fn verifying_key(&self) -> Self::VerifyingKey {
self.0.verifying_key()
}
}
impl<'a, S, Sig> signature::Signer<Sig> for Sha3Signer<'a, S>
where
S: PrehashSigner<Sig>,
{
fn try_sign(&self, msg: &[u8]) -> core::result::Result<Sig, signature::Error> {
sign_canonical::<sha3::Sha3_256, Sig>(self.0, msg)
}
}
impl<'a, S> From<&'a S> for Sha3Signer<'a, S> {
fn from(s: &'a S) -> Self {
Sha3Signer(s)
}
}
#[cfg(feature = "secp256k1")]
pub fn secp256k1_signer_identifier(verifying_key: &ecdsa::VerifyingKey<ecdsa::Secp256k1>) -> Result<SignerIdentifier> {
let public_key_der = verifying_key
.to_public_key_der()
.map_err(|_| TightBeamError::SignatureEncodingError)?;
let mut hasher = sha3::Sha3_256::new();
hasher.update(public_key_der.as_bytes());
let skid_bytes = hasher.finalize();
let octet_string = OctetString::new(&skid_bytes[..20]).map_err(TightBeamError::SerializationError)?;
Ok(SignerIdentifier::SubjectKeyIdentifier(SubjectKeyIdentifier::from(octet_string)))
}
pub trait SignatureVerifier {
fn verify_signature(&self, content: &[u8], signature: &[u8], signer_id: &SignerIdentifier) -> Result<()>;
}
#[cfg(all(feature = "signature", feature = "secp256k1"))]
pub struct EcdsaSignatureVerifier<V, S, D>
where
V: PrehashVerifier<S>,
S: SignatureEncoding,
D: Digest,
{
verifying_key: V,
expected_sid: Option<SignerIdentifier>,
_phantom: core::marker::PhantomData<(S, D)>,
}
#[cfg(all(feature = "signature", feature = "secp256k1"))]
impl<V, S, D> EcdsaSignatureVerifier<V, S, D>
where
V: PrehashVerifier<S>,
S: SignatureEncoding,
D: Digest,
{
pub fn from_signing_key<K>(signing_key: &K) -> Result<Self>
where
K: Signatory<S>,
K::VerifyingKey: Into<V>,
{
let verifying_key = signing_key.verifying_key().into();
let expected_sid = signing_key.signer_identifier()?;
Ok(Self {
verifying_key,
expected_sid: Some(expected_sid),
_phantom: core::marker::PhantomData,
})
}
pub fn from_verifying_key_with_sid(verifying_key: V, expected_sid: SignerIdentifier) -> Self {
Self {
verifying_key,
expected_sid: Some(expected_sid),
_phantom: core::marker::PhantomData,
}
}
}
#[cfg(all(feature = "signature", feature = "secp256k1"))]
impl<V, S, D> SignatureVerifier for EcdsaSignatureVerifier<V, S, D>
where
V: PrehashVerifier<S>,
S: SignatureEncoding,
D: Digest,
{
fn verify_signature(&self, content: &[u8], signature_bytes: &[u8], signer_id: &SignerIdentifier) -> Result<()> {
if let Some(ref expected_sid) = self.expected_sid {
if signer_id != expected_sid {
return Err(TightBeamError::SignatureEncodingError);
}
}
let signature = S::try_from(signature_bytes).map_err(|_| TightBeamError::SignatureEncodingError)?;
verify_canonical::<D, S>(&self.verifying_key, content, &signature)
.map_err(|_| TightBeamError::SignatureEncodingError)?;
Ok(())
}
}
#[cfg(feature = "secp256k1")]
impl SignatureAlgorithmIdentifier for ecdsa::Secp256k1Signature {
const ALGORITHM_OID: ObjectIdentifier = SIGNER_ECDSA_WITH_SHA3_256;
}
#[cfg(all(test, feature = "secp256k1"))]
mod tests {
use signature::hazmat::PrehashVerifier;
use super::*;
use crate::crypto::hash::Sha3_256;
use crate::random::OsRng;
type SigningKey = ecdsa::Secp256k1SigningKey;
type Signature = ecdsa::Secp256k1Signature;
type VerifyingKey = ecdsa::Secp256k1VerifyingKey;
const CONTENT: &[u8] = b"cross-convention signing content";
#[test]
fn signer_info_verifies_through_ecdsa_verifier() -> crate::error::Result<()> {
let signing_key = SigningKey::random(&mut OsRng);
let signer_info = signing_key.to_signer_info(CONTENT)?;
let verifier = EcdsaSignatureVerifier::<VerifyingKey, Signature, Sha3_256>::from_signing_key(&signing_key)?;
verifier.verify_signature(CONTENT, signer_info.signature.as_bytes(), &signer_info.sid)?;
Ok(())
}
#[test]
fn signature_matches_advertised_sha3_oid() -> crate::error::Result<()> {
let signing_key = SigningKey::random(&mut OsRng);
let signer_info = signing_key.to_signer_info(CONTENT)?;
let mut hasher = Sha3_256::new();
hasher.update(CONTENT);
let prehash = hasher.finalize();
let signature = Signature::from_slice(signer_info.signature.as_bytes())?;
signing_key.verifying_key().verify_prehash(&prehash, &signature)?;
Ok(())
}
}