key.rs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. use crate::{schema, UIDCError};
  2. use microrm::prelude::*;
  3. use ring::{
  4. rand::SecureRandom,
  5. signature::{Ed25519KeyPair, KeyPair},
  6. };
  7. use sha2::Digest;
  8. use itertools::Itertools;
  9. #[derive(Debug)]
  10. pub enum KeyError {
  11. Plain(&'static str),
  12. }
  13. #[non_exhaustive]
  14. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
  15. pub enum HMacType {
  16. Sha256,
  17. Sha512,
  18. }
  19. impl HMacType {
  20. pub fn digest_width(&self) -> usize {
  21. match self {
  22. Self::Sha256 => 32,
  23. Self::Sha512 => 64,
  24. }
  25. }
  26. }
  27. #[non_exhaustive]
  28. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
  29. pub enum KeyType {
  30. HMac(HMacType),
  31. RSA2048,
  32. RSA4096,
  33. Ed25519,
  34. }
  35. impl std::fmt::Display for KeyType {
  36. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  37. <Self as std::fmt::Debug>::fmt(self, f)
  38. }
  39. }
  40. pub const KEY_TYPE_NAMES: &[(&str, KeyType)] = &[
  41. ("hmac_sha256", KeyType::HMac(HMacType::Sha256)),
  42. ("hmac_sha512", KeyType::HMac(HMacType::Sha512)),
  43. ("rsa2048", KeyType::RSA2048),
  44. ("rsa4096", KeyType::RSA4096),
  45. ("ed25519", KeyType::Ed25519),
  46. ];
  47. impl std::str::FromStr for KeyType {
  48. type Err = UIDCError;
  49. fn from_str(s: &str) -> Result<Self, Self::Err> {
  50. for (spec, kty) in KEY_TYPE_NAMES {
  51. if s == *spec {
  52. return Ok(*kty);
  53. }
  54. }
  55. Err(UIDCError::AbortString(format!(
  56. "invalid keytype: must be one of {}",
  57. KEY_TYPE_NAMES.iter().map(|v| v.0).join(",")
  58. )))
  59. }
  60. }
  61. #[derive(serde::Serialize, serde::Deserialize, Debug)]
  62. struct RsaPublicData<'l> {
  63. modulus: &'l [u8],
  64. exponent: &'l [u8],
  65. }
  66. impl schema::Key {
  67. pub fn into_jwk(self) -> jsonwebtoken::jwk::Jwk {
  68. use jsonwebtoken::jwk;
  69. match self.key_type.as_ref() {
  70. KeyType::RSA2048 | KeyType::RSA4096 => {
  71. let rpubdata: RsaPublicData =
  72. bincode::deserialize(self.public_data.as_slice()).unwrap();
  73. jwk::Jwk {
  74. common: jwk::CommonParameters {
  75. public_key_use: Some(jwk::PublicKeyUse::Signature),
  76. key_algorithm: Some(jwk::KeyAlgorithm::RS256),
  77. key_id: Some(self.key_id),
  78. ..Default::default()
  79. },
  80. algorithm: jwk::AlgorithmParameters::RSA(jwk::RSAKeyParameters {
  81. key_type: jwk::RSAKeyType::RSA,
  82. n: base64::encode(rpubdata.modulus),
  83. e: base64::encode(rpubdata.exponent),
  84. }),
  85. }
  86. }
  87. KeyType::Ed25519 => jwk::Jwk {
  88. common: jwk::CommonParameters {
  89. public_key_use: Some(jwk::PublicKeyUse::Signature),
  90. key_algorithm: Some(jwk::KeyAlgorithm::EdDSA),
  91. key_id: Some(self.key_id),
  92. ..Default::default()
  93. },
  94. algorithm: jwk::AlgorithmParameters::OctetKeyPair(jwk::OctetKeyPairParameters {
  95. key_type: jwk::OctetKeyPairType::OctetKeyPair,
  96. curve: jwk::EllipticCurve::Ed25519,
  97. x: base64::encode(self.public_data),
  98. }),
  99. },
  100. _ => todo!(),
  101. }
  102. }
  103. }
  104. fn pubkey_id(data: &[u8]) -> String {
  105. let mut key_hasher = sha2::Sha256::new();
  106. key_hasher.update(data);
  107. let mut key_id = base64::encode(key_hasher.finalize());
  108. key_id.truncate(16);
  109. key_id
  110. }
  111. fn generate_rsa(realm: &schema::Realm, kty: KeyType, bits: usize) -> Result<String, UIDCError> {
  112. // ring does not generate RSA keypairs, so we need to shell out to openssl for this.
  113. let openssl_output = std::process::Command::new("sh")
  114. .arg("-c")
  115. .arg(format!(
  116. "openssl genpkey \
  117. -algorithm RSA \
  118. -pkeyopt rsa_keygen_pubexp:65537 \
  119. -pkeyopt rsa_keygen_bits:{bits} \
  120. -outform der"
  121. ))
  122. .output()
  123. .map_err(|_| UIDCError::Abort("couldn't invoke openssl"))?;
  124. let secret = openssl_output.stdout;
  125. let keypair = ring::signature::RsaKeyPair::from_der(&secret)
  126. .map_err(|_| UIDCError::Abort("couldn't parse PKCS#8 output from openssl"))?;
  127. let public = keypair.public_key();
  128. let key_id = pubkey_id(public.as_ref());
  129. let expiry = time::OffsetDateTime::now_utc() + time::Duration::days(730);
  130. let public_data = bincode::serialize(&RsaPublicData {
  131. modulus: public.modulus().big_endian_without_leading_zero(),
  132. exponent: public.exponent().big_endian_without_leading_zero(),
  133. })
  134. .unwrap();
  135. realm.keys.insert(schema::Key {
  136. key_id: key_id.clone(),
  137. key_type: kty.into(),
  138. key_state: schema::KeyState::Active.into(),
  139. public_data,
  140. secret_data: secret,
  141. expiry,
  142. })?;
  143. Ok(key_id)
  144. }
  145. pub fn generate_in(realm: &schema::Realm, kty: KeyType) -> Result<String, UIDCError> {
  146. let rng = ring::rand::SystemRandom::new();
  147. match kty {
  148. KeyType::HMac(hmty) => {
  149. let rng = ring::rand::SystemRandom::new();
  150. let mut key_id = [0u8; 16];
  151. rng.fill(&mut key_id)
  152. .map_err(|_| UIDCError::Abort("couldn't generate random values"))?;
  153. let key_id = pubkey_id(&key_id);
  154. let mut keydata = vec![0; hmty.digest_width()];
  155. rng.fill(keydata.as_mut_slice())
  156. .map_err(|_| UIDCError::Abort("couldn't generate random values"))?;
  157. let expiry = time::OffsetDateTime::now_utc() + time::Duration::days(730);
  158. realm.keys.insert(schema::Key {
  159. key_id: key_id.clone(),
  160. key_type: kty.into(),
  161. key_state: schema::KeyState::Active.into(),
  162. // no separate public data for HMAC keys
  163. public_data: vec![],
  164. secret_data: keydata.clone(),
  165. expiry,
  166. })?;
  167. Ok(key_id)
  168. }
  169. KeyType::RSA2048 => generate_rsa(realm, KeyType::RSA2048, 2048),
  170. KeyType::RSA4096 => generate_rsa(realm, KeyType::RSA4096, 4096),
  171. KeyType::Ed25519 => {
  172. let generated_keypair = Ed25519KeyPair::generate_pkcs8(&rng)
  173. .map_err(|_| KeyError::Plain("failed to generate key"))?;
  174. let keydata = generated_keypair.as_ref().to_owned();
  175. let loaded_key = Ed25519KeyPair::from_pkcs8(keydata.as_slice())
  176. .expect("couldn't load just-generated key");
  177. let pubkey = loaded_key.public_key();
  178. let key_id = pubkey_id(pubkey.as_ref());
  179. let expiry = time::OffsetDateTime::now_utc() + time::Duration::days(730);
  180. realm.keys.insert(schema::Key {
  181. key_id: key_id.clone(),
  182. key_type: kty.into(),
  183. key_state: schema::KeyState::Active.into(),
  184. public_data: pubkey.as_ref().into(),
  185. secret_data: keydata,
  186. expiry,
  187. })?;
  188. Ok(key_id)
  189. }
  190. }
  191. }
  192. pub fn list(realm: &schema::Realm) -> Result<(), UIDCError> {
  193. let keys = realm.keys.get()?;
  194. for key in keys {
  195. println!(
  196. "- [{}] {:?}, expires {}",
  197. key.key_id,
  198. key.key_type,
  199. key.expiry
  200. .format(&time::format_description::well_known::Rfc3339)
  201. .unwrap()
  202. );
  203. }
  204. Ok(())
  205. }
  206. pub fn remove(realm: &schema::Realm, key_id: &String) -> Result<(), UIDCError> {
  207. realm.keys.with(schema::Key::KeyId, key_id).delete()?;
  208. Ok(())
  209. }