Use a SigningKey to produce a Signature.
This commit is contained in:
parent
2b358f8cc7
commit
68fccbddd6
98
src/lib.rs
98
src/lib.rs
@ -7,13 +7,14 @@ extern crate rustc_serialize;
|
|||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate sodiumoxide;
|
extern crate sodiumoxide;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::collections::HashSet;
|
||||||
use std::fmt::Display;
|
use std::error::Error as StdError;
|
||||||
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
|
|
||||||
use rustc_serialize::base64::{CharacterSet, Config, Newline, ToBase64};
|
use rustc_serialize::base64::{CharacterSet, Config, Newline, ToBase64};
|
||||||
use serde_json::{Value, to_string};
|
use serde_json::{Value, to_string};
|
||||||
use sodiumoxide::init;
|
use sodiumoxide::init;
|
||||||
use sodiumoxide::crypto::sign::{SecretKey, Signature, sign_detached};
|
use sodiumoxide::crypto::sign::{SecretKey, Signature as SodiumSignature, sign_detached};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref _LIBSODIUM_INIT: bool = init();
|
static ref _LIBSODIUM_INIT: bool = init();
|
||||||
@ -28,37 +29,51 @@ static BASE64_CONFIG: Config = Config {
|
|||||||
|
|
||||||
/// An error produced when signing data fails.
|
/// An error produced when signing data fails.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SigningError {
|
pub struct Error {
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SigningError {
|
impl Error {
|
||||||
pub fn new<T>(message: T) -> Self where T: Into<String> {
|
pub fn new<T>(message: T) -> Self where T: Into<String> {
|
||||||
SigningError {
|
Error {
|
||||||
message: message.into(),
|
message: message.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for SigningError {
|
impl StdError for Error {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
self.message.as_ref()
|
&self.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SigningError {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||||
write!(f, "{}", self.message)
|
write!(f, "{}", self.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The algorithm used for signing.
|
/// A single digital signature.
|
||||||
|
///
|
||||||
|
/// Generated from `SigningKey`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
pub struct Signature {
|
||||||
|
algorithm: SigningAlgorithm,
|
||||||
|
signature: SodiumSignature,
|
||||||
|
version: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A set of signatures created by a single homeserver.
|
||||||
|
pub type SignatureSet = HashSet<Signature>;
|
||||||
|
|
||||||
|
/// The algorithm used for signing.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum SigningAlgorithm {
|
pub enum SigningAlgorithm {
|
||||||
|
/// The Ed25519 digital signature algorithm.
|
||||||
Ed25519,
|
Ed25519,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A signing key, consisting of an algoritm, a secret key, and a key version.
|
/// A signing key, consisting of an algorithm, a secret key, and a key version.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SigningKey {
|
pub struct SigningKey {
|
||||||
algorithm: SigningAlgorithm,
|
algorithm: SigningAlgorithm,
|
||||||
@ -66,8 +81,46 @@ pub struct SigningKey {
|
|||||||
version: String,
|
version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
/// The algorithm used to generate the signature.
|
||||||
|
pub fn algorithm(&self) -> SigningAlgorithm {
|
||||||
|
self.algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The raw bytes of the signature.
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.signature.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Base64 encoding of the signature.
|
||||||
|
pub fn base64(&self) -> String {
|
||||||
|
self.signature.as_ref().to_base64(BASE64_CONFIG)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A string containing the signature algorithm and the key "version" separated by a colon.
|
||||||
|
pub fn id(&self) -> String {
|
||||||
|
format!("ed25519:{}", self.version())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The "version" of the key used for this signature.
|
||||||
|
///
|
||||||
|
/// Versions are used as an identifier to distinguish signatures generated from different keys
|
||||||
|
/// but using the same algorithm on the same homeserver.
|
||||||
|
pub fn version(&self) -> &str {
|
||||||
|
&self.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SigningKey {
|
impl SigningKey {
|
||||||
/// Initialize a new signing key.
|
/// Initialize a new signing key.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * algorithm: The digital signature algorithm to use.
|
||||||
|
/// * key: A 64-byte secret key.
|
||||||
|
/// * version: The "version" of the key used for this signature.
|
||||||
|
/// Versions are used as an identifier to distinguish signatures generated from different keys
|
||||||
|
/// but using the same algorithm on the same homeserver.
|
||||||
pub fn new(algorithm: SigningAlgorithm, key: [u8; 64], version: String) -> Self {
|
pub fn new(algorithm: SigningAlgorithm, key: [u8; 64], version: String) -> Self {
|
||||||
SigningKey {
|
SigningKey {
|
||||||
algorithm: algorithm,
|
algorithm: algorithm,
|
||||||
@ -77,9 +130,9 @@ impl SigningKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a JSON object.
|
/// Sign a JSON object.
|
||||||
pub fn sign(&self, value: &Value) -> Result<Signature, SigningError> {
|
pub fn sign(&self, value: &Value) -> Result<Signature, Error> {
|
||||||
if !value.is_object() {
|
if !value.is_object() {
|
||||||
return Err(SigningError::new("JSON value must be a JSON object"));
|
return Err(Error::new("JSON value must be a JSON object"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut owned_value = value.clone();
|
let mut owned_value = value.clone();
|
||||||
@ -92,17 +145,14 @@ impl SigningKey {
|
|||||||
|
|
||||||
let json = match to_string(&owned_value) {
|
let json = match to_string(&owned_value) {
|
||||||
Ok(json) => json,
|
Ok(json) => json,
|
||||||
Err(error) => return Err(SigningError::new(error.description())),
|
Err(error) => return Err(Error::new(error.description())),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(sign_detached(json.as_bytes(), &self.key))
|
Ok(Signature {
|
||||||
}
|
algorithm: self.algorithm,
|
||||||
|
signature: sign_detached(json.as_bytes(), &self.key),
|
||||||
/// Sign and Base64 encode a JSON object.
|
version: self.version.clone(),
|
||||||
pub fn sign_and_base64_encode(&self, value: &Value) -> Result<String, SigningError> {
|
})
|
||||||
let signature = try!(self.sign(value));
|
|
||||||
|
|
||||||
Ok(signature.as_ref().to_base64(BASE64_CONFIG))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +172,7 @@ mod test {
|
|||||||
let SecretKey(raw_seckey) = seckey;
|
let SecretKey(raw_seckey) = seckey;
|
||||||
let signing_key = SigningKey::new(SigningAlgorithm::Ed25519, raw_seckey, "1".to_owned());
|
let signing_key = SigningKey::new(SigningAlgorithm::Ed25519, raw_seckey, "1".to_owned());
|
||||||
let value = from_str("{}").unwrap();
|
let value = from_str("{}").unwrap();
|
||||||
let actual = signing_key.sign_and_base64_encode(&value).unwrap();
|
let actual = signing_key.sign(&value).unwrap().base64();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
actual,
|
actual,
|
||||||
"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"
|
"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user