mod key_v1;
mod secret_key_v1;
use super::DataType;
use super::Error;
use super::Header;
use super::HeaderType;
use super::KeySubtype;
pub use super::KeyVersion;
use super::Result;
use key_v1::{KeyV1Private, KeyV1Public};
use secret_key_v1::SecretKeyV1;
use std::borrow::Borrow;
use std::convert::TryFrom;
use zeroize::Zeroizing;
#[cfg(feature = "fuzz")]
use arbitrary::Arbitrary;
#[cfg(feature = "wbindgen")]
use wasm_bindgen::prelude::*;
#[derive(Clone)]
pub struct KeyPair {
pub private_key: PrivateKey,
pub public_key: PublicKey,
}
#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
#[derive(Clone, Debug)]
pub struct PublicKey {
pub(crate) header: Header<PublicKey>,
payload: PublicKeyPayload,
}
#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
#[derive(Clone, Debug)]
pub struct PrivateKey {
pub(crate) header: Header<PrivateKey>,
payload: PrivateKeyPayload,
}
impl HeaderType for PublicKey {
type Version = KeyVersion;
type Subtype = KeySubtype;
fn data_type() -> DataType {
DataType::Key
}
fn subtype() -> Self::Subtype {
KeySubtype::Public
}
}
impl HeaderType for PrivateKey {
type Version = KeyVersion;
type Subtype = KeySubtype;
fn data_type() -> DataType {
DataType::Key
}
fn subtype() -> Self::Subtype {
KeySubtype::Private
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
enum PrivateKeyPayload {
V1(KeyV1Private),
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
enum PublicKeyPayload {
V1(KeyV1Public),
}
pub fn generate_keypair(version: KeyVersion) -> KeyPair {
let (private_header, public_header) = keypair_headers(version);
let (private_key, public_key) = match version {
KeyVersion::V1 | KeyVersion::Latest => {
let keypair = key_v1::generate_keypair();
(
PrivateKeyPayload::V1(keypair.private_key),
PublicKeyPayload::V1(keypair.public_key),
)
}
};
KeyPair {
private_key: PrivateKey {
header: private_header,
payload: private_key,
},
public_key: PublicKey {
header: public_header,
payload: public_key,
},
}
}
pub fn mix_key_exchange(private_key: &PrivateKey, public_key: &PublicKey) -> Result<Vec<u8>> {
Ok(match (&private_key.payload, &public_key.payload) {
(PrivateKeyPayload::V1(private_key), PublicKeyPayload::V1(public_key)) => {
key_v1::mix_key_exchange(private_key, public_key)
} })
}
fn keypair_headers(version: KeyVersion) -> (Header<PrivateKey>, Header<PublicKey>) {
let mut private_header = Header::default();
let mut public_header = Header::default();
match version {
KeyVersion::V1 | KeyVersion::Latest => {
private_header.version = KeyVersion::V1;
public_header.version = KeyVersion::V1;
}
}
(private_header, public_header)
}
impl From<PublicKey> for Vec<u8> {
fn from(data: PublicKey) -> Self {
let mut header: Self = data.header.borrow().into();
let mut payload: Self = data.payload.into();
header.append(&mut payload);
header
}
}
impl TryFrom<&[u8]> for PublicKey {
type Error = Error;
fn try_from(data: &[u8]) -> Result<Self> {
if data.len() < Header::len() {
return Err(Error::InvalidLength);
};
let header = Header::try_from(&data[0..Header::len()])?;
if header.data_subtype != KeySubtype::Public {
return Err(Error::InvalidDataType);
}
let payload = match header.version {
KeyVersion::V1 => PublicKeyPayload::V1(KeyV1Public::try_from(&data[Header::len()..])?),
_ => return Err(Error::UnknownVersion),
};
Ok(Self { header, payload })
}
}
impl From<PrivateKey> for Vec<u8> {
fn from(data: PrivateKey) -> Self {
let mut header: Self = data.header.borrow().into();
let mut payload: Self = data.payload.into();
header.append(&mut payload);
header
}
}
impl TryFrom<&[u8]> for PrivateKey {
type Error = Error;
fn try_from(data: &[u8]) -> Result<Self> {
if data.len() < Header::len() {
return Err(Error::InvalidLength);
};
let header = Header::try_from(&data[0..Header::len()])?;
if header.data_subtype != KeySubtype::Private {
return Err(Error::InvalidDataType);
}
let payload = match header.version {
KeyVersion::V1 => {
PrivateKeyPayload::V1(KeyV1Private::try_from(&data[Header::len()..])?)
}
_ => return Err(Error::UnknownVersion),
};
Ok(Self { header, payload })
}
}
impl From<PrivateKeyPayload> for Vec<u8> {
fn from(data: PrivateKeyPayload) -> Self {
match data {
PrivateKeyPayload::V1(x) => x.into(),
}
}
}
impl From<PublicKeyPayload> for Vec<u8> {
fn from(data: PublicKeyPayload) -> Self {
match data {
PublicKeyPayload::V1(x) => x.into(),
}
}
}
impl From<&PublicKey> for x25519_dalek::PublicKey {
fn from(data: &PublicKey) -> Self {
match &data.payload {
PublicKeyPayload::V1(x) => Self::from(x),
}
}
}
impl From<&PrivateKey> for x25519_dalek::StaticSecret {
fn from(data: &PrivateKey) -> Self {
match &data.payload {
PrivateKeyPayload::V1(x) => Self::from(x),
}
}
}
#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
#[derive(Clone, Debug)]
pub struct SecretKey {
pub(crate) header: Header<SecretKey>,
payload: SecretKeyPayload,
}
impl HeaderType for SecretKey {
type Version = KeyVersion;
type Subtype = KeySubtype;
fn data_type() -> DataType {
DataType::Key
}
fn subtype() -> Self::Subtype {
KeySubtype::Secret
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
enum SecretKeyPayload {
V1(SecretKeyV1),
}
impl SecretKey {
pub fn as_bytes(&self) -> &[u8] {
match &self.payload {
SecretKeyPayload::V1(k) => k.as_bytes(),
}
}
}
pub(crate) fn secret_key_from_raw(bytes: Zeroizing<Vec<u8>>) -> Result<SecretKey> {
let mut header: Header<SecretKey> = Header::default();
header.version = KeyVersion::V1;
let payload = SecretKeyPayload::V1(SecretKeyV1::try_from(bytes.as_slice())?);
Ok(SecretKey { header, payload })
}
pub fn generate_secret_key(version: KeyVersion) -> SecretKey {
let mut header: Header<SecretKey> = Header::default();
match version {
KeyVersion::V1 | KeyVersion::Latest => {
header.version = KeyVersion::V1;
}
}
SecretKey {
header,
payload: SecretKeyPayload::V1(SecretKeyV1::generate()),
}
}
impl From<SecretKey> for Vec<u8> {
fn from(data: SecretKey) -> Self {
let mut header: Self = data.header.borrow().into();
let mut payload: Self = data.payload.into();
header.append(&mut payload);
header
}
}
impl TryFrom<&[u8]> for SecretKey {
type Error = Error;
fn try_from(data: &[u8]) -> Result<Self> {
if data.len() < Header::len() {
return Err(Error::InvalidLength);
};
let header = Header::try_from(&data[0..Header::len()])?;
if header.data_subtype != KeySubtype::Secret {
return Err(Error::InvalidDataType);
}
let payload = match header.version {
KeyVersion::V1 => SecretKeyPayload::V1(SecretKeyV1::try_from(&data[Header::len()..])?),
_ => return Err(Error::UnknownVersion),
};
Ok(Self { header, payload })
}
}
impl From<SecretKeyPayload> for Vec<u8> {
fn from(data: SecretKeyPayload) -> Self {
match data {
SecretKeyPayload::V1(x) => x.into(),
}
}
}
#[test]
fn ecdh_test() {
let bob_keypair = generate_keypair(KeyVersion::Latest);
let alice_keypair = generate_keypair(KeyVersion::Latest);
let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_keypair.public_key).unwrap();
let alice_shared =
mix_key_exchange(&alice_keypair.private_key, &bob_keypair.public_key).unwrap();
assert_eq!(bob_shared, alice_shared);
}
#[test]
fn secret_key_generate_roundtrip() {
let key = generate_secret_key(KeyVersion::Latest);
let original_bytes = key.as_bytes().to_vec();
let serialized: Vec<u8> = key.into();
let deserialized = SecretKey::try_from(serialized.as_slice()).unwrap();
assert_eq!(deserialized.as_bytes(), original_bytes.as_slice());
}
#[test]
fn secret_key_wrong_subtype_rejected() {
use std::convert::TryFrom as _;
let keypair = generate_keypair(KeyVersion::Latest);
let private_bytes: Vec<u8> = keypair.private_key.into();
let result = SecretKey::try_from(private_bytes.as_slice());
assert!(matches!(result, Err(Error::InvalidDataType)));
}
#[test]
fn secret_key_wrong_length_rejected() {
let short = [0u8; 4];
let result = SecretKey::try_from(short.as_slice());
assert!(matches!(result, Err(Error::InvalidLength)));
}