ed25519/rsassa-pss signing
fixes #81
fixes #82
diff --git a/src/crypto.rs b/src/crypto.rs
index 9d73fc9..484d5ed 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -3,12 +3,14 @@
use data_encoding::HEXLOWER;
use ring;
use ring::digest::{self, SHA256};
-use ring::signature::{ED25519, RSA_PSS_2048_8192_SHA256, RSA_PSS_2048_8192_SHA512};
+use ring::rand::SystemRandom;
+use ring::signature::{RSAKeyPair, RSASigningState, Ed25519KeyPair, 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::{Serialize, Serializer, SerializeTupleStruct, Error as SerializeError};
use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::str::FromStr;
+use std::sync::Arc;
use untrusted::Input;
use Result;
@@ -215,6 +217,75 @@
}
}
+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 public key.
+pub struct PrivateKey {
+ private: PrivateKeyType,
+}
+
+impl PrivateKey {
+ /// Create an Ed25519 private key from PKCS#8v2 DER bytes.
+ pub 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()))?;
+ Ok(PrivateKey {
+ private: PrivateKeyType::Ed25519(key),
+ })
+ }
+
+ /// Create an RSA private key from PKCS#8v2 DER bytes.
+ pub fn rsa_from_pkcs8(der_key: &[u8]) -> Result<Self> {
+ let key = RSAKeyPair::from_pkcs8(Input::from(der_key))
+ .map_err(|_| Error::Encoding("Could not parse key as PKCS#8v2".into()))?;
+ Ok(PrivateKey {
+ private: PrivateKeyType::Rsa(Arc::new(key)),
+ })
+ }
+
+ /// Sign a message with the given scheme.
+ pub fn sign(&self, msg: &[u8], scheme: &SignatureScheme) -> Result<SignatureValue> {
+ match (&self.private, 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()))?;
+ Ok(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()))?;
+ Ok(SignatureValue(buf))
+ }
+ (&PrivateKeyType::Ed25519(ref ed), &SignatureScheme::Ed25519) => {
+ Ok(SignatureValue(ed.sign(msg).as_ref().into()))
+ }
+ (k, s) => Err(Error::IllegalArgument(format!("Key {:?} can't be used with scheme {:?}", k, s)))
+ }
+ }
+}
+
+
/// A structure containing information about a public key.
#[derive(Clone, Debug, PartialEq)]
pub struct PublicKey {
@@ -407,3 +478,38 @@
&self.0
}
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn rsa_2048_ead_pkcs8_and_sign() {
+ let der = include_bytes!("../tests/rsa/rsa-2048-private-key.pk8");
+ let key = PrivateKey::rsa_from_pkcs8(der.as_ref()).unwrap();
+ let msg = b"test";
+ let _ = key.sign(msg, &SignatureScheme::RsaSsaPssSha256).unwrap();
+ let _ = key.sign(msg, &SignatureScheme::RsaSsaPssSha512).unwrap();
+ assert!(key.sign(msg, &SignatureScheme::Ed25519).is_err());
+ }
+
+ #[test]
+ fn rsa_4096_read_pkcs8_and_sign() {
+ let der = include_bytes!("../tests/rsa/rsa-4096-private-key.pk8");
+ let key = PrivateKey::rsa_from_pkcs8(der.as_ref()).unwrap();
+ let msg = b"test";
+ let _ = key.sign(msg, &SignatureScheme::RsaSsaPssSha256).unwrap();
+ let _ = key.sign(msg, &SignatureScheme::RsaSsaPssSha512).unwrap();
+ assert!(key.sign(msg, &SignatureScheme::Ed25519).is_err());
+ }
+
+ #[test]
+ fn ed25519_read_pkcs8_and_sign() {
+ let der = include_bytes!("../tests/ed25519/ed25519-1.pk8");
+ let key = PrivateKey::ed25519_from_pkcs8(der.as_ref()).unwrap();
+ let msg = b"test";
+ let _ = key.sign(msg, &SignatureScheme::Ed25519).unwrap();
+ assert!(key.sign(msg, &SignatureScheme::RsaSsaPssSha256).is_err());
+ assert!(key.sign(msg, &SignatureScheme::RsaSsaPssSha512).is_err());
+ }
+}
diff --git a/tests/ed25519/ed25519-1.pk8 b/tests/ed25519/ed25519-1.pk8
index 4582000..ea5dda6 100644
--- a/tests/ed25519/ed25519-1.pk8
+++ b/tests/ed25519/ed25519-1.pk8
Binary files differ
diff --git a/tests/rsa/rsa-2048-private-key.pk8 b/tests/rsa/rsa-2048-private-key.pk8
new file mode 100644
index 0000000..16ed14a
--- /dev/null
+++ b/tests/rsa/rsa-2048-private-key.pk8
Binary files differ
diff --git a/tests/rsa/rsa-4096-private-key.pk8 b/tests/rsa/rsa-4096-private-key.pk8
new file mode 100644
index 0000000..6f0d154
--- /dev/null
+++ b/tests/rsa/rsa-4096-private-key.pk8
Binary files differ