geiserx_ts_keys 0.7.2

tailscale cryptographic key types
Documentation
/// Generates a struct that implements all the fields/methods needed by both public and private
/// X25519 keys. Used by `create_x25519_{public_key, private_key, keypair}_type{s}` macros, not
/// intended to be used by itself.
macro_rules! _create_x25519_base_key_type {
    ($(#[$attr:meta])* $key_name:ident, $key_prefix:literal) => {
        $(#[$attr])*
        #[derive(Clone, Copy, Eq, PartialEq, ::zerocopy::FromBytes, ::zerocopy::Immutable, ::zerocopy::IntoBytes, ::zerocopy::KnownLayout)]
        pub struct $key_name(
            [u8; $key_name::KEY_LEN_BYTES]
        );

        impl $key_name {
            /// The length of this key type, in bytes.
            pub const KEY_LEN_BYTES: usize = 32;
            /// The length of a hexidecimal string representation of this key, excluding the
            /// prefix and colon.
            pub const KEY_LEN_HEX_STR: usize = $key_name::KEY_LEN_BYTES * 2;
            /// The length of a hexidecimal string representation of this key, including the
            /// prefix and colon.
            pub const KEY_LEN_FULL_STR: usize = $key_name::KEY_LEN_HEX_STR + $key_name::KEY_PREFIX.len() + 1;
            /// The prefix placed in front of string representations of this key type, such
            /// as "$key_prefix:abcd..."
            pub const KEY_PREFIX: &'static str = $key_prefix;

            /// Return this key as a `u8` byte array.
            pub fn to_bytes(&self) -> [u8; $key_name::KEY_LEN_BYTES] {
                self.0
            }
        }

        impl ::core::fmt::Display for $key_name {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                ::core::write!(f, "{}:", $key_name::KEY_PREFIX)?;
                for b in self.0.iter() {
                    ::core::write!(f, "{b:02x}")?;
                }
                Ok(())
            }
        }

        impl ::core::str::FromStr for $key_name {
            type Err = $crate::ParseError;

            fn from_str(s: &str) -> Result<Self, Self::Err> {
                if s.len() != $key_name::KEY_LEN_FULL_STR {
                    return Err($crate::ParseError::WrongLength);
                }

                let mut parts = s.split(':');
                let Some(prefix) = parts.next() else {
                    return Err($crate::ParseError::InvalidFormat);
                };
                if prefix != $key_name::KEY_PREFIX {
                    return Err($crate::ParseError::BadPrefix);
                }

                let Some(hex_str) = parts.next() else {
                    return Err($crate::ParseError::WrongLength);
                };
                if hex_str.len() != $key_name::KEY_LEN_HEX_STR {
                    return Err($crate::ParseError::WrongLength);
                }

                // s.split(':') should only return 2 parts: the prefix and the hex string. If
                // the string contained additional colons, it's malformed and not a valid key
                // string.
                if parts.next().is_some() {
                    return Err($crate::ParseError::InvalidFormat)
                }

                let mut key = $key_name([0u8; $key_name::KEY_LEN_BYTES]);
                for i in (0..$key_name::KEY_LEN_HEX_STR).step_by(2) {
                    let slice = hex_str.get(i..i + 2).unwrap();
                    let keyidx = i / 2;
                    let x = u8::from_str_radix(slice, 16).unwrap();
                    key.0[keyidx] = x;
                }
                Ok(key)
            }
        }

        impl From<[u8; $key_name::KEY_LEN_BYTES]> for $key_name {
            fn from(v: [u8; $key_name::KEY_LEN_BYTES]) -> Self {
                $key_name(v)
            }
        }

        impl From<$key_name> for [u8; $key_name::KEY_LEN_BYTES] {
            fn from(v: $key_name) -> [u8; $key_name::KEY_LEN_BYTES] {
                v.0
            }
        }

        #[cfg(feature = "serde")]
        impl<'de> ::serde::Deserialize<'de> for $key_name {
            fn deserialize<D>(deserializer: D) -> ::core::result::Result<$key_name, D::Error> where D: ::serde::Deserializer<'de> {
                use ::core::str::FromStr;

                struct KeyVisitor;

                impl<'a> ::serde::de::Visitor<'a> for KeyVisitor {
                    type Value = $key_name;

                    fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
                        ::core::write!(
                            formatter,
                            "a {}-character string with the prefix '{}:' followed by {} hex characters",
                            $key_name::KEY_LEN_FULL_STR, $key_name::KEY_PREFIX, $key_name::KEY_LEN_HEX_STR
                        )
                    }

                    fn visit_str<E>(self, value: &str) -> ::core::result::Result<Self::Value, E> where E: ::serde::de::Error {
                        $key_name::from_str(value).map_err(|e| ::serde::de::Error::custom(e))
                    }
                }

                deserializer.deserialize_str(KeyVisitor)
            }
        }

        #[cfg(feature = "serde")]
        impl ::serde::Serialize for $key_name {
            fn serialize<S>(&self, serializer: S) -> ::core::result::Result<S::Ok, S::Error> where S: ::serde::Serializer {
                serializer.serialize_str(&::alloc::format!("{self}"))
            }
        }
    }
}

/// Generates a struct that implements all the fields/methods needed by X25519 public keys.
macro_rules! create_x25519_public_key_type {
    ($(#[$attr:meta])* $public_name:ident, $key_prefix:literal) => {
        _create_x25519_base_key_type!($(#[$attr])* #[derive(Default, Hash, PartialOrd, Ord)] $public_name, $key_prefix);

        // Public keys are not secret: `Debug` prints the full `prefix:hex` form (== `Display`).
        impl ::core::fmt::Debug for $public_name {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                ::core::write!(f, "{self}")
            }
        }

        impl From<$public_name> for ::x25519_dalek::PublicKey {
            fn from(v: $public_name) -> Self {
                v.0.into()
            }
        }

        impl From<$public_name> for ::crypto_box::PublicKey {
            fn from(v: $public_name) -> Self {
                v.0.into()
            }
        }

        impl From<&$public_name> for ::x25519_dalek::PublicKey {
            fn from(v: &$public_name) -> Self {
                v.0.into()
            }
        }

        impl From<&$public_name> for ::crypto_box::PublicKey {
            fn from(v: &$public_name) -> Self {
                v.0.into()
            }
        }
    }
}

/// Generates a struct that implements all the fields/methods needed by X25519 private keys.
macro_rules! create_x25519_private_key_type {
    ($(#[$attr:meta])* $private_name:ident, $public_name:ident, $key_prefix:literal) => {
        _create_x25519_base_key_type!($(#[$attr])* $private_name, $key_prefix);

        // SECURITY: private keys must NEVER print their bytes. `Debug` is redacted so a stray
        // `{:?}`/`tracing` of a key (or any struct containing one) cannot leak the secret to logs.
        // The raw bytes remain reachable only via the explicit `to_bytes()`/`Display` paths.
        impl ::core::fmt::Debug for $private_name {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                ::core::write!(f, "{}(<redacted>)", ::core::stringify!($private_name))
            }
        }

        impl $private_name {
            /// Generate a new X25519 private key.
            pub fn random() -> Self {
                $private_name(::x25519_dalek::StaticSecret::random().to_bytes())
            }

            /// Calculate the corresponding public key for this private key.
            pub fn public_key(self) -> $public_name {
                ::crypto_box::SecretKey::from(self).public_key().to_bytes().into()
            }
        }

        impl From<$private_name> for ::x25519_dalek::StaticSecret {
            fn from(v: $private_name) -> Self {
                v.0.into()
            }
        }

        impl From<&$private_name> for ::x25519_dalek::StaticSecret {
            fn from(v: &$private_name) -> Self {
                v.0.into()
            }
        }

        impl From<$private_name> for ::crypto_box::SecretKey {
            fn from(v: $private_name) -> Self {
                v.0.into()
            }
        }

        impl From<&$private_name> for ::crypto_box::SecretKey {
            fn from(v: &$private_name) -> Self {
                v.0.into()
            }
        }
    }
}

/// Generates the public key, private key, and key pair structs with all the fields/methods needed
/// to work with X25519 keys.
macro_rules! create_x25519_keypair_types {
    ($(#[$public_attr:meta])* $public_name:ident, $public_prefix:literal, $(#[$private_attr:meta])* $private_name:ident, $private_prefix:literal, $(#[$pair_attr:meta])* $keypair_name:ident) => {
        create_x25519_public_key_type! { $(#[$public_attr])* $public_name, $public_prefix }
        create_x25519_private_key_type! { $(#[$private_attr])* $private_name, $public_name, $private_prefix }

        impl From<$private_name> for $public_name {
            fn from(v: $private_name) -> Self {
                let private = ::x25519_dalek::StaticSecret::from(v.0);
                let public = ::x25519_dalek::PublicKey::from(&private);
                $public_name(public.to_bytes())
            }
        }

        $(#[$pair_attr])*
        #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
        #[derive(Clone, Copy, Debug, Eq, PartialEq, ::zerocopy::FromBytes, ::zerocopy::Immutable, ::zerocopy::IntoBytes, ::zerocopy::KnownLayout)]
        pub struct $keypair_name {
            /// This keypair's public key.
            pub public: $public_name,
            /// This keypair's private key.
            pub private: $private_name,
        }

        impl $keypair_name {
            /// Generate a new X25519 public/private key pair.
            pub fn new() -> Self {
                let private = $private_name::random();
                Self {
                    private,
                    public: private.into(),
                }
            }
        }

        impl Default for $keypair_name {
            fn default() -> Self {
                Self::new()
            }
        }

        impl From<$private_name> for $keypair_name {
            fn from(private: $private_name) -> Self {
                Self {
                    private,
                    public: private.into(),
                }
            }
        }
    }
}

/// Generates a struct that implements all the fields/methods needed by Ed25519 (RFC 8032) public
/// keys. Reuses the crypto-agnostic [`_create_x25519_base_key_type`] for the byte/Display/FromStr/
/// serde/zerocopy surface. No fallible `From<_> for VerifyingKey` is provided because not every
/// 32-byte string is a valid Ed25519 point and no downstream caller needs a dalek conversion on the
/// public key (callers only use Display/serde/`.public`).
macro_rules! create_ed25519_public_key_type {
    ($(#[$attr:meta])* $public_name:ident, $key_prefix:literal) => {
        _create_x25519_base_key_type!($(#[$attr])* #[derive(Default, Hash, PartialOrd, Ord)] $public_name, $key_prefix);

        // Public keys are not secret: `Debug` prints the full `prefix:hex` form (== `Display`).
        impl ::core::fmt::Debug for $public_name {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                ::core::write!(f, "{self}")
            }
        }
    }
}

/// Generates a struct that implements all the fields/methods needed by Ed25519 (RFC 8032) private
/// keys. The wrapped 32 bytes are the Ed25519 *seed* (matching Go's `ed25519.PrivateKey` seed
/// semantics and `key.NLPrivate`).
macro_rules! create_ed25519_private_key_type {
    ($(#[$attr:meta])* $private_name:ident, $public_name:ident, $key_prefix:literal) => {
        _create_x25519_base_key_type!($(#[$attr])* $private_name, $key_prefix);

        // SECURITY: redacted `Debug` (the wrapped bytes are an Ed25519 seed). See the X25519
        // private-key macro for rationale — never leak secret key material via `{:?}`/logs.
        impl ::core::fmt::Debug for $private_name {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                ::core::write!(f, "{}(<redacted>)", ::core::stringify!($private_name))
            }
        }

        impl $private_name {
            /// Generate a new Ed25519 private key.
            ///
            /// Sources 32 uniformly-random bytes for the Ed25519 seed directly from `getrandom`.
            /// We deliberately do NOT reuse `x25519_dalek::StaticSecret`, whose bytes are bit
            /// clamped (low bits zeroed, bit 254 set); clamping reduces seed entropy and is wrong
            /// for an Ed25519 seed.
            pub fn random() -> Self {
                let mut seed = [0u8; $private_name::KEY_LEN_BYTES];
                ::getrandom::fill(&mut seed).expect("getrandom failed");
                $private_name(seed)
            }

            /// Calculate the corresponding public key for this private key.
            ///
            /// This is the standard RFC 8032 seed->public derivation, matching Go's
            /// `ed25519.PrivateKey.Public()`.
            pub fn public_key(self) -> $public_name {
                self.signing_key().verifying_key().to_bytes().into()
            }

            /// Return this key as an `ed25519_dalek::SigningKey`, treating the wrapped bytes as the
            /// Ed25519 seed (RFC 8032).
            pub fn signing_key(&self) -> ::ed25519_dalek::SigningKey {
                ::ed25519_dalek::SigningKey::from_bytes(&self.0)
            }
        }
    }
}

/// Generates the public key, private key, and key pair structs with all the fields/methods needed
/// to work with Ed25519 (RFC 8032) keys. Mirrors [`create_x25519_keypair_types`] but derives the
/// public key via the Ed25519 seed->public derivation instead of X25519 scalar multiplication.
macro_rules! create_ed25519_keypair_types {
    ($(#[$public_attr:meta])* $public_name:ident, $public_prefix:literal, $(#[$private_attr:meta])* $private_name:ident, $private_prefix:literal, $(#[$pair_attr:meta])* $keypair_name:ident) => {
        create_ed25519_public_key_type! { $(#[$public_attr])* $public_name, $public_prefix }
        create_ed25519_private_key_type! { $(#[$private_attr])* $private_name, $public_name, $private_prefix }

        impl From<$private_name> for $public_name {
            fn from(v: $private_name) -> Self {
                let public = ::ed25519_dalek::SigningKey::from_bytes(&v.0)
                    .verifying_key()
                    .to_bytes();
                $public_name(public)
            }
        }

        $(#[$pair_attr])*
        #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
        #[derive(Clone, Copy, Debug, Eq, PartialEq, ::zerocopy::FromBytes, ::zerocopy::Immutable, ::zerocopy::IntoBytes, ::zerocopy::KnownLayout)]
        pub struct $keypair_name {
            /// This keypair's public key.
            pub public: $public_name,
            /// This keypair's private key.
            pub private: $private_name,
        }

        impl $keypair_name {
            /// Generate a new Ed25519 public/private key pair.
            pub fn new() -> Self {
                let private = $private_name::random();
                Self {
                    private,
                    public: private.into(),
                }
            }
        }

        impl Default for $keypair_name {
            fn default() -> Self {
                Self::new()
            }
        }

        impl From<$private_name> for $keypair_name {
            fn from(private: $private_name) -> Self {
                Self {
                    private,
                    public: private.into(),
                }
            }
        }
    }
}

pub(crate) use _create_x25519_base_key_type;
pub(crate) use create_ed25519_keypair_types;
pub(crate) use create_ed25519_private_key_type;
pub(crate) use create_ed25519_public_key_type;
pub(crate) use create_x25519_keypair_types;
pub(crate) use create_x25519_private_key_type;
pub(crate) use create_x25519_public_key_type;