identity_credential/sd_jwt_vc/metadata/
issuer.rs

1// Copyright 2020-2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use identity_core::common::Url;
5use identity_verification::jwk::JwkSet;
6use serde::Deserialize;
7use serde::Serialize;
8
9use crate::sd_jwt_vc::Error;
10use crate::sd_jwt_vc::SdJwtVc;
11#[allow(unused_imports)]
12use crate::sd_jwt_vc::SdJwtVcClaims;
13
14/// Path used to query [`IssuerMetadata`] for a given JWT VC issuer.
15pub const WELL_KNOWN_VC_ISSUER: &str = "/.well-known/jwt-vc-issuer";
16
17/// SD-JWT VC issuer's metadata. Contains information about one issuer's
18/// public keys, either as an embedded JWK Set or a reference to one.
19/// ## Notes
20/// - [`IssuerMetadata::issuer`] must exactly match [`SdJwtVcClaims::iss`] in order to be considered valid.
21#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
22pub struct IssuerMetadata {
23  /// Issuer URI.
24  pub issuer: Url,
25  /// JWK Set containing the issuer's public keys.
26  #[serde(flatten)]
27  pub jwks: Jwks,
28}
29
30impl IssuerMetadata {
31  /// Checks the validity of this [`IssuerMetadata`].
32  /// [`IssuerMetadata::issuer`] must match `sd_jwt_vc`'s iss claim's value.
33  pub fn validate(&self, sd_jwt_vc: &SdJwtVc) -> Result<(), Error> {
34    let expected_issuer = &sd_jwt_vc.claims().iss;
35    let actual_issuer = &self.issuer;
36    if actual_issuer != expected_issuer {
37      Err(Error::InvalidIssuerMetadata(anyhow::anyhow!(
38        "expected issuer \"{expected_issuer}\", but found \"{actual_issuer}\""
39      )))
40    } else {
41      Ok(())
42    }
43  }
44}
45
46/// JWK Set containing the issuer's public keys or a URL string referencing them.
47#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
48pub enum Jwks {
49  /// Reference to a JWK set.
50  #[serde(rename = "jwks_uri")]
51  Uri(Url),
52  /// An embedded JWK set.
53  #[serde(rename = "jwks")]
54  Object(JwkSet),
55}
56
57#[cfg(test)]
58mod tests {
59  use super::*;
60
61  const EXAMPLE_URI_ISSUER_METADATA: &str = r#"
62{
63  "issuer":"https://example.com",
64  "jwks_uri":"https://jwt-vc-issuer.example.org/my_public_keys.jwks"
65}
66  "#;
67  const EXAMPLE_JWKS_ISSUER_METADATA: &str = r#"
68{
69  "issuer":"https://example.com",
70  "jwks":{
71    "keys":[
72      {
73        "kid":"doc-signer-05-25-2022",
74        "e":"AQAB",
75        "n":"nj3YJwsLUFl9BmpAbkOswCNVx17Eh9wMO-_AReZwBqfaWFcfGHrZXsIV2VMCNVNU8Tpb4obUaSXcRcQ-VMsfQPJm9IzgtRdAY8NN8Xb7PEcYyklBjvTtuPbpzIaqyiUepzUXNDFuAOOkrIol3WmflPUUgMKULBN0EUd1fpOD70pRM0rlp_gg_WNUKoW1V-3keYUJoXH9NztEDm_D2MQXj9eGOJJ8yPgGL8PAZMLe2R7jb9TxOCPDED7tY_TU4nFPlxptw59A42mldEmViXsKQt60s1SLboazxFKveqXC_jpLUt22OC6GUG63p-REw-ZOr3r845z50wMuzifQrMI9bQ",
76        "kty":"RSA"
77      }
78    ]
79  }
80}
81  "#;
82
83  #[test]
84  fn deserializing_uri_metadata_works() {
85    let issuer_metadata: IssuerMetadata = serde_json::from_str(EXAMPLE_URI_ISSUER_METADATA).unwrap();
86    assert!(matches!(issuer_metadata.jwks, Jwks::Uri(_)));
87  }
88
89  #[test]
90  fn deserializing_jwks_metadata_works() {
91    let issuer_metadata: IssuerMetadata = serde_json::from_str(EXAMPLE_JWKS_ISSUER_METADATA).unwrap();
92    assert!(matches!(issuer_metadata.jwks, Jwks::Object { .. }));
93  }
94}