1use base64::Engine;
2use base64::engine::general_purpose::URL_SAFE_NO_PAD;
3use elliptic_curve::SecretKey;
4use jacquard_common::CowStr;
5use jose_jwk::{Key, crypto};
6use rand::{CryptoRng, RngCore, rngs::ThreadRng};
7use sha2::{Digest, Sha256};
8use std::cmp::Ordering;
9
10use crate::{FALLBACK_ALG, types::OAuthAuthorizationServerMetadata};
11
12pub fn generate_key(allowed_algos: &[CowStr]) -> Option<Key> {
13 for alg in allowed_algos {
14 #[allow(clippy::single_match)]
15 match alg.as_ref() {
16 "ES256" => {
17 return Some(Key::from(&crypto::Key::from(
18 SecretKey::<p256::NistP256>::random(&mut ThreadRng::default()),
19 )));
20 }
21 _ => {
22 // TODO: Implement other algorithms?
23 }
24 }
25 }
26 None
27}
28
29pub fn generate_nonce() -> CowStr<'static> {
30 URL_SAFE_NO_PAD
31 .encode(get_random_values::<_, 16>(&mut ThreadRng::default()))
32 .into()
33}
34
35pub fn generate_verifier() -> CowStr<'static> {
36 URL_SAFE_NO_PAD
37 .encode(get_random_values::<_, 43>(&mut ThreadRng::default()))
38 .into()
39}
40
41pub fn get_random_values<R, const LEN: usize>(rng: &mut R) -> [u8; LEN]
42where
43 R: RngCore + CryptoRng,
44{
45 let mut bytes = [0u8; LEN];
46 rng.fill_bytes(&mut bytes);
47 bytes
48}
49
50// 256K > ES (256 > 384 > 512) > PS (256 > 384 > 512) > RS (256 > 384 > 512) > other (in original order)
51pub fn compare_algos(a: &CowStr, b: &CowStr) -> Ordering {
52 if a.as_ref() == "ES256K" {
53 return Ordering::Less;
54 }
55 if b.as_ref() == "ES256K" {
56 return Ordering::Greater;
57 }
58 for prefix in ["ES", "PS", "RS"] {
59 if let Some(stripped_a) = a.strip_prefix(prefix) {
60 if let Some(stripped_b) = b.strip_prefix(prefix) {
61 if let (Ok(len_a), Ok(len_b)) =
62 (stripped_a.parse::<u32>(), stripped_b.parse::<u32>())
63 {
64 return len_a.cmp(&len_b);
65 }
66 } else {
67 return Ordering::Less;
68 }
69 } else if b.starts_with(prefix) {
70 return Ordering::Greater;
71 }
72 }
73 Ordering::Equal
74}
75
76pub fn generate_pkce() -> (CowStr<'static>, CowStr<'static>) {
77 // https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
78 let verifier = generate_verifier();
79 (
80 URL_SAFE_NO_PAD
81 .encode(Sha256::digest(&verifier.as_str()))
82 .into(),
83 verifier,
84 )
85}
86
87pub fn generate_dpop_key(metadata: &OAuthAuthorizationServerMetadata) -> Option<Key> {
88 let mut algs = metadata
89 .dpop_signing_alg_values_supported
90 .clone()
91 .unwrap_or(vec![FALLBACK_ALG.into()]);
92 algs.sort_by(compare_algos);
93 generate_key(&algs)
94}