• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! This module describes a public key that is restricted to one of the supported algorithms.
2 
3 use anyhow::{ensure, Context, Result};
4 use openssl::hash::MessageDigest;
5 use openssl::nid::Nid;
6 use openssl::pkey::{HasParams, Id, PKey, PKeyRef, Public};
7 use openssl::sign::Verifier;
8 use std::error::Error;
9 use std::fmt;
10 
11 /// Enumeration of the kinds of key that are supported.
12 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
13 pub(crate) enum Kind {
14     Ed25519,
15     Ec(EcKind),
16 }
17 
18 /// Enumeration of the kinds of elliptic curve keys that are supported.
19 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
20 pub(crate) enum EcKind {
21     P256,
22     P384,
23 }
24 
25 /// Struct wrapping the public key and relevant validation methods.
26 #[derive(Clone, Debug)]
27 pub struct PublicKey {
28     kind: Kind,
29     pkey: PKey<Public>,
30 }
31 
32 impl PublicKey {
kind(&self) -> Kind33     pub(crate) fn kind(&self) -> Kind {
34         self.kind
35     }
36 
pkey(&self) -> &PKeyRef<Public>37     pub(crate) fn pkey(&self) -> &PKeyRef<Public> {
38         &self.pkey
39     }
40 
41     /// Verify that the signature obtained from signing the given message
42     /// with the PublicKey matches the signature provided.
verify(&self, signature: &[u8], message: &[u8]) -> Result<()>43     pub fn verify(&self, signature: &[u8], message: &[u8]) -> Result<()> {
44         let mut verifier = match self.kind {
45             Kind::Ed25519 => Verifier::new_without_digest(&self.pkey),
46             Kind::Ec(ec) => Verifier::new(digest_for_ec(ec), &self.pkey),
47         }
48         .with_context(|| format!("Failed to create verifier {:?}", self.kind))?;
49         let verified =
50             verifier.verify_oneshot(signature, message).context("Failed to verify signature")?;
51         ensure!(verified, "Signature verification failed.");
52         Ok(())
53     }
54 
55     /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
to_pem(&self) -> String56     pub fn to_pem(&self) -> String {
57         String::from_utf8(self.pkey.public_key_to_pem().unwrap()).unwrap()
58     }
59 }
60 
61 /// The error type returned when converting from [`PKey'] to [`PublicKey`] fails.
62 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
63 pub struct TryFromPKeyError(());
64 
65 impl fmt::Display for TryFromPKeyError {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result66     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
67         write!(fmt, "unsupported public key conversion attempted")
68     }
69 }
70 
71 impl Error for TryFromPKeyError {}
72 
73 impl TryFrom<PKey<Public>> for PublicKey {
74     type Error = TryFromPKeyError;
75 
try_from(pkey: PKey<Public>) -> Result<Self, Self::Error>76     fn try_from(pkey: PKey<Public>) -> Result<Self, Self::Error> {
77         let kind = pkey_kind(&pkey).ok_or(TryFromPKeyError(()))?;
78         Ok(Self { kind, pkey })
79     }
80 }
81 
pkey_kind<T: HasParams>(pkey: &PKeyRef<T>) -> Option<Kind>82 fn pkey_kind<T: HasParams>(pkey: &PKeyRef<T>) -> Option<Kind> {
83     match pkey.id() {
84         Id::ED25519 => Some(Kind::Ed25519),
85         Id::EC => match pkey.ec_key().unwrap().group().curve_name() {
86             Some(Nid::X9_62_PRIME256V1) => Some(Kind::Ec(EcKind::P256)),
87             Some(Nid::SECP384R1) => Some(Kind::Ec(EcKind::P384)),
88             _ => None,
89         },
90         _ => None,
91     }
92 }
93 
digest_for_ec(ec: EcKind) -> MessageDigest94 fn digest_for_ec(ec: EcKind) -> MessageDigest {
95     match ec {
96         EcKind::P256 => MessageDigest::sha256(),
97         EcKind::P384 => MessageDigest::sha384(),
98     }
99 }
100 
101 #[cfg(test)]
102 mod tests {
103     use super::*;
104 
105     #[test]
from_ed25519_pkey()106     fn from_ed25519_pkey() {
107         let pkey = load_public_pkey(testkeys::ED25519_KEY_PEM[0]);
108         let key: PublicKey = pkey.clone().try_into().unwrap();
109         assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
110     }
111 
112     #[test]
from_p256_pkey()113     fn from_p256_pkey() {
114         let pkey = load_public_pkey(testkeys::P256_KEY_PEM[0]);
115         let key: PublicKey = pkey.clone().try_into().unwrap();
116         assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
117     }
118 
119     #[test]
from_p384_pkey()120     fn from_p384_pkey() {
121         let pkey = load_public_pkey(testkeys::P384_KEY_PEM[0]);
122         let key: PublicKey = pkey.clone().try_into().unwrap();
123         assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
124     }
125 
126     #[test]
from_p521_pkey_not_supported()127     fn from_p521_pkey_not_supported() {
128         let pkey = load_public_pkey(testkeys::P521_KEY_PEM[0]);
129         assert!(PublicKey::try_from(pkey).is_err());
130     }
131 
132     #[test]
from_rsa2048_pkey_not_supported()133     fn from_rsa2048_pkey_not_supported() {
134         let pkey = load_public_pkey(testkeys::RSA2048_KEY_PEM[0]);
135         assert!(PublicKey::try_from(pkey).is_err());
136     }
137 
load_public_pkey(pem: &str) -> PKey<Public>138     pub fn load_public_pkey(pem: &str) -> PKey<Public> {
139         testkeys::public_from_private(&PKey::private_key_from_pem(pem.as_bytes()).unwrap())
140     }
141 }
142 
143 /// Keys and key handling utilities for use in tests.
144 #[cfg(test)]
145 pub(crate) mod testkeys {
146     use super::*;
147     use openssl::pkey::Private;
148     use openssl::sign::Signer;
149 
150     pub struct PrivateKey {
151         kind: Kind,
152         pkey: PKey<Private>,
153     }
154 
155     impl PrivateKey {
from_pem(pem: &str) -> Self156         pub fn from_pem(pem: &str) -> Self {
157             let pkey = PKey::private_key_from_pem(pem.as_bytes()).unwrap();
158             let kind = pkey_kind(&pkey).expect("unsupported private key");
159             Self { kind, pkey }
160         }
161 
kind(&self) -> Kind162         pub(crate) fn kind(&self) -> Kind {
163             self.kind
164         }
165 
public_key(&self) -> PublicKey166         pub fn public_key(&self) -> PublicKey {
167             public_from_private(&self.pkey).try_into().unwrap()
168         }
169 
sign(&self, message: &[u8]) -> Result<Vec<u8>>170         pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>> {
171             let mut signer = match self.kind {
172                 Kind::Ed25519 => Signer::new_without_digest(&self.pkey)?,
173                 Kind::Ec(ec) => Signer::new(digest_for_ec(ec), &self.pkey)?,
174             };
175             signer.sign_oneshot_to_vec(message).context("signing message")
176         }
177     }
178 
179     /// Gives the public key that matches the private key.
public_from_private(pkey: &PKey<Private>) -> PKey<Public>180     pub fn public_from_private(pkey: &PKey<Private>) -> PKey<Public> {
181         // It feels like there should be a more direct way to do this but I haven't found it.
182         PKey::public_key_from_der(&pkey.public_key_to_der().unwrap()).unwrap()
183     }
184 
185     /// A selection of Ed25519 private keys.
186     pub const ED25519_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
187         MC4CAQAwBQYDK2VwBCIEILKW0KEeuieFxhDAzigQPE4XRTiQx+0/AlAjJqHmUWE6\n\
188         -----END PRIVATE KEY-----\n"];
189 
190     /// A selection of elliptic curve P-256 private keys.
191     pub const P256_KEY_PEM: &[&str] = &[
192         "-----BEGIN PRIVATE KEY-----\n\
193         MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+CO3ZBuAsimwPKAL\n\
194         IeDyCh4cRZ5EMd6llGu5MQCpibGhRANCAAQObPxc4bIPjupILrvKJjTrpTcyCf6q\n\
195         V552FlS67fGphwhg2LDfQ8adEdkuRfQvk+IvKJz8MDcPjErBG3Wlps1N\n\
196         -----END PRIVATE KEY-----\n",
197         "-----BEGIN PRIVATE KEY-----\n\
198         MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgw1OPIcfQv5twO68B\n\
199         H+xNstW3DLXC6e4PGEYG/VppYVahRANCAAQMyWyv4ffVMu+wVNhNEk2mQSaTmSl/\n\
200         dLdRbEowfqPwMzdqdQ3QlKSV4ZcU2lsJEuQMkZzmVPz02enY2qcKctmj\n\
201         -----END PRIVATE KEY-----\n",
202         "-----BEGIN PRIVATE KEY-----\n\
203         MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgbXO6ee7i7sY4YfFS\n\
204         Gn60ScPuL3QuYFMX4nJbcqPSQ7+hRANCAAS8i9xA8cIcWStbMG97YrttQsYEIR2a\n\
205         15+alxbb6b7422FuxBB0qG5nJ4m+Jd3Bp+N2lwx4rHBFDqU4cp8VlQav\n\
206         -----END PRIVATE KEY-----\n",
207         "-----BEGIN PRIVATE KEY-----\n\
208         MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/JuxkbpPyyouat11\n\
209         szDR+OA7d/fuMk9IhGkH7z1xHzChRANCAASRlY0D7Uh5T/FmB6txGr21w6jqKW2x\n\
210         RXdsaZgCB6XnrXlkgkvuWDc0CTLSBWdPFgW6OX0fyXViglEBH95REyQr\n\
211         -----END PRIVATE KEY-----\n",
212     ];
213 
214     /// A selection of EC keys that should have leading zeros in their coordinates
215     pub const P256_KEY_WITH_LEADING_ZEROS_PEM: &[&str] = &[
216         // 31 byte Y coordinate:
217         "-----BEGIN PRIVATE KEY-----\n\
218         MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCWbRSB3imI03F5YNVq\n\
219         8AN8ZbyzW/h+5BQ53caD5VkWJg==\n\
220         -----END PRIVATE KEY-----\n",
221         // 31 byte X coordinate:
222         "-----BEGIN PRIVATE KEY-----\n\
223         MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDe5E5WqNmCLxtsCNTc\n\
224         UOb9CPXCn6l3CZpbrp0aivb+Bw==\n\
225         -----END PRIVATE KEY-----\n",
226         // X & Y both have MSB set, and some stacks will add a padding byte
227         "-----BEGIN PRIVATE KEY-----\n\
228         MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCWOWcXPDEVZ4Qz3EBK\n\
229         uvSqhD9HmxDGxcNe3yxKi9pazw==\n\
230         -----END PRIVATE KEY-----\n",
231     ];
232 
233     /// A selection of elliptic curve P-384 private keys.
234     pub const P384_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
235         MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBMZ414LiUpcuNTNq5W\n\
236         Ig/qbnbFn0MpuZZxUn5YZ8/+2/tyXFFHRyQoQ4YpNN1P/+qhZANiAAScPDyisb21\n\
237         GldmGksI5g82hjPRYscWNs/6pFxQTMcxABE+/1lWaryLR193ZD74VxVRIKDBluRs\n\
238         uuHi+VayOreTX1/qlUoxgBT+XTI0nTdLn6WwO6vVO1NIkGEVnYvB2eM=\n\
239         -----END PRIVATE KEY-----\n"];
240 
241     /// A selection of EC keys that should have leading zeros in their coordinates
242     pub const P384_KEY_WITH_LEADING_ZEROS_PEM: &[&str] = &[
243         // 47 byte Y coordinate:
244         "-----BEGIN PRIVATE KEY-----\n\
245         ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDCzgVHCz7wgmSdb7/IixYik\n\
246         3AuQceCtBTiFrJpgpGFluwgLUR0S2NpzIuty4M7xU74=\n\
247         -----END PRIVATE KEY-----\n",
248         // 47 byte X coordinate:
249         "-----BEGIN PRIVATE KEY-----\n\
250         ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDBoW+8zbvwf5fYOS8YPyPEH\n\
251         jHP71Vr1MnRYRp/yG1wbthW2XEu0UWbp4qrZ5WTnZPg=\n\
252         -----END PRIVATE KEY-----\n",
253         // X & Y both have MSB set, and some stacks will add a padding byte
254         "-----BEGIN PRIVATE KEY-----\n\
255         ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDD2A69j5M/6oc6/WGoYln4t\n\
256         Alnn0C6kpJz1EVC+eH6y0YNrcGamz8pPY4NkzUB/tj4=\n\
257         -----END PRIVATE KEY-----\n",
258     ];
259 
260     /// A selection of elliptic curve P-521 private keys.
261     pub const P521_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
262         MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBQuD8Db3jT2yPYR5t\n\
263         Y1ZqESxOe4eBWzekFKg7cjVWsSiJEvWTPC1H7CLtXQBHZglO90dwMt4flL91xHkl\n\
264         iZOzyHahgYkDgYYABAHACwmmKkZu01fp1QTTTQ0cv7IAfYv9FEBz8yfhNGPnI2WY\n\
265         iH1/lYeCfYc9d33aSc/ELY9+vIFzVStJumS/B/WTewEhxVomlKPAkUJeLdCaK5av\n\
266         nlUNj7pNQ/5v5FZVxmoFJvAtUAnZqnJqo/QkLtEnmKlzpLte2LuwTPZhG35z0HeL\n\
267         2g==\n\
268         -----END PRIVATE KEY-----\n"];
269 
270     /// A selection of 2048-bit RSA private keys.
271     pub const RSA2048_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
272         MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDbOJh7Ys7CuIju\n\
273         VVKMlFlZWwDEGBX5bVYD/xNBNNF1FY9bOcV/BG20IwoZkdV0N+vm8eWSuv/uwIJp\n\
274         sN2PMWPAEIWbPGGMnSdePpkwrdpFFywhEQqUrfdCFXZ8zeF85Nz5mL8ysl4vlMsL\n\
275         mbErCkrq++K0lzs+k7w/FtPCgs4M3WypJfZef5zM0CGWxpHZGoUGm0HW9fen4sv8\n\
276         hTmMGNY/r0SJhdZREGmiGCx2v+ksOEBon1r/6QKVTP8S73XFsyNCWyop0hYTakut\n\
277         D3HtJ5sWzu2RU8rrch3Txinz0jpGF8PATHk35YMw/9jwwwSqjDw+pOQcYk8SviAf\n\
278         glZf8aZlAgMBAAECggEAAS67PK67tuaOWywJSWHLsWGmqJ4I2tZiTzCT9EZ2MOVx\n\
279         +4ZChNfjHUsskXUp4XNL/FE0J3WvhEYjXR1u+L37nvqc48mJpjoPN7o/CMb6rM/J\n\
280         +ly9A2ZOvEB4ppOYDYh5QVDm7/otmvEMzJxuUOpvxYxqnJpAPgl9dBpNQ0nSt3YX\n\
281         jJS4+vuzQpwwSTfchpcCZYU3AX9DpQpxnrLX3/7d3GTs2NuedmSwRz+mCfwaOlFk\n\
282         jdrJ2uJJrDLcK6yhSdsE9aNgKkmX6aNLhxbbCFTyDNiGY5HHayyL3mVvyaeovYcn\n\
283         ZS+Z+0TJGCgXDRHHSFyIAsgVonxHfn49x9uvfpuMFQKBgQD2cVp26+aQgt46ajVv\n\
284         yn4fxbNpfovL0pgtSjo7ekZOWYJ3Is1SDmnni8k1ViKgUYC210dTTlrljxUil8T0\n\
285         83e03k2xasDi2c+h/7JFYJPDyZwIm1o19ciUwY73D54iJaRbrzEximFeA0h4LGKw\n\
286         Yjd4xkKMJw16CU00gInyI193BwKBgQDjuP0/QEEPpYvzpag5Ul4+h22K/tiOUrFj\n\
287         NuSgd+IvQG1hW48zHEa9vXvORQ/FteiQ708racz6ByqY+n2w6QdtdRMj7Hsyo2fk\n\
288         SEeNaLrR7Sif6MfkYajbSGFySDD82vj4Jt76vzdt3MjpZfs6ryPmnKLVPWNA3mnS\n\
289         4+u2J/+QMwKBgFfiJnugNnG0aaF1PKcoFAAqlYd6XEoMSL5l6QxK14WbP/5SR9wK\n\
290         TdQHsnI1zFVVm0wYy1O27o1MkCHs84zSwg6a9CPfyPdc60F/GMjK3wcD/4PGOs5h\n\
291         Xu1FdUE/rYnJ2KnleOqMyZooG5DXaz4xWEzWjubCCnlJleGyMP9LhADDAoGAR/jK\n\
292         iXgcV/6haeMcdOl0gdy5oWmENg8qo0nRHmplYTvCljei3at9LDC79WhcYMdqdow8\n\
293         AGOS9h7XtrvMh+JOh6it4Pe3xDxi9IJnoujLytditI+Uxbib7ppEuiLY4MGwWHWo\n\
294         maVftmhGU4X4zgZWmWc+C5k4SmNBHPcOI2cm3YMCgYB5/Ni+tBxng0S/PRAtwCeG\n\
295         dVnQnYvS2C5nHCn9D5rmAcVXUKrIJ1/1K4se8vQ15DDcpuBF1SejYTJzdUP8Zgcs\n\
296         p8wVq7neK8uSsmG+AfUgxMjbetoAVTP3L8+GbjocznR9auB7BEjFVO25iYSiTp/w\n\
297         NNzbIKQRDN+c3vUpneJcuw==\n\
298         -----END PRIVATE KEY-----\n"];
299 }
300