1use {
2 crate::{
3 crypto::{hmac_sha256, SHA256_OUTPUT_LEN},
4 KeyLengthError,
5 },
6 chrono::NaiveDate,
7 derive_builder::Builder,
8 scratchstack_aws_principal::{Principal, SessionData},
9 std::{
10 fmt::{Debug, Display, Formatter, Result as FmtResult},
11 future::Future,
12 str::FromStr,
13 },
14 tower::{service_fn, util::ServiceFn, BoxError},
15};
16
17const AWS4_REQUEST: &str = "aws4_request";
19
20pub(crate) static KSECRETKEY_LENGTH: usize = 44;
22
23#[derive(Clone, Copy, PartialEq, Eq)]
25pub struct KSecretKey<const M: usize = KSECRETKEY_LENGTH> {
26 prefixed_key: [u8; M],
28
29 len: usize,
31}
32
33#[derive(Clone, Copy, PartialEq, Eq)]
35pub struct KDateKey {
36 key: [u8; SHA256_OUTPUT_LEN],
38}
39
40#[derive(Clone, Copy, PartialEq, Eq)]
42pub struct KRegionKey {
43 key: [u8; SHA256_OUTPUT_LEN],
45}
46
47#[derive(Clone, Copy, PartialEq, Eq)]
49pub struct KServiceKey {
50 key: [u8; SHA256_OUTPUT_LEN],
52}
53
54#[derive(Clone, Copy, PartialEq, Eq)]
56pub struct KSigningKey {
57 key: [u8; SHA256_OUTPUT_LEN],
59}
60
61impl AsRef<[u8]> for KSecretKey {
62 fn as_ref(&self) -> &[u8] {
63 &self.prefixed_key[4..self.len]
65 }
66}
67
68impl AsRef<[u8; SHA256_OUTPUT_LEN]> for KDateKey {
69 fn as_ref(&self) -> &[u8; SHA256_OUTPUT_LEN] {
70 &self.key
71 }
72}
73
74impl AsRef<[u8; SHA256_OUTPUT_LEN]> for KRegionKey {
75 fn as_ref(&self) -> &[u8; SHA256_OUTPUT_LEN] {
76 &self.key
77 }
78}
79
80impl AsRef<[u8; SHA256_OUTPUT_LEN]> for KServiceKey {
81 fn as_ref(&self) -> &[u8; SHA256_OUTPUT_LEN] {
82 &self.key
83 }
84}
85
86impl AsRef<[u8; SHA256_OUTPUT_LEN]> for KSigningKey {
87 fn as_ref(&self) -> &[u8; SHA256_OUTPUT_LEN] {
88 &self.key
89 }
90}
91
92impl Debug for KSecretKey {
93 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
94 f.write_str("KSecretKey")
95 }
96}
97
98impl Debug for KDateKey {
99 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
100 f.write_str("KDateKey")
101 }
102}
103
104impl Debug for KRegionKey {
105 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
106 f.write_str("KRegionKey")
107 }
108}
109
110impl Debug for KServiceKey {
111 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
112 f.write_str("KServiceKey")
113 }
114}
115
116impl Debug for KSigningKey {
117 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
118 f.write_str("KSigningKey")
119 }
120}
121
122impl Display for KSecretKey {
123 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
124 f.write_str("KSecretKey")
125 }
126}
127
128impl Display for KDateKey {
129 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
130 f.write_str("KDateKey")
131 }
132}
133
134impl Display for KRegionKey {
135 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
136 f.write_str("KRegionKey")
137 }
138}
139
140impl Display for KServiceKey {
141 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
142 f.write_str("KServiceKey")
143 }
144}
145
146impl Display for KSigningKey {
147 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
148 f.write_str("KSigningKey")
149 }
150}
151
152impl<const M: usize> FromStr for KSecretKey<M> {
153 type Err = KeyLengthError;
154
155 fn from_str(raw: &str) -> Result<Self, KeyLengthError> {
157 let len = raw.len();
158 if len > M - 4 {
159 return Err(KeyLengthError::TooLong);
160 }
161 if len + 4 < M {
162 return Err(KeyLengthError::TooShort);
163 }
164
165 let mut prefixed_key = [0; M];
166
167 prefixed_key[..4].copy_from_slice(b"AWS4");
168 prefixed_key[4..].copy_from_slice(raw.as_bytes());
169 Ok(Self {
170 prefixed_key,
171 len: len + 4,
172 })
173 }
174}
175
176impl KSecretKey {
177 pub fn to_kdate(&self, date: NaiveDate) -> KDateKey {
179 let date = date.format("%Y%m%d").to_string();
180 let date = date.as_bytes();
181 let key = hmac_sha256(self.prefixed_key.as_slice(), date);
182 let mut key_bytes = [0; SHA256_OUTPUT_LEN];
183 key_bytes.copy_from_slice(key.as_ref());
184 KDateKey {
185 key: key_bytes,
186 }
187 }
188
189 pub fn to_kregion(&self, date: NaiveDate, region: &str) -> KRegionKey {
191 self.to_kdate(date).to_kregion(region)
192 }
193
194 pub fn to_kservice(&self, date: NaiveDate, region: &str, service: &str) -> KServiceKey {
196 self.to_kdate(date).to_kservice(region, service)
197 }
198
199 pub fn to_ksigning(&self, date: NaiveDate, region: &str, service: &str) -> KSigningKey {
201 self.to_kdate(date).to_ksigning(region, service)
202 }
203}
204
205impl KDateKey {
206 pub fn to_kregion(&self, region: &str) -> KRegionKey {
208 let region = region.as_bytes();
209 let key = hmac_sha256(self.key.as_slice(), region);
210 let mut key_bytes = [0; SHA256_OUTPUT_LEN];
211 key_bytes.copy_from_slice(key.as_ref());
212 KRegionKey {
213 key: key_bytes,
214 }
215 }
216
217 pub fn to_kservice(&self, region: &str, service: &str) -> KServiceKey {
219 self.to_kregion(region).to_kservice(service)
220 }
221
222 pub fn to_ksigning(&self, region: &str, service: &str) -> KSigningKey {
224 self.to_kregion(region).to_ksigning(service)
225 }
226}
227
228impl KRegionKey {
229 pub fn to_kservice(&self, service: &str) -> KServiceKey {
231 let service = service.as_bytes();
232 let key = hmac_sha256(self.key.as_slice(), service);
233 let mut key_bytes = [0; SHA256_OUTPUT_LEN];
234 key_bytes.copy_from_slice(key.as_ref());
235 KServiceKey {
236 key: key_bytes,
237 }
238 }
239
240 pub fn to_ksigning(&self, service: &str) -> KSigningKey {
242 self.to_kservice(service).to_ksigning()
243 }
244}
245
246impl KServiceKey {
247 pub fn to_ksigning(&self) -> KSigningKey {
249 let key = hmac_sha256(self.key.as_slice(), AWS4_REQUEST.as_bytes());
250 let mut key_bytes = [0; SHA256_OUTPUT_LEN];
251 key_bytes.copy_from_slice(key.as_ref());
252 KSigningKey {
253 key: key_bytes,
254 }
255 }
256}
257
258#[derive(Builder, Clone, Debug)]
263#[non_exhaustive]
264pub struct GetSigningKeyRequest {
265 #[builder(setter(into))]
267 access_key: String,
268
269 #[builder(setter(into), default)]
271 session_token: Option<String>,
272
273 request_date: NaiveDate,
275
276 #[builder(setter(into))]
278 region: String,
279
280 #[builder(setter(into))]
282 service: String,
283}
284
285impl GetSigningKeyRequest {
286 #[inline]
288 pub fn builder() -> GetSigningKeyRequestBuilder {
289 GetSigningKeyRequestBuilder::default()
290 }
291
292 #[inline]
294 pub fn access_key(&self) -> &str {
295 &self.access_key
296 }
297
298 #[inline]
300 pub fn session_token(&self) -> Option<&str> {
301 self.session_token.as_deref()
302 }
303
304 #[inline]
306 pub fn request_date(&self) -> NaiveDate {
307 self.request_date
308 }
309
310 #[inline]
312 pub fn region(&self) -> &str {
313 &self.region
314 }
315
316 #[inline]
318 pub fn service(&self) -> &str {
319 &self.service
320 }
321}
322
323#[derive(Builder, Clone, Debug)]
328pub struct GetSigningKeyResponse {
329 #[builder(setter(into), default)]
331 pub(crate) principal: Principal,
332
333 #[builder(setter(into), default)]
335 pub(crate) session_data: SessionData,
336
337 signing_key: KSigningKey,
339}
340
341impl GetSigningKeyResponse {
342 #[inline]
344 pub fn builder() -> GetSigningKeyResponseBuilder {
345 GetSigningKeyResponseBuilder::default()
346 }
347
348 #[inline]
350 pub fn principal(&self) -> &Principal {
351 &self.principal
352 }
353
354 #[inline]
356 pub fn session_data(&self) -> &SessionData {
357 &self.session_data
358 }
359
360 #[inline]
362 pub fn signing_key(&self) -> &KSigningKey {
363 &self.signing_key
364 }
365}
366
367impl Default for GetSigningKeyResponse {
368 fn default() -> Self {
369 Self {
370 signing_key: KSigningKey {
371 key: [0; SHA256_OUTPUT_LEN],
372 },
373 session_data: SessionData::default(),
374 principal: Principal::new(vec![]),
375 }
376 }
377}
378
379pub fn service_for_signing_key_fn<F, Fut>(f: F) -> ServiceFn<F>
391where
392 F: FnOnce(GetSigningKeyRequest) -> Fut + Send + 'static,
393 Fut: Future<Output = Result<GetSigningKeyResponse, BoxError>> + Send + 'static,
394{
395 service_fn(f)
396}
397
398#[cfg(test)]
399mod tests {
400 use {
401 crate::{GetSigningKeyRequest, GetSigningKeyResponse, KSecretKey, KSECRETKEY_LENGTH},
402 chrono::NaiveDate,
403 scratchstack_aws_principal::{AssumedRole, Principal},
404 std::str::FromStr,
405 };
406
407 #[test_log::test]
408 fn test_signing_key_derived() {
409 let date = NaiveDate::from_ymd_opt(2015, 8, 30).unwrap();
410
411 let ksecret1a = KSecretKey::from_str("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY").unwrap();
412 let ksecret1b = KSecretKey::from_str("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY").unwrap();
413 let ksecret2 = KSecretKey::from_str("wJalrXUtnFEMI/K7MDENG+bPxRfiCZEXAMPLEKEY").unwrap();
414
415 assert_eq!(ksecret1a, ksecret1b);
416 assert_eq!(ksecret1a, ksecret1a.clone());
417 assert_ne!(ksecret1a, ksecret2);
418 assert_eq!(format!("{:?}", ksecret1a).as_str(), "KSecretKey");
419 assert_eq!(format!("{}", ksecret1a).as_str(), "KSecretKey");
420 assert_eq!(ksecret1a.as_ref(), b"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY");
421
422 let kdate1a = ksecret1a.to_kdate(date);
423 let kdate1b = ksecret1b.to_kdate(date);
424 let kdate2 = ksecret2.to_kdate(date);
425 assert_eq!(
426 kdate1a.as_ref(),
427 &[
428 0x01u8, 0x38u8, 0xc7u8, 0xa6u8, 0xcbu8, 0xd6u8, 0x0au8, 0xa7u8, 0x27u8, 0xb2u8, 0xf6u8, 0x53u8, 0xa5u8,
429 0x22u8, 0x56u8, 0x74u8, 0x39u8, 0xdfu8, 0xb9u8, 0xf3u8, 0xe7u8, 0x2bu8, 0x21u8, 0xf9u8, 0xb2u8, 0x59u8,
430 0x41u8, 0xa4u8, 0x2fu8, 0x04u8, 0xa7u8, 0xcdu8
431 ]
432 );
433 assert_eq!(kdate1a, kdate1b);
434 assert_eq!(kdate1a, kdate1a.clone());
435 assert_ne!(kdate1a, kdate2);
436 assert_eq!(format!("{:?}", kdate1a).as_str(), "KDateKey");
437 assert_eq!(format!("{}", kdate1a).as_str(), "KDateKey");
438
439 let kregion1a = kdate1a.to_kregion("us-east-1");
440 let kregion1b = kdate1b.to_kregion("us-east-1");
441 let kregion2 = kdate2.to_kregion("us-east-1");
442 assert_eq!(
443 kregion1a.as_ref(),
444 &[
445 0xf3u8, 0x3du8, 0x58u8, 0x08u8, 0x50u8, 0x4bu8, 0xf3u8, 0x48u8, 0x12u8, 0xe5u8, 0xfau8, 0xdeu8, 0x63u8,
446 0x30u8, 0x8bu8, 0x42u8, 0x4bu8, 0x24u8, 0x4cu8, 0x59u8, 0x18u8, 0x9bu8, 0xe2u8, 0xa5u8, 0x91u8, 0xddu8,
447 0x22u8, 0x82u8, 0xc7u8, 0xcbu8, 0x56u8, 0x3fu8
448 ]
449 );
450 assert_eq!(kregion1a, kregion1b);
451 assert_eq!(kregion1a, kregion1a.clone());
452 assert_ne!(kregion1a, kregion2);
453 assert_eq!(format!("{:?}", kregion1a).as_str(), "KRegionKey");
454 assert_eq!(format!("{}", kregion1a).as_str(), "KRegionKey");
455
456 let kservice1a = kregion1a.to_kservice("example");
457 let kservice1b = kregion1b.to_kservice("example");
458 let kservice2 = kregion2.to_kservice("example");
459 assert_eq!(
460 kservice1a.as_ref(),
461 &[
462 0xc6u8, 0x0cu8, 0xc4u8, 0xb1u8, 0xd0u8, 0x34u8, 0xc7u8, 0x57u8, 0x34u8, 0x8fu8, 0x2cu8, 0x67u8, 0x30u8,
463 0x04u8, 0xc1u8, 0x89u8, 0x08u8, 0xbbu8, 0xa9u8, 0xa4u8, 0x6fu8, 0xa1u8, 0xdbu8, 0x87u8, 0xa9u8, 0x83u8,
464 0x50u8, 0xf2u8, 0x7eu8, 0x7bu8, 0x2du8, 0xf6u8
465 ]
466 );
467 assert_eq!(kservice1a, kservice1b);
468 assert_eq!(kservice1a, kservice1a.clone());
469 assert_ne!(kservice1a, kservice2);
470 assert_eq!(format!("{:?}", kservice1a).as_str(), "KServiceKey");
471 assert_eq!(format!("{}", kservice1a).as_str(), "KServiceKey");
472
473 let ksigning1a = kservice1a.to_ksigning();
474 let ksigning1b = kservice1b.to_ksigning();
475 let ksigning2 = kservice2.to_ksigning();
476 assert_eq!(
477 ksigning1a.as_ref(),
478 &[
479 0x43u8, 0x1cu8, 0xc9u8, 0xefu8, 0x58u8, 0x76u8, 0x28u8, 0x7du8, 0xbbu8, 0x92u8, 0x5du8, 0x4bu8, 0xa4u8,
480 0x62u8, 0x9fu8, 0x45u8, 0x90u8, 0x02u8, 0xadu8, 0x1du8, 0x26u8, 0xb7u8, 0xc7u8, 0x51u8, 0x60u8, 0x1bu8,
481 0xb2u8, 0x04u8, 0xe1u8, 0x17u8, 0x18u8, 0xb8u8
482 ]
483 );
484 assert_eq!(ksigning1a, ksigning1b);
485 assert_eq!(ksigning1a, ksigning1a.clone());
486 assert_ne!(ksigning1a, ksigning2);
487 assert_eq!(format!("{:?}", ksigning1a).as_str(), "KSigningKey");
488 assert_eq!(format!("{}", ksigning1a).as_str(), "KSigningKey");
489
490 assert_eq!(ksecret1a.to_kregion(date, "us-east-1"), kregion1a);
491 assert_eq!(ksecret1a.to_kservice(date, "us-east-1", "example"), kservice1a);
492 assert_eq!(ksecret1a.to_ksigning(date, "us-east-1", "example"), ksigning1a);
493
494 assert_eq!(kdate1a.to_kservice("us-east-1", "example"), kservice1a);
495 assert_eq!(kdate1a.to_ksigning("us-east-1", "example"), ksigning1a);
496
497 assert_eq!(kregion1a.to_kservice("example"), kservice1a);
498 }
499
500 #[test_log::test]
501 fn test_gsk_derived() {
502 let date = NaiveDate::from_ymd_opt(2015, 8, 30).unwrap();
503
504 let gsk_req1a = GetSigningKeyRequest {
505 access_key: "AKIDEXAMPLE".to_string(),
506 session_token: Some("token".to_string()),
507 request_date: date,
508 region: "us-east-1".to_string(),
509 service: "example".to_string(),
510 };
511
512 let _ = format!("{:?}", gsk_req1a);
514
515 let gsk_req1b = gsk_req1a.clone();
517 assert_eq!(gsk_req1a.access_key, gsk_req1b.access_key);
518 assert_eq!(gsk_req1a.session_token, gsk_req1b.session_token);
519 assert_eq!(gsk_req1a.request_date, gsk_req1b.request_date);
520 assert_eq!(gsk_req1a.region, gsk_req1b.region);
521 assert_eq!(gsk_req1a.service, gsk_req1b.service);
522
523 let signing_key = KSecretKey::from_str("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY").unwrap().to_ksigning(
524 date,
525 "us-east-1",
526 "example",
527 );
528 let principal = Principal::from(AssumedRole::new("aws", "123456789012", "role", "session").unwrap());
529
530 let gsk_resp1a =
531 GetSigningKeyResponse::builder().signing_key(signing_key).principal(principal).build().unwrap();
532
533 let _ = format!("{:?}", gsk_resp1a);
535
536 let gsk_resp1b = gsk_resp1a.clone();
538 assert_eq!(gsk_resp1a.signing_key, gsk_resp1b.signing_key);
539 assert_eq!(gsk_resp1a.principal, gsk_resp1b.principal);
540 }
541
542 #[test_log::test]
543 fn test_gsk_reponse_derived() {
544 let response: GetSigningKeyResponse = Default::default();
545 assert_eq!(response.signing_key.as_ref(), &[0u8; 32]);
546 }
547
548 #[test_log::test]
549 fn test_response_builder() {
550 let date = NaiveDate::from_ymd_opt(2015, 8, 30).unwrap();
551 let signing_key = KSecretKey::from_str("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY").unwrap().to_ksigning(
552 date,
553 "us-east-1",
554 "example",
555 );
556 let response = GetSigningKeyResponse::builder().signing_key(signing_key).build().unwrap();
557 assert!(response.principal().is_empty());
558 assert!(response.session_data().is_empty());
559 }
560
561 #[test]
562 fn test_key_from_str_length() {
563 assert_eq!(KSecretKey::from_str("123"), Err(crate::KeyLengthError::TooShort));
564 assert_eq!(
565 KSecretKey::from_str("123456789012345678901234567890123456789012345"),
566 Err(crate::KeyLengthError::TooLong)
567 );
568 assert!(KSecretKey::<KSECRETKEY_LENGTH>::from_str("1234567890123456789012345678901234567890").is_ok());
569 }
570}