Skip to content

Commit

Permalink
Sign certificates with RSA PrivateKey type
Browse files Browse the repository at this point in the history
  • Loading branch information
obelisk committed Feb 10, 2021
1 parent 9d0244b commit 1450b5c
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 15 deletions.
18 changes: 16 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sshcerts"
version = "0.3.9"
version = "0.3.10"
authors = ["Mitchell Grenier <[email protected]>"]
edition = "2018"
license-file = "LICENSE"
Expand All @@ -17,6 +17,8 @@ yubikey = ["yubikey-piv"]
[dependencies]
base64 = "0.13"
log = "0.4"
simple_asn1 = "0.5"
num-bigint = "0.3"
ring = "0.16"

yubikey-piv = {version = "0.1.0", features = ["untested"], optional = true}
Expand Down
6 changes: 3 additions & 3 deletions src/ssh/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ impl Certificate {
Ok(()) => (),
Err(_) => return Err(Error::with_kind(ErrorKind::UnexpectedEof)),
};

// Write the nonce
writer.write_bytes(&nonce);

Expand Down Expand Up @@ -391,9 +392,8 @@ impl Certificate {
None => return Err(Error::with_kind(ErrorKind::SigningError)),
};

match verify_signature(&signature, &writer.as_bytes(), &ca_pubkey) {
Ok(_) => (),
Err(e) => return Err(e),
if let Err(e) = verify_signature(&signature, &writer.as_bytes(), &ca_pubkey) {
return Err(e)
}

writer.write_bytes(&signature);
Expand Down
7 changes: 7 additions & 0 deletions src/ssh/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ impl From<string::FromUtf8Error> for Error {
}
}


impl From<simple_asn1::ASN1EncodeErr> for Error {
fn from(_e: simple_asn1::ASN1EncodeErr) -> Self {
Error::with_kind(ErrorKind::InvalidFormat)
}
}

impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self.kind {
Expand Down
65 changes: 59 additions & 6 deletions src/ssh/privkey.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use super::keytype::{Curve, KeyType, KeyTypeKind};
use super::error::{Error, ErrorKind};
use num_bigint::{BigInt, BigUint, Sign};
use super::PublicKey;
use super::reader::Reader;
use simple_asn1::{ASN1Block, ASN1Class, ToASN1};

use std::fs::File;
use std::io::{Read};
use std::path::Path;


/// RSA private key.
#[derive(Debug, PartialEq, Clone)]
pub struct RsaPrivateKey {
Expand All @@ -27,6 +30,12 @@ pub struct RsaPrivateKey {

/// Prime factor q of n
pub q: Vec<u8>,

/// Exponent using p
pub exp: Vec<u8>,

/// Exponent using q
pub exq: Vec<u8>,
}

/// ECDSA private key.
Expand Down Expand Up @@ -75,6 +84,29 @@ pub struct PrivateKey {
pub comment: Option<String>,
}

impl ToASN1 for RsaPrivateKey {
type Error = Error;

fn to_asn1_class(&self, _class: ASN1Class) -> Result<Vec<simple_asn1::ASN1Block>, Self::Error> {
Ok(vec![ASN1Block::Sequence(
0,
[
vec![ASN1Block::Integer(0, BigInt::new(Sign::Plus, vec![0]))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.n))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.e))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.d))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.p))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.q))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.exp))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.exq))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.coefficient))],
Vec::new(),
]
.concat(),
)])
}
}

impl PrivateKey {
/// Reads an OpenSSH private key from a given path.
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<PrivateKey, Error> {
Expand Down Expand Up @@ -162,13 +194,34 @@ impl PrivateKey {

let kind = match kt.kind {
KeyTypeKind::Rsa => {
let n = reader.read_mpint()?;
let e = reader.read_mpint()?;
let d = reader.read_mpint()?;
let coefficient = reader.read_mpint()?;
let p = reader.read_mpint()?;
let q = reader.read_mpint()?;

let exp = BigUint::from_bytes_be(&d)
.modpow(
&BigUint::from_slice(&[0x1]),
&(BigUint::from_bytes_be(&p) - 1_u8)
);

let exq = BigUint::from_bytes_be(&d)
.modpow(
&BigUint::from_slice(&[0x1]),
&(BigUint::from_bytes_be(&q) - 1_u8)
);

let k = RsaPrivateKey {
n: reader.read_mpint()?,
e: reader.read_mpint()?,
d: reader.read_mpint()?,
coefficient: reader.read_mpint()?,
p: reader.read_mpint()?,
q: reader.read_mpint()?,
n,
e,
d,
coefficient,
p,
q,
exp: exp.to_bytes_be(),
exq: exq.to_bytes_be(),
};

let pubkey = match &pubkey.kind {
Expand Down
23 changes: 21 additions & 2 deletions src/ssh/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,27 @@ pub fn ssh_cert_signer(buf: &[u8], privkey: &PrivateKey) -> Option<Vec<u8>> {
let rng = rand::SystemRandom::new();

let (signature, sig_type) = match &privkey.kind {
PrivateKeyKind::Rsa(_key) => {
return None
PrivateKeyKind::Rsa(key) => {
let asn_privkey = match simple_asn1::der_encode(key) {
Ok(apk) => apk,
Err(_) => return None,
};

let keypair = match signature::RsaKeyPair::from_der(&asn_privkey) {
Ok(kp) => kp,
Err(_) => return None,
};

let rng = rand::SystemRandom::new();
let mut signature = vec![0; keypair.public_modulus_len()];

if let Err(_) = keypair.sign(&signature::RSA_PKCS1_SHA512, &rng, buf, &mut signature) {
return None
}

let mut encoding = (signature.len() as u32).to_be_bytes().to_vec();
encoding.extend(signature);
(encoding, "rsa-sha2-512")
},
PrivateKeyKind::Ecdsa(key) => {
let (alg, alg_name) = match key.curve.kind {
Expand Down
Loading

0 comments on commit 1450b5c

Please sign in to comment.