#![allow(clippy::use_self)]
use crate::schema::decoded::{Decoded, EcdsaFlex, Hex, RsaPem};
use crate::schema::error::{self, Result};
use olpc_cjson::CanonicalFormatter;
use ring::digest::{digest, SHA256};
use ring::signature::VerificationAlgorithm;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use snafu::ResultExt;
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[serde(tag = "keytype")]
pub enum Key {
Rsa {
keyval: RsaKey,
scheme: RsaScheme,
#[serde(flatten)]
_extra: HashMap<String, Value>,
},
Ed25519 {
keyval: Ed25519Key,
scheme: Ed25519Scheme,
#[serde(flatten)]
_extra: HashMap<String, Value>,
},
#[serde(rename = "ecdsa-sha2-nistp256")]
Ecdsa {
keyval: EcdsaKey,
scheme: EcdsaScheme,
#[serde(flatten)]
_extra: HashMap<String, Value>,
},
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum RsaScheme {
RsassaPssSha256,
}
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
pub struct RsaKey {
pub public: Decoded<RsaPem>,
#[serde(flatten)]
pub _extra: HashMap<String, Value>,
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum Ed25519Scheme {
Ed25519,
}
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
pub struct Ed25519Key {
pub public: Decoded<Hex>,
#[serde(flatten)]
pub _extra: HashMap<String, Value>,
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum EcdsaScheme {
EcdsaSha2Nistp256,
}
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
pub struct EcdsaKey {
pub public: Decoded<EcdsaFlex>,
#[serde(flatten)]
pub _extra: HashMap<String, Value>,
}
impl Key {
pub fn key_id(&self) -> Result<Decoded<Hex>> {
let mut buf = Vec::new();
let mut ser = serde_json::Serializer::with_formatter(&mut buf, CanonicalFormatter::new());
self.serialize(&mut ser)
.context(error::JsonSerializationSnafu {
what: "key".to_owned(),
})?;
Ok(digest(&SHA256, &buf).as_ref().to_vec().into())
}
pub(super) fn verify(&self, msg: &[u8], signature: &[u8]) -> bool {
let (alg, public_key): (&dyn VerificationAlgorithm, untrusted::Input<'_>) = match self {
Key::Ecdsa {
scheme: EcdsaScheme::EcdsaSha2Nistp256,
keyval,
..
} => (
&ring::signature::ECDSA_P256_SHA256_ASN1,
untrusted::Input::from(&keyval.public),
),
Key::Ed25519 {
scheme: Ed25519Scheme::Ed25519,
keyval,
..
} => (
&ring::signature::ED25519,
untrusted::Input::from(&keyval.public),
),
Key::Rsa {
scheme: RsaScheme::RsassaPssSha256,
keyval,
..
} => (
&ring::signature::RSA_PSS_2048_8192_SHA256,
untrusted::Input::from(&keyval.public),
),
};
alg.verify(
public_key,
untrusted::Input::from(msg),
untrusted::Input::from(signature),
)
.is_ok()
}
}
impl FromStr for Key {
type Err = KeyParseError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if let Ok(public) = serde_plain::from_str::<Decoded<RsaPem>>(s) {
Ok(Key::Rsa {
keyval: RsaKey {
public,
_extra: HashMap::new(),
},
scheme: RsaScheme::RsassaPssSha256,
_extra: HashMap::new(),
})
} else if let Ok(public) = serde_plain::from_str::<Decoded<Hex>>(s) {
if public.len() == ring::signature::ED25519_PUBLIC_KEY_LEN {
Ok(Key::Ed25519 {
keyval: Ed25519Key {
public,
_extra: HashMap::new(),
},
scheme: Ed25519Scheme::Ed25519,
_extra: HashMap::new(),
})
} else {
Err(KeyParseError(()))
}
} else if let Ok(public) = serde_plain::from_str::<Decoded<EcdsaFlex>>(s) {
Ok(Key::Ecdsa {
keyval: EcdsaKey {
public,
_extra: HashMap::new(),
},
scheme: EcdsaScheme::EcdsaSha2Nistp256,
_extra: HashMap::new(),
})
} else {
Err(KeyParseError(()))
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct KeyParseError(());
impl fmt::Display for KeyParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unrecognized or invalid public key")
}
}
impl std::error::Error for KeyParseError {}