pub use crate::error::*;
use ct_codecs::{Encoder, Hex};
#[cfg(feature = "openssh")]
use ssh_keys::{self, openssh};
use std::collections::HashSet;
use std::fs::File;
use std::io::{self, prelude::*};
use std::path::Path;
use std::{fmt, str};
pub(crate) const ED25519_PK_ID: u8 = 0x01;
pub(crate) const ED25519_SK_ID: u8 = 0x81;
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct PublicKey {
pub pk: ed25519_compact::PublicKey,
pub key_id: Option<Vec<u8>>,
}
impl PublicKey {
pub fn from_bytes(pk: &[u8]) -> Result<Self, WSError> {
let mut reader = io::Cursor::new(pk);
let mut id = [0u8];
reader.read_exact(&mut id)?;
if id[0] != ED25519_PK_ID {
return Err(WSError::UnsupportedKeyType);
}
let mut bytes = vec![];
reader.read_to_end(&mut bytes)?;
Ok(Self {
pk: ed25519_compact::PublicKey::from_slice(&bytes)?,
key_id: None,
})
}
pub fn from_pem(pem: &str) -> Result<Self, WSError> {
let pk = ed25519_compact::PublicKey::from_pem(pem)?;
Ok(Self { pk, key_id: None })
}
pub fn from_der(der: &[u8]) -> Result<Self, WSError> {
let pk = ed25519_compact::PublicKey::from_der(der)?;
Ok(Self { pk, key_id: None })
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![ED25519_PK_ID];
bytes.extend_from_slice(self.pk.as_ref());
bytes
}
pub fn to_pem(&self) -> String {
self.pk.to_pem()
}
pub fn to_der(&self) -> Vec<u8> {
self.pk.to_der()
}
pub fn from_file(file: impl AsRef<Path>) -> Result<Self, WSError> {
let mut fp = File::open(file)?;
let mut bytes = vec![];
fp.read_to_end(&mut bytes)?;
Self::from_bytes(&bytes)
}
pub fn to_file(&self, file: impl AsRef<Path>) -> Result<(), WSError> {
let mut fp = File::create(file)?;
fp.write_all(&self.to_bytes())?;
Ok(())
}
#[cfg(feature = "openssh")]
pub fn from_openssh(lines: &str) -> Result<Self, WSError> {
for line in lines.lines() {
let line = line.trim();
if let Ok(ssh_keys::PublicKey::Ed25519(raw)) = openssh::parse_public_key(line) {
let mut bytes = vec![ED25519_PK_ID];
bytes.extend_from_slice(&raw);
if let Ok(pk) = PublicKey::from_bytes(&bytes) {
return Ok(pk);
}
};
}
Err(WSError::ParseError)
}
#[cfg(feature = "openssh")]
pub fn from_openssh_file(file: impl AsRef<Path>) -> Result<Self, WSError> {
let mut fp = File::open(file)?;
let mut lines = String::new();
fp.read_to_string(&mut lines)?;
Self::from_openssh(&lines)
}
pub fn from_any(data: &[u8]) -> Result<Self, WSError> {
if let Ok(pk) = Self::from_bytes(data) {
return Ok(pk);
}
if let Ok(pk) = Self::from_der(data) {
return Ok(pk);
}
let s = str::from_utf8(data).map_err(|_| WSError::ParseError)?;
if let Ok(pk) = Self::from_pem(s) {
return Ok(pk);
}
#[cfg(feature = "openssh")]
if let Ok(pk) = Self::from_openssh(s) {
return Ok(pk);
}
Err(WSError::ParseError)
}
pub fn from_any_file(file: impl AsRef<Path>) -> Result<Self, WSError> {
let mut fp = File::open(file)?;
let mut bytes = vec![];
fp.read_to_end(&mut bytes)?;
Self::from_any(&bytes)
}
pub fn key_id(&self) -> Option<&Vec<u8>> {
self.key_id.as_ref()
}
pub fn attach_default_key_id(mut self) -> Self {
if self.key_id.is_none() {
self.key_id = Some(hmac_sha256::HMAC::mac(b"key_id", self.pk.as_ref())[0..12].to_vec());
}
self
}
}
impl fmt::Debug for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"PublicKey {{ [{}] - key_id: {:?} }}",
Hex::encode_to_string(self.pk.as_ref()).unwrap(),
self.key_id()
.map(|key_id| format!("[{}]", Hex::encode_to_string(key_id).unwrap()))
)
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct SecretKey {
pub sk: ed25519_compact::SecretKey,
}
impl SecretKey {
pub fn from_bytes(sk: &[u8]) -> Result<Self, WSError> {
let mut reader = io::Cursor::new(sk);
let mut id = [0u8];
reader.read_exact(&mut id)?;
if id[0] != ED25519_SK_ID {
return Err(WSError::UnsupportedKeyType);
}
let mut bytes = vec![];
reader.read_to_end(&mut bytes)?;
Ok(Self {
sk: ed25519_compact::SecretKey::from_slice(&bytes)?,
})
}
pub fn from_pem(pem: &str) -> Result<Self, WSError> {
let sk = ed25519_compact::SecretKey::from_pem(pem)?;
Ok(Self { sk })
}
pub fn from_der(der: &[u8]) -> Result<Self, WSError> {
let sk = ed25519_compact::SecretKey::from_der(der)?;
Ok(Self { sk })
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![ED25519_SK_ID];
bytes.extend_from_slice(self.sk.as_ref());
bytes
}
pub fn to_pem(&self) -> String {
self.sk.to_pem()
}
pub fn to_der(&self) -> Vec<u8> {
self.sk.to_der()
}
pub fn from_file(file: impl AsRef<Path>) -> Result<Self, WSError> {
let mut fp = File::open(file)?;
let mut bytes = vec![];
fp.read_to_end(&mut bytes)?;
Self::from_bytes(&bytes)
}
pub fn to_file(&self, file: impl AsRef<Path>) -> Result<(), WSError> {
let mut fp = File::create(file)?;
fp.write_all(&self.to_bytes())?;
Ok(())
}
#[cfg(feature = "openssh")]
pub fn from_openssh(lines: &str) -> Result<Self, WSError> {
for sk in openssh::parse_private_key(lines).map_err(|_| WSError::ParseError)? {
if let ssh_keys::PrivateKey::Ed25519(raw) = sk {
let mut bytes = vec![ED25519_SK_ID];
bytes.extend_from_slice(&raw);
return Self::from_bytes(&bytes);
}
}
Err(WSError::UnsupportedKeyType)
}
#[cfg(feature = "openssh")]
pub fn from_openssh_file(file: impl AsRef<Path>) -> Result<Self, WSError> {
let mut fp = File::open(file)?;
let mut lines = String::new();
fp.read_to_string(&mut lines)?;
Self::from_openssh(&lines)
}
pub fn from_any(data: &[u8]) -> Result<Self, WSError> {
if let Ok(sk) = Self::from_bytes(data) {
return Ok(sk);
}
if let Ok(sk) = Self::from_der(data) {
return Ok(sk);
}
let s = str::from_utf8(data).map_err(|_| WSError::ParseError)?;
if let Ok(sk) = Self::from_pem(s) {
return Ok(sk);
}
#[cfg(feature = "openssh")]
if let Ok(sk) = Self::from_openssh(s) {
return Ok(sk);
}
Err(WSError::ParseError)
}
pub fn from_any_file(file: impl AsRef<Path>) -> Result<Self, WSError> {
let mut fp = File::open(file)?;
let mut bytes = vec![];
fp.read_to_end(&mut bytes)?;
Self::from_any(&bytes)
}
}
impl fmt::Debug for SecretKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SecretKey {{ [{}] }}",
Hex::encode_to_string(self.sk.as_ref()).unwrap(),
)
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct KeyPair {
pub pk: PublicKey,
pub sk: SecretKey,
}
impl KeyPair {
pub fn generate() -> Self {
let kp = ed25519_compact::KeyPair::from_seed(ed25519_compact::Seed::generate());
KeyPair {
pk: PublicKey {
pk: kp.pk,
key_id: None,
},
sk: SecretKey { sk: kp.sk },
}
}
}
#[derive(Debug, Clone)]
pub struct PublicKeySet {
pub pks: HashSet<PublicKey>,
}
impl PublicKeySet {
pub fn empty() -> Self {
PublicKeySet {
pks: HashSet::new(),
}
}
pub fn new(pks: HashSet<PublicKey>) -> Self {
PublicKeySet { pks }
}
#[cfg(feature = "openssh")]
pub fn from_openssh(lines: &str) -> Result<Self, WSError> {
let mut pks = PublicKeySet::empty();
for line in lines.lines() {
let line = line.trim();
if let Ok(ssh_keys::PublicKey::Ed25519(raw)) = openssh::parse_public_key(line) {
let mut bytes = vec![ED25519_PK_ID];
bytes.extend_from_slice(&raw);
if let Ok(pk) = PublicKey::from_bytes(&bytes) {
pks.pks.insert(pk);
}
};
}
Ok(pks)
}
#[cfg(feature = "openssh")]
pub fn from_openssh_file(file: impl AsRef<Path>) -> Result<Self, WSError> {
let mut fp = File::open(file)?;
let mut lines = String::new();
fp.read_to_string(&mut lines)?;
Self::from_openssh(&lines)
}
pub fn len(&self) -> usize {
self.pks.len()
}
pub fn is_empty(&self) -> bool {
self.pks.is_empty()
}
pub fn insert(&mut self, pk: PublicKey) -> Result<(), WSError> {
if !self.pks.insert(pk) {
return Err(WSError::DuplicatePublicKey);
}
Ok(())
}
pub fn insert_any(&mut self, data: &[u8]) -> Result<(), WSError> {
#[cfg(feature = "openssh")]
if let Ok(s) = str::from_utf8(data) {
if let Ok(pk) = PublicKey::from_openssh(s) {
self.insert(pk)?;
return Ok(());
}
}
let pk = PublicKey::from_any(data)?;
self.insert(pk)
}
pub fn insert_any_file(&mut self, file: impl AsRef<Path>) -> Result<(), WSError> {
let mut fp = File::open(file)?;
let mut data = vec![];
fp.read_to_end(&mut data)?;
self.insert_any(&data)
}
pub fn merge(&mut self, other: &PublicKeySet) -> Result<(), WSError> {
for pk in other.pks.iter() {
self.insert(pk.clone())?;
}
Ok(())
}
pub fn remove(&mut self, pk: &PublicKey) -> Result<(), WSError> {
if !self.pks.remove(pk) {
return Err(WSError::UnknownPublicKey);
}
Ok(())
}
pub fn items(&self) -> &HashSet<PublicKey> {
&self.pks
}
pub fn items_mut(&mut self) -> &mut HashSet<PublicKey> {
&mut self.pks
}
pub fn attach_default_key_id(mut self) -> Self {
self.pks = self
.pks
.into_iter()
.map(|pk| pk.attach_default_key_id())
.collect();
self
}
}