]> git.proxmox.com Git - proxmox.git/blame - proxmox-acme/src/key.rs
bump proxmox-login to 0.1.1-1
[proxmox.git] / proxmox-acme / src / key.rs
CommitLineData
aa230682
WB
1use std::convert::{TryFrom, TryInto};
2
3use openssl::hash::{Hasher, MessageDigest};
4use openssl::pkey::{HasPublic, Id, PKeyRef};
5use serde::Serialize;
6
7use crate::b64u;
8use crate::Error;
9
10/// An RSA public key.
11#[derive(Clone, Debug, Serialize)]
12#[serde(deny_unknown_fields)]
13pub struct RsaPublicKey {
14 #[serde(with = "b64u::bytes")]
15 e: Vec<u8>,
16 #[serde(with = "b64u::bytes")]
17 n: Vec<u8>,
18}
19
20/// An EC public key.
21#[derive(Clone, Debug, Serialize)]
22#[serde(deny_unknown_fields)]
23pub struct EcPublicKey {
24 crv: &'static str,
25 #[serde(with = "b64u::bytes")]
26 x: Vec<u8>,
27 #[serde(with = "b64u::bytes")]
28 y: Vec<u8>,
29}
30
31/// A public key.
32///
33/// Internally tagged, so this already contains the 'kty' member.
34#[derive(Clone, Debug, Serialize)]
35#[serde(tag = "kty")]
36pub enum PublicKey {
37 #[serde(rename = "RSA")]
38 Rsa(RsaPublicKey),
39 #[serde(rename = "EC")]
40 Ec(EcPublicKey),
41}
42
43impl PublicKey {
44 /// The thumbprint is the b64u encoded sha256sum of the *canonical* json representation.
45 pub fn thumbprint(&self) -> Result<String, Error> {
46 let mut hasher = Hasher::new(MessageDigest::sha256())?;
47 crate::json::to_hash_canonical(&serde_json::to_value(self)?, &mut hasher)?;
48 Ok(b64u::encode(hasher.finish()?.as_ref()))
49 }
50}
51
52#[derive(Clone, Debug, Serialize)]
53pub struct Jwk {
54 #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
55 pub usage: Option<String>,
56
57 /// The key data is internally tagged, we can just flatten it.
58 #[serde(flatten)]
59 pub key: PublicKey,
60}
61
62impl<P: HasPublic> TryFrom<&PKeyRef<P>> for Jwk {
63 type Error = Error;
64
65 fn try_from(key: &PKeyRef<P>) -> Result<Self, Self::Error> {
66 Ok(Self {
67 key: key.try_into()?,
68 usage: None,
69 })
70 }
71}
72
73impl<P: HasPublic> TryFrom<&PKeyRef<P>> for PublicKey {
74 type Error = Error;
75
76 fn try_from(key: &PKeyRef<P>) -> Result<Self, Self::Error> {
77 match key.id() {
78 Id::RSA => Ok(PublicKey::Rsa(RsaPublicKey::try_from(&key.rsa()?)?)),
79 Id::EC => Ok(PublicKey::Ec(EcPublicKey::try_from(&key.ec_key()?)?)),
80 _ => Err(Error::UnsupportedKeyType),
81 }
82 }
83}
84
85impl<P: HasPublic> TryFrom<&openssl::rsa::Rsa<P>> for RsaPublicKey {
86 type Error = Error;
87
88 fn try_from(key: &openssl::rsa::Rsa<P>) -> Result<Self, Self::Error> {
89 Ok(RsaPublicKey {
90 e: key.e().to_vec(),
91 n: key.n().to_vec(),
92 })
93 }
94}
95
96impl<P: HasPublic> TryFrom<&openssl::ec::EcKey<P>> for EcPublicKey {
97 type Error = Error;
98
99 fn try_from(key: &openssl::ec::EcKey<P>) -> Result<Self, Self::Error> {
100 let group = key.group();
101
102 if group.curve_name() != Some(openssl::nid::Nid::X9_62_PRIME256V1) {
103 return Err(Error::UnsupportedGroup);
104 }
105
106 let mut ctx = openssl::bn::BigNumContext::new()?;
107 let mut x = openssl::bn::BigNum::new()?;
108 let mut y = openssl::bn::BigNum::new()?;
e499b084
WB
109 key.public_key()
110 .affine_coordinates(group, &mut x, &mut y, &mut ctx)?;
aa230682
WB
111
112 Ok(EcPublicKey {
113 crv: "P-256",
114 x: x.to_vec(),
115 y: y.to_vec(),
116 })
117 }
118}
e499b084
WB
119
120#[test]
121fn test_key_conversion() -> Result<(), Error> {
122 let key = openssl::ec::EcKey::generate(
123 openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1)?.as_ref(),
124 )?;
125
126 let _ = EcPublicKey::try_from(&key).expect("failed to jsonify ec key");
127
128 Ok(())
129}