Skip to main content

dhttp_identity/
identity.rs

1use std::sync::Arc;
2
3use futures::future::BoxFuture;
4use rustls::{
5    SignatureScheme,
6    pki_types::{CertificateDer, PrivateKeyDer, SubjectPublicKeyInfoDer},
7};
8use snafu::{OptionExt, ResultExt, Snafu};
9use x509_parser::prelude::FromDer;
10use x509_parser::{
11    extensions::ParsedExtension,
12    oid_registry::{
13        OID_EC_P256, OID_KEY_TYPE_EC_PUBLIC_KEY, OID_NIST_EC_P384, OID_PKCS1_RSAENCRYPTION,
14        OID_SIG_ED25519,
15    },
16    x509::SubjectPublicKeyInfo,
17};
18
19use crate::{certificate::DhttpSubjectKeyIdentifier, name::Name};
20
21const RSA_CANONICAL_SCHEME: SignatureScheme = SignatureScheme::RSA_PSS_SHA512;
22const ECDSA_CANONICAL_SCHEMES: &[SignatureScheme] = &[
23    SignatureScheme::ECDSA_NISTP256_SHA256,
24    SignatureScheme::ECDSA_NISTP384_SHA384,
25];
26const ED25519_CANONICAL_SCHEME: SignatureScheme = SignatureScheme::ED25519;
27
28/// A TLS identity backed by a certificate chain and private key.
29#[derive(Debug, Clone, PartialEq)]
30pub struct Identity {
31    pub name: Name<'static>,
32    pub certs: Arc<Vec<CertificateDer<'static>>>,
33    pub key: Arc<PrivateKeyDer<'static>>,
34    pub ocsp: Arc<Option<Vec<u8>>>,
35}
36
37#[derive(Debug, Snafu)]
38#[snafu(module)]
39pub enum ExtractSubjectKeyIdentifierError {
40    #[snafu(display("certificate chain is empty"))]
41    EmptyCertificateChain,
42    #[snafu(display("failed to parse leaf certificate"))]
43    ParseCertificate {
44        source: x509_parser::nom::Err<x509_parser::error::X509Error>,
45    },
46    #[snafu(display("failed to parse subject key identifier extension"))]
47    ParseExtension,
48}
49
50#[derive(Debug, Snafu)]
51#[snafu(module)]
52pub enum ExtractDhttpSubjectKeyIdentifierError {
53    #[snafu(transparent)]
54    ExtractSubjectKeyIdentifier {
55        source: ExtractSubjectKeyIdentifierError,
56    },
57    #[snafu(display("leaf certificate is missing subject key identifier"))]
58    MissingSubjectKeyIdentifier,
59    #[snafu(display("subject key identifier is not a dhttp subject key identifier"))]
60    InvalidDhttpSubjectKeyIdentifier {
61        source: crate::certificate::InvalidDhttpSubjectKeyIdentifier,
62    },
63}
64
65#[derive(Debug, Snafu)]
66#[snafu(module)]
67pub enum SignError {
68    #[snafu(display("unsupported signing key type"))]
69    UnsupportedKey,
70    #[snafu(display("cryptographic operation failed"))]
71    Crypto { source: rustls::Error },
72}
73
74#[derive(Debug, Snafu)]
75#[snafu(module)]
76pub enum VerifyError {
77    #[snafu(display("unsupported public key type"))]
78    UnsupportedKey,
79}
80
81impl Identity {
82    pub fn new(
83        name: Name<'static>,
84        certs: Vec<CertificateDer<'static>>,
85        key: PrivateKeyDer<'static>,
86    ) -> Self {
87        Self {
88            name,
89            certs: Arc::new(certs),
90            key: Arc::new(key),
91            ocsp: Arc::new(None),
92        }
93    }
94
95    pub fn name(&self) -> &Name<'static> {
96        &self.name
97    }
98
99    pub fn cert_chain(&self) -> &[CertificateDer<'static>] {
100        &self.certs
101    }
102
103    pub fn certs(&self) -> &[CertificateDer<'static>] {
104        self.cert_chain()
105    }
106
107    pub fn key(&self) -> &PrivateKeyDer<'static> {
108        &self.key
109    }
110
111    pub fn public_key(&self) -> SubjectPublicKeyInfoDer<'_> {
112        match x509_parser::certificate::X509Certificate::from_der(&self.certs[0]) {
113            Ok((_remain, certificate)) => {
114                let spki = certificate.public_key().raw;
115                spki.to_owned().into()
116            }
117            Err(_) if self.certs.len() == 1 => self.certs[0].as_ref().into(),
118            Err(_) => unreachable!("rustls returned an invalid peer_certificates"),
119        }
120    }
121
122    pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, SignError> {
123        let key = rustls::crypto::ring::sign::any_supported_type(&self.key)
124            .context(sign_error::CryptoSnafu)?;
125        sign_with_key(key.as_ref(), data)
126    }
127
128    pub fn verify(&self, data: &[u8], signature: &[u8]) -> Result<bool, VerifyError> {
129        verify_signature(self.public_key(), data, signature)
130    }
131
132    pub fn subject_key_identifier(
133        &self,
134    ) -> Result<Option<&[u8]>, ExtractSubjectKeyIdentifierError> {
135        extract_subject_key_identifier(self.cert_chain())
136    }
137
138    pub fn dhttp_subject_key_identifier(
139        &self,
140    ) -> Result<DhttpSubjectKeyIdentifier, ExtractDhttpSubjectKeyIdentifierError> {
141        extract_dhttp_subject_key_identifier(self.cert_chain())
142    }
143}
144
145/// Local authority for DHTTP identity material.
146///
147/// Signatures use DHTTP's canonical key-to-signature-scheme policy instead of
148/// accepting a caller-supplied scheme. The policy is:
149///
150/// - Ed25519 keys use [`SignatureScheme::ED25519`].
151/// - ECDSA P-256 keys use [`SignatureScheme::ECDSA_NISTP256_SHA256`].
152/// - ECDSA P-384 keys use [`SignatureScheme::ECDSA_NISTP384_SHA384`].
153/// - RSA keys use [`SignatureScheme::RSA_PSS_SHA512`], matching the QUIC/TLS
154///   RSA signing preference used by rustls.
155///
156/// Callers should treat `sign` and `verify` as DHTTP identity operations, not
157/// as general-purpose cryptographic primitives with negotiable algorithms.
158pub trait LocalAuthority: Send + Sync + std::fmt::Debug {
159    fn name(&self) -> &str;
160
161    fn cert_chain(&self) -> &[CertificateDer<'static>];
162
163    fn sign(&self, data: &[u8]) -> BoxFuture<'_, Result<Vec<u8>, SignError>>;
164
165    fn public_key(&self) -> SubjectPublicKeyInfoDer<'_> {
166        extract_public_key(self.cert_chain())
167    }
168
169    fn verify(&self, data: &[u8], signature: &[u8]) -> BoxFuture<'_, Result<bool, VerifyError>> {
170        let result = verify_signature(self.public_key(), data, signature);
171        Box::pin(std::future::ready(result))
172    }
173}
174
175/// Remote authority for DHTTP identity material.
176///
177/// Verification uses the same DHTTP canonical key-to-signature-scheme policy
178/// as [`LocalAuthority`]. The policy is:
179///
180/// - Ed25519 keys use [`SignatureScheme::ED25519`].
181/// - ECDSA P-256 keys use [`SignatureScheme::ECDSA_NISTP256_SHA256`].
182/// - ECDSA P-384 keys use [`SignatureScheme::ECDSA_NISTP384_SHA384`].
183/// - RSA keys use [`SignatureScheme::RSA_PSS_SHA512`], matching the QUIC/TLS
184///   RSA signing preference used by rustls.
185///
186/// A remote authority does not carry an explicit signature scheme in its API;
187/// the scheme is derived from the authority public key according to the
188/// documented DHTTP policy.
189pub trait RemoteAuthority: Send + Sync + std::fmt::Debug {
190    fn name(&self) -> &str;
191
192    fn cert_chain(&self) -> &[CertificateDer<'static>];
193
194    fn public_key(&self) -> SubjectPublicKeyInfoDer<'_> {
195        extract_public_key(self.cert_chain())
196    }
197
198    fn verify(&self, data: &[u8], signature: &[u8]) -> BoxFuture<'_, Result<bool, VerifyError>> {
199        let result = verify_signature(self.public_key(), data, signature);
200        Box::pin(std::future::ready(result))
201    }
202}
203
204pub fn extract_subject_key_identifier<'a>(
205    cert_chain: &'a [CertificateDer<'a>],
206) -> Result<Option<&'a [u8]>, ExtractSubjectKeyIdentifierError> {
207    let leaf = cert_chain
208        .first()
209        .context(extract_subject_key_identifier_error::EmptyCertificateChainSnafu)?;
210    let (_remain, certificate) = x509_parser::certificate::X509Certificate::from_der(leaf)
211        .context(extract_subject_key_identifier_error::ParseCertificateSnafu)?;
212
213    for extension in certificate.extensions() {
214        if let ParsedExtension::SubjectKeyIdentifier(identifier) = extension.parsed_extension() {
215            return Ok(Some(identifier.0));
216        }
217        if extension.oid == x509_parser::oid_registry::OID_X509_EXT_SUBJECT_KEY_IDENTIFIER {
218            return extract_subject_key_identifier_error::ParseExtensionSnafu.fail();
219        }
220    }
221
222    Ok(None)
223}
224
225pub fn extract_dhttp_subject_key_identifier(
226    cert_chain: &[CertificateDer<'_>],
227) -> Result<DhttpSubjectKeyIdentifier, ExtractDhttpSubjectKeyIdentifierError> {
228    let ski = extract_subject_key_identifier(cert_chain)?
229        .context(extract_dhttp_subject_key_identifier_error::MissingSubjectKeyIdentifierSnafu)?;
230    DhttpSubjectKeyIdentifier::try_from_subject_key_identifier_bytes(ski)
231        .context(extract_dhttp_subject_key_identifier_error::InvalidDhttpSubjectKeyIdentifierSnafu)
232}
233
234mod private {
235    pub trait Sealed {}
236
237    impl<T: ?Sized> Sealed for T {}
238}
239
240pub trait LocalAuthorityCertificateExt: private::Sealed {
241    fn subject_key_identifier(&self) -> Result<Option<&[u8]>, ExtractSubjectKeyIdentifierError>;
242
243    fn dhttp_subject_key_identifier(
244        &self,
245    ) -> Result<DhttpSubjectKeyIdentifier, ExtractDhttpSubjectKeyIdentifierError>;
246}
247
248impl<T: ?Sized + LocalAuthority> LocalAuthorityCertificateExt for T {
249    fn subject_key_identifier(&self) -> Result<Option<&[u8]>, ExtractSubjectKeyIdentifierError> {
250        extract_subject_key_identifier(self.cert_chain())
251    }
252
253    fn dhttp_subject_key_identifier(
254        &self,
255    ) -> Result<DhttpSubjectKeyIdentifier, ExtractDhttpSubjectKeyIdentifierError> {
256        extract_dhttp_subject_key_identifier(self.cert_chain())
257    }
258}
259
260pub trait RemoteAuthorityCertificateExt: private::Sealed {
261    fn subject_key_identifier(&self) -> Result<Option<&[u8]>, ExtractSubjectKeyIdentifierError>;
262
263    fn dhttp_subject_key_identifier(
264        &self,
265    ) -> Result<DhttpSubjectKeyIdentifier, ExtractDhttpSubjectKeyIdentifierError>;
266}
267
268impl<T: ?Sized + RemoteAuthority> RemoteAuthorityCertificateExt for T {
269    fn subject_key_identifier(&self) -> Result<Option<&[u8]>, ExtractSubjectKeyIdentifierError> {
270        extract_subject_key_identifier(self.cert_chain())
271    }
272
273    fn dhttp_subject_key_identifier(
274        &self,
275    ) -> Result<DhttpSubjectKeyIdentifier, ExtractDhttpSubjectKeyIdentifierError> {
276        extract_dhttp_subject_key_identifier(self.cert_chain())
277    }
278}
279
280pub fn extract_public_key<'d>(cert_chain: &'d [CertificateDer<'d>]) -> SubjectPublicKeyInfoDer<'d> {
281    match x509_parser::certificate::X509Certificate::from_der(&cert_chain[0]) {
282        Ok((_remain, certificate)) => {
283            let spki = certificate.public_key().raw;
284            spki.to_owned().into()
285        }
286        Err(_) if cert_chain.len() == 1 => cert_chain[0].as_ref().into(),
287        Err(_) => unreachable!("rustls returned an invalid peer_certificates"),
288    }
289}
290
291pub fn sign_with_key(
292    key: &(impl rustls::sign::SigningKey + ?Sized),
293    data: &[u8],
294) -> Result<Vec<u8>, SignError> {
295    for scheme in canonical_signing_schemes(key.algorithm()) {
296        if let Some(signer) = key.choose_scheme(&[*scheme]) {
297            return signer.sign(data).context(sign_error::CryptoSnafu);
298        }
299    }
300
301    sign_error::UnsupportedKeySnafu.fail()
302}
303
304pub fn verify_signature(
305    spki: SubjectPublicKeyInfoDer,
306    data: &[u8],
307    signature: &[u8],
308) -> Result<bool, VerifyError> {
309    let scheme = canonical_verification_scheme(spki.as_ref())?;
310    let algorithm: &'static dyn ring::signature::VerificationAlgorithm = match scheme {
311        SignatureScheme::ECDSA_NISTP384_SHA384 => &ring::signature::ECDSA_P384_SHA384_ASN1,
312        SignatureScheme::ECDSA_NISTP256_SHA256 => &ring::signature::ECDSA_P256_SHA256_ASN1,
313        SignatureScheme::ED25519 => &ring::signature::ED25519,
314        SignatureScheme::RSA_PSS_SHA512 => &ring::signature::RSA_PSS_2048_8192_SHA512,
315        _ => return verify_error::UnsupportedKeySnafu.fail(),
316    };
317
318    let public_key = match SubjectPublicKeyInfo::from_der(&spki) {
319        Ok((_remain, spki)) => spki.subject_public_key,
320        Err(_) => return verify_error::UnsupportedKeySnafu.fail(),
321    };
322
323    Ok(
324        ring::signature::UnparsedPublicKey::new(algorithm, public_key)
325            .verify(data, signature)
326            .is_ok(),
327    )
328}
329
330fn canonical_signing_schemes(algorithm: rustls::SignatureAlgorithm) -> &'static [SignatureScheme] {
331    match algorithm {
332        rustls::SignatureAlgorithm::RSA => &[RSA_CANONICAL_SCHEME],
333        rustls::SignatureAlgorithm::ECDSA => ECDSA_CANONICAL_SCHEMES,
334        rustls::SignatureAlgorithm::ED25519 => &[ED25519_CANONICAL_SCHEME],
335        _ => &[],
336    }
337}
338
339fn canonical_verification_scheme(spki: &[u8]) -> Result<SignatureScheme, VerifyError> {
340    let Ok((_remain, spki)) = SubjectPublicKeyInfo::from_der(spki) else {
341        return verify_error::UnsupportedKeySnafu.fail();
342    };
343
344    if spki.algorithm.algorithm == OID_SIG_ED25519 {
345        return Ok(ED25519_CANONICAL_SCHEME);
346    }
347
348    if spki.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
349        return Ok(RSA_CANONICAL_SCHEME);
350    }
351
352    if spki.algorithm.algorithm != OID_KEY_TYPE_EC_PUBLIC_KEY {
353        return verify_error::UnsupportedKeySnafu.fail();
354    }
355
356    let Some(curve) = spki
357        .algorithm
358        .parameters
359        .as_ref()
360        .and_then(|parameters| parameters.as_oid().ok())
361    else {
362        return verify_error::UnsupportedKeySnafu.fail();
363    };
364
365    if curve == OID_EC_P256 {
366        Ok(SignatureScheme::ECDSA_NISTP256_SHA256)
367    } else if curve == OID_NIST_EC_P384 {
368        Ok(SignatureScheme::ECDSA_NISTP384_SHA384)
369    } else {
370        verify_error::UnsupportedKeySnafu.fail()
371    }
372}
373
374impl LocalAuthority for Identity {
375    fn name(&self) -> &str {
376        self.name.as_str()
377    }
378
379    fn cert_chain(&self) -> &[CertificateDer<'static>] {
380        self.cert_chain()
381    }
382
383    fn sign(&self, data: &[u8]) -> BoxFuture<'_, Result<Vec<u8>, SignError>> {
384        let result = Identity::sign(self, data);
385        Box::pin(std::future::ready(result))
386    }
387}
388
389impl RemoteAuthority for Identity {
390    fn name(&self) -> &str {
391        self.name.as_str()
392    }
393
394    fn cert_chain(&self) -> &[CertificateDer<'static>] {
395        self.cert_chain()
396    }
397}
398
399#[cfg(test)]
400mod tests {
401    use std::sync::Arc;
402
403    use ring::signature::KeyPair;
404    use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
405    use rustls::sign::{Signer, SigningKey};
406    use rustls::{SignatureAlgorithm, SignatureScheme};
407
408    use crate::certificate::CertificateChainKind;
409    use crate::identity::{Identity, LocalAuthorityCertificateExt, RemoteAuthorityCertificateExt};
410    use crate::name::Name;
411
412    fn dummy_name() -> Name<'static> {
413        "test.example.com".parse().unwrap()
414    }
415
416    fn dummy_certs() -> Vec<CertificateDer<'static>> {
417        Vec::new()
418    }
419
420    fn dummy_key() -> PrivateKeyDer<'static> {
421        PrivateKeyDer::Pkcs8(b"dummy".to_vec().into())
422    }
423
424    fn fixture_identity(name: &str, der: &'static [u8]) -> Identity {
425        Identity::new(
426            name.parse().unwrap(),
427            vec![CertificateDer::from(der.to_vec())],
428            dummy_key(),
429        )
430    }
431
432    fn valid_dhttp_ski_identity() -> Identity {
433        fixture_identity(
434            "client.example.com.dhttp.net",
435            include_bytes!("../tests/fixtures/valid.der"),
436        )
437    }
438
439    fn ed25519_identity() -> Identity {
440        let rng = ring::rand::SystemRandom::new();
441        let pkcs8 = ring::signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
442        let keypair = ring::signature::Ed25519KeyPair::from_pkcs8(pkcs8.as_ref()).unwrap();
443
444        let mut spki = Vec::with_capacity(44);
445        spki.extend_from_slice(&[
446            0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00,
447        ]);
448        spki.extend_from_slice(keypair.public_key().as_ref());
449
450        Identity::new(
451            dummy_name(),
452            vec![CertificateDer::from(spki)],
453            PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(pkcs8.as_ref().to_vec())),
454        )
455    }
456
457    #[derive(Debug)]
458    struct RsaPssSha512OnlyKey;
459
460    #[derive(Debug)]
461    struct RsaPssSha512Signer;
462
463    impl SigningKey for RsaPssSha512OnlyKey {
464        fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
465            offered
466                .contains(&SignatureScheme::RSA_PSS_SHA512)
467                .then(|| Box::new(RsaPssSha512Signer) as Box<dyn Signer>)
468        }
469
470        fn algorithm(&self) -> SignatureAlgorithm {
471            SignatureAlgorithm::RSA
472        }
473    }
474
475    impl Signer for RsaPssSha512Signer {
476        fn sign(&self, _message: &[u8]) -> Result<Vec<u8>, rustls::Error> {
477            Ok(b"rsa-pss-sha512".to_vec())
478        }
479
480        fn scheme(&self) -> SignatureScheme {
481            SignatureScheme::RSA_PSS_SHA512
482        }
483    }
484
485    fn rsa_subject_public_key_info() -> Vec<u8> {
486        vec![
487            0x30, 0x12, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
488            0x01, 0x05, 0x00, 0x03, 0x01, 0x00,
489        ]
490    }
491
492    #[test]
493    fn construct_identity() {
494        let id = Identity::new(dummy_name(), dummy_certs(), dummy_key());
495        assert_eq!(&id.name, &"test.example.com".parse::<Name>().unwrap());
496        assert!(id.certs.is_empty());
497    }
498
499    #[test]
500    fn clone_shares_certs_via_arc() {
501        let id = Identity::new(dummy_name(), dummy_certs(), dummy_key());
502        let cloned = id.clone();
503        assert!(Arc::ptr_eq(&id.certs, &cloned.certs));
504    }
505
506    #[test]
507    fn clone_shares_key_via_arc() {
508        let id = Identity::new(dummy_name(), dummy_certs(), dummy_key());
509        let cloned = id.clone();
510        assert!(Arc::ptr_eq(&id.key, &cloned.key));
511    }
512
513    #[test]
514    fn ocsp_defaults_to_none() {
515        let id = Identity::new(dummy_name(), dummy_certs(), dummy_key());
516        assert!(id.ocsp.is_none());
517    }
518
519    #[test]
520    fn identity_is_async_authority() {
521        fn assert_local_authority<T: crate::identity::LocalAuthority>() {}
522        fn assert_remote_authority<T: crate::identity::RemoteAuthority>() {}
523
524        assert_local_authority::<Identity>();
525        assert_remote_authority::<Identity>();
526    }
527
528    #[test]
529    fn rsa_canonical_scheme_matches_quic_tls_preference() {
530        assert_eq!(
531            super::sign_with_key(&RsaPssSha512OnlyKey, b"payload")
532                .expect("rsa canonical signature"),
533            b"rsa-pss-sha512"
534        );
535        assert_eq!(
536            super::canonical_verification_scheme(&rsa_subject_public_key_info())
537                .expect("rsa canonical verification scheme"),
538            SignatureScheme::RSA_PSS_SHA512
539        );
540    }
541
542    #[test]
543    fn identity_signs_and_verifies_with_canonical_scheme() {
544        let identity = ed25519_identity();
545        let signature = identity.sign(b"payload").expect("canonical signature");
546
547        assert!(
548            identity
549                .verify(b"payload", &signature)
550                .expect("canonical verification")
551        );
552        assert!(
553            !identity
554                .verify(b"wrong payload", &signature)
555                .expect("canonical verification")
556        );
557    }
558
559    #[test]
560    fn identity_extracts_dhttp_subject_key_identifier() {
561        let identity = valid_dhttp_ski_identity();
562        let raw = identity
563            .subject_key_identifier()
564            .expect("extract raw ski")
565            .expect("fixture has ski");
566
567        assert_eq!(
568            raw,
569            b"0:0:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
570        );
571
572        let dhttp = identity
573            .dhttp_subject_key_identifier()
574            .expect("extract dhttp ski");
575        assert_eq!(dhttp.chain().kind(), CertificateChainKind::Primary);
576        assert_eq!(dhttp.chain().sequence().get(), 0);
577    }
578
579    #[test]
580    fn identity_reports_missing_subject_key_identifier() {
581        let identity = fixture_identity(
582            "missing.example.com.dhttp.net",
583            include_bytes!("../tests/fixtures/missing.der"),
584        );
585
586        assert!(identity.subject_key_identifier().unwrap().is_none());
587        assert!(matches!(
588            identity.dhttp_subject_key_identifier().unwrap_err(),
589            super::ExtractDhttpSubjectKeyIdentifierError::MissingSubjectKeyIdentifier
590        ));
591    }
592
593    #[test]
594    fn identity_reports_malformed_dhttp_subject_key_identifier() {
595        let identity = fixture_identity(
596            "malformed.example.com.dhttp.net",
597            include_bytes!("../tests/fixtures/malformed.der"),
598        );
599
600        assert!(matches!(
601            identity.dhttp_subject_key_identifier().unwrap_err(),
602            super::ExtractDhttpSubjectKeyIdentifierError::InvalidDhttpSubjectKeyIdentifier { .. }
603        ));
604    }
605
606    #[test]
607    fn authority_extension_traits_extract_dhttp_subject_key_identifier() {
608        let identity = valid_dhttp_ski_identity();
609
610        let local = LocalAuthorityCertificateExt::dhttp_subject_key_identifier(&identity)
611            .expect("local authority dhttp ski");
612        let remote = RemoteAuthorityCertificateExt::dhttp_subject_key_identifier(&identity)
613            .expect("remote authority dhttp ski");
614
615        assert_eq!(local, remote);
616    }
617
618    #[test]
619    fn authority_traits_do_not_require_signature_scheme() {
620        let identity = ed25519_identity();
621        let signature = futures::executor::block_on(crate::identity::LocalAuthority::sign(
622            &identity, b"payload",
623        ))
624        .expect("canonical authority signature");
625
626        assert!(
627            futures::executor::block_on(crate::identity::LocalAuthority::verify(
628                &identity, b"payload", &signature,
629            ))
630            .expect("canonical local authority verification")
631        );
632        assert!(
633            futures::executor::block_on(crate::identity::RemoteAuthority::verify(
634                &identity, b"payload", &signature,
635            ))
636            .expect("canonical remote authority verification")
637        );
638    }
639}