geiserx_ts_keys 0.34.2

tailscale cryptographic key types
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
/// 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) => {
        // NOTE: this base macro derives ONLY the traits common to both public and private keys
        // (`Clone`, `Eq`, `PartialEq`). Everything that DIFFERS between them is supplied by the
        // caller via `$attr`: public keys add `Copy` + the `zerocopy` wire traits (they are not
        // secret), while private keys add `Zeroize` + `ZeroizeOnDrop` and deliberately drop `Copy`.
        // `Copy` and `Drop` are mutually exclusive in Rust (E0184) and `ZeroizeOnDrop` *is* a `Drop`
        // impl, so a private key cannot be both freely bit-copied and wiped on drop — we choose
        // wipe-on-drop. `Clone` is fine alongside `ZeroizeOnDrop` (it is not `Drop`).
        $(#[$attr])*
        #[derive(Clone, Eq, PartialEq)]
        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) {
                    // The length check above guarantees the byte length, but a multi-byte UTF-8
                    // char makes `get(i..i+2)` return `None` (split boundary), and a non-hex digit
                    // makes `from_str_radix` error — both must surface as a parse error, never a
                    // panic. A malformed key in an (authenticated but possibly buggy) control
                    // response would otherwise unwind and kill the netmap decoder. Go's key parse
                    // returns an error here.
                    let slice = hex_str
                        .get(i..i + 2)
                        .ok_or($crate::ParseError::InvalidFormat)?;
                    let keyidx = i / 2;
                    let x = u8::from_str_radix(slice, 16)
                        .map_err(|_| $crate::ParseError::InvalidFormat)?;
                    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) => {
        // Public keys are not secret, so they keep `Copy` and the full `zerocopy` wire surface
        // (`FromBytes`/`IntoBytes`/`Immutable`/`KnownLayout`) for cheap, allocation-free wire
        // (de)serialization — unchanged from before the private/public derive split.
        _create_x25519_base_key_type!(
            $(#[$attr])*
            #[derive(Copy, Default, Hash, PartialOrd, Ord, ::zerocopy::FromBytes, ::zerocopy::Immutable, ::zerocopy::IntoBytes, ::zerocopy::KnownLayout)]
            $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) => {
        // SECURITY: private keys are `Copy`-free and zeroize their 32-byte secret on drop.
        // Dropping `Copy` stops the secret being silently bit-copied to scattered stack/heap
        // locations the zeroizer can never reach; `ZeroizeOnDrop` wipes the in-place `[u8; 32]`
        // buffer when the last owner drops. They are intentionally NOT `zerocopy` wire types — a
        // private key is never (de)serialized straight off the wire (only public keys are).
        _create_x25519_base_key_type!(
            $(#[$attr])*
            #[derive(::zeroize::Zeroize, ::zeroize::ZeroizeOnDrop)]
            $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.
            ///
            /// Takes `&self` (not `self`): deriving the public key must not consume — and thus
            /// drop/zeroize — the private key now that it is not `Copy`. Mirrors Go's
            /// `key.NodePrivate.Public()` pointer receiver.
            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 }

        // Derive the public key from a BORROW of the private key. The owned `From<$private_name>`
        // below delegates here. Taking `&self` matters now that the private key is not `Copy`:
        // callers (and the keypair constructors) can derive the public key without consuming —
        // and thus dropping/zeroizing — the private key (mirrors Go's `key.NodePrivate.Public()`
        // pointer receiver). Reading the `[u8; 32]` field through the reference is a copy of those
        // bytes (the array is `Copy`), not a move out of the secret.
        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())
            }
        }

        impl From<$private_name> for $public_name {
            fn from(v: $private_name) -> Self {
                $public_name::from(&v)
            }
        }

        // The keypair holds a secret (the private key), so it is deliberately NOT `Copy` and NOT a
        // `zerocopy` wire type. It needs no explicit `ZeroizeOnDrop`: when the keypair drops, its
        // `private` field — a `ZeroizeOnDrop` type — wipes itself by drop-glue composition. `Debug`
        // is safe to derive because the private field's own `Debug` is redacted.
        $(#[$pair_attr])*
        #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
        #[derive(Clone, Debug, Eq, PartialEq)]
        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();
                let public = $public_name::from(&private);
                Self { private, public }
            }
        }

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

        impl From<$private_name> for $keypair_name {
            fn from(private: $private_name) -> Self {
                let public = $public_name::from(&private);
                Self { private, public }
            }
        }
    }
}

/// 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) => {
        // Public keys are not secret: keep `Copy` + the full `zerocopy` wire surface (same as the
        // X25519 public key). Only the PRIVATE key types drop `Copy`/zerocopy and gain zeroize.
        _create_x25519_base_key_type!(
            $(#[$attr])*
            #[derive(Copy, Default, Hash, PartialOrd, Ord, ::zerocopy::FromBytes, ::zerocopy::Immutable, ::zerocopy::IntoBytes, ::zerocopy::KnownLayout)]
            $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) => {
        // SECURITY: like the X25519 private key — `Copy`-free, zeroize-on-drop, not a `zerocopy`
        // wire type. The wrapped 32 bytes are the Ed25519 *seed*; wiping them on drop keeps the
        // TKA/network-lock signing secret from lingering in freed memory.
        _create_x25519_base_key_type!(
            $(#[$attr])*
            #[derive(::zeroize::Zeroize, ::zeroize::ZeroizeOnDrop)]
            $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()`. Takes `&self` (not `self`) so deriving the public key
            /// does not consume/zeroize the private seed now that it is not `Copy`.
            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 }

        // Derive the public key from a BORROW of the private seed (see the X25519 keypair macro for
        // why `&self` matters now the private key is not `Copy`). The owned `From` delegates here.
        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)
            }
        }

        impl From<$private_name> for $public_name {
            fn from(v: $private_name) -> Self {
                $public_name::from(&v)
            }
        }

        // Holds a secret seed → NOT `Copy`, NOT a `zerocopy` wire type. The `private` field's
        // `ZeroizeOnDrop` wipes the seed when the keypair drops (drop-glue composition); `Debug`
        // is safe because the private field's own `Debug` is redacted.
        $(#[$pair_attr])*
        #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
        #[derive(Clone, Debug, Eq, PartialEq)]
        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();
                let public = $public_name::from(&private);
                Self { private, public }
            }
        }

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

        impl From<$private_name> for $keypair_name {
            fn from(private: $private_name) -> Self {
                let public = $public_name::from(&private);
                Self { private, public }
            }
        }
    }
}

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;