bh_jws_utils/
x509_chain.rs1use bherror::traits::{ForeignBoxed as _, ForeignError, PropagateError};
17use bhx5chain::X5Chain;
18
19use crate::{
20 openssl_impl::public_key_from_jwk_es256, BoxError, CryptoError, HasJwkKid, HasX5Chain,
21 JwkPublic, Signer, SigningAlgorithm,
22};
23
24#[derive(Debug)]
30pub struct SignerWithChain<S> {
31 pub(crate) signer: S,
32 pub(crate) x5chain: X5Chain,
33}
34
35impl<S: Signer> SignerWithChain<S> {
36 pub fn new(signer: S, x5chain: X5Chain) -> bherror::Result<Self, CryptoError> {
47 public_key_matches(&signer, &x5chain)?;
48
49 Ok(Self { signer, x5chain })
50 }
51
52 pub fn certificate_chain(&self) -> &X5Chain {
54 &self.x5chain
55 }
56
57 pub fn public_jwk(&self) -> bherror::Result<JwkPublic, CryptoError> {
59 self.signer
60 .public_jwk()
61 .map_err(|boxed_error| downcast_or_chain(boxed_error, || CryptoError::CryptoBackend))
62 }
63}
64
65fn public_key_matches<S: Signer>(
66 signer: &S,
67 x5chain: &X5Chain,
68) -> bherror::Result<(), CryptoError> {
69 let signer_public_key = signer_public_key_openssl(signer)?;
70
71 let leaf_public_key = x5chain
72 .leaf_certificate_key()
73 .with_err(|| CryptoError::InvalidX5Chain)?;
74
75 if !leaf_public_key.public_eq(&signer_public_key) {
76 return Err(bherror::Error::root(CryptoError::PublicKeyMismatch));
77 }
78
79 Ok(())
80}
81
82fn signer_public_key_openssl<S: Signer>(
83 signer: &S,
84) -> bherror::Result<openssl::pkey::PKey<openssl::pkey::Public>, CryptoError> {
85 let signer_public_jwk = signer
86 .public_jwk()
87 .map_err(|boxed_error| downcast_or_chain(boxed_error, || CryptoError::CryptoBackend))?;
88
89 match signer.algorithm() {
90 SigningAlgorithm::Es256 => {
91 let signer_public_key = public_key_from_jwk_es256(&signer_public_jwk)
92 .with_err(|| CryptoError::InvalidPublicKey)?;
93 Ok(openssl::pkey::PKey::from_ec_key(signer_public_key)
94 .foreign_err(|| CryptoError::CryptoBackend)?)
95 }
96 _ => Err(bherror::Error::root(CryptoError::Unsupported(
97 "only ES256 is currently supported".to_owned(),
98 ))),
99 }
100}
101
102fn downcast_or_chain<E, F>(boxed_error: BoxError, f: F) -> bherror::Error<E>
105where
106 E: bherror::BhError,
107 F: FnOnce() -> E,
108{
109 match boxed_error.downcast() {
110 Ok(boxed_downcast_error) => *boxed_downcast_error,
111 original_error_result @ Err(_) => original_error_result.foreign_boxed_err(f).unwrap_err(),
112 }
113}
114
115impl<S: Signer> Signer for SignerWithChain<S> {
116 fn algorithm(&self) -> SigningAlgorithm {
117 self.signer.algorithm()
118 }
119
120 fn sign(&self, message: &[u8]) -> Result<Vec<u8>, BoxError> {
121 self.signer.sign(message)
122 }
123
124 fn public_jwk(&self) -> Result<JwkPublic, BoxError> {
125 self.signer.public_jwk()
126 }
127}
128
129impl<S: Signer> HasX5Chain for SignerWithChain<S> {
130 fn x5chain(&self) -> X5Chain {
131 self.x5chain.clone()
132 }
133}
134
135impl<S: HasJwkKid> HasJwkKid for SignerWithChain<S> {
136 fn jwk_kid(&self) -> &str {
137 self.signer.jwk_kid()
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use crate::{CryptoError, Es256Signer};
144
145 use super::SignerWithChain;
146
147 #[test]
148 fn signer_with_chain_construction() {
149 let correct_key = Es256Signer::generate("correct".into()).unwrap();
150 let certificate_chain = bhx5chain::Builder::dummy()
151 .generate_x5chain(&correct_key.public_key_pem().unwrap(), None)
152 .unwrap();
153
154 let _signer = SignerWithChain::new(correct_key, certificate_chain.clone()).unwrap();
155
156 let incorrect_key = Es256Signer::generate("incorrect".into()).unwrap();
158
159 let Err(error) = SignerWithChain::new(incorrect_key, certificate_chain) else {
161 unreachable!()
162 };
163 assert_eq!(error.error, CryptoError::PublicKeyMismatch);
164 }
165}