akv_cli/jose/
mod.rs

1// Copyright 2025 Heath Stewart.
2// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
3
4#![allow(non_camel_case_types)]
5
6//! Basic JSON Web Encryption support.
7
8mod jwe;
9
10use std::fmt;
11
12use crate::Result;
13use azure_core::{
14    base64,
15    json::{from_json, to_json},
16};
17use clap::ValueEnum;
18pub use jwe::{Jwe, JweEncryptor, WrapKeyResult};
19use serde::{de::DeserializeOwned, Deserialize, Serialize};
20
21/// Implemented by types that can encode and decode data.
22pub trait Encode
23where
24    Self: Sized,
25{
26    /// Decode data from a string.
27    fn decode(value: &str) -> Result<Self>;
28
29    /// Encode data to a string.
30    fn encode(&self) -> Result<String>;
31}
32
33impl<T: DeserializeOwned + Serialize> Encode for T {
34    fn decode(value: &str) -> Result<Self> {
35        let buf = base64::decode_url_safe(value)?;
36        Ok(from_json(buf)?)
37    }
38
39    fn encode(&self) -> Result<String> {
40        let buf = to_json(self)?;
41        Ok(base64::encode_url_safe(buf))
42    }
43}
44
45/// A JSON web algorithm header that precedes a JWE or JWS.
46#[derive(Debug, Serialize, Deserialize)]
47pub struct Header {
48    /// For JWEs, the algorithm used to encrypt the content encryption key (CEK).
49    ///
50    /// Only algorithms supported fully on Key Vault are supported.
51    #[serde(rename = "alg")]
52    pub alg: Algorithm,
53
54    /// For JWEs, the algorithm used to encrypt content with the content encryption key (CEK).
55    #[serde(rename = "enc", skip_serializing_if = "Option::is_none")]
56    pub enc: Option<EncryptionAlgorithm>,
57
58    /// The ID of the key in Key Vault used for [`Header::alg`].
59    #[serde(rename = "kid", skip_serializing_if = "Option::is_none")]
60    pub kid: Option<String>,
61
62    /// The type of the payload following this header.
63    #[serde(rename = "typ")]
64    pub typ: Type,
65}
66
67/// The type of JWE.
68#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
69#[serde(rename_all = "UPPERCASE")]
70pub enum Type {
71    /// JSON Web Encryption
72    JWE,
73
74    /// Other type
75    #[serde(untagged)]
76    Other(String),
77}
78
79impl fmt::Display for Type {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            Self::JWE => f.write_str("JWE"),
83            Self::Other(s) => f.write_str(s),
84        }
85    }
86}
87
88/// Supported JWE algorithms
89#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
90#[serde(rename_all = "UPPERCASE")]
91pub enum Algorithm {
92    // JWS algorithms
93    // RS256,
94    // RS384,
95    // RS512,
96    // ES256,
97    // ES384,
98    // ES512,
99    // PS256,
100    // PS384,
101    // PS512,
102
103    // JWE algorithms
104    /// RSA1_5
105    #[serde(rename = "RSA1_5")]
106    RSA1_5,
107
108    /// RSA-OAEP
109    #[serde(rename = "RSA-OAEP")]
110    RSA_OAEP,
111
112    /// RSA-OAEP-256
113    #[serde(rename = "RSA-OAEP-256")]
114    RSA_OAEP_256,
115
116    /// Other algorithm
117    #[serde(untagged)]
118    #[value(skip)]
119    Other(String),
120}
121
122/// Supported JWE encryption algorithms.
123#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
124#[serde(rename_all = "UPPERCASE")]
125pub enum EncryptionAlgorithm {
126    // A128CBC_HS256,
127    // A192CBC_HS384,
128    // A256CBC_HS512,
129    /// A128GCM
130    A128GCM,
131
132    /// A192GCM
133    A192GCM,
134
135    /// A256GCM
136    A256GCM,
137
138    /// Other JWE encryption algorithm
139    #[serde(untagged)]
140    #[value(skip)]
141    Other(String),
142}
143
144impl fmt::Display for EncryptionAlgorithm {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        match self {
147            Self::A128GCM => f.write_str("A128GCM"),
148            Self::A192GCM => f.write_str("A192GCM"),
149            Self::A256GCM => f.write_str("A256GCM"),
150            Self::Other(s) => f.write_str(s),
151        }
152    }
153}
154
155#[derive(Debug)]
156#[doc(hidden)]
157pub enum Set {}
158
159#[derive(Debug)]
160#[doc(hidden)]
161pub enum Unset {}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use serde_json::json;
167
168    #[test]
169    fn header_encode_decode() {
170        let header = Header {
171            alg: Algorithm::RSA_OAEP_256,
172            enc: Some(EncryptionAlgorithm::A256GCM),
173            kid: Some("https://myvault.vault.azure.net/keys/mykey/1234567890abcdef".into()),
174            typ: Type::JWE,
175        };
176
177        let expected_json = json!({
178            "alg": "RSA-OAEP-256",
179            "enc": "A256GCM",
180            "kid": "https://myvault.vault.azure.net/keys/mykey/1234567890abcdef",
181            "typ": "JWE"
182        });
183        let expected_bytes = serde_json::to_vec(&expected_json).unwrap();
184        let expected_b64 = azure_core::base64::encode_url_safe(expected_bytes);
185
186        // Encode
187        let encoded = header.encode().unwrap();
188        assert_eq!(encoded, expected_b64);
189
190        // Decode
191        let decoded = Header::decode(&encoded).unwrap();
192        assert_eq!(decoded.alg, Algorithm::RSA_OAEP_256);
193        assert_eq!(decoded.enc, Some(EncryptionAlgorithm::A256GCM));
194        assert_eq!(
195            decoded.kid,
196            Some("https://myvault.vault.azure.net/keys/mykey/1234567890abcdef".into()),
197        );
198        assert_eq!(decoded.typ, Type::JWE);
199    }
200}