dpp_crypto/jws/algorithm.rs
1//! JOSE algorithm constants and allowlist for DPP signatures.
2
3/// JOSE algorithm identifier for Ed25519/EdDSA (RFC 8037 §2).
4pub const EDDSA_ALG: &str = "EdDSA";
5
6/// JWK curve name for Ed25519 (RFC 8037 §2).
7pub const ED25519_CRV: &str = "Ed25519";
8
9/// The single allowed signing algorithm for all DPP credentials and passport proofs.
10///
11/// Pinned at compile time so a future algorithm addition requires a deliberate
12/// change here plus a corresponding bump of the `algorithm` field in `KeyRecord`.
13/// Rejects `alg:none` and all substitution attacks by exhaustive allowlist.
14#[inline]
15pub fn is_allowed_alg(alg: &str) -> bool {
16 alg == EDDSA_ALG
17}
18
19/// The signature algorithm a key pair uses.
20///
21/// There is exactly one variant, and [`is_allowed_alg`] still admits only it.
22/// The type exists so that the algorithm travels *with* the key rather than
23/// being assumed by every reader of it: a verifier can then bind the JWS `alg`
24/// header to what the key record says, instead of letting an attacker-supplied
25/// header select the verification path.
26///
27/// `#[non_exhaustive]` so that adding a second algorithm later is not a
28/// breaking change for downstream matches.
29#[non_exhaustive]
30#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
31pub enum KeyAlgorithm {
32 /// Ed25519 signatures, JOSE `EdDSA` (RFC 8037 §3.1).
33 #[serde(rename = "EdDSA")]
34 Ed25519,
35}
36
37impl KeyAlgorithm {
38 /// The JOSE `alg` header value.
39 #[inline]
40 pub fn jose_alg(self) -> &'static str {
41 match self {
42 Self::Ed25519 => EDDSA_ALG,
43 }
44 }
45
46 /// Parse a JOSE `alg` header value. `None` for anything not allowed here,
47 /// which includes `none`.
48 #[inline]
49 pub fn from_jose_alg(alg: &str) -> Option<Self> {
50 match alg {
51 EDDSA_ALG => Some(Self::Ed25519),
52 _ => None,
53 }
54 }
55
56 /// The JWK members describing `public_key`, for a DID document's
57 /// `publicKeyJwk`.
58 ///
59 /// Lives here rather than in the DID-document builder so the per-algorithm
60 /// match stays exhaustive in the crate that defines the algorithm. A second
61 /// algorithm needs a `kty`/parameter set that only this crate should have
62 /// to know — P-256, for instance, is `kty: "EC"` with both `x` and `y`,
63 /// where Ed25519 is `kty: "OKP"` with `x` alone.
64 pub fn public_key_jwk(self, public_key: &[u8]) -> serde_json::Value {
65 use base64::Engine;
66 let b64 = base64::engine::general_purpose::URL_SAFE_NO_PAD;
67 match self {
68 Self::Ed25519 => serde_json::json!({
69 "kty": "OKP",
70 "crv": ED25519_CRV,
71 "x": b64.encode(public_key),
72 }),
73 }
74 }
75}