1use std::collections::HashMap;
2use std::fmt;
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6
7use ring::signature::{
8 ECDSA_P256_SHA256_FIXED,
9 ECDSA_P384_SHA384_FIXED,
10 RSA_PKCS1_2048_8192_SHA256,
11 RSA_PKCS1_2048_8192_SHA512,
12 ED25519,
13 UnparsedPublicKey,
14 RsaPublicKeyComponents};
15
16use ring::rand::{SystemRandom, SecureRandom};
17
18use super::error::{Error, ErrorKind, Result};
20use super::keytype::{KeyType};
21use super::pubkey::{PublicKey, PublicKeyKind};
22use super::reader::Reader;
23
24use std::convert::TryFrom;
25
26#[derive(Debug, PartialEq, Clone, Copy)]
28pub enum CertType {
29 User = 1,
31
32 Host = 2,
34}
35
36impl TryFrom<&str> for CertType {
37 type Error = &'static str;
38
39 fn try_from(s: &str) -> std::result::Result<Self, Self::Error> {
40 match s {
41 "user" | "User" => Ok(CertType::User),
42 "host" | "Host" => Ok(CertType::Host),
43 _ => Err("Unknown certificate type"),
44 }
45 }
46}
47
48impl fmt::Display for CertType {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 match *self {
51 CertType::User => write!(f, "user certificate"),
52 CertType::Host => write!(f, "host certificate"),
53 }
54 }
55}
56
57const STANDARD_EXTENSIONS: [(&str, &str); 5] = [
58 ("permit-agent-forwarding", ""),
59 ("permit-port-forwarding", ""),
60 ("permit-pty", ""),
61 ("permit-user-rc", ""),
62 ("permit-X11-forwarding", ""),
63];
64
65impl From<Extensions> for HashMap<String, String> {
66 fn from(extensions: Extensions) -> Self {
67 match extensions {
68 Extensions::Standard => {
69 let mut hm = HashMap::new();
70 for extension in &STANDARD_EXTENSIONS {
71 hm.insert(String::from(extension.0), String::from(extension.1));
72 }
73 hm
74 },
75 Extensions::Custom(co) => co,
76 }
77 }
78}
79
80#[derive(Debug)]
82pub enum Extensions {
83 Standard,
85 Custom(HashMap<String, String>)
88}
89
90#[derive(Debug)]
94pub enum CriticalOptions {
95 None,
97 Custom(HashMap<String, String>)
99}
100
101impl From<CriticalOptions> for HashMap<String, String> {
102 fn from(critical_options: CriticalOptions) -> Self {
103 match critical_options {
104 CriticalOptions::None => HashMap::new(),
105 CriticalOptions::Custom(co) => co,
106 }
107 }
108}
109
110#[derive(Debug)]
114pub struct Certificate {
115 pub key_type: KeyType,
117
118 pub nonce: Vec<u8>,
120
121 pub key: PublicKey,
123
124 pub serial: u64,
126
127 pub cert_type: CertType,
129
130 pub key_id: String,
132
133 pub principals: Vec<String>,
135
136 pub valid_after: u64,
138
139 pub valid_before: u64,
141
142 pub critical_options: HashMap<String, String>,
145
146 pub extensions: HashMap<String, String>,
149
150 pub reserved: Vec<u8>,
152
153 pub signature_key: PublicKey,
155
156 pub signature: Vec<u8>,
158
159 pub comment: Option<String>,
161
162 pub serialized: Vec<u8>,
164}
165
166impl Certificate {
167 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Certificate> {
179 let mut contents = String::new();
180 File::open(path)?.read_to_string(&mut contents)?;
181
182 Certificate::from_string(&contents)
183 }
184
185 pub fn from_string(s: &str) -> Result<Certificate> {
197 let mut iter = s.split_whitespace();
198
199 let kt_name = iter
200 .next()
201 .ok_or_else(|| Error::with_kind(ErrorKind::InvalidFormat))?;
202
203 let key_type = KeyType::from_name(&kt_name)?;
204 if !key_type.is_cert {
205 return Err(Error::with_kind(ErrorKind::NotCertificate));
206 }
207
208 let data = iter
209 .next()
210 .ok_or_else(|| Error::with_kind(ErrorKind::InvalidFormat))?;
211
212 let comment = iter.next().map(String::from);
213 let decoded = base64::decode(&data)?;
214 let mut reader = Reader::new(&decoded);
215
216 let kt_from_reader = reader.read_string()?;
218 if kt_name != kt_from_reader {
219 return Err(Error::with_kind(ErrorKind::KeyTypeMismatch));
220 }
221
222 let nonce = reader.read_bytes()?;
223 let key = PublicKey::from_reader(&kt_name, &mut reader)?;
224 let serial = reader.read_u64()?;
225
226 let cert_type = match reader.read_u32()? {
227 1 => CertType::User,
228 2 => CertType::Host,
229 n => return Err(Error::with_kind(ErrorKind::InvalidCertType(n))),
230 };
231
232 let key_id = reader.read_string()?;
233 let principals = reader.read_bytes().and_then(|v| read_principals(&v))?;
234 let valid_after = reader.read_u64()?;
235 let valid_before = reader.read_u64()?;
236 let critical_options = reader.read_bytes().and_then(|v| read_options(&v))?;
237 let extensions = reader.read_bytes().and_then(|v| read_options(&v))?;
238 let reserved = reader.read_bytes()?;
239 let signature_key = reader
240 .read_bytes()
241 .and_then(|v| PublicKey::from_bytes(&v))?;
242
243 let signed_len = reader.get_offset();
244 let signature = reader.read_bytes()?;
245
246 reader.set_offset(0).unwrap();
247 let signed_bytes = reader.read_raw_bytes(signed_len).unwrap();
248
249 verify_signature(&signature, &signed_bytes, &signature_key)?;
251
252 let cert = Certificate {
253 key_type,
254 nonce,
255 key,
256 serial,
257 cert_type,
258 key_id,
259 principals,
260 valid_after,
261 valid_before,
262 critical_options,
263 extensions,
264 reserved,
265 signature_key,
266 signature,
267 comment,
268 serialized: decoded,
269 };
270
271 Ok(cert)
272 }
273
274 #[allow(clippy::too_many_arguments)]
308 pub fn new(
309 pubkey: PublicKey,
310 cert_type: CertType,
311 serial: u64,
312 key_id: String,
313 principals: Vec<String>,
314 valid_after: u64,
315 valid_before: u64,
316 critical_options: CriticalOptions,
317 extensions: Extensions,
318 ca_pubkey: PublicKey,
319 signer: impl Fn(&[u8]) -> Option<Vec<u8>>,
320 ) -> Result<Certificate> {
321 let mut writer = super::Writer::new();
322 let kt_name = format!("{}-cert-v01@openssh.com", pubkey.key_type.name);
323 writer.write_string(kt_name.as_str());
325
326 let mut nonce = [0x0u8; 32];
328 let rng = SystemRandom::new();
329 match SecureRandom::fill(&rng, &mut nonce) {
330 Ok(()) => (),
331 Err(_) => return Err(Error::with_kind(ErrorKind::UnexpectedEof)),
332 };
333 writer.write_bytes(&nonce);
335
336 writer.write_pub_key(&pubkey);
338
339 writer.write_u64(serial);
341
342 writer.write_u32(cert_type as u32);
344
345 writer.write_string(&key_id);
347
348 writer.write_string_vec(&principals);
350
351 writer.write_u64(valid_after);
353
354 writer.write_u64(valid_before);
356
357 let critical_options = match critical_options {
359 CriticalOptions::None => {
360 writer.write_string_map(&HashMap::new());
361 HashMap::new()
362 },
363 CriticalOptions::Custom(co) => {
364 writer.write_string_map(&co);
365 co
366 },
367 };
368
369 let extensions = match extensions {
371 Extensions::Standard => {
372 let stdex = STANDARD_EXTENSIONS.iter().map(|x| (String::from(x.0), String::from(x.1))).collect();
373 writer.write_string_map(&stdex);
374 stdex
375 },
376 Extensions::Custom(co) => {
377 writer.write_string_map(&co);
378 co
379 },
380 };
381
382 writer.write_u32(0x0);
384
385 writer.write_bytes(&ca_pubkey.encode());
387
388 let signature = match signer(writer.as_bytes()) {
390 Some(sig) => sig,
391 None => return Err(Error::with_kind(ErrorKind::SigningError)),
392 };
393
394 match verify_signature(&signature, &writer.as_bytes(), &ca_pubkey) {
395 Ok(_) => (),
396 Err(e) => return Err(e),
397 }
398
399 writer.write_bytes(&signature);
400
401 Ok(Certificate {
402 key_type: KeyType::from_name(kt_name.as_str()).unwrap(),
403 nonce: nonce.to_vec(),
404 key: pubkey,
405 serial,
406 cert_type,
407 key_id,
408 principals,
409 valid_after,
410 valid_before,
411 critical_options,
412 extensions,
413 reserved: vec![0,0,0,0,0,0,0,0],
414 signature_key: ca_pubkey,
415 signature,
416 comment: None,
417 serialized: writer.into_bytes(),
418 })
419 }
420}
421
422fn read_options(buf: &[u8]) -> Result<HashMap<String, String>> {
434 let mut reader = Reader::new(&buf);
435 let mut options = HashMap::new();
436
437 loop {
440 let name = match reader.read_string() {
441 Ok(v) => v,
442 Err(e) => match e.kind {
443 ErrorKind::UnexpectedEof => break,
444 _ => return Err(e),
445 },
446 };
447
448 let value_buf = reader.read_bytes()?;
451 let value = if !value_buf.is_empty() {
452 Reader::new(&value_buf).read_string()?
453 } else {
454 "".to_string()
455 };
456
457 options.insert(name, value);
458 }
459
460 Ok(options)
461}
462
463fn read_principals(buf: &[u8]) -> Result<Vec<String>> {
469 let mut reader = Reader::new(&buf);
470 let mut items = Vec::new();
471
472 loop {
473 let principal = match reader.read_string() {
474 Ok(v) => v,
475 Err(e) => match e.kind {
476 ErrorKind::UnexpectedEof => break,
477 _ => return Err(e),
478 },
479 };
480
481 items.push(principal);
482 }
483
484 Ok(items)
485}
486
487fn verify_signature(signature_buf: &[u8], signed_bytes: &[u8], public_key: &PublicKey) -> Result<Vec<u8>> {
505 let mut reader = Reader::new(&signature_buf);
506 let sig_type = reader.read_string().and_then(|v| KeyType::from_name(&v))?;
507
508 match &public_key.kind {
509 PublicKeyKind::Ecdsa(key) => {
510 let sig_reader = reader.read_bytes()?;
511 let mut reader = Reader::new(&sig_reader);
512
513 let mut sig = reader.read_mpint()?;
515 sig.extend(reader.read_mpint()?);
517
518 let alg = match sig_type.name {
519 "ecdsa-sha2-nistp256" => &ECDSA_P256_SHA256_FIXED,
520 "ecdsa-sha2-nistp384" => &ECDSA_P384_SHA384_FIXED,
521 _ => return Err(Error::with_kind(ErrorKind::KeyTypeMismatch)),
522 };
523
524 let result = UnparsedPublicKey::new(alg, &key.key).verify(&signed_bytes, &sig);
525 match result {
526 Ok(()) => Ok(signature_buf.to_vec()),
527 Err(_) => Err(Error::with_kind(ErrorKind::CertificateInvalidSignature)),
528 }
529 },
530 PublicKeyKind::Rsa(key) => {
531 let alg = match sig_type.name {
532 "rsa-sha2-256" => &RSA_PKCS1_2048_8192_SHA256,
533 "rsa-sha2-512" => &RSA_PKCS1_2048_8192_SHA512,
534 _ => return Err(Error::with_kind(ErrorKind::KeyTypeMismatch)),
535 };
536 let signature = reader.read_bytes()?;
537 let public_key = RsaPublicKeyComponents { n: &key.n, e: &key.e };
538 let result = public_key.verify(alg, &signed_bytes, &signature);
539 match result {
540 Ok(()) => Ok(signature_buf.to_vec()),
541 Err(e) => {
542 println!("Error: {}", e);
543 Err(Error::with_kind(ErrorKind::CertificateInvalidSignature))
544 }
545 }
546 },
547 PublicKeyKind::Ed25519(key) => {
548 let alg = &ED25519;
549 let signature = reader.read_bytes()?;
550 let peer_public_key = UnparsedPublicKey::new(alg, &key.key);
551 match peer_public_key.verify(&signed_bytes, &signature) {
552 Ok(()) => Ok(signature_buf.to_vec()),
553 Err(e) => {
554 println!("Error: {}", e);
555 Err(Error::with_kind(ErrorKind::CertificateInvalidSignature))
556 }
557 }
558 },
559 }
560}
561
562impl fmt::Display for Certificate {
563 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
564 if !f.alternate() {
565 write!(f, "{} {} {}", &self.key_type.name, base64::encode(&self.serialized), &self.key_id)
566 } else {
567 writeln!(f, "Type: {} {}", self.key_type, self.cert_type).unwrap();
568 writeln!(f, "Public Key: {} {}:{}", self.key_type.short_name, self.key.fingerprint().kind, self.key.fingerprint().hash).unwrap();
569 writeln!(f, "Signing CA: {} {}:{} (using {})", self.signature_key.key_type.short_name, self.signature_key.fingerprint().kind, self.signature_key.fingerprint().hash, self.signature_key.key_type).unwrap();
570 writeln!(f, "Key ID: \"{}\"", self.key_id).unwrap();
571 writeln!(f, "Serial: {}", self.serial).unwrap();
572
573 if self.valid_before == 0xFFFFFFFFFFFFFFFF && self.valid_after == 0x0 {
574 writeln!(f, "Valid: forever").unwrap();
575 } else {
576 writeln!(f, "Valid between: {} and {}", self.valid_after, self.valid_before).unwrap();
577 }
578
579 if self.principals.is_empty() {
580 writeln!(f, "Principals: (none)").unwrap();
581 } else {
582 writeln!(f, "Principals:").unwrap();
583 for principal in &self.principals {
584 writeln!(f, "\t{}", principal).unwrap();
585 }
586 }
587
588 if self.critical_options.is_empty() {
589 writeln!(f, "Critical Options: (none)").unwrap();
590 } else {
591 writeln!(f, "Critical Options:").unwrap();
592 for (name, value) in &self.critical_options {
593 writeln!(f, "\t{} {}", name, value).unwrap();
594 }
595 }
596
597 if self.extensions.is_empty() {
598 writeln!(f, "Extensions: (none)").unwrap();
599 } else {
600 writeln!(f, "Extensions:").unwrap();
601 for name in self.extensions.keys() {
602 writeln!(f, "\t{}", name).unwrap();
603 }
604 }
605
606 write!(f, "")
607 }
608 }
609}