use data_encoding::BASE64;
use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH};
use super::{SignatureRef, VerifyingKey};
pub struct SigningKey<S> {
name: String,
signing_key: S,
}
impl<S> SigningKey<S>
where
S: ed25519::signature::Signer<ed25519::Signature>,
{
pub fn new(name: String, signing_key: S) -> Self {
Self { name, signing_key }
}
pub(crate) fn sign<'a>(&'a self, fp: &[u8]) -> SignatureRef<'a> {
SignatureRef::new(&self.name, self.signing_key.sign(fp).to_bytes())
}
pub fn name(&self) -> &str {
&self.name
}
}
pub fn parse_keypair(
input: &str,
) -> Result<(SigningKey<ed25519_dalek::SigningKey>, VerifyingKey), Error> {
let (name, bytes64) = input.split_once(':').ok_or(Error::MissingSeparator)?;
if name.is_empty()
|| !name
.chars()
.all(|c| char::is_alphanumeric(c) || c == '-' || c == '.')
{
return Err(Error::InvalidName(name.to_string()));
}
const DECODED_BYTES_LEN: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH;
if bytes64.len() != BASE64.encode_len(DECODED_BYTES_LEN) {
return Err(Error::InvalidSigningKeyLen(bytes64.len()));
}
let mut buf = [0; DECODED_BYTES_LEN + 2]; let mut bytes = [0; DECODED_BYTES_LEN];
match BASE64.decode_mut(bytes64.as_bytes(), &mut buf) {
Ok(len) if len == DECODED_BYTES_LEN => {
bytes.copy_from_slice(&buf[..DECODED_BYTES_LEN]);
}
Ok(_) => unreachable!(),
Err(_) => return Err(Error::DecodeError(input.to_string())),
}
let bytes_signing_key: [u8; SECRET_KEY_LENGTH] = {
let mut b = [0u8; SECRET_KEY_LENGTH];
b.copy_from_slice(&bytes[0..SECRET_KEY_LENGTH]);
b
};
let bytes_verifying_key: [u8; PUBLIC_KEY_LENGTH] = {
let mut b = [0u8; PUBLIC_KEY_LENGTH];
b.copy_from_slice(&bytes[SECRET_KEY_LENGTH..]);
b
};
let signing_key = SigningKey::new(
name.to_string(),
ed25519_dalek::SigningKey::from_bytes(&bytes_signing_key),
);
let verifying_key = VerifyingKey::new(
name.to_string(),
ed25519_dalek::VerifyingKey::from_bytes(&bytes_verifying_key)
.map_err(Error::InvalidVerifyingKey)?,
);
Ok((signing_key, verifying_key))
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Invalid name: {0}")]
InvalidName(String),
#[error("Missing separator")]
MissingSeparator,
#[error("Invalid signing key len: {0}")]
InvalidSigningKeyLen(usize),
#[error("Unable to base64-decode signing key: {0}")]
DecodeError(String),
#[error("VerifyingKey error: {0}")]
InvalidVerifyingKey(ed25519_dalek::SignatureError),
}
#[cfg(test)]
mod test {
use crate::narinfo::DUMMY_KEYPAIR;
#[test]
fn parse() {
let (_signing_key, _verifying_key) =
super::parse_keypair(DUMMY_KEYPAIR).expect("must succeed");
}
#[test]
fn parse_fail() {
assert!(super::parse_keypair("cache.example.com-1:cCta2MEsRNuYCgWYyeRXLyfoFpKhQJKn8gLMeXWAb7vIpRKKo/3JoxJ24OYa3DxT2JVV38KjK/1ywHWuMe2JE").is_err());
assert!(super::parse_keypair("cache.example.com-1cCta2MEsRNuYCgWYyeRXLyfoFpKhQJKn8gLMeXWAb7vIpRKKo/3JoxJ24OYa3DxT2JVV38KjK/1ywHWuMe2JE").is_err());
}
}