1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use crate::sign;
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct Header {
/// The algorithm that this object is/will be signed with.
/// Corresponds to the `alg` header parameter.
///
/// See [section 4.1.1 of RFC 7515](https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.1).
#[serde(rename = "alg")]
pub algorithm: Algorithm,
/// The type of the object that is encoded with this header.
/// Corresponds to the `kid` header parameter.
///
/// See [section 4.1.4 of RFC 7515](https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.4).
#[serde(rename = "kid")]
pub key_id: Option<String>,
/// The type of the object that is encoded with this header.
/// Corresponds to the `typ` header parameter.
///
/// See [section 4.1.9 of RFC 7515](https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.9).
#[serde(rename = "typ")]
pub obj_type: Option<String>,
/// A list of parameters, i.e. field names, that the JWS implementation (i.e. `jwt2`) is
/// required to process.
/// Corresponds to the `crit` header parameter.
///
/// This is a field that `jwt2` doesn't really support yet, but it is left in for the sake of
/// being standards-compliant.
///
/// To validate this, use the [`Header::required_extensions`] function.
///
/// See [section 4.1.11 of RFC 7515](https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11).
#[serde(rename = "crit")]
pub required_extensions: Option<Vec<String>>,
}
impl Header {
pub fn new(algorithm: Algorithm) -> Self {
Self {
algorithm,
key_id: None,
obj_type: None,
required_extensions: None,
}
}
pub fn recommended<R>(recommender: &R) -> Self
where
R: RecommendHeaderParams + ?Sized,
{
Self {
algorithm: recommender.alg(),
key_id: recommender.kid().map(str::to_string),
obj_type: None,
required_extensions: None,
}
}
/// Checks if this library supports the required extensions.
///
/// # Implementation details
/// For now, this will return true if [`Self::required_extensions`] is `Some`.
///
/// This is standards-compliant and "correct" behaviour since if it returns `true`:
/// 1. the array is empty, which is not standards-compliant:
/// > Producers MUST NOT use the empty list `[]` as the `crit` value.
/// 2. the array contains headers which are technically supported by `jwt2`.
/// At the moment, `jwt2` only supports header parameters that are specified by
/// [the JWS RFC](https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1)
/// or those specified by [the JWA RFC](https://www.rfc-editor.org/rfc/rfc7518.html).
/// > Recipients MAY consider the JWS to be invalid if the critical list contains
/// > any Header Parameter names defined by this specification or JWA for use with JWS
/// > or if any other constraints on its use are violated.
/// 3. the array specifies any parameter that `jwt2` does not recognise,
/// being the genuinely correct case.
/// > If any of the listed extension Header Parameters are not understood
/// > and supported by the recipient, then the JWS is invalid.
///
/// In the future, however, this function should properly check if the parameters are handled.
pub fn supports_required_extensions(&self) -> bool {
self.required_extensions.is_some()
}
}
/// JSON Web Algorithm.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Algorithm {
/// The `none` algorithm, indicating that no digital signature
#[serde(rename = "none")]
None,
/// An algorithm for use with JSON Web Signatures.
#[serde(untagged)]
Signing(sign::SigningAlgorithm),
}
impl PartialEq<sign::SigningAlgorithm> for Algorithm {
fn eq(&self, other: &sign::SigningAlgorithm) -> bool {
match self {
Self::Signing(me) => me == other,
_ => false,
}
}
}
// Don't ask me why I chose core::fmt instead of std::fmt
impl core::fmt::Display for Algorithm {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Self::None => f.write_str("<none>"),
Self::Signing(alg) => core::fmt::Display::fmt(alg, f),
}
}
}
/// Something that can recommend header parameters. Useful with [`sign::JwsSigner`].
pub trait RecommendHeaderParams {
/// Recommends an algorithm.
fn alg(&self) -> Algorithm;
/// Recommends a key ID. See [`crate::util::WithKeyId`].
fn kid(&self) -> Option<&str> {
None
}
}
/// Indicates that something can validate header parameters. Useful with [`sign::JwsVerifier`].
pub trait ValidateHeaderParams {
/// Check that the header is supported by this verifier.
fn validate_header(&self, header: &Header) -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alg_value() {
use sign::SigningAlgorithm;
macro_rules! test {
($value:expr => $expected:expr) => {
let value = $value;
let json = serde_json::to_string(&value).expect("Could not serialise");
let expected = $expected;
assert_eq!(json, expected);
};
}
test!(Algorithm::None => "\"none\"");
#[cfg(feature = "hmac-sha2")]
{
test!(Algorithm::Signing(SigningAlgorithm::HS256) => "\"HS256\"");
test!(Algorithm::Signing(SigningAlgorithm::HS384) => "\"HS384\"");
test!(Algorithm::Signing(SigningAlgorithm::HS512) => "\"HS512\"");
}
#[cfg(feature = "rsa-pkcs1")]
{
test!(Algorithm::Signing(SigningAlgorithm::RS256) => "\"RS256\"");
test!(Algorithm::Signing(SigningAlgorithm::RS384) => "\"RS384\"");
test!(Algorithm::Signing(SigningAlgorithm::RS512) => "\"RS512\"");
}
#[cfg(feature = "ecdsa")]
{
test!(Algorithm::Signing(SigningAlgorithm::ES256) => "\"ES256\"");
test!(Algorithm::Signing(SigningAlgorithm::ES384) => "\"ES384\"");
// test!(Algorithm::Signing(SigningAlgorithm::ES512) => "\"ES512\"");
}
}
}