blob: c4d733f39f2c9c89dd9d1c339dcbee4ca066bfb5 [file] [log] [blame]
//! Cryptographic structures and functions.
use data_encoding::BASE64URL;
use derp::{self, Der, Tag};
use ring;
use ring::digest::{self, SHA256, SHA512};
use ring::rand::SystemRandom;
use ring::signature::{
Ed25519KeyPair, RSAKeyPair, RSASigningState, ED25519, RSA_PSS_2048_8192_SHA256,
RSA_PSS_2048_8192_SHA512, RSA_PSS_SHA256, RSA_PSS_SHA512,
};
use serde::de::{Deserialize, Deserializer, Error as DeserializeError};
use serde::ser::{Error as SerializeError, Serialize, Serializer};
use serde_derive::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::{self, Debug, Display};
use std::hash;
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std::str::FromStr;
use std::sync::Arc;
use untrusted::Input;
use crate::error::Error;
use crate::shims;
use crate::Result;
const HASH_ALG_PREFS: &[HashAlgorithm] = &[HashAlgorithm::Sha512, HashAlgorithm::Sha256];
/// 1.2.840.113549.1.1.1 rsaEncryption(PKCS #1)
const RSA_SPKI_OID: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
/// 1.3.101.112 curveEd25519(EdDSA 25519 signature algorithm)
const ED25519_SPKI_OID: &[u8] = &[0x2b, 0x65, 0x70];
/// Given a map of hash algorithms and their values, get the prefered algorithm and the hash
/// calculated by it. Returns an `Err` if there is no match.
///
/// ```
/// use std::collections::HashMap;
/// use tuf::crypto::{hash_preference, HashValue, HashAlgorithm};
///
/// let mut map = HashMap::new();
/// assert!(hash_preference(&map).is_err());
///
/// let _ = map.insert(HashAlgorithm::Sha512, HashValue::new(vec![0x00, 0x01]));
/// assert_eq!(hash_preference(&map).unwrap().0, &HashAlgorithm::Sha512);
///
/// let _ = map.insert(HashAlgorithm::Sha256, HashValue::new(vec![0x02, 0x03]));
/// assert_eq!(hash_preference(&map).unwrap().0, &HashAlgorithm::Sha512);
/// ```
pub fn hash_preference<'a>(
hashes: &'a HashMap<HashAlgorithm, HashValue>,
) -> Result<(&'static HashAlgorithm, &'a HashValue)> {
for alg in HASH_ALG_PREFS {
match hashes.get(alg) {
Some(v) => return Ok((alg, v)),
None => continue,
}
}
Err(Error::NoSupportedHashAlgorithm)
}
/// Calculate the size and hash digest from a given `Read`.
pub fn calculate_hashes<R: Read>(
mut read: R,
hash_algs: &[HashAlgorithm],
) -> Result<(u64, HashMap<HashAlgorithm, HashValue>)> {
if hash_algs.is_empty() {
return Err(Error::IllegalArgument(
"Cannot provide empty set of hash algorithms".into(),
));
}
let mut size = 0;
let mut hashes = HashMap::new();
for alg in hash_algs {
let context = match *alg {
HashAlgorithm::Sha256 => digest::Context::new(&SHA256),
HashAlgorithm::Sha512 => digest::Context::new(&SHA512),
HashAlgorithm::Unknown(ref s) => {
return Err(Error::IllegalArgument(format!(
"Unknown hash algorithm: {}",
s
)));
}
};
let _ = hashes.insert(alg, context);
}
let mut buf = vec![0; 1024];
loop {
match read.read(&mut buf) {
Ok(read_bytes) => {
if read_bytes == 0 {
break;
}
size += read_bytes as u64;
for context in hashes.values_mut() {
context.update(&buf[0..read_bytes]);
}
}
e @ Err(_) => e.map(|_| ())?,
}
}
let hashes = hashes
.drain()
.map(|(k, v)| (k.clone(), HashValue::new(v.finish().as_ref().to_vec())))
.collect();
Ok((size, hashes))
}
fn calculate_key_id(public_key: &[u8]) -> KeyId {
let mut context = digest::Context::new(&SHA256);
context.update(&public_key);
KeyId(context.finish().as_ref().to_vec())
}
/// Wrapper type for public key's ID.
///
/// # Calculating
/// A `KeyId` is calculated as `sha256(spki(pub_key_bytes))` where `spki` is a function that takes
/// any encoding for a public key an converts it into the `SubjectPublicKeyInfo` (SPKI) DER
/// encoding as defined in [RFC 3280 Appendix A](https://www.ietf.org/rfc/rfc3280.txt).
///
/// Note: Historically the TUF spec says that a key's ID should be calculated with
/// `sha256(cjson(encoded(pub_key_bytes)))`, but since there could be multiple supported data
/// interchange formats, relying on an encoding that uses JSON does not make sense.
///
/// # ASN.1
/// ```bash
/// PublicKey ::= CHOICE {
/// -- This field is checked for consistency against `subjectPublicKey`.
/// -- The OID determines how we attempt to parse the `BIT STRING`.
/// algorithm AlgorithmIdentifier,
/// -- Either:
/// -- 1. Encapsulates an `RsaPublicKey`
/// -- 2. Equals an `Ed25519PublicKey`
/// subjectPublicKey BIT STRING
/// }
///
/// AlgorithmIdentifier ::= SEQUENCE {
/// -- Either:
/// -- 1. 1.2.840.113549.1.1.1 rsaEncryption(PKCS #1)
/// -- 2. 1.3.101.112 curveEd25519(EdDSA 25519 signature algorithm)
/// algorithm OBJECT IDENTIFIER,
/// -- In our cases, this is always `NULL`.
/// parameters ANY DEFINED BY algorithm OPTIONAL
/// }
///
/// RsaPublicKey ::= SEQUENCE {
/// modulus INTEGER (1..MAX),
/// exponent INTEGER (1..MAX)
/// }
///
/// Ed25519PublicKey ::= BIT STRING
/// ```
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct KeyId(Vec<u8>);
impl KeyId {
/// Parse a key ID from a base64url string.
pub fn from_string(string: &str) -> Result<Self> {
if string.len() != 44 {
return Err(Error::IllegalArgument(
"Base64 key ID must be 44 characters long".into(),
));
}
Ok(KeyId(BASE64URL.decode(string.as_bytes())?))
}
}
impl Debug for KeyId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "KeyId {{ \"{}\" }}", BASE64URL.encode(&self.0))
}
}
impl Serialize for KeyId {
fn serialize<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
BASE64URL.encode(&self.0).serialize(ser)
}
}
impl<'de> Deserialize<'de> for KeyId {
fn deserialize<D: Deserializer<'de>>(de: D) -> ::std::result::Result<Self, D::Error> {
let string: String = Deserialize::deserialize(de)?;
KeyId::from_string(&string).map_err(|e| DeserializeError::custom(format!("{:?}", e)))
}
}
/// Cryptographic signature schemes.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SignatureScheme {
/// [Ed25519](https://ed25519.cr.yp.to/)
#[serde(rename = "ed25519")]
Ed25519,
/// [RSASSA-PSS](https://tools.ietf.org/html/rfc5756) calculated over SHA256
#[serde(rename = "rsassa-pss-sha256")]
RsaSsaPssSha256,
/// [RSASSA-PSS](https://tools.ietf.org/html/rfc5756) calculated over SHA512
#[serde(rename = "rsassa-pss-sha512")]
RsaSsaPssSha512,
/// Placeholder for an unknown scheme.
Unknown(String),
}
/// Wrapper type for the value of a cryptographic signature.
#[derive(Clone, PartialEq)]
pub struct SignatureValue(Vec<u8>);
impl SignatureValue {
/// Create a new `SignatureValue` from the given bytes.
///
/// Note: It is unlikely that you ever want to do this manually.
pub fn new(bytes: Vec<u8>) -> Self {
SignatureValue(bytes)
}
/// Create a new `SignatureValue` from the given base64url string.
///
/// Note: It is unlikely that you ever want to do this manually.
pub fn from_string(string: &str) -> Result<Self> {
Ok(SignatureValue(BASE64URL.decode(string.as_bytes())?))
}
}
impl Debug for SignatureValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SignatureValue {{ \"{}\" }}", BASE64URL.encode(&self.0))
}
}
impl Serialize for SignatureValue {
fn serialize<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
BASE64URL.encode(&self.0).serialize(ser)
}
}
impl<'de> Deserialize<'de> for SignatureValue {
fn deserialize<D: Deserializer<'de>>(de: D) -> ::std::result::Result<Self, D::Error> {
let string: String = Deserialize::deserialize(de)?;
SignatureValue::from_string(&string).map_err(|e| {
DeserializeError::custom(format!("Signature value was not valid base64url: {:?}", e))
})
}
}
/// Types of public keys.
#[derive(Clone, PartialEq, Debug, Eq)]
pub enum KeyType {
/// [Ed25519](https://ed25519.cr.yp.to/)
Ed25519,
/// [RSA](https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29)
Rsa,
/// Placeholder for an unknown key type.
Unknown(String),
}
impl KeyType {
fn from_oid(oid: &[u8]) -> Result<Self> {
match oid {
x if x == RSA_SPKI_OID => Ok(KeyType::Rsa),
x if x == ED25519_SPKI_OID => Ok(KeyType::Ed25519),
x => Err(Error::Encoding(format!(
"Unknown OID: {}",
x.iter().map(|b| format!("{:x}", b)).collect::<String>()
))),
}
}
fn as_oid(&self) -> Result<&'static [u8]> {
match *self {
KeyType::Rsa => Ok(RSA_SPKI_OID),
KeyType::Ed25519 => Ok(ED25519_SPKI_OID),
KeyType::Unknown(ref s) => Err(Error::UnknownKeyType(s.clone())),
}
}
}
impl FromStr for KeyType {
type Err = Error;
fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
match s {
"ed25519" => Ok(KeyType::Ed25519),
"rsa" => Ok(KeyType::Rsa),
typ => Err(Error::Encoding(typ.into())),
}
}
}
impl ToString for KeyType {
fn to_string(&self) -> String {
match *self {
KeyType::Ed25519 => "ed25519".to_string(),
KeyType::Rsa => "rsa".to_string(),
KeyType::Unknown(ref s) => s.to_string(),
}
}
}
impl Serialize for KeyType {
fn serialize<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
ser.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for KeyType {
fn deserialize<D: Deserializer<'de>>(de: D) -> ::std::result::Result<Self, D::Error> {
let string: String = Deserialize::deserialize(de)?;
string
.parse()
.map_err(|e| DeserializeError::custom(format!("{:?}", e)))
}
}
enum PrivateKeyType {
Ed25519(Ed25519KeyPair),
Rsa(Arc<RSAKeyPair>),
}
impl Debug for PrivateKeyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match *self {
PrivateKeyType::Ed25519(_) => "ed25519",
PrivateKeyType::Rsa(_) => "rsa",
};
write!(f, "PrivateKeyType {{ \"{}\" }}", s)
}
}
/// A structure containing information about a private key.
pub struct PrivateKey {
private: PrivateKeyType,
public: PublicKey,
}
impl PrivateKey {
/// Generate a new `PrivateKey`.
///
/// Note: For RSA keys, `openssl` needs to the on the `$PATH`.
pub fn new(key_type: KeyType) -> Result<Vec<u8>> {
match key_type {
KeyType::Ed25519 => Ed25519KeyPair::generate_pkcs8(&SystemRandom::new())
.map(|bytes| bytes.to_vec())
.map_err(|_| Error::Opaque("Failed to generate Ed25519 key".into())),
KeyType::Rsa => Self::rsa_gen(),
KeyType::Unknown(s) => Err(Error::IllegalArgument(format!("Unknown key type: {}", s))),
}
}
/// Create a private key from PKCS#8v2 DER bytes.
///
/// # Generating Keys
///
/// If you use `cargo install tuf`, you will have access to the TUF CLI tool that will allow
/// you to generate keys. If you do not want to do this, the following can be used instead.
///
/// ## Ed25519
///
/// ```bash
/// $ touch ed25519-private-key.pk8
/// $ chmod 0600 ed25519-private-key.pk8
/// ```
///
/// ```no_run
/// # use ring::rand::SystemRandom;
/// # use ring::signature::Ed25519KeyPair;
/// # use std::fs::File;
/// # use std::io::Write;
/// # fn main() {
/// let mut file = File::open("ed25519-private-key.pk8").unwrap();
/// let key = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()).unwrap();
/// file.write_all(&key).unwrap()
/// # }
/// ```
///
/// ## RSA
///
/// ```bash
/// $ umask 077
/// $ openssl genpkey -algorithm RSA \
/// -pkeyopt rsa_keygen_bits:4096 \
/// -pkeyopt rsa_keygen_pubexp:65537 | \
/// openssl pkcs8 -topk8 -nocrypt -outform der > rsa-4096-private-key.pk8
/// ```
pub fn from_pkcs8(der_key: &[u8], scheme: SignatureScheme) -> Result<Self> {
match Self::ed25519_from_pkcs8(der_key) {
Ok(k) => {
match scheme {
SignatureScheme::Ed25519 => (),
s => {
return Err(Error::IllegalArgument(format!(
"Cannot use signature scheme {:?} with Ed25519 keys",
s
)));
}
};
Ok(k)
}
Err(e1) => match Self::rsa_from_pkcs8(der_key, scheme) {
Ok(k) => Ok(k),
Err(e2) => Err(Error::Opaque(format!(
"Key was neither Ed25519 nor RSA: {:?} {:?}",
e1, e2
))),
},
}
}
fn ed25519_from_pkcs8(der_key: &[u8]) -> Result<Self> {
let key = Ed25519KeyPair::from_pkcs8(Input::from(der_key))
.map_err(|_| Error::Encoding("Could not parse key as PKCS#8v2".into()))?;
let public = PublicKey {
typ: KeyType::Ed25519,
scheme: SignatureScheme::Ed25519,
key_id: calculate_key_id(&write_spki(key.public_key_bytes(), &KeyType::Ed25519)?),
value: PublicKeyValue(key.public_key_bytes().to_vec()),
};
let private = PrivateKeyType::Ed25519(key);
Ok(PrivateKey { private, public })
}
fn rsa_from_pkcs8(der_key: &[u8], scheme: SignatureScheme) -> Result<Self> {
if let SignatureScheme::Ed25519 = scheme {
return Err(Error::IllegalArgument(
"RSA keys do not support the Ed25519 signing scheme".into(),
));
}
let key = RSAKeyPair::from_pkcs8(Input::from(der_key))
.map_err(|_| Error::Encoding("Could not parse key as PKCS#8v2".into()))?;
if key.public_modulus_len() < 256 {
return Err(Error::IllegalArgument(format!(
"RSA public modulus must be 2048 or greater. Found {}",
key.public_modulus_len() * 8
)));
}
let pub_key = extract_rsa_pub_from_pkcs8(der_key)?;
let public = PublicKey {
typ: KeyType::Rsa,
scheme,
key_id: calculate_key_id(&write_spki(&pub_key, &KeyType::Rsa)?),
value: PublicKeyValue(pub_key),
};
let private = PrivateKeyType::Rsa(Arc::new(key));
Ok(PrivateKey { private, public })
}
/// Sign a message.
pub fn sign(&self, msg: &[u8]) -> Result<Signature> {
let value = match (&self.private, &self.public.scheme) {
(&PrivateKeyType::Rsa(ref rsa), &SignatureScheme::RsaSsaPssSha256) => {
let mut signing_state = RSASigningState::new(rsa.clone())
.map_err(|_| Error::Opaque("Could not initialize RSA signing state.".into()))?;
let rng = SystemRandom::new();
let mut buf = vec![0; signing_state.key_pair().public_modulus_len()];
signing_state
.sign(&RSA_PSS_SHA256, &rng, msg, &mut buf)
.map_err(|_| Error::Opaque("Failed to sign message.".into()))?;
SignatureValue(buf)
}
(&PrivateKeyType::Rsa(ref rsa), &SignatureScheme::RsaSsaPssSha512) => {
let mut signing_state = RSASigningState::new(rsa.clone())
.map_err(|_| Error::Opaque("Could not initialize RSA signing state.".into()))?;
let rng = SystemRandom::new();
let mut buf = vec![0; signing_state.key_pair().public_modulus_len()];
signing_state
.sign(&RSA_PSS_SHA512, &rng, msg, &mut buf)
.map_err(|_| Error::Opaque("Failed to sign message.".into()))?;
SignatureValue(buf)
}
(&PrivateKeyType::Ed25519(ref ed), &SignatureScheme::Ed25519) => {
SignatureValue(ed.sign(msg).as_ref().into())
}
(k, s) => {
return Err(Error::IllegalArgument(format!(
"Key {:?} can't be used with scheme {:?}",
k, s
)));
}
};
Ok(Signature {
key_id: self.key_id().clone(),
value,
})
}
fn rsa_gen() -> Result<Vec<u8>> {
let gen = Command::new("openssl")
.args(&[
"genpkey",
"-algorithm",
"RSA",
"-pkeyopt",
"rsa_keygen_bits:4096",
"-pkeyopt",
"rsa_keygen_pubexp:65537",
"-outform",
"der",
])
.output()?;
let mut pk8 = Command::new("openssl")
.args(&[
"pkcs8", "-inform", "der", "-topk8", "-nocrypt", "-outform", "der",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
match pk8.stdin {
Some(ref mut stdin) => stdin.write_all(&gen.stdout)?,
None => return Err(Error::Opaque("openssl has no stdin".into())),
};
Ok(pk8.wait_with_output()?.stdout)
}
/// Return the public component of the key.
pub fn public(&self) -> &PublicKey {
&self.public
}
/// Return the key ID of the public key.
pub fn key_id(&self) -> &KeyId {
&self.public.key_id
}
}
/// A structure containing information about a public key.
#[derive(Clone, Debug)]
pub struct PublicKey {
typ: KeyType,
key_id: KeyId,
scheme: SignatureScheme,
value: PublicKeyValue,
}
impl PublicKey {
/// Parse DER bytes as an SPKI key.
///
/// See the documentation on `KeyValue` for more information on SPKI.
pub fn from_spki(der_bytes: &[u8], scheme: SignatureScheme) -> Result<Self> {
let input = Input::from(der_bytes);
let (typ, value) = input.read_all(derp::Error::Read, |input| {
derp::nested(input, Tag::Sequence, |input| {
let typ = derp::nested(input, Tag::Sequence, |input| {
let typ = derp::expect_tag_and_get_value(input, Tag::Oid)?;
let typ = KeyType::from_oid(typ.as_slice_less_safe())
.map_err(|_| derp::Error::WrongValue)?;
// for RSA / ed25519 this is null, so don't both parsing it
derp::read_null(input)?;
Ok(typ)
})?;
let value = derp::bit_string_with_no_unused_bits(input)?;
Ok((typ, value.as_slice_less_safe().to_vec()))
})
})?;
let key_id = calculate_key_id(der_bytes);
Ok(PublicKey {
typ,
key_id,
scheme,
value: PublicKeyValue(value),
})
}
/// Write the public key as SPKI DER bytes.
///
/// See the documentation on `KeyValue` for more information on SPKI.
pub fn as_spki(&self) -> Result<Vec<u8>> {
Ok(write_spki(&self.value.0, &self.typ)?)
}
/// An immutable reference to the key's type.
pub fn typ(&self) -> &KeyType {
&self.typ
}
/// An immutable referece to the key's authorized signing scheme.
pub fn scheme(&self) -> &SignatureScheme {
&self.scheme
}
/// An immutable reference to the key's ID.
pub fn key_id(&self) -> &KeyId {
&self.key_id
}
/// Use this key to verify a message with a signature.
pub fn verify(&self, msg: &[u8], sig: &Signature) -> Result<()> {
let alg: &ring::signature::VerificationAlgorithm = match self.scheme {
SignatureScheme::Ed25519 => &ED25519,
SignatureScheme::RsaSsaPssSha256 => &RSA_PSS_2048_8192_SHA256,
SignatureScheme::RsaSsaPssSha512 => &RSA_PSS_2048_8192_SHA512,
SignatureScheme::Unknown(ref s) => {
return Err(Error::IllegalArgument(format!(
"Unknown signature scheme: {}",
s
)));
}
};
ring::signature::verify(
alg,
Input::from(&self.value.0),
Input::from(msg),
Input::from(&sig.value.0),
)
.map_err(|_| Error::BadSignature)
}
}
impl PartialEq for PublicKey {
fn eq(&self, other: &Self) -> bool {
// the other two fields are derived from this one, we ignore them
self.value == other.value
}
}
impl Eq for PublicKey {}
impl Ord for PublicKey {
fn cmp(&self, other: &Self) -> Ordering {
self.key_id.cmp(&other.key_id)
}
}
impl PartialOrd for PublicKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.key_id.cmp(&other.key_id))
}
}
impl hash::Hash for PublicKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
// the other two fields are derived from this one, we ignore them
self.value.hash(state);
}
}
impl Serialize for PublicKey {
fn serialize<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = self
.as_spki()
.map_err(|e| SerializeError::custom(format!("Couldn't write key as SPKI: {:?}", e)))?;
shims::PublicKey::new(self.typ.clone(), self.scheme.clone(), &bytes).serialize(ser)
}
}
impl<'de> Deserialize<'de> for PublicKey {
fn deserialize<D: Deserializer<'de>>(de: D) -> ::std::result::Result<Self, D::Error> {
let intermediate: shims::PublicKey = Deserialize::deserialize(de)?;
let bytes = BASE64URL
.decode(intermediate.public_key().as_bytes())
.map_err(|e| DeserializeError::custom(format!("{:?}", e)))?;
let key = PublicKey::from_spki(&bytes, intermediate.scheme().clone()).map_err(|e| {
DeserializeError::custom(format!("Couldn't parse key as SPKI: {:?}", e))
})?;
if intermediate.typ() != &key.typ {
return Err(DeserializeError::custom(format!(
"Key type listed in the metadata did not match the type extrated \
from the key. {:?} vs. {:?}",
intermediate.typ(),
key.typ,
)));
}
Ok(key)
}
}
#[derive(Clone, PartialEq, Hash, Eq)]
struct PublicKeyValue(Vec<u8>);
impl Debug for PublicKeyValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PublicKeyValue {{ \"{}\" }}", BASE64URL.encode(&self.0))
}
}
/// A structure that contains a `Signature` and associated data for verifying it.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Signature {
key_id: KeyId,
value: SignatureValue,
}
impl Signature {
/// An immutable reference to the `KeyId` of the key that produced the signature.
pub fn key_id(&self) -> &KeyId {
&self.key_id
}
/// An immutable reference to the `SignatureValue`.
pub fn value(&self) -> &SignatureValue {
&self.value
}
}
/// The available hash algorithms.
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum HashAlgorithm {
/// SHA256 as describe in [RFC-6234](https://tools.ietf.org/html/rfc6234)
#[serde(rename = "sha256")]
Sha256,
/// SHA512 as describe in [RFC-6234](https://tools.ietf.org/html/rfc6234)
#[serde(rename = "sha512")]
Sha512,
/// Placeholder for an unknown hash algorithm.
Unknown(String),
}
/// Wrapper for the value of a hash digest.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct HashValue(Vec<u8>);
impl HashValue {
/// Create a new `HashValue` from the given digest bytes.
pub fn new(bytes: Vec<u8>) -> Self {
HashValue(bytes)
}
/// An immutable reference to the bytes of the hash value.
pub fn value(&self) -> &[u8] {
&self.0
}
}
impl Serialize for HashValue {
fn serialize<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
BASE64URL.encode(&self.0).serialize(ser)
}
}
impl<'de> Deserialize<'de> for HashValue {
fn deserialize<D: Deserializer<'de>>(de: D) -> ::std::result::Result<Self, D::Error> {
let s: String = Deserialize::deserialize(de)?;
let bytes = BASE64URL
.decode(s.as_bytes())
.map_err(|e| DeserializeError::custom(format!("Base64: {:?}", e)))?;
Ok(HashValue(bytes))
}
}
impl Debug for HashValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HashValue {{ \"{}\" }}", BASE64URL.encode(&self.0))
}
}
impl Display for HashValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", BASE64URL.encode(&self.0))
}
}
fn write_spki(public: &[u8], key_type: &KeyType) -> ::std::result::Result<Vec<u8>, derp::Error> {
let mut output = Vec::new();
{
let mut der = Der::new(&mut output);
der.sequence(|der| {
der.sequence(|der| match key_type.as_oid().ok() {
Some(tag) => {
der.element(Tag::Oid, tag)?;
der.null()
}
None => Err(derp::Error::WrongValue),
})?;
der.bit_string(0, public)
})?;
}
Ok(output)
}
fn extract_rsa_pub_from_pkcs8(der_key: &[u8]) -> ::std::result::Result<Vec<u8>, derp::Error> {
let input = Input::from(der_key);
input.read_all(derp::Error::Read, |input| {
derp::nested(input, Tag::Sequence, |input| {
if derp::small_nonnegative_integer(input)? != 0 {
return Err(derp::Error::WrongValue);
}
derp::nested(input, Tag::Sequence, |input| {
let actual_alg_id = derp::expect_tag_and_get_value(input, Tag::Oid)?;
if actual_alg_id.as_slice_less_safe() != RSA_SPKI_OID {
return Err(derp::Error::WrongValue);
}
let _ = derp::expect_tag_and_get_value(input, Tag::Null)?;
Ok(())
})?;
derp::nested(input, Tag::OctetString, |input| {
derp::nested(input, Tag::Sequence, |input| {
if derp::small_nonnegative_integer(input)? != 0 {
return Err(derp::Error::WrongValue);
}
let n = derp::positive_integer(input)?;
let e = derp::positive_integer(input)?;
let _ = input.skip_to_end();
write_pkcs1(n.as_slice_less_safe(), e.as_slice_less_safe())
})
})
})
})
}
fn write_pkcs1(n: &[u8], e: &[u8]) -> ::std::result::Result<Vec<u8>, derp::Error> {
let mut output = Vec::new();
{
let mut der = Der::new(&mut output);
der.sequence(|der| {
der.positive_integer(n)?;
der.positive_integer(e)
})?;
}
Ok(output)
}
#[cfg(test)]
mod test {
use super::*;
use serde_json::{self, json};
const RSA_2048_PK8: &'static [u8] = include_bytes!("../tests/rsa/rsa-2048.pk8.der");
const RSA_2048_SPKI: &'static [u8] = include_bytes!("../tests/rsa/rsa-2048.spki.der");
const RSA_2048_PKCS1: &'static [u8] = include_bytes!("../tests/rsa/rsa-2048.pkcs1.der");
const RSA_4096_PK8: &'static [u8] = include_bytes!("../tests/rsa/rsa-4096.pk8.der");
const RSA_4096_SPKI: &'static [u8] = include_bytes!("../tests/rsa/rsa-4096.spki.der");
const RSA_4096_PKCS1: &'static [u8] = include_bytes!("../tests/rsa/rsa-4096.pkcs1.der");
const ED25519_PK8: &'static [u8] = include_bytes!("../tests/ed25519/ed25519-1.pk8.der");
const ED25519_SPKI: &'static [u8] = include_bytes!("../tests/ed25519/ed25519-1.spki.der");
#[test]
fn parse_rsa_2048_spki() {
let key = PublicKey::from_spki(RSA_2048_SPKI, SignatureScheme::RsaSsaPssSha256).unwrap();
assert_eq!(key.typ, KeyType::Rsa);
}
#[test]
fn parse_rsa_4096_spki() {
let key = PublicKey::from_spki(RSA_4096_SPKI, SignatureScheme::RsaSsaPssSha256).unwrap();
assert_eq!(key.typ, KeyType::Rsa);
}
#[test]
fn rsa_2048_read_pkcs8_and_sign() {
let msg = b"test";
let key = PrivateKey::from_pkcs8(RSA_2048_PK8, SignatureScheme::RsaSsaPssSha256).unwrap();
let sig = key.sign(msg).unwrap();
key.public.verify(msg, &sig).unwrap();
let key = PrivateKey::from_pkcs8(RSA_2048_PK8, SignatureScheme::RsaSsaPssSha512).unwrap();
let sig = key.sign(msg).unwrap();
key.public.verify(msg, &sig).unwrap();
}
#[test]
fn rsa_4096_read_pkcs8_and_sign() {
let msg = b"test";
let key = PrivateKey::from_pkcs8(RSA_4096_PK8, SignatureScheme::RsaSsaPssSha256).unwrap();
let sig = key.sign(msg).unwrap();
key.public.verify(msg, &sig).unwrap();
let key = PrivateKey::from_pkcs8(RSA_4096_PK8, SignatureScheme::RsaSsaPssSha512).unwrap();
let sig = key.sign(msg).unwrap();
key.public.verify(msg, &sig).unwrap();
}
#[test]
fn extract_pkcs1_from_rsa_2048_pkcs8() {
let res = extract_rsa_pub_from_pkcs8(RSA_2048_PK8).unwrap();
assert_eq!(res.as_slice(), RSA_2048_PKCS1);
}
#[test]
fn extract_pkcs1_from_rsa_4096_pkcs8() {
let res = extract_rsa_pub_from_pkcs8(RSA_4096_PK8).unwrap();
assert_eq!(res.as_slice(), RSA_4096_PKCS1);
}
#[test]
fn ed25519_read_pkcs8_and_sign() {
let key = PrivateKey::from_pkcs8(ED25519_PK8, SignatureScheme::Ed25519).unwrap();
let msg = b"test";
let sig = key.sign(msg).unwrap();
let public =
PublicKey::from_spki(&key.public.as_spki().unwrap(), SignatureScheme::Ed25519).unwrap();
public.verify(msg, &sig).unwrap();
}
#[test]
fn serde_key_id() {
let s = "T5vfRrM1iHpgzGwAHe7MbJH_7r4chkOAphV3OPCCv0I=";
let jsn = json!(s);
let parsed: KeyId = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
assert_eq!(parsed, KeyId::from_string(s).unwrap());
let encoded = serde_json::to_value(&parsed).unwrap();
assert_eq!(encoded, jsn);
}
#[test]
fn serde_signature_value() {
let s = "T5vfRrM1iHpgzGwAHe7MbJH_7r4chkOAphV3OPCCv0I=";
let jsn = json!(s);
let parsed: SignatureValue = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
assert_eq!(parsed, SignatureValue::from_string(s).unwrap());
let encoded = serde_json::to_value(&parsed).unwrap();
assert_eq!(encoded, jsn);
}
#[test]
fn serde_rsa_public_key() {
let der = RSA_2048_SPKI;
let pub_key = PublicKey::from_spki(der, SignatureScheme::RsaSsaPssSha256).unwrap();
let encoded = serde_json::to_value(&pub_key).unwrap();
let jsn = json!({
"type": "rsa",
"scheme": "rsassa-pss-sha256",
"public_key": BASE64URL.encode(der),
});
assert_eq!(encoded, jsn);
let decoded: PublicKey = serde_json::from_value(encoded).unwrap();
assert_eq!(decoded, pub_key);
}
#[test]
fn serde_ed25519_public_key() {
let der = ED25519_SPKI;
let pub_key = PublicKey::from_spki(der, SignatureScheme::Ed25519).unwrap();
let encoded = serde_json::to_value(&pub_key).unwrap();
let jsn = json!({
"type": "ed25519",
"scheme": "ed25519",
"public_key": BASE64URL.encode(der),
});
assert_eq!(encoded, jsn);
let decoded: PublicKey = serde_json::from_value(encoded).unwrap();
assert_eq!(decoded, pub_key);
}
#[test]
fn serde_signature() {
let key = PrivateKey::from_pkcs8(ED25519_PK8, SignatureScheme::Ed25519).unwrap();
let msg = b"test";
let sig = key.sign(msg).unwrap();
let encoded = serde_json::to_value(&sig).unwrap();
let jsn = json!({
"key_id": "qfrfBrkB4lBBSDEBlZgaTGS_SrE6UfmON9kP4i3dJFY=",
"value": "_k0Tsqc8Azod5_UQeyBfx7oOFWbLlbkjScrmqkU4lWATv-D3v5d8sHK7Z\
eh4K18zoFc_54gWKZoBfKW6VZ45DA==",
});
assert_eq!(encoded, jsn);
let decoded: Signature = serde_json::from_value(encoded).unwrap();
assert_eq!(decoded, sig);
}
#[test]
#[cfg(not(windows))]
fn new_rsa_key() {
let bytes = PrivateKey::new(KeyType::Rsa).unwrap();
let _ = PrivateKey::from_pkcs8(&bytes, SignatureScheme::RsaSsaPssSha256).unwrap();
}
#[test]
fn new_ed25519_key() {
let bytes = PrivateKey::new(KeyType::Ed25519).unwrap();
let _ = PrivateKey::from_pkcs8(&bytes, SignatureScheme::Ed25519).unwrap();
}
}