implemented serialization

This commit is contained in:
Monica Moniot
2024-02-28 13:12:46 -05:00
parent 69435b9569
commit b1930f7665
5 changed files with 237 additions and 23 deletions
+16 -7
View File
@@ -28,6 +28,7 @@ impl Address {
/// force, but untargeted birthday type collisions are possible with sufficient storage.)
pub const REQUIRED_PREFIX: u8 = 0xfc;
pub const SIZE: usize = 48;
/// Length of a full address in string format.
pub const STRING_SIZE: usize = 76;
@@ -57,6 +58,18 @@ impl Address {
pub(crate) fn is_valid(&self) -> bool {
self.as_bytes()[0] == Self::REQUIRED_PREFIX
}
pub fn write_to_string(&self, s: &mut String, prefix: bool) {
if prefix {
s.push_str(PREFIX_ADDRESS);
}
first_128_to_string(&self.as_bytes()[..16], s);
s.push('.');
base62::encode_8to11(u64::from_be(self.0[2]), s);
base62::encode_8to11(u64::from_be(self.0[3]), s);
base62::encode_8to11(u64::from_be(self.0[4]), s);
base62::encode_8to11(u64::from_be(self.0[5]), s);
}
}
impl TryFrom<[u8; 48]> for Address {
@@ -101,12 +114,7 @@ impl ToFromBytes for Address {
impl ToString for Address {
fn to_string(&self) -> String {
let mut s = String::with_capacity(Self::STRING_SIZE);
first_128_to_string(&self.as_bytes()[..16], &mut s);
s.push('.');
base62::encode_8to11(u64::from_be(self.0[2]), &mut s);
base62::encode_8to11(u64::from_be(self.0[3]), &mut s);
base62::encode_8to11(u64::from_be(self.0[4]), &mut s);
base62::encode_8to11(u64::from_be(self.0[5]), &mut s);
self.write_to_string(&mut s, true);
s
}
}
@@ -123,8 +131,9 @@ impl FromStr for Address {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let s = s.strip_prefix(PREFIX_ADDRESS).unwrap_or(s);
let sb = s.as_bytes();
if s.len() == sb.len() && sb.len() == Self::STRING_SIZE && sb[31] == b'.' {
if sb.len() == Self::STRING_SIZE && sb[31] == b'.' {
let prefix = ShortAddress::from_str(&s[..31])?;
Ok(Address([
prefix.0[0],
+173 -3
View File
@@ -59,17 +59,25 @@ impl<'a> PeerIdentifierRef<'a> {
/// Otherwise returns `false`.
/// This function will debug panic if `self` and `other` are not identifiers for the same peer.
/// The caller must check for equality before calling this function.
/// TODO: make this function take into account identity lifetime.
pub fn upgrade_check(self, other: Self) -> bool {
pub fn upgrade_check(&self, other: &Self) -> bool {
debug_assert_eq!(self, other);
use PeerIdentifierRef::*;
match (self, other) {
(Identity(id1), Identity(id2)) => id1.timestamp < id2.timestamp,
(Identity(_), _) => false,
(_, Identity(_)) => true,
(Short(_), Address(_)) => true,
_ => false,
}
}
pub fn write_to_string(&self, s: &mut String, prefix: bool) {
match self {
Self::Identity(a) => a.write_to_string(s, prefix),
Self::Address(a) => a.write_to_string(s, prefix),
Self::Short(a) => a.write_to_string(s, prefix),
}
}
}
impl PeerIdentifier {
@@ -100,7 +108,15 @@ impl PeerIdentifier {
/// TODO: make this function take into account identity lifetime.
pub fn upgrade_check(&self, other: &Self) -> bool {
let r: PeerIdentifierRef = self.into();
r.upgrade_check(other.into())
r.upgrade_check(&other.into())
}
pub fn write_to_string(&self, s: &mut String, prefix: bool) {
match self {
Self::Identity(a) => a.write_to_string(s, prefix),
Self::Address(a) => a.write_to_string(s, prefix),
Self::Short(a) => a.write_to_string(s, prefix),
}
}
}
@@ -132,6 +148,13 @@ impl AnyAddress {
_ => false,
}
}
pub fn write_to_string(&self, s: &mut String, prefix: bool) {
match self {
Self::Address(a) => a.write_to_string(s, prefix),
Self::Short(a) => a.write_to_string(s, prefix),
}
}
}
/// Does not preserve transitivity.
@@ -167,6 +190,153 @@ impl PartialEq for AnyAddress {
}
}
impl<'a> ToString for PeerIdentifierRef<'a> {
fn to_string(&self) -> String {
match self {
Self::Identity(id) => id.to_string(),
Self::Address(addr) => addr.to_string(),
Self::Short(addr) => addr.to_string(),
}
}
}
impl ToString for PeerIdentifier {
fn to_string(&self) -> String {
match self {
Self::Identity(id) => id.to_string(),
Self::Address(addr) => addr.to_string(),
Self::Short(addr) => addr.to_string(),
}
}
}
impl ToString for AnyAddress {
fn to_string(&self) -> String {
match self {
Self::Address(addr) => addr.to_string(),
Self::Short(addr) => addr.to_string(),
}
}
}
impl FromStr for PeerIdentifier {
type Err = InvalidParameterError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let l = s.len();
if l >= Identity::STRING_SIZE {
Identity::from_str(s).map(Self::Identity)
} else if l >= Address::STRING_SIZE {
Address::from_str(s).map(Self::Address)
} else {
ShortAddress::from_str(s).map(Self::Short)
}
}
}
impl FromStr for AnyAddress {
type Err = InvalidParameterError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let l = s.len();
if l >= Address::STRING_SIZE {
Address::from_str(s).map(Self::Address)
} else {
ShortAddress::from_str(s).map(Self::Short)
}
}
}
impl<'a> serde::Serialize for PeerIdentifierRef<'a> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self {
Self::Identity(id) => id.serialize(s),
Self::Address(addr) => addr.serialize(s),
Self::Short(addr) => addr.serialize(s),
}
}
}
impl serde::Serialize for PeerIdentifier {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self {
Self::Identity(id) => id.serialize(s),
Self::Address(addr) => addr.serialize(s),
Self::Short(addr) => addr.serialize(s),
}
}
}
impl serde::Serialize for AnyAddress {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self {
Self::Address(addr) => addr.serialize(s),
Self::Short(addr) => addr.serialize(s),
}
}
}
impl<'de> Deserialize<'de> for PeerIdentifier {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
Self::from_str(<&str>::deserialize(deserializer)?).map_err(|_| serde::de::Error::custom(ADDRESS_ERR.0))
} else {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = PeerIdentifier;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a zerotier identifier")
}
fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E>
{
match v.len() {
Identity::SIZE => Identity::from_bytes(v).map(Self::Value::Identity),
Address::SIZE => Address::from_bytes(v).map(Self::Value::Address),
ShortAddress::SIZE => ShortAddress::from_bytes(v).map(Self::Value::Short),
_ => return Err(serde::de::Error::custom(ADDRESS_ERR.0))
}
.map_err(|_| serde::de::Error::custom(ADDRESS_ERR.0))
}
}
deserializer.deserialize_bytes(Visitor)
}
}
}
impl<'de> Deserialize<'de> for AnyAddress {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
Self::from_str(<&str>::deserialize(deserializer)?).map_err(|_| serde::de::Error::custom(ADDRESS_ERR.0))
} else {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = AnyAddress;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a zerotier identifier")
}
fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E>
{
match v.len() {
Address::SIZE => Address::from_bytes(v).map(Self::Value::Address),
ShortAddress::SIZE => ShortAddress::from_bytes(v).map(Self::Value::Short),
_ => return Err(serde::de::Error::custom(ADDRESS_ERR.0))
}
.map_err(|_| serde::de::Error::custom(ADDRESS_ERR.0))
}
}
deserializer.deserialize_bytes(Visitor)
}
}
}
/* Start of Conversions */
impl<'a> From<&'a PeerIdentifier> for PeerIdentifierRef<'a> {
+26 -10
View File
@@ -7,7 +7,8 @@
*/
use crate::p384::*;
const TIMESTAMP_START: usize = P384_PUBLIC_KEY_SIZE;
const MASTER_KEY_START: usize = 1;
const TIMESTAMP_START: usize = MASTER_KEY_START + P384_PUBLIC_KEY_SIZE;
const SUBKEY_ECDH_START: usize = TIMESTAMP_START + 8;
const SUBKEY_ECDSA_START: usize = SUBKEY_ECDH_START + P384_PUBLIC_KEY_SIZE;
const MASTER_SIG_START: usize = SUBKEY_ECDSA_START + P384_PUBLIC_KEY_SIZE;
@@ -27,6 +28,11 @@ pub struct Identity {
}
impl Identity {
pub const SIZE: usize = P384_IDENTITY_SIZE;
pub const STRING_SIZE: usize = 547;
pub const STRING_SIZE_NO_PREFIX: usize = 542;
pub(crate) fn locally_validate(&self) -> bool {
let to_sign: &[&[u8]] = &[
self.master_signing_key.as_bytes(),
@@ -48,15 +54,22 @@ impl Identity {
pub fn replaces(&self, other: &Identity) -> bool {
self.address == other.address && self.timestamp > other.timestamp
}
pub fn write_to_string(&self, s: &mut String, prefix: bool) {
if prefix {
s.push_str(PREFIX_IDENTITY);
}
self.address.write_to_string(s, false);
s.push_str(":1:");
s.push_str(base64::to_string(self.to_bytes_on_stack::<1024>().as_bytes()).as_str());
}
}
impl ToString for Identity {
fn to_string(&self) -> String {
let mut tmp = String::with_capacity(1024);
tmp.push_str(self.address.to_string().as_str());
tmp.push_str(":1:");
tmp.push_str(base64::to_string(self.to_bytes_on_stack::<1024>().as_bytes()).as_str());
tmp
let mut s = String::with_capacity(Self::STRING_SIZE);
self.write_to_string(&mut s, true);
s
}
}
@@ -78,6 +91,8 @@ impl FromStr for Identity {
type Err = InvalidParameterError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let s = s.strip_prefix(PREFIX_IDENTITY).unwrap_or(s);
if let Some(div_idx) = s.rfind(':') {
if div_idx > 0 && div_idx < s.len() {
if let Some(bytes) = base64::from_string(s[div_idx + 1..].as_bytes()) {
@@ -85,7 +100,7 @@ impl FromStr for Identity {
}
}
}
return Err(IDENTITY_ERR);
Err(IDENTITY_ERR)
}
}
@@ -94,7 +109,7 @@ impl ToFromBytes for Identity {
let mut tmp = [0u8; P384_IDENTITY_SIZE];
r.read_exact(&mut tmp)?;
if let (Some(master_signing_key), Some(ecdh), Some(ecdsa)) = (
P384PublicKey::from_bytes(&tmp[..TIMESTAMP_START]),
P384PublicKey::from_bytes(&tmp[MASTER_KEY_START..TIMESTAMP_START]),
P384PublicKey::from_bytes(&tmp[SUBKEY_ECDH_START..SUBKEY_ECDSA_START]),
P384PublicKey::from_bytes(&tmp[SUBKEY_ECDSA_START..MASTER_SIG_START]),
) {
@@ -111,13 +126,14 @@ impl ToFromBytes for Identity {
return Ok(id);
}
}
return Err(std::io::Error::new(std::io::ErrorKind::Other, IDENTITY_ERR.0));
Err(std::io::Error::new(std::io::ErrorKind::Other, IDENTITY_ERR.0))
}
/// This function cannot rollback changes to `w` if an error occurs.
fn write_bytes<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
// The address is SHA384(master_signing_key) so we do not need to output it. We will want
// to recalculate it to check it anyway.
w.write_all(&[IDENTITY_VARIANT_P384])?;
w.write_all(self.master_signing_key.as_bytes())?;
w.write_all(&self.timestamp.to_be_bytes())?;
w.write_all(self.ecdh.as_bytes())?;
@@ -156,7 +172,7 @@ impl<'de> Deserialize<'de> for Identity {
type Value = Identity;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a pair of ratchet states")
formatter.write_str("a zerotier identifier")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+11 -2
View File
@@ -32,8 +32,17 @@ pub use identity::*;
pub use identity_secret::*;
pub use short_address::*;
const DOMAIN_MASTER_SIG: &[u8] = b"ZTID_MASTERSIG_P384";
const DOMAIN_SUBKEY_SIG: &[u8] = b"ZTID_SUBKEYSIG_P384";
/// We won't have more than one identity type for a while, but putting a variant number will allow
/// us to treat an identity as a discriminated union when we add more.
/// For security purposes this must be a statically assigned unique number.
const IDENTITY_VARIANT_P384: u8 = 1;
const DOMAIN_MASTER_SIG: &[u8] = b"ZTID1_MASTERSIG";
const DOMAIN_SUBKEY_SIG: &[u8] = b"ZTID1_SUBKEYSIG";
const PREFIX_IDENTITY: &str = "zt:i:";
const PREFIX_ADDRESS: &str = "zt:a:";
const PREFIX_SHORT: &str = "zt:s:";
fn first_128_to_string(b: &[u8], s: &mut String) {
base24::encode_4to7(&b[0..4], s);
+11 -1
View File
@@ -18,6 +18,7 @@ pub struct ShortAddress(pub(crate) [u64; 2]); // treated as [u8; 16]
impl ShortAddress {
pub const SIZE: usize = 16;
pub const STRING_SIZE: usize = 31;
#[inline(always)]
pub fn as_bytes(&self) -> &[u8; Self::SIZE] {
@@ -35,6 +36,13 @@ impl ShortAddress {
fn is_valid(&self) -> bool {
self.as_bytes()[0] == Address::REQUIRED_PREFIX
}
pub fn write_to_string(&self, s: &mut String, prefix: bool) {
if prefix {
s.push_str(PREFIX_SHORT);
}
first_128_to_string(self.as_bytes(), s);
}
}
impl TryFrom<[u8; 16]> for ShortAddress {
@@ -79,6 +87,7 @@ impl ToFromBytes for ShortAddress {
impl ToString for ShortAddress {
fn to_string(&self) -> String {
let mut s = String::with_capacity(32);
s.push_str(PREFIX_SHORT);
first_128_to_string(self.as_bytes(), &mut s);
s
}
@@ -96,7 +105,8 @@ impl FromStr for ShortAddress {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.len() == 31 {
let s = s.strip_prefix(PREFIX_SHORT).unwrap_or(s);
if s.len() == Self::STRING_SIZE {
let mut tmp = [0u8; 16];
let mut w = &mut tmp[..];
for ss in s.split('.') {