haste_server/auth_n/certificates/
mod.rs

1use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
2use haste_config::{Config, ConfigType, get_config};
3use haste_fhir_model::r4::generated::terminology::IssueType;
4use haste_fhir_operation_error::OperationOutcomeError;
5use rand::rngs::OsRng;
6use rsa::{
7    RsaPrivateKey, RsaPublicKey,
8    pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey, EncodeRsaPublicKey},
9    pkcs8::LineEnding,
10    traits::PublicKeyParts,
11};
12use serde::{Deserialize, Serialize};
13use sha1::{Digest, Sha1};
14use std::{path::Path, sync::LazyLock};
15
16use crate::ServerEnvironmentVariables;
17
18static PRIVATE_KEY_FILENAME: &str = "private_key.pem";
19static PUBLIC_KEY_FILENAME: &str = "public_key.pem";
20
21pub fn create_certifications(
22    config: &dyn Config<ServerEnvironmentVariables>,
23) -> Result<(), OperationOutcomeError> {
24    let certificate_dir = config
25        .get(ServerEnvironmentVariables::CertificationDir)
26        .unwrap();
27    let dir: &Path = Path::new(&certificate_dir);
28
29    let mut rng = OsRng;
30    let bits = 2048;
31
32    let private_key_file = dir.join(PRIVATE_KEY_FILENAME);
33    let public_key_file = dir.join(PUBLIC_KEY_FILENAME);
34
35    // If no private key than write.
36    if !private_key_file.exists() {
37        let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
38        let pub_key = RsaPublicKey::from(&priv_key);
39        std::fs::create_dir_all(certificate_dir).unwrap();
40        std::fs::write(
41            private_key_file,
42            priv_key.to_pkcs1_pem(LineEnding::default()).unwrap(),
43        )
44        .map_err(|e| OperationOutcomeError::fatal(IssueType::Exception(None), e.to_string()))?;
45
46        std::fs::write(
47            public_key_file,
48            pub_key.to_pkcs1_pem(LineEnding::default()).unwrap(),
49        )
50        .map_err(|e| OperationOutcomeError::fatal(IssueType::Exception(None), e.to_string()))?;
51    }
52
53    Ok(())
54}
55
56#[derive(Serialize, Deserialize, Debug)]
57pub enum JSONWebKeyAlgorithm {
58    RS256,
59}
60
61#[derive(Serialize, Deserialize, Debug)]
62pub enum JSONWebKeyType {
63    RSA,
64}
65
66#[derive(Serialize, Deserialize, Debug)]
67pub struct JSONWebKey {
68    kid: String,
69
70    alg: JSONWebKeyAlgorithm,
71    kty: JSONWebKeyType,
72    // Base64 URL SAFE
73    e: String,
74    n: String,
75    x5t: Option<String>,
76}
77
78#[derive(Serialize, Deserialize, Debug)]
79pub struct JSONWebKeySet {
80    keys: Vec<JSONWebKey>,
81}
82
83pub static JWK_SET: LazyLock<JSONWebKeySet> = LazyLock::new(|| {
84    let config = get_config(ConfigType::Environment);
85    let certificate_dir = config
86        .get(ServerEnvironmentVariables::CertificationDir)
87        .unwrap();
88    let cert_dir: &Path = Path::new(&certificate_dir);
89    let rsa_private = RsaPrivateKey::from_pkcs1_pem(
90        &std::fs::read_to_string(&cert_dir.join(PRIVATE_KEY_FILENAME)).unwrap(),
91    )
92    .unwrap();
93    let rsa_public_key = rsa_private.to_public_key();
94
95    let mut hasher = Sha1::new();
96    hasher.update(rsa_public_key.to_pkcs1_der().unwrap().as_bytes());
97    let x5t = hasher.finalize();
98
99    let rsa_public = JSONWebKey {
100        kid: URL_SAFE_NO_PAD.encode(&x5t),
101        alg: JSONWebKeyAlgorithm::RS256,
102        kty: JSONWebKeyType::RSA,
103        e: URL_SAFE_NO_PAD.encode(&rsa_public_key.e().clone().to_bytes_be()),
104        n: URL_SAFE_NO_PAD.encode(&rsa_public_key.n().clone().to_bytes_be()),
105        x5t: Some(URL_SAFE_NO_PAD.encode(&x5t)),
106    };
107
108    JSONWebKeySet {
109        keys: vec![rsa_public],
110    }
111});
112
113#[allow(unused)]
114// Only used if an environment.
115static DECODING_KEY: LazyLock<jsonwebtoken::DecodingKey> = LazyLock::new(|| {
116    // let key = CERTIFICATES.public_key.clone();
117    let config = get_config(ConfigType::Environment);
118    let certificate_dir = config
119        .get(ServerEnvironmentVariables::CertificationDir)
120        .unwrap();
121    let cert_dir: &Path = Path::new(&certificate_dir);
122    jsonwebtoken::DecodingKey::from_rsa_pem(
123        &std::fs::read(cert_dir.join(PUBLIC_KEY_FILENAME)).unwrap(),
124    )
125    .unwrap()
126});
127
128static ENCODING_KEY: LazyLock<jsonwebtoken::EncodingKey> = LazyLock::new(|| {
129    // let key = CERTIFICATES.public_key.clone();
130    let config = get_config(ConfigType::Environment);
131    let certificate_dir = config
132        .get(ServerEnvironmentVariables::CertificationDir)
133        .unwrap();
134    let cert_dir: &Path = Path::new(&certificate_dir);
135    jsonwebtoken::EncodingKey::from_rsa_pem(
136        &std::fs::read(cert_dir.join(PRIVATE_KEY_FILENAME)).unwrap(),
137    )
138    .unwrap()
139});
140
141#[allow(unused)]
142pub fn decoding_key() -> &'static jsonwebtoken::DecodingKey {
143    &*DECODING_KEY
144}
145
146pub fn encoding_key() -> &'static jsonwebtoken::EncodingKey {
147    &*ENCODING_KEY
148}