Test request serialization
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
55e10a60c6
commit
5625bd95a4
|
@ -5,7 +5,7 @@ use ::zeroize::Zeroize;
|
|||
|
||||
/// User identifier. 16 bytes for easy uuid conversion
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct UserID([u8; 16]);
|
||||
pub struct UserID(pub [u8; 16]);
|
||||
|
||||
impl From<[u8; 16]> for UserID {
|
||||
fn from(raw: [u8; 16]) -> Self {
|
||||
|
@ -36,7 +36,7 @@ impl UserID {
|
|||
/// Authentication Token, basically just 32 random bytes
|
||||
#[derive(Clone, Zeroize)]
|
||||
#[zeroize(drop)]
|
||||
pub struct Token([u8; 32]);
|
||||
pub struct Token(pub [u8; 32]);
|
||||
|
||||
impl Token {
|
||||
/// New random token, anonymous should not check this anyway
|
||||
|
@ -110,7 +110,7 @@ impl Domain {
|
|||
pub const SERVICEID_AUTH: ServiceID = ServiceID([0; 16]);
|
||||
/// The Service ID is a UUID associated with the service.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct ServiceID([u8; 16]);
|
||||
pub struct ServiceID(pub [u8; 16]);
|
||||
|
||||
impl From<[u8; 16]> for ServiceID {
|
||||
fn from(raw: [u8; 16]) -> Self {
|
||||
|
|
|
@ -97,7 +97,9 @@ pub struct Req {
|
|||
pub exchange_key: ExchangePubKey,
|
||||
/// encrypted data
|
||||
pub data: ReqInner,
|
||||
// Security: Add padding to min: 1200 bytes to avoid amplification attaks
|
||||
// SECURITY: TODO: Add padding to min: 1200 bytes
|
||||
// to avoid amplification attaks
|
||||
// also: 1200 < 1280 to allow better vpn compatibility
|
||||
}
|
||||
|
||||
impl Req {
|
||||
|
@ -125,7 +127,9 @@ impl Req {
|
|||
+ HkdfKind::len()
|
||||
+ CipherKind::len()
|
||||
+ self.exchange_key.kind().pub_len()
|
||||
+ self.cipher.nonce_len().0
|
||||
+ self.data.len()
|
||||
+ self.cipher.tag_len().0
|
||||
}
|
||||
/// Serialize into raw bytes
|
||||
/// NOTE: assumes that there is exactly as much buffer as needed
|
||||
|
@ -135,8 +139,21 @@ impl Req {
|
|||
tag_len: TagLen,
|
||||
out: &mut [u8],
|
||||
) {
|
||||
//assert!(out.len() > , ": not enough buffer to serialize");
|
||||
todo!()
|
||||
out[0..2].copy_from_slice(&self.key_id.0.to_le_bytes());
|
||||
out[2] = self.exchange as u8;
|
||||
out[3] = self.hkdf as u8;
|
||||
out[4] = self.cipher as u8;
|
||||
let key_len = self.exchange_key.len();
|
||||
let written_next = 5 + key_len;
|
||||
self.exchange_key.serialize_into(&mut out[5..written_next]);
|
||||
let written = written_next;
|
||||
if let ReqInner::ClearText(data) = &self.data {
|
||||
let from = written + head_len.0;
|
||||
let to = out.len() - tag_len.0;
|
||||
data.serialize(&mut out[from..to]);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +164,7 @@ impl super::HandshakeParsing for Req {
|
|||
return Err(Error::NotEnoughData);
|
||||
}
|
||||
let key_id: KeyID =
|
||||
KeyID(u16::from_le_bytes(raw[0..1].try_into().unwrap()));
|
||||
KeyID(u16::from_le_bytes(raw[0..2].try_into().unwrap()));
|
||||
use ::num_traits::FromPrimitive;
|
||||
let exchange: KeyExchangeKind = match KeyExchangeKind::from_u8(raw[2]) {
|
||||
Some(exchange) => exchange,
|
||||
|
@ -161,7 +178,7 @@ impl super::HandshakeParsing for Req {
|
|||
Some(cipher) => cipher,
|
||||
None => return Err(Error::Parsing),
|
||||
};
|
||||
let (exchange_key, len) = match ExchangePubKey::from_slice(&raw[5..]) {
|
||||
let (exchange_key, len) = match ExchangePubKey::deserialize(&raw[5..]) {
|
||||
Ok(exchange_key) => exchange_key,
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
@ -235,6 +252,21 @@ impl AuthInfo {
|
|||
pub fn len(&self) -> usize {
|
||||
Self::MIN_PKT_LEN + self.domain.len()
|
||||
}
|
||||
/// serialize into a buffer
|
||||
/// Note: assumes there is enough space
|
||||
pub fn serialize(&self, out: &mut [u8]) {
|
||||
out[..auth::UserID::len()].copy_from_slice(&self.user.0);
|
||||
const WRITTEN_TOKEN: usize = auth::UserID::len() + auth::Token::len();
|
||||
out[auth::UserID::len()..WRITTEN_TOKEN].copy_from_slice(&self.token.0);
|
||||
const WRITTEN_SERVICE_ID: usize =
|
||||
WRITTEN_TOKEN + auth::ServiceID::len();
|
||||
out[WRITTEN_TOKEN..WRITTEN_SERVICE_ID]
|
||||
.copy_from_slice(&self.service_id.0);
|
||||
let domain_len = self.domain.0.as_bytes().len() as u8;
|
||||
out[WRITTEN_SERVICE_ID] = domain_len;
|
||||
const WRITTEN_DOMAIN_LEN: usize = WRITTEN_SERVICE_ID + 1;
|
||||
out[WRITTEN_DOMAIN_LEN..].copy_from_slice(&self.domain.0.as_bytes());
|
||||
}
|
||||
/// deserialize from raw bytes
|
||||
pub fn deserialize(raw: &[u8]) -> Result<Self, Error> {
|
||||
if raw.len() < Self::MIN_PKT_LEN {
|
||||
|
@ -295,6 +327,19 @@ impl ReqData {
|
|||
/// Minimum byte length of the request data
|
||||
pub const MIN_PKT_LEN: usize =
|
||||
16 + KeyID::len() + ID::len() + AuthInfo::MIN_PKT_LEN;
|
||||
/// serialize into a buffer
|
||||
/// Note: assumes there is enough space
|
||||
pub fn serialize(&self, out: &mut [u8]) {
|
||||
out[..Nonce::len()].copy_from_slice(&self.nonce.0);
|
||||
const WRITTEN_KEY: usize = Nonce::len() + KeyID::len();
|
||||
out[Nonce::len()..WRITTEN_KEY]
|
||||
.copy_from_slice(&self.client_key_id.0.to_le_bytes());
|
||||
const WRITTEN: usize = WRITTEN_KEY;
|
||||
const WRITTEN_ID: usize = WRITTEN + 8;
|
||||
out[WRITTEN..WRITTEN_ID]
|
||||
.copy_from_slice(&self.id.as_u64().to_le_bytes());
|
||||
self.auth.serialize(&mut out[WRITTEN_ID..]);
|
||||
}
|
||||
/// Parse the cleartext raw data
|
||||
pub fn deserialize(raw: &[u8]) -> Result<Self, Error> {
|
||||
if raw.len() < Self::MIN_PKT_LEN {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Handhsake handling
|
||||
|
||||
pub mod dirsync;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::{
|
||||
auth::ServiceID,
|
||||
|
@ -194,7 +196,7 @@ impl HandshakeData {
|
|||
/// Kind of handshake
|
||||
#[derive(::num_derive::FromPrimitive, Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Kind {
|
||||
pub enum HandshakeKind {
|
||||
/// 1-RTT, Directory synchronized handshake
|
||||
/// Request
|
||||
DirSyncReq = 0,
|
||||
|
@ -210,6 +212,12 @@ pub enum Kind {
|
|||
....
|
||||
*/
|
||||
}
|
||||
impl HandshakeKind {
|
||||
/// Length of the serialized field
|
||||
pub const fn len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsed handshake
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -230,7 +238,7 @@ impl Handshake {
|
|||
}
|
||||
/// return the total length of the handshake
|
||||
pub fn len(&self) -> usize {
|
||||
ProtocolVersion::len() + self.data.len()
|
||||
ProtocolVersion::len() + HandshakeKind::len() + self.data.len()
|
||||
}
|
||||
const MIN_PKT_LEN: usize = 8;
|
||||
/// Parse the packet and return the parsed handshake
|
||||
|
@ -242,13 +250,15 @@ impl Handshake {
|
|||
Some(fenrir_version) => fenrir_version,
|
||||
None => return Err(Error::Parsing),
|
||||
};
|
||||
let handshake_kind = match Kind::from_u8(raw[1]) {
|
||||
let handshake_kind = match HandshakeKind::from_u8(raw[1]) {
|
||||
Some(handshake_kind) => handshake_kind,
|
||||
None => return Err(Error::Parsing),
|
||||
};
|
||||
let data = match handshake_kind {
|
||||
Kind::DirSyncReq => dirsync::Req::deserialize(&raw[2..])?,
|
||||
Kind::DirSyncResp => dirsync::Resp::deserialize(&raw[2..])?,
|
||||
HandshakeKind::DirSyncReq => dirsync::Req::deserialize(&raw[2..])?,
|
||||
HandshakeKind::DirSyncResp => {
|
||||
dirsync::Resp::deserialize(&raw[2..])?
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
fenrir_version,
|
||||
|
@ -263,9 +273,14 @@ impl Handshake {
|
|||
tag_len: TagLen,
|
||||
out: &mut [u8],
|
||||
) {
|
||||
assert!(out.len() > 1, "Handshake: not enough buffer to serialize");
|
||||
self.fenrir_version.serialize(&mut out[0]);
|
||||
self.data.serialize(head_len, tag_len, &mut out[1..]);
|
||||
out[0] = self.fenrir_version as u8;
|
||||
out[1] = match &self.data {
|
||||
HandshakeData::DirSync(d) => match d {
|
||||
dirsync::DirSync::Req(_) => HandshakeKind::DirSyncReq,
|
||||
dirsync::DirSync::Resp(_) => HandshakeKind::DirSyncResp,
|
||||
},
|
||||
} as u8;
|
||||
self.data.serialize(head_len, tag_len, &mut out[2..]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
use crate::{
|
||||
auth,
|
||||
connection::{handshake::*, ID},
|
||||
enc,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_handshake_dirsync_req() {
|
||||
let rand = enc::Random::new();
|
||||
let secret = enc::Secret::new_rand(&rand);
|
||||
let cipher_send = enc::sym::CipherSend::new(
|
||||
enc::sym::CipherKind::XChaCha20Poly1305,
|
||||
secret,
|
||||
&rand,
|
||||
);
|
||||
|
||||
let (_, exchange_key) =
|
||||
match enc::asym::KeyExchangeKind::X25519DiffieHellman.new_keypair(&rand)
|
||||
{
|
||||
Ok(pair) => pair,
|
||||
Err(_) => {
|
||||
assert!(false, "Can't generate random keypair");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let data = dirsync::ReqInner::ClearText(dirsync::ReqData {
|
||||
nonce: dirsync::Nonce::new(&rand),
|
||||
client_key_id: KeyID(2424),
|
||||
id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()),
|
||||
auth: dirsync::AuthInfo {
|
||||
user: auth::UserID::new(&rand),
|
||||
token: auth::Token::new_anonymous(&rand),
|
||||
service_id: auth::SERVICEID_AUTH,
|
||||
domain: auth::Domain("example.com".to_owned()),
|
||||
},
|
||||
});
|
||||
|
||||
let h_req = Handshake::new(HandshakeData::DirSync(dirsync::DirSync::Req(
|
||||
dirsync::Req {
|
||||
key_id: KeyID(4224),
|
||||
exchange: enc::asym::KeyExchangeKind::X25519DiffieHellman,
|
||||
hkdf: enc::hkdf::HkdfKind::Sha3,
|
||||
cipher: enc::sym::CipherKind::XChaCha20Poly1305,
|
||||
exchange_key,
|
||||
data,
|
||||
},
|
||||
)));
|
||||
|
||||
let mut bytes = Vec::<u8>::with_capacity(h_req.len());
|
||||
bytes.resize(h_req.len(), 0);
|
||||
h_req.serialize(
|
||||
cipher_send.kind().nonce_len(),
|
||||
cipher_send.kind().tag_len(),
|
||||
&mut bytes,
|
||||
);
|
||||
|
||||
let deserialized = match Handshake::deserialize(&bytes) {
|
||||
Ok(deserialized) => deserialized,
|
||||
Err(e) => {
|
||||
assert!(false, "{}", e.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -152,34 +152,56 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
// The record was generated with:
|
||||
// f-dnssec generate dnssec \
|
||||
// -a 1 2 42 directory_synchronized 127.0.0.1 31337 \
|
||||
// -p 42 x25519 x25519.pub \
|
||||
// -x x25519diffiehellman \
|
||||
// -c xchacha20poly1305
|
||||
const TXT_RECORD: &'static str = "v=Fenrir1 \
|
||||
5fBgo5ovk=0Dk}g0V)6>0cKP8KO-Vna846zp@MaLF|nim_XH&nQvT-I|B9HfJpcd";
|
||||
let rand = enc::Random::new();
|
||||
let (_, exchange_key) =
|
||||
match enc::asym::KeyExchangeKind::X25519DiffieHellman
|
||||
.new_keypair(&rand)
|
||||
{
|
||||
Ok(pair) => pair,
|
||||
Err(_) => {
|
||||
assert!(false, "Can't generate random keypair");
|
||||
return;
|
||||
}
|
||||
};
|
||||
use crate::enc;
|
||||
let record = Record {
|
||||
public_keys : [(enc::asym::KeyID(42),
|
||||
enc::asym::PubKey::Exchange(exchange_key))].to_vec(),
|
||||
addresses: [record::Address {
|
||||
ip: ::std::net::IpAddr::V4(::std::net::Ipv4Addr::new(127,0,0,1)),
|
||||
port: Some(::core::num::NonZeroU16::new(31337).unwrap()),
|
||||
priority: record::AddressPriority::P1,
|
||||
weight: record::AddressWeight::W1,
|
||||
handshake_ids: [crate::connection::handshake::HandshakeID::DirectorySynchronized].to_vec(),
|
||||
public_key_idx : [record::PubKeyIdx(0)].to_vec(),
|
||||
|
||||
let record = match Dnssec::parse_txt_record(TXT_RECORD) {
|
||||
}].to_vec(),
|
||||
key_exchanges: [enc::asym::KeyExchangeKind::X25519DiffieHellman].to_vec(),
|
||||
hkdfs: [enc::hkdf::HkdfKind::Sha3].to_vec(),
|
||||
ciphers: [enc::sym::CipherKind::XChaCha20Poly1305].to_vec(),
|
||||
|
||||
};
|
||||
let encoded = match record.encode() {
|
||||
Ok(encoded) => encoded,
|
||||
Err(e) => {
|
||||
assert!(false, "{}", e.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
let full_record = "v=Fenrir1 ".to_string() + &encoded;
|
||||
let record = match Dnssec::parse_txt_record(&full_record) {
|
||||
Ok(record) => record,
|
||||
Err(e) => {
|
||||
assert!(false, "{}", e.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
let re_encoded = match record.encode() {
|
||||
let _re_encoded = match record.encode() {
|
||||
Ok(re_encoded) => re_encoded,
|
||||
Err(e) => {
|
||||
assert!(false, "{}", e.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
assert!(
|
||||
TXT_RECORD[10..] == re_encoded,
|
||||
"DNSSEC record decoding->encoding failed:\n{}\n{}",
|
||||
TXT_RECORD,
|
||||
re_encoded
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -429,7 +429,7 @@ impl Record {
|
|||
+ self
|
||||
.public_keys
|
||||
.iter()
|
||||
.map(|(_, key)| 4 + key.kind().pub_len())
|
||||
.map(|(_, key)| 3 + key.kind().pub_len())
|
||||
.sum::<usize>()
|
||||
+ self.key_exchanges.len()
|
||||
+ self.hkdfs.len()
|
||||
|
@ -463,7 +463,7 @@ impl Record {
|
|||
let written_next = written + KeyID::len();
|
||||
raw[written..written_next].copy_from_slice(&key_id_bytes);
|
||||
written = written_next;
|
||||
raw[written] = public_key.kind().pub_len() as u8;
|
||||
raw[written] = public_key.len() as u8;
|
||||
written = written + 1;
|
||||
let written_next = written + public_key.len();
|
||||
public_key.serialize_into(&mut raw[written..written_next]);
|
||||
|
@ -531,10 +531,10 @@ impl Record {
|
|||
let raw_key_id =
|
||||
u16::from_le_bytes([raw[bytes_parsed], raw[bytes_parsed + 1]]);
|
||||
let id = KeyID(raw_key_id);
|
||||
bytes_parsed = bytes_parsed + 2;
|
||||
bytes_parsed = bytes_parsed + KeyID::len();
|
||||
let pubkey_length = raw[bytes_parsed] as usize;
|
||||
bytes_parsed = bytes_parsed + 1;
|
||||
let bytes_next_key = bytes_parsed + 1 + pubkey_length;
|
||||
let bytes_next_key = bytes_parsed + pubkey_length;
|
||||
if bytes_next_key > raw.len() {
|
||||
return Err(Error::NotEnoughData(bytes_parsed));
|
||||
}
|
||||
|
@ -551,7 +551,7 @@ impl Record {
|
|||
return Err(Error::UnsupportedData(bytes_parsed));
|
||||
}
|
||||
};
|
||||
if bytes != 1 + pubkey_length {
|
||||
if bytes != pubkey_length {
|
||||
return Err(Error::UnsupportedData(bytes_parsed));
|
||||
}
|
||||
bytes_parsed = bytes_parsed + bytes;
|
||||
|
|
|
@ -93,12 +93,15 @@ pub enum KeyKind {
|
|||
#[strum(serialize = "x25519")]
|
||||
X25519,
|
||||
}
|
||||
// FIXME: actually check this
|
||||
const MIN_KEY_SIZE: usize = 32;
|
||||
impl KeyKind {
|
||||
/// Length of the serialized field
|
||||
pub const fn len() -> usize {
|
||||
1
|
||||
}
|
||||
/// return the expected length of the public key
|
||||
pub fn pub_len(&self) -> usize {
|
||||
match self {
|
||||
KeyKind::len()
|
||||
+ match self {
|
||||
// FIXME: 99% wrong size
|
||||
KeyKind::Ed25519 => ::ring::signature::ED25519_PUBLIC_KEY_LEN,
|
||||
KeyKind::X25519 => 32,
|
||||
|
@ -185,7 +188,7 @@ pub enum PubKey {
|
|||
impl PubKey {
|
||||
/// Get the serialized key length
|
||||
pub fn len(&self) -> usize {
|
||||
1 + match self {
|
||||
match self {
|
||||
PubKey::Exchange(ex) => ex.len(),
|
||||
PubKey::Signing => todo!(),
|
||||
}
|
||||
|
@ -215,17 +218,12 @@ impl PubKey {
|
|||
/// serialize the key into the buffer
|
||||
/// NOTE: Assumes there is enough space
|
||||
pub fn serialize_into(&self, out: &mut [u8]) {
|
||||
assert!(
|
||||
out.len() >= 1 + self.kind().pub_len(),
|
||||
"Not enough out buffer",
|
||||
);
|
||||
out[0] = self.kind() as u8;
|
||||
match self {
|
||||
PubKey::Signing => {
|
||||
::tracing::error!("serializing ed25519 not supported");
|
||||
return;
|
||||
}
|
||||
PubKey::Exchange(ex) => ex.serialize_into(&mut out[1..]),
|
||||
PubKey::Exchange(ex) => ex.serialize_into(out),
|
||||
}
|
||||
}
|
||||
/// Try to deserialize the pubkey from raw bytes
|
||||
|
@ -238,7 +236,7 @@ impl PubKey {
|
|||
Some(kind) => kind,
|
||||
None => return Err(Error::UnsupportedKey(1)),
|
||||
};
|
||||
if raw.len() < 1 + kind.pub_len() {
|
||||
if raw.len() < kind.pub_len() {
|
||||
return Err(Error::NotEnoughData(1));
|
||||
}
|
||||
match kind {
|
||||
|
@ -259,7 +257,7 @@ impl PubKey {
|
|||
};
|
||||
Ok((
|
||||
PubKey::Exchange(ExchangePubKey::X25519(pub_key)),
|
||||
1 + kind.pub_len(),
|
||||
kind.pub_len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +279,7 @@ pub enum PrivKey {
|
|||
impl PrivKey {
|
||||
/// Get the serialized key length
|
||||
pub fn len(&self) -> usize {
|
||||
1 + match self {
|
||||
match self {
|
||||
PrivKey::Exchange(ex) => ex.len(),
|
||||
PrivKey::Signing => todo!(),
|
||||
}
|
||||
|
@ -296,9 +294,8 @@ impl PrivKey {
|
|||
/// serialize the key into the buffer
|
||||
/// NOTE: Assumes there is enough space
|
||||
pub fn serialize_into(&self, out: &mut [u8]) {
|
||||
out[0] = self.kind() as u8;
|
||||
match self {
|
||||
PrivKey::Exchange(ex) => ex.serialize_into(&mut out[1..]),
|
||||
PrivKey::Exchange(ex) => ex.serialize_into(out),
|
||||
PrivKey::Signing => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -346,9 +343,10 @@ impl ExchangePrivKey {
|
|||
/// serialize the key into the buffer
|
||||
/// NOTE: Assumes there is enough space
|
||||
pub fn serialize_into(&self, out: &mut [u8]) {
|
||||
out[0] = self.kind() as u8;
|
||||
match self {
|
||||
ExchangePrivKey::X25519(key) => {
|
||||
out[0..32].copy_from_slice(&key.to_bytes());
|
||||
out[1..33].copy_from_slice(&key.to_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,21 +376,18 @@ impl ExchangePubKey {
|
|||
/// serialize the key into the buffer
|
||||
/// NOTE: Assumes there is enough space
|
||||
pub fn serialize_into(&self, out: &mut [u8]) {
|
||||
out[0] = self.kind() as u8;
|
||||
match self {
|
||||
ExchangePubKey::X25519(pk) => {
|
||||
let bytes = pk.as_bytes();
|
||||
assert!(bytes.len() == 32, "x25519 should have been 32 bytes");
|
||||
out[..32].copy_from_slice(bytes);
|
||||
out[1..33].copy_from_slice(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Load public key used for key exchange from it raw bytes
|
||||
/// The riesult is "unparsed" since we don't verify
|
||||
/// the actual key
|
||||
pub fn from_slice(raw: &[u8]) -> Result<(Self, usize), Error> {
|
||||
if raw.len() < 1 + MIN_KEY_SIZE {
|
||||
return Err(Error::NotEnoughData(0));
|
||||
}
|
||||
pub fn deserialize(raw: &[u8]) -> Result<(Self, usize), Error> {
|
||||
match KeyKind::from_u8(raw[0]) {
|
||||
Some(kind) => match kind {
|
||||
KeyKind::Ed25519 => {
|
||||
|
|
Loading…
Reference in New Issue