1use crate::algorithm::store::Store;
2use crate::algorithm::SigningAlgorithm;
3use crate::error::Error;
4use crate::header::{BorrowedKeyHeader, Header, JoseHeader};
5use crate::token::{Signed, Unsigned};
6use crate::{ToBase64, Token, SEPARATOR};
7
8pub trait SignWithKey<T> {
10 fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<T, Error>;
11}
12
13pub trait SignWithStore<T> {
15 fn sign_with_store<S, A>(self, store: &S) -> Result<T, Error>
16 where
17 S: Store<Algorithm = A>,
18 A: SigningAlgorithm;
19}
20
21impl<H, C> Token<H, C, Unsigned> {
22 pub fn new(header: H, claims: C) -> Self {
24 Token {
25 header,
26 claims,
27 signature: Unsigned,
28 }
29 }
30
31 pub fn header_mut(&mut self) -> &mut H {
32 &mut self.header
33 }
34
35 pub fn claims_mut(&mut self) -> &mut C {
36 &mut self.claims
37 }
38}
39
40impl<H, C> Default for Token<H, C, Unsigned>
41where
42 H: Default,
43 C: Default,
44{
45 fn default() -> Self {
46 Token::new(H::default(), C::default())
47 }
48}
49
50impl<C: ToBase64> SignWithKey<String> for C {
51 fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<String, Error> {
52 let header = Header {
53 algorithm: key.algorithm_type(),
54 ..Default::default()
55 };
56
57 let token = Token::new(header, self).sign_with_key(key)?;
58 Ok(token.signature.token_string)
59 }
60}
61
62impl<'a, C: ToBase64> SignWithStore<String> for (&'a str, C) {
63 fn sign_with_store<S, A>(self, store: &S) -> Result<String, Error>
64 where
65 S: Store<Algorithm = A>,
66 A: SigningAlgorithm,
67 {
68 let (key_id, claims) = self;
69 let key = store
70 .get(key_id)
71 .ok_or_else(|| Error::NoKeyWithKeyId(key_id.to_owned()))?;
72
73 let header = BorrowedKeyHeader {
74 algorithm: key.algorithm_type(),
75 key_id,
76 };
77
78 let token = Token::new(header, claims).sign_with_key(key)?;
79 Ok(token.signature.token_string)
80 }
81}
82
83impl<H, C> SignWithKey<Token<H, C, Signed>> for Token<H, C, Unsigned>
84where
85 H: ToBase64 + JoseHeader,
86 C: ToBase64,
87{
88 fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<Token<H, C, Signed>, Error> {
89 let header_algorithm = self.header.algorithm_type();
90 let key_algorithm = key.algorithm_type();
91 if header_algorithm != key_algorithm {
92 return Err(Error::AlgorithmMismatch(header_algorithm, key_algorithm));
93 }
94
95 let header = self.header.to_base64()?;
96 let claims = self.claims.to_base64()?;
97 let signature = key.sign(&header, &claims)?;
98
99 let token_string = [&*header, &*claims, &signature].join(SEPARATOR);
100
101 Ok(Token {
102 header: self.header,
103 claims: self.claims,
104 signature: Signed { token_string },
105 })
106 }
107}
108
109impl<H, C> SignWithStore<Token<H, C, Signed>> for Token<H, C, Unsigned>
110where
111 H: ToBase64 + JoseHeader,
112 C: ToBase64,
113{
114 fn sign_with_store<S, A>(self, store: &S) -> Result<Token<H, C, Signed>, Error>
115 where
116 S: Store<Algorithm = A>,
117 A: SigningAlgorithm,
118 {
119 let key_id = self.header.key_id().ok_or(Error::NoKeyId)?;
120 let key = store
121 .get(key_id)
122 .ok_or_else(|| Error::NoKeyWithKeyId(key_id.to_owned()))?;
123 self.sign_with_key(key)
124 }
125}
126
127impl<'a, H, C> Token<H, C, Signed> {
128 pub fn as_str(&self) -> &str {
130 &self.signature.token_string
131 }
132}
133
134impl<H, C> From<Token<H, C, Signed>> for String {
135 fn from(token: Token<H, C, Signed>) -> Self {
136 token.signature.token_string
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use std::collections::BTreeMap;
143
144 use hmac::{Hmac, Mac};
145 use serde::Serialize;
146 use sha2::{Sha256, Sha512};
147
148 use crate::algorithm::AlgorithmType;
149 use crate::error::Error;
150 use crate::header::Header;
151 use crate::token::signed::{SignWithKey, SignWithStore};
152 use crate::Token;
153
154 #[derive(Serialize)]
155 struct Claims<'a> {
156 name: &'a str,
157 }
158
159 #[test]
160 pub fn sign_claims() -> Result<(), Error> {
161 let claims = Claims { name: "John Doe" };
162 let key: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
163
164 let signed_token = claims.sign_with_key(&key)?;
165
166 assert_eq!(signed_token, "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.LlTGHPZRXbci-y349jXXN0byQniQQqwKGybzQCFIgY0");
167 Ok(())
168 }
169
170 #[test]
171 pub fn sign_unsigned_with_store() -> Result<(), Error> {
172 let mut key_store = BTreeMap::new();
173 let key1: Hmac<Sha512> = Hmac::new_from_slice(b"first")?;
174 let key2: Hmac<Sha512> = Hmac::new_from_slice(b"second")?;
175 key_store.insert("first_key".to_owned(), key1);
176 key_store.insert("second_key".to_owned(), key2);
177
178 let header = Header {
179 algorithm: AlgorithmType::Hs512,
180 key_id: Some(String::from("second_key")),
181 ..Default::default()
182 };
183 let claims = Claims { name: "Jane Doe" };
184 let token = Token::new(header, claims);
185 let signed_token = token.sign_with_store(&key_store)?;
186
187 assert_eq!(signed_token.as_str(), "eyJhbGciOiJIUzUxMiIsImtpZCI6InNlY29uZF9rZXkifQ.eyJuYW1lIjoiSmFuZSBEb2UifQ.t2ON5s8DDb2hefBIWAe0jaEcp-T7b2Wevmj0kKJ8BFxKNQURHpdh4IA-wbmBmqtiCnqTGoRdqK45hhW0AOtz0A");
188 Ok(())
189 }
190}