1#[cfg(not(any(feature = "openssl", feature = "ring")))]
10use std::marker::PhantomData;
11
12#[cfg(all(not(feature = "ring"), feature = "openssl"))]
13use openssl::bn::BigNum;
14#[cfg(all(not(feature = "ring"), feature = "openssl"))]
15use openssl::bn::BigNumContext;
16#[cfg(all(not(feature = "ring"), feature = "openssl"))]
17use openssl::ec::{EcGroup, EcKey, EcPoint};
18#[cfg(all(not(feature = "ring"), feature = "openssl"))]
19use openssl::nid::Nid;
20#[cfg(all(not(feature = "ring"), feature = "openssl"))]
21use openssl::pkey::{PKey, Public};
22#[cfg(all(not(feature = "ring"), feature = "openssl"))]
23use openssl::rsa::Rsa as OpenSslRsa;
24#[cfg(all(not(feature = "ring"), feature = "openssl"))]
25use openssl::sign::Verifier;
26#[cfg(feature = "ring")]
27use ring::signature::{self, ED25519_PUBLIC_KEY_LEN};
28
29use crate::error::*;
30use crate::rr::dnssec::Algorithm;
31#[cfg(all(not(feature = "ring"), feature = "openssl"))]
32use crate::rr::dnssec::DigestType;
33
34#[cfg(any(feature = "openssl", feature = "ring"))]
35use crate::rr::dnssec::ec_public_key::ECPublicKey;
36#[cfg(any(feature = "openssl", feature = "ring"))]
37use crate::rr::dnssec::rsa_public_key::RSAPublicKey;
38
39pub trait PublicKey {
43 fn public_bytes(&self) -> &[u8];
45
46 #[allow(unused)]
59 fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()>;
60}
61
62#[cfg(all(not(feature = "ring"), feature = "openssl"))]
63fn verify_with_pkey(
64 pkey: &PKey<Public>,
65 algorithm: Algorithm,
66 message: &[u8],
67 signature: &[u8],
68) -> ProtoResult<()> {
69 let digest_type = DigestType::from(algorithm).to_openssl_digest()?;
70 let mut verifier = Verifier::new(digest_type, pkey)?;
71 verifier.update(message)?;
72 verifier
73 .verify(signature)
74 .map_err(Into::into)
75 .and_then(|b| {
76 if b {
77 Ok(())
78 } else {
79 Err("could not verify".into())
80 }
81 })
82}
83
84#[cfg(all(not(feature = "ring"), feature = "openssl"))]
86#[cfg_attr(docsrs, doc(cfg(all(not(feature = "ring"), feature = "openssl"))))]
87pub struct Ec<'k> {
88 raw: &'k [u8],
89 pkey: PKey<Public>,
90}
91
92#[cfg(all(not(feature = "ring"), feature = "openssl"))]
93#[cfg_attr(docsrs, doc(cfg(all(not(feature = "ring"), feature = "openssl"))))]
94impl<'k> Ec<'k> {
95 pub fn from_public_bytes(public_key: &'k [u8], algorithm: Algorithm) -> ProtoResult<Self> {
128 let curve = match algorithm {
129 Algorithm::ECDSAP256SHA256 => Nid::X9_62_PRIME256V1,
130 Algorithm::ECDSAP384SHA384 => Nid::SECP384R1,
131 _ => return Err("only ECDSAP256SHA256 and ECDSAP384SHA384 are supported by Ec".into()),
132 };
133 let k = ECPublicKey::from_unprefixed(public_key, algorithm)?;
135 EcGroup::from_curve_name(curve)
136 .and_then(|group| BigNumContext::new().map(|ctx| (group, ctx)))
137 .and_then(|(group, mut ctx)| {
139 EcPoint::from_bytes(&group, k.prefixed_bytes(), &mut ctx)
140 .map(|point| (group, point))
141 })
142 .and_then(|(group, point)| EcKey::from_public_key(&group, &point))
143 .and_then(PKey::from_ec_key)
144 .map_err(Into::into)
145 .map(|pkey| Ec {
146 raw: public_key,
147 pkey,
148 })
149 }
150}
151
152#[cfg(all(not(feature = "ring"), feature = "openssl"))]
153fn asn1_emit_integer(output: &mut Vec<u8>, int: &[u8]) {
154 assert!(!int.is_empty());
155 output.push(0x02); if int[0] > 0x7f {
157 output.push((int.len() + 1) as u8);
158 output.push(0x00); output.extend(int);
160 return;
161 }
162 let mut pos = 0;
164 while pos < int.len() {
165 if int[pos] == 0 {
166 if pos == int.len() - 1 {
167 break;
168 }
169 pos += 1;
170 continue;
171 }
172 if int[pos] > 0x7f {
173 pos -= 1;
175 }
176 break;
177 }
178 let int_output = &int[pos..];
179 output.push(int_output.len() as u8);
180 output.extend(int_output);
181}
182
183#[cfg(all(not(feature = "ring"), feature = "openssl"))]
185#[cfg_attr(docsrs, doc(cfg(all(not(feature = "ring"), feature = "openssl"))))]
186pub fn dnssec_ecdsa_signature_to_der(signature: &[u8]) -> ProtoResult<Vec<u8>> {
187 if signature.is_empty() || signature.len() & 1 != 0 || signature.len() > 127 {
188 return Err("invalid signature length".into());
189 }
190 let part_len = signature.len() / 2;
191 let mut signature_asn1 = vec![0x30, 0x00];
193 asn1_emit_integer(&mut signature_asn1, &signature[..part_len]);
194 asn1_emit_integer(&mut signature_asn1, &signature[part_len..]);
195 signature_asn1[1] = (signature_asn1.len() - 2) as u8;
196 Ok(signature_asn1)
197}
198
199#[cfg(all(not(feature = "ring"), feature = "openssl"))]
200#[cfg_attr(docsrs, doc(cfg(all(not(feature = "ring"), feature = "openssl"))))]
201impl PublicKey for Ec<'_> {
202 fn public_bytes(&self) -> &[u8] {
203 self.raw
204 }
205
206 fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()> {
207 let signature_asn1 = dnssec_ecdsa_signature_to_der(signature)?;
208 verify_with_pkey(&self.pkey, algorithm, message, &signature_asn1)
209 }
210}
211
212#[cfg(feature = "ring")]
214#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
215pub type Ec = ECPublicKey;
216
217#[cfg(feature = "ring")]
218impl Ec {
219 pub fn from_public_bytes(public_key: &[u8], algorithm: Algorithm) -> ProtoResult<Self> {
252 Self::from_unprefixed(public_key, algorithm)
253 }
254}
255
256#[cfg(feature = "ring")]
257#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
258impl PublicKey for Ec {
259 fn public_bytes(&self) -> &[u8] {
260 self.unprefixed_bytes()
261 }
262
263 fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()> {
264 let alg = match algorithm {
266 Algorithm::ECDSAP256SHA256 => &signature::ECDSA_P256_SHA256_FIXED,
267 Algorithm::ECDSAP384SHA384 => &signature::ECDSA_P384_SHA384_FIXED,
268 _ => return Err("only ECDSAP256SHA256 and ECDSAP384SHA384 are supported by Ec".into()),
269 };
270 let public_key = signature::UnparsedPublicKey::new(alg, self.prefixed_bytes());
271 public_key.verify(message, signature).map_err(Into::into)
272 }
273}
274
275#[cfg(feature = "ring")]
277#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
278pub struct Ed25519<'k> {
279 raw: &'k [u8],
280}
281
282#[cfg(feature = "ring")]
283#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
284impl<'k> Ed25519<'k> {
285 pub fn from_public_bytes(public_key: &'k [u8]) -> ProtoResult<Self> {
295 if public_key.len() != ED25519_PUBLIC_KEY_LEN {
296 return Err(format!(
297 "expected {} byte public_key: {}",
298 ED25519_PUBLIC_KEY_LEN,
299 public_key.len()
300 )
301 .into());
302 }
303
304 Ok(Ed25519 { raw: public_key })
305 }
306}
307
308#[cfg(feature = "ring")]
309impl PublicKey for Ed25519<'_> {
310 fn public_bytes(&self) -> &[u8] {
312 self.raw
313 }
314
315 fn verify(&self, _: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()> {
316 let public_key = signature::UnparsedPublicKey::new(&signature::ED25519, self.raw);
317 public_key.verify(message, signature).map_err(Into::into)
318 }
319}
320
321#[cfg(any(feature = "openssl", feature = "ring"))]
323#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
324pub struct Rsa<'k> {
325 raw: &'k [u8],
326
327 #[cfg(all(not(feature = "ring"), feature = "openssl"))]
328 pkey: PKey<Public>,
329
330 #[cfg(feature = "ring")]
331 pkey: RSAPublicKey<'k>,
332}
333
334#[cfg(any(feature = "openssl", feature = "ring"))]
335#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
336impl<'k> Rsa<'k> {
337 pub fn from_public_bytes(raw: &'k [u8]) -> ProtoResult<Self> {
370 let parsed = RSAPublicKey::try_from(raw)?;
371 let pkey = into_pkey(parsed)?;
372 Ok(Rsa { raw, pkey })
373 }
374}
375
376#[cfg(all(not(feature = "ring"), feature = "openssl"))]
377fn into_pkey(parsed: RSAPublicKey<'_>) -> ProtoResult<PKey<Public>> {
378 let e = BigNum::from_slice(parsed.e())?;
380 let n = BigNum::from_slice(parsed.n())?;
381
382 OpenSslRsa::from_public_components(n, e)
383 .and_then(PKey::from_rsa)
384 .map_err(Into::into)
385}
386
387#[cfg(feature = "ring")]
388#[allow(clippy::unnecessary_wraps)]
389fn into_pkey(parsed: RSAPublicKey<'_>) -> ProtoResult<RSAPublicKey<'_>> {
390 Ok(parsed)
391}
392
393#[cfg(any(feature = "openssl", feature = "ring"))]
394impl PublicKey for Rsa<'_> {
395 fn public_bytes(&self) -> &[u8] {
396 self.raw
397 }
398
399 #[cfg(all(not(feature = "ring"), feature = "openssl"))]
400 fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()> {
401 verify_with_pkey(&self.pkey, algorithm, message, signature)
402 }
403
404 #[cfg(feature = "ring")]
405 fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()> {
406 #[allow(deprecated)]
407 let alg = match algorithm {
408 Algorithm::RSASHA256 => &signature::RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY,
409 Algorithm::RSASHA512 => &signature::RSA_PKCS1_1024_8192_SHA512_FOR_LEGACY_USE_ONLY,
410 Algorithm::RSASHA1 => &signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY,
411 Algorithm::RSASHA1NSEC3SHA1 => {
412 return Err("*ring* doesn't support RSASHA1NSEC3SHA1 yet".into())
413 }
414 _ => unreachable!("non-RSA algorithm passed to RSA verify()"),
415 };
416 let public_key = signature::RsaPublicKeyComponents {
417 n: self.pkey.n(),
418 e: self.pkey.e(),
419 };
420 public_key
421 .verify(alg, message, signature)
422 .map_err(Into::into)
423 }
424}
425
426#[non_exhaustive]
428pub enum PublicKeyEnum<'k> {
429 #[cfg(any(feature = "openssl", feature = "ring"))]
431 #[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
432 Rsa(Rsa<'k>),
433 #[cfg(all(not(feature = "ring"), feature = "openssl"))]
435 #[cfg_attr(docsrs, doc(cfg(any(all(not(feature = "ring"), feature = "openssl")))))]
436 Ec(Ec<'k>),
437 #[cfg(feature = "ring")]
439 #[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
440 Ec(Ec),
441 #[cfg(feature = "ring")]
443 #[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
444 Ed25519(Ed25519<'k>),
445 #[cfg(not(any(feature = "ring", feature = "openssl")))]
447 #[cfg_attr(docsrs, doc(cfg(not(any(feature = "ring", feature = "openssl")))))]
448 Phantom(&'k PhantomData<()>),
449}
450
451impl<'k> PublicKeyEnum<'k> {
452 #[allow(unused_variables, clippy::match_single_binding)]
454 pub fn from_public_bytes(public_key: &'k [u8], algorithm: Algorithm) -> ProtoResult<Self> {
455 #[allow(deprecated)]
456 match algorithm {
457 #[cfg(any(feature = "openssl", feature = "ring"))]
458 Algorithm::ECDSAP256SHA256 | Algorithm::ECDSAP384SHA384 => Ok(PublicKeyEnum::Ec(
459 Ec::from_public_bytes(public_key, algorithm)?,
460 )),
461 #[cfg(feature = "ring")]
462 Algorithm::ED25519 => Ok(PublicKeyEnum::Ed25519(Ed25519::from_public_bytes(
463 public_key,
464 )?)),
465 #[cfg(any(feature = "openssl", feature = "ring"))]
466 Algorithm::RSASHA1
467 | Algorithm::RSASHA1NSEC3SHA1
468 | Algorithm::RSASHA256
469 | Algorithm::RSASHA512 => Ok(PublicKeyEnum::Rsa(Rsa::from_public_bytes(public_key)?)),
470 _ => Err("public key algorithm not supported".into()),
471 }
472 }
473}
474
475impl PublicKey for PublicKeyEnum<'_> {
476 #[allow(clippy::match_single_binding, clippy::match_single_binding)]
477 fn public_bytes(&self) -> &[u8] {
478 match *self {
479 #[cfg(any(feature = "openssl", feature = "ring"))]
480 PublicKeyEnum::Ec(ref ec) => ec.public_bytes(),
481 #[cfg(feature = "ring")]
482 PublicKeyEnum::Ed25519(ref ed) => ed.public_bytes(),
483 #[cfg(any(feature = "openssl", feature = "ring"))]
484 PublicKeyEnum::Rsa(ref rsa) => rsa.public_bytes(),
485 #[cfg(not(any(feature = "ring", feature = "openssl")))]
486 _ => panic!("no public keys registered, enable ring or openssl features"),
487 }
488 }
489
490 #[allow(unused_variables, clippy::match_single_binding)]
491 fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()> {
492 match *self {
493 #[cfg(any(feature = "openssl", feature = "ring"))]
494 PublicKeyEnum::Ec(ref ec) => ec.verify(algorithm, message, signature),
495 #[cfg(feature = "ring")]
496 PublicKeyEnum::Ed25519(ref ed) => ed.verify(algorithm, message, signature),
497 #[cfg(any(feature = "openssl", feature = "ring"))]
498 PublicKeyEnum::Rsa(ref rsa) => rsa.verify(algorithm, message, signature),
499 #[cfg(not(any(feature = "ring", feature = "openssl")))]
500 _ => panic!("no public keys registered, enable ring or openssl features"),
501 }
502 }
503}
504
505pub struct PublicKeyBuf {
507 key_buf: Vec<u8>,
508}
509
510impl PublicKeyBuf {
511 pub fn new(key_buf: Vec<u8>) -> Self {
513 Self { key_buf }
514 }
515}
516
517impl PublicKey for PublicKeyBuf {
518 fn public_bytes(&self) -> &[u8] {
519 &self.key_buf
520 }
521
522 fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> ProtoResult<()> {
523 let public_key = PublicKeyEnum::from_public_bytes(&self.key_buf, algorithm)?;
524
525 public_key.verify(algorithm, message, signature)
526 }
527}
528
529#[cfg(all(not(feature = "ring"), feature = "openssl"))]
530#[cfg(test)]
531mod tests {
532 #[cfg(feature = "openssl")]
533 #[test]
534 fn test_asn1_emit_integer() {
535 fn test_case(source: &[u8], expected_data: &[u8]) {
536 use crate::rr::dnssec::public_key::asn1_emit_integer;
537
538 let mut output = Vec::<u8>::new();
539 asn1_emit_integer(&mut output, source);
540 assert_eq!(output[0], 0x02);
541 assert_eq!(output[1], expected_data.len() as u8);
542 assert_eq!(&output[2..], expected_data);
543 }
544 test_case(&[0x00], &[0x00]);
545 test_case(&[0x00, 0x00], &[0x00]);
546 test_case(&[0x7f], &[0x7f]);
547 test_case(&[0x80], &[0x00, 0x80]);
548 test_case(&[0x00, 0x80], &[0x00, 0x80]);
549 test_case(&[0x00, 0x00, 0x80], &[0x00, 0x80]);
550 test_case(&[0x7f, 0x00, 0x80], &[0x7f, 0x00, 0x80]);
551 test_case(&[0x00, 0x7f, 0x00, 0x80], &[0x7f, 0x00, 0x80]);
552 test_case(&[0x80, 0x00, 0x80], &[0x00, 0x80, 0x00, 0x80]);
553 test_case(&[0xff, 0x00, 0x80], &[0x00, 0xff, 0x00, 0x80]);
554 }
555}