secure_gate/macros/fixed_newtype.rs
1//! `fixed_newtype!` — nominal newtype over `Fixed<[u8; N]>`.
2//!
3//! Ships in 0.9.0; backported to the 0.8 line in 0.8.0-rc.11. Design record:
4//! [`docs/design/nominal_newtypes.md`](https://github.com/Slurp9187/secure-gate/blob/main/docs/design/nominal_newtypes.md) — a repository file, not part of the
5//! published crate, so this is a link and not a path. It describes `main`.
6
7/// Creates a distinct nominal type wrapping [`Fixed<[u8; N]>`](crate::Fixed).
8///
9/// Generates a `struct` rather than a `type` alias: two `fixed_newtype!` types
10/// of the same `N` are **not** interchangeable, where two `type` aliases of
11/// that `N` are one and the same type. Use it when distinct cryptographic
12/// roles share a shape — an encryption key and a MAC key are both
13/// `Fixed<[u8; 32]>`, and under a plain alias the compiler cannot tell them
14/// apart.
15///
16/// # Syntax
17///
18/// ```text
19/// fixed_newtype!(pub Name, N); // public, auto-generated doc
20/// fixed_newtype!(pub(crate) Name, N); // crate-visible
21/// fixed_newtype!(Name, N); // private
22/// fixed_newtype!(pub Name, N, "doc string"); // with custom doc
23/// fixed_newtype!(pub Name, N, derive: [ConstantTimeEq]); // with opt-in impls
24/// fixed_newtype!(pub Name, N, "doc", derive: [ConstantTimeEq]);
25/// fixed_newtype!(pub Name, generic [T; N]); // array: adds SecretLen
26/// fixed_newtype!(pub Name, generic T); // reduced API, opted into
27/// fixed_newtype!(pub Name, generic T, "doc string");
28/// fixed_newtype!(pub Name, generic T, derive: [WrapperAccess]);
29/// fixed_newtype!(pub Name, generic T, "doc", derive: [WrapperAccess]);
30/// ```
31///
32/// Supported `derive:` options are `ConstantTimeEq`, `Deserialize`, `FromWrapper`, `IntoWrapper`, and `WrapperAccess` (= both directions). The first two constrain the inner type — `ConstantTimeEq` and `Deserialize` must be implemented for it, which rules them out for most `generic` inner types (`[i16; 256]` has neither), while the three wrapper-access tokens apply to any. See
33/// *Cloning and serialization* below for the two that are deliberately absent.
34///
35/// # Examples
36///
37/// All three visibility forms:
38///
39/// ```rust
40/// use secure_gate::{fixed_newtype, SecretLen};
41///
42/// fixed_newtype!(pub EncKey, 32); // public
43/// fixed_newtype!(pub(crate) HmacKey, 32); // crate-visible
44/// fixed_newtype!(local_nonce, 12); // private
45///
46/// let key = EncKey::new([42u8; 32]);
47/// assert_eq!(key.len(), 32);
48/// ```
49///
50/// Distinct roles do not mix — this is the point:
51///
52/// ```rust,compile_fail
53/// use secure_gate::fixed_newtype;
54///
55/// fixed_newtype!(pub EncKey, 32);
56/// fixed_newtype!(pub MacKey, 32);
57///
58/// fn seal(_enc: &EncKey, _mac: &MacKey) {}
59///
60/// let enc = EncKey::new([1u8; 32]);
61/// let mac = MacKey::new([2u8; 32]);
62/// seal(&mac, &enc); // E0308: roles swapped
63/// ```
64///
65/// Zero-size is a **compile error**, raised where the type is declared rather
66/// than where one is first built:
67///
68/// ```rust,compile_fail
69/// use secure_gate::fixed_newtype;
70/// fixed_newtype!(pub Bad, 0); // compile-time error: index out of bounds
71/// ```
72///
73/// # Inner types other than bytes
74///
75/// The size-literal forms cover `Fixed<[u8; N]>`, the shape nearly every
76/// secret takes. [`Fixed<T>`](crate::Fixed) holds more than that, and the
77/// `generic` marker opts a newtype into it. The requirement is
78/// `Zeroize + `[`SentinelValue`](crate::SentinelValue)` + `[`FixedStorage`](crate::FixedStorage), checked at the
79/// declaration — slightly narrower than `Fixed<T>` itself, which needs only
80/// `Zeroize`, because the generated [`into_inner`](crate::RevealSecret::into_inner)
81/// has to leave an inert value behind. Arrays of a `Default` element, `String`
82/// and `Vec<T>` already satisfy both; a custom inner type needs its own
83/// `impl SentinelValue`, which is why that trait is public:
84///
85/// ```rust
86/// use secure_gate::{fixed_newtype, RevealSecret};
87///
88/// fixed_newtype!(pub Poly, generic [i16; 256]);
89///
90/// let poly = Poly::new([0i16; 256]);
91/// assert_eq!(poly.with_secret(|coeffs| coeffs.len()), 256);
92/// ```
93///
94/// The case this exists for is `no_std`. With no allocator there is no
95/// [`Dynamic`](crate::Dynamic), so `Fixed` is the only wrapper, and a secret on
96/// such a target is frequently not bytes: an ML-KEM secret polynomial
97/// `[i16; 256]`, Ed25519 scalar limbs `[u64; 4]`, AES-256 expanded round keys
98/// `[u32; 60]`. An inner type missing either bound is a compile error reported
99/// from the expansion: `Zeroize` fails against `Fixed<T>`'s own bound, and
100/// `SentinelValue` against the one the generated `RevealSecret` restates.
101/// [`dynamic_newtype!`](crate::dynamic_newtype)'s `generic` arm carries the
102/// same pair, for the same reason.
103///
104/// Every `generic` form emits [`RevealSecret`](crate::RevealSecret),
105/// [`RevealSecretMut`](crate::RevealSecretMut), redacted `Debug`, `Zeroize`,
106/// `ZeroizeOnDrop`, `new`, and [`new_with`](crate::Fixed::new_with). What sits on top
107/// of that depends on how much shape the macro can read off the tokens you wrote, and
108/// there are two answers.
109///
110/// **A literal array — `generic [i16; 256]` — takes the array arm.** The element type
111/// and the length are both still tokens there, so the expansion can see the shape and
112/// emits two more things: [`SecretLen`](crate::SecretLen), and the declaration-site
113/// `N = 0` guard the size-literal arm has always carried. `len()` counts elements and
114/// `byte_len()` multiplies by `size_of::<T>()`, because that is what
115/// `Fixed<[T; N]>`'s own impl computes — 256 and 512 for a polynomial. The arm needs a
116/// *literal* length: `generic [i16; KEY_LEN]` is a different token sequence and falls
117/// through to the opaque arm, the same remainder a path-qualified spelling has.
118///
119/// **A true opaque inner type — `generic Poly`, a struct, an alias — takes the reduced
120/// arm.** `SecretLen` is absent there, and so is the declaration-site guard; a
121/// zero-sized inner value is refused by [`Fixed`](crate::Fixed) at construction
122/// instead. That is a limit on the macro's field of view rather than a verdict on the
123/// type: the base wrapper still implements `SecretLen` for every `Fixed<[T; N]>`, so
124/// the capability is one layer down and `derive: [IntoWrapper]` reaches it in one call.
125/// What the arm withholds is the newtype's *own* `len()`, not the answer.
126///
127/// **`From`, `TryFrom`, the encoders and the RNG constructors are absent from both.**
128/// The encoders have no defined byte order over `[i16]`, and byte order is not the
129/// whole of it: the canonical encoding of these secrets is domain-specific — ML-KEM
130/// serialises as bit-packed 12-bit coefficients, not a 16-bit dump — so an encoder here
131/// would round-trip inside this crate and disagree with every implementation outside
132/// it. RNG is a separate question with a separate answer: the byte-level fill is well
133/// defined, but uniform bits are not a valid ML-KEM coefficient, a reduced scalar, or a
134/// derived round key. It is tracked in #219 rather than settled here.
135///
136/// **`generic [u8; N]` is a compile error**, not a reduced byte array. It is the
137/// same payload as `fixed_newtype!(pub K, N)` with strictly less API, and
138/// [`dynamic_newtype!`](crate::dynamic_newtype) already refuses the analogous
139/// spellings (`generic Vec<u8>`, `generic String`). Write the size literal.
140///
141/// **The inner type must not be able to reallocate.** This arm used to accept
142/// `generic Vec<u8>` and `generic String`, which put a growable buffer inside the
143/// wrapper the documentation calls free of any realloc surface; measured, each
144/// abandoned an unwiped buffer holding the whole secret on any capacity change, and
145/// with no `io::Write` escape of the sort [`Dynamic<Vec<u8>>`](crate::Dynamic) has.
146/// [`Fixed::new`](crate::Fixed::new) now requires
147/// [`FixedStorage`](crate::FixedStorage), so those forms are a compile error reported
148/// at this declaration. Arrays, the primitives, tuples and `Option` are covered for you,
149/// which is every shape this arm exists for; a custom inner type adds
150/// `impl FixedStorage for MyType {}`, which asserts that it owns no buffer whose
151/// capacity can change. For a growable payload reach for
152/// [`dynamic_newtype!`](crate::dynamic_newtype) instead, where the residue is
153/// documented and one safe growth path exists.
154///
155/// **`new_with` is forwarded.** [`Fixed::new_with`](crate::Fixed::new_with) is an
156/// inherent on every `T: `[`FixedStorage`](crate::FixedStorage)` + `[`SentinelValue`](crate::SentinelValue),
157/// not only `[u8; N]`: the slot starts as `T::sentinel_value()` and the closure
158/// writes into the wrapper's own storage. The generic arm emits it, so a
159/// polynomial does not have to take the secret by value the way `new` does.
160/// Prefer a size-literal newtype when the secret *is* a byte array — that arm
161/// is the full API, and `generic [u8; N]` is refused rather than quietly
162/// reduced.
163///
164/// # Cloning and serialization are not generated
165///
166/// There is no `derive: [Clone]` or `derive: [Serialize]`, by design. Neither
167/// can be forwarded from the wrapper — `Fixed<[u8; N]>: Clone` requires
168/// `[u8; N]: `[`CloneableSecret`](crate::CloneableSecret), which the orphan
169/// rule makes permanently unimplementable downstream (deliberately so). A
170/// generated impl would therefore have to route through `with_secret` and
171/// rebuild, opting the secret into cloning with no marker impl anywhere — a
172/// second door around the opt-in system, spelled in one word inside a macro
173/// expansion. Asking for either is a compile error that says so.
174///
175/// The generated type is local to **your** crate, so write the impl by hand
176/// when you mean it. The decision then lives in your code, where it is
177/// visible, greppable, and reviewable:
178///
179/// ```rust
180/// use secure_gate::{fixed_newtype, Fixed, RevealSecret};
181///
182/// fixed_newtype!(pub SessionKey, 32);
183///
184/// // Deliberate: each clone is an independent copy, zeroized on its own drop.
185/// // Cloning widens the window for memory-extraction attacks — audit each use.
186/// impl Clone for SessionKey {
187/// fn clone(&self) -> Self {
188/// Self::new(self.with_secret(|bytes| *bytes))
189/// }
190/// }
191///
192/// let key = SessionKey::new([7u8; 32]);
193/// let copy = key.clone();
194/// assert_eq!(copy.expose_secret()[0], 7);
195/// ```
196///
197/// The same applies to `Serialize`, with a higher bar: serialization exposes
198/// the full secret, so treat a hand-written impl as a security decision and
199/// see [`SerializableSecret`](crate::SerializableSecret) for the risk notes.
200///
201/// # Implementation Notes
202///
203/// The generated type is `#[repr(transparent)]` over the wrapper, so
204/// `size_of::<EncKey>() == N` and delegation is `#[inline]` throughout — the
205/// newtype costs nothing at runtime.
206///
207/// The size-literal and array expansions both emit
208/// `const _: () = { let _ = [(); N][0]; };`, a declaration-site courtesy: `N = 0`
209/// fails on the line that declares the type, which is the line worth pointing at.
210/// Whether the length arrives as the size argument or inside a literal array, it is
211/// a literal the macro can count at expansion.
212///
213/// It is not the only guard, and the two are not redundant.
214/// [`Fixed::new`](crate::Fixed::new) and
215/// [`Fixed::new_with`](crate::Fixed::new_with) carry a `const` assertion rejecting a
216/// zero-sized inner value at construction, which is what covers a plain `type` alias
217/// and the opaque `generic` arm; there the error falls at the first concrete
218/// construction instead. The macro guard checks a *length*, the wrapper's checks a
219/// *size* — so `generic [(); 4]` passes the first and is caught by the second, which
220/// is exactly the case `tests/compile-fail/fixed_zero_size.rs` pins.
221///
222/// **Do not add your own `Drop` impl.** None is needed: the wrapped
223/// [`Fixed`](crate::Fixed) still runs its own, so zeroization is unaffected.
224/// Adding one makes the inner field unmovable (**E0509**) and silently costs
225/// you [`into_inner`](crate::RevealSecret::into_inner).
226///
227/// # Security
228///
229/// Generated types inherit every [`Fixed`](crate::Fixed) guarantee: zeroize on
230/// drop, `Debug` that always prints `[REDACTED]`, and access only through
231/// [`RevealSecret`](crate::RevealSecret) /
232/// [`RevealSecretMut`](crate::RevealSecretMut). There is no `Deref` — a
233/// generated newtype is not coercible to its wrapper, which is what keeps the
234/// nominal separation total rather than by-value-only.
235///
236/// **Nominal separation guards against mistakes, not intent** — but nothing is
237/// generated that would undo it by accident. There is no `From<Wrapper>` and
238/// no `Deref`, so a base-typed value cannot flow into a newtype through
239/// `.into()`, and `&Newtype` never coerces to `&Wrapper` at a call site. By
240/// default the only way material enters or leaves is the 3-tier access API —
241/// a `with_secret` round trip — which is explicit and shows up in the audit
242/// sweep. Base-wrapper access is opt-in, per newtype, and **split by
243/// direction**: `derive: [FromWrapper]` adds `from_wrapper` (a base value
244/// *enters* this role), `derive: [IntoWrapper]` adds `as_wrapper`,
245/// `as_wrapper_mut`, and `into_wrapper` (material *leaves* this role toward
246/// the base type), and `derive: [WrapperAccess]` is shorthand for both.
247///
248/// These cross a different wall from `into_inner`. A newtype has two:
249///
250/// | Wall | Going in | Going out |
251/// |---|---|---|
252/// | Contents (the wrapper's protection) | `new`, `From<[u8; N]>` | `with_secret`, `expose_secret`, `into_inner` |
253/// | Role (the nominal label) | `from_wrapper` | `as_wrapper`, `as_wrapper_mut`, `into_wrapper` |
254///
255/// `into_inner` leaves the protection: it hands back the plain value, so the
256/// contents are in the caller's hands (tier 3 of the access model, audited).
257/// `into_wrapper` is a **label** drop; `into_inner` is a **protection** drop.
258/// Confusing those two names is the main way this model gets misread. `into_wrapper` only removes the label: the
259/// result is still a `Fixed`, still unreadable without `with_secret`. The
260/// role row exists so that dropping a label never forces opening the
261/// contents — without it, reaching base-typed code costs an `into_inner` plus
262/// a rebuild, a reveal the job never needed.
263///
264/// **Which direction is safe depends on the pool.** In a mixed tree the base
265/// type is not raw material: every plain `type` alias sharing it *is* that
266/// type, so a directional token connects this role to all of them at once.
267/// `FromWrapper` lets anything in the pool become this role — the source never
268/// opts in, because the source is just the base type — so a type that guards
269/// a boundary must never take it. `IntoWrapper` lets this role become anything
270/// in the pool, which is safe only when the role is no more sensitive than
271/// the least-sensitive alias sharing its base; on a secret role it is an
272/// explicit, greppable downgrade, not a neutral operation. The default,
273/// neither token, is sufficient more often than it looks. `IntoWrapper` also
274/// reopens whatever the base wrapper implements for that inner type, which on a
275/// `generic` newtype includes the surface the arm withheld: `as_wrapper().len()`
276/// and `.byte_len()` answer for any `Fixed<[T; N]>`. The reduced surface is a
277/// property of the label, not a barrier. This is now worth granting less often than
278/// it was — a literal array gets its own `len()` from the array arm, so reaching for
279/// the outbound token merely to measure a secret is no longer the trade it used to
280/// be. Where it is still the only route is the opaque arm. The exposure is
281/// largest during a partial migration — the regime real consumers live in —
282/// because while most aliases stay plain the base type is a universal donor.
283/// Audit these methods the way you audit `expose_secret()`.
284///
285/// # See also
286///
287/// - [`dynamic_newtype!`](crate::dynamic_newtype) — heap-allocated counterpart
288/// - a plain `type` alias — `pub type Aes256Key = Fixed<[u8; 32]>;` — when a
289/// readable name rather than role separation is the goal, and the name should
290/// stay interchangeable with its base. Two aliases over one shape are the
291/// same type, so the compiler will not keep two roles apart; that is the
292/// whole of the choice, and the crate's README argues it at length
293#[macro_export]
294macro_rules! fixed_newtype {
295 // ---- `generic` applied to a byte array: rejected ----
296 //
297 // `generic [u8; N]` is strictly dominated by the size-literal arm. It takes
298 // the payload this crate already specialises and then withholds `SecretLen`,
299 // `From`, `TryFrom`, the encoders and `from_random`. Same bytes, strictly
300 // less API, and no reason to write it. These must precede every
301 // `generic $inner:ty` arm: once a `:ty` fragment is parsed there is no
302 // backtracking. They match literal tokens only, so `generic [u8; KEY_LEN]`
303 // with an ident in the length slot is a different sequence and is NOT
304 // caught — the same remainder `dynamic_newtype!` documents for paths.
305 ($(#[$attr:meta])* $vis:vis $name:ident, generic [u8; $n:literal], $doc:literal, derive: [$($opt:ident),* $(,)?]) => {
306 $crate::__sg_fixed_generic_is_bytes!($n);
307 };
308 ($(#[$attr:meta])* $vis:vis $name:ident, generic [u8; $n:literal], $doc:literal) => {
309 $crate::__sg_fixed_generic_is_bytes!($n);
310 };
311 ($(#[$attr:meta])* $vis:vis $name:ident, generic [u8; $n:literal], derive: [$($opt:ident),* $(,)?]) => {
312 $crate::__sg_fixed_generic_is_bytes!($n);
313 };
314 ($(#[$attr:meta])* $vis:vis $name:ident, generic [u8; $n:literal]) => {
315 $crate::__sg_fixed_generic_is_bytes!($n);
316 };
317
318 // ---- `generic` applied to a non-byte array: the shaped array arm ----
319 //
320 // `[i16; 256]` is an array and the macro can see that it is, so it should not
321 // have to pretend it is an opaque struct. The no_std case the `generic` marker
322 // exists for is exactly this shape. This arm emits what is meaningful for any
323 // `[T; N]` and nothing that is not: the declaration-site `N = 0` guard the
324 // size-literal arm already carries, and `SecretLen`, whose answers
325 // `Fixed<[T; N]>` already computes correctly — `len` in elements, `byte_len`
326 // multiplied by `size_of::<T>()`.
327 //
328 // Still withheld: `From`/`TryFrom`, the encoders and the RNG constructors. Hex
329 // over `[i16]` has no defined byte order, and for the structured secrets this
330 // arm exists for the canonical encoding is bit-packed rather than a raw element
331 // dump, so an encoder here would round-trip inside this crate and disagree with
332 // every implementation outside it. RNG is a different question — the byte-level
333 // fill is well defined, but uniform bits are not a valid ML-KEM coefficient, a
334 // reduced scalar, or a derived round key — and is tracked separately in #219.
335 //
336 // One arm, not four. The pattern consumes a `:ty` fragment for the element
337 // type, so two arms both opening `generic [$t:ty; ...` cannot coexist: the
338 // same no-backtracking rule that forces the opaque arm below into the
339 // optional-group form. The `[u8; N]` rejects above still win, because they
340 // precede this arm and match `u8` as a literal token.
341 ($(#[$attr:meta])* $vis:vis $name:ident, generic [$t:ty; $n:literal]
342 $(, $doc:literal)? $(, derive: [$($opt:ident),* $(,)?])?) => {
343 const _: () = { let _ = [(); $n][0]; };
344
345 $crate::__sg_newtype_base!(
346 $(#[$attr])* $(#[doc = $doc])* $vis $name($crate::Fixed<[$t; $n]>),
347 derive: [$($($opt),*)?]
348 );
349 $crate::__sg_newtype_len!($name);
350
351 impl $name {
352 /// Wraps an array in place. `const fn`, like `Fixed::new`.
353 #[inline(always)]
354 pub const fn new(value: [$t; $n]) -> Self {
355 Self($crate::Fixed::new(value))
356 }
357 /// Scoped construction — writes directly into the wrapper's storage.
358 #[inline(always)]
359 pub fn new_with<F>(f: F) -> Self
360 where F: ::core::ops::FnOnce(&mut [$t; $n]) {
361 Self($crate::Fixed::new_with(f))
362 }
363 }
364 };
365
366 // ---- explicit generic arms: caller opts in to the reduced API ----
367 //
368 // Placed after the array arm. They match the literal token `generic` in the
369 // inner-type position, so they cannot shadow a size literal, and matching the
370 // marker before any `:literal` or `:ty` fragment is parsed keeps the ordering
371 // safe: `macro_rules!` does not backtrack once a fragment has been
372 // consumed. The doc-literal forms get their own arms, exactly as
373 // `dynamic_newtype!`'s generic form does, so that the third positional
374 // argument means the same thing on both macros and on every arm of each.
375 ($(#[$attr:meta])* $vis:vis $name:ident, generic $inner:ty
376 $(, $doc:literal)? $(, derive: [$($opt:ident),* $(,)?])?) => {
377 $crate::__sg_newtype_base!(
378 $(#[$attr])* $(#[doc = $doc])* $vis $name($crate::Fixed<$inner>),
379 derive: [$($($opt),*)?]
380 );
381
382 impl $name {
383 /// Wraps a value in place. `const fn`, like `Fixed::new`.
384 #[inline(always)]
385 pub const fn new(value: $inner) -> Self {
386 Self($crate::Fixed::new(value))
387 }
388 /// Scoped construction — writes directly into the wrapper's storage.
389 #[inline(always)]
390 pub fn new_with<F>(f: F) -> Self
391 where F: ::core::ops::FnOnce(&mut $inner) {
392 Self($crate::Fixed::new_with(f))
393 }
394 }
395 };
396
397 ($(#[$attr:meta])* $vis:vis $name:ident, $size:literal, $doc:literal, derive: [$($opt:ident),* $(,)?]) => {
398 $crate::fixed_newtype!($(#[$attr])* #[doc = $doc] $vis $name, $size, derive: [$($opt),*]);
399 };
400 ($(#[$attr:meta])* $vis:vis $name:ident, $size:literal, $doc:literal) => {
401 $crate::fixed_newtype!($(#[$attr])* #[doc = $doc] $vis $name, $size, derive: []);
402 };
403 ($(#[$attr:meta])* $vis:vis $name:ident, $size:literal) => {
404 $crate::fixed_newtype!($(#[$attr])* $vis $name, $size, derive: []);
405 };
406 ($(#[$attr:meta])* $vis:vis $name:ident, $size:literal, derive: [$($opt:ident),* $(,)?]) => {
407 const _: () = { let _ = [(); $size][0]; };
408
409 $crate::__sg_newtype_base!(
410 $(#[$attr])* $vis $name($crate::Fixed<[u8; $size]>), derive: [$($opt),*]
411 );
412 $crate::__sg_newtype_len!($name);
413
414 impl $name {
415 /// Wraps a fixed-size byte array. `const fn`, like `Fixed::new`.
416 #[inline(always)]
417 pub const fn new(value: [u8; $size]) -> Self {
418 Self($crate::Fixed::new(value))
419 }
420 /// Scoped construction — writes directly into the wrapper's storage.
421 #[inline(always)]
422 pub fn new_with<F>(f: F) -> Self
423 where F: ::core::ops::FnOnce(&mut [u8; $size]) {
424 Self($crate::Fixed::new_with(f))
425 }
426 }
427
428 impl ::core::convert::From<[u8; $size]> for $name {
429 #[inline(always)]
430 fn from(value: [u8; $size]) -> Self { Self($crate::Fixed::new(value)) }
431 }
432
433 impl ::core::convert::TryFrom<&[u8]> for $name {
434 type Error = $crate::FromSliceError;
435 #[inline]
436 fn try_from(slice: &[u8]) -> ::core::result::Result<Self, Self::Error> {
437 ::core::result::Result::Ok(Self(
438 <$crate::Fixed<[u8; $size]> as ::core::convert::TryFrom<&[u8]>>::try_from(slice)?
439 ))
440 }
441 }
442
443 $crate::__sg_if_rand! {
444 impl $name {
445 /// Generates the secret from the system RNG.
446 #[inline]
447 pub fn from_random() -> Self { Self($crate::Fixed::from_random()) }
448
449 /// Generates the secret from a caller-supplied CSPRNG.
450 #[inline]
451 pub fn from_rng<R>(rng: &mut R) -> ::core::result::Result<Self, R::Error>
452 where
453 R: $crate::__private::TryRngCore + $crate::__private::TryCryptoRng,
454 {
455 ::core::result::Result::Ok(Self($crate::Fixed::from_rng(rng)?))
456 }
457 }
458 }
459
460 $crate::__sg_if_hex! {
461 impl $name {
462 /// Constant-time hex decode into this secret type.
463 #[inline]
464 pub fn try_from_hex(hex: &str) -> ::core::result::Result<Self, $crate::HexError> {
465 ::core::result::Result::Ok(Self($crate::Fixed::try_from_hex(hex)?))
466 }
467 }
468 $crate::__sg_if_alloc! {
469 impl $crate::ToHex for $name {
470 #[inline]
471 fn to_hex(&self) -> $crate::EncodedSecret {
472 $crate::ToHex::to_hex(&self.0)
473 }
474 #[inline]
475 fn to_hex_upper(&self) -> $crate::EncodedSecret {
476 $crate::ToHex::to_hex_upper(&self.0)
477 }
478 }
479 }
480 }
481
482 $crate::__sg_if_base32! {
483 impl $name {
484 /// Constant-time Base32 decode into this secret type.
485 #[inline]
486 pub fn try_from_base32(s: &str) -> ::core::result::Result<Self, $crate::Base32Error> {
487 ::core::result::Result::Ok(Self($crate::Fixed::try_from_base32(s)?))
488 }
489 }
490 $crate::__sg_if_alloc! {
491 impl $crate::ToBase32 for $name {
492 #[inline]
493 fn to_base32(&self) -> $crate::EncodedSecret {
494 $crate::ToBase32::to_base32(&self.0)
495 }
496 }
497 }
498 }
499
500 $crate::__sg_if_base64! {
501 impl $name {
502 /// Constant-time Base64url decode into this secret type.
503 #[inline]
504 pub fn try_from_base64url(s: &str) -> ::core::result::Result<Self, $crate::Base64Error> {
505 ::core::result::Result::Ok(Self($crate::Fixed::try_from_base64url(s)?))
506 }
507 }
508 $crate::__sg_if_alloc! {
509 impl $crate::ToBase64Url for $name {
510 #[inline]
511 fn to_base64url(&self) -> $crate::EncodedSecret {
512 $crate::ToBase64Url::to_base64url(&self.0)
513 }
514 }
515 }
516 }
517
518 $crate::__sg_if_bech32m! {
519 impl $name {
520 /// HRP-validated Bech32m decode into this secret type.
521 #[inline]
522 pub fn try_from_bech32m(s: &str, expected_hrp: &str)
523 -> ::core::result::Result<Self, $crate::Bech32Error> {
524 ::core::result::Result::Ok(Self($crate::Fixed::try_from_bech32m(s, expected_hrp)?))
525 }
526 /// Bech32m decode without HRP validation.
527 #[inline]
528 pub fn try_from_bech32m_unchecked(s: &str)
529 -> ::core::result::Result<Self, $crate::Bech32Error> {
530 ::core::result::Result::Ok(Self($crate::Fixed::try_from_bech32m_unchecked(s)?))
531 }
532 /// HRP-validated Bech32m decode accepting strings up to `C` characters.
533 #[inline]
534 pub fn try_from_bech32m_sized<const C: usize>(s: &str, expected_hrp: &str)
535 -> ::core::result::Result<Self, $crate::Bech32Error> {
536 ::core::result::Result::Ok(Self(
537 $crate::Fixed::try_from_bech32m_sized::<C>(s, expected_hrp)?,
538 ))
539 }
540 /// Bech32m decode without HRP validation, accepting strings up to `C` characters.
541 #[inline]
542 pub fn try_from_bech32m_unchecked_sized<const C: usize>(s: &str)
543 -> ::core::result::Result<Self, $crate::Bech32Error> {
544 ::core::result::Result::Ok(Self(
545 $crate::Fixed::try_from_bech32m_unchecked_sized::<C>(s)?,
546 ))
547 }
548 }
549 $crate::__sg_if_alloc! {
550 impl $crate::ToBech32m for $name {
551 #[inline]
552 fn try_to_bech32m(
553 &self,
554 hrp: &str,
555 case: $crate::Case,
556 ) -> ::core::result::Result<$crate::EncodedSecret, $crate::Bech32Error> {
557 $crate::ToBech32m::try_to_bech32m(&self.0, hrp, case)
558 }
559 #[inline]
560 fn try_to_bech32m_sized<const C: usize>(
561 &self,
562 hrp: &str,
563 case: $crate::Case,
564 ) -> ::core::result::Result<$crate::EncodedSecret, $crate::Bech32Error> {
565 $crate::ToBech32m::try_to_bech32m_sized::<C>(&self.0, hrp, case)
566 }
567 }
568 }
569 }
570
571 $crate::__sg_if_bech32! {
572 $crate::__sg_if_alloc! {
573 impl $crate::ToBech32 for $name {
574 #[inline]
575 fn try_to_bech32(
576 &self,
577 hrp: &str,
578 case: $crate::Case,
579 ) -> ::core::result::Result<$crate::EncodedSecret, $crate::Bech32Error> {
580 $crate::ToBech32::try_to_bech32(&self.0, hrp, case)
581 }
582 #[inline]
583 fn try_to_bech32_sized<const C: usize>(
584 &self,
585 hrp: &str,
586 case: $crate::Case,
587 ) -> ::core::result::Result<$crate::EncodedSecret, $crate::Bech32Error> {
588 $crate::ToBech32::try_to_bech32_sized::<C>(&self.0, hrp, case)
589 }
590 }
591 }
592 impl $name {
593 /// HRP-validated Bech32 decode into this secret type.
594 #[inline]
595 pub fn try_from_bech32(s: &str, expected_hrp: &str)
596 -> ::core::result::Result<Self, $crate::Bech32Error> {
597 ::core::result::Result::Ok(Self($crate::Fixed::try_from_bech32(s, expected_hrp)?))
598 }
599 /// Bech32 decode without HRP validation.
600 #[inline]
601 pub fn try_from_bech32_unchecked(s: &str)
602 -> ::core::result::Result<Self, $crate::Bech32Error> {
603 ::core::result::Result::Ok(Self($crate::Fixed::try_from_bech32_unchecked(s)?))
604 }
605 /// HRP-validated Bech32 decode accepting strings up to `C` characters.
606 #[inline]
607 pub fn try_from_bech32_sized<const C: usize>(s: &str, expected_hrp: &str)
608 -> ::core::result::Result<Self, $crate::Bech32Error> {
609 ::core::result::Result::Ok(Self(
610 $crate::Fixed::try_from_bech32_sized::<C>(s, expected_hrp)?,
611 ))
612 }
613 /// Bech32 decode without HRP validation, accepting strings up to `C` characters.
614 #[inline]
615 pub fn try_from_bech32_unchecked_sized<const C: usize>(s: &str)
616 -> ::core::result::Result<Self, $crate::Bech32Error> {
617 ::core::result::Result::Ok(Self(
618 $crate::Fixed::try_from_bech32_unchecked_sized::<C>(s)?,
619 ))
620 }
621 }
622 }
623 };
624
625 // ---- catch-all: neither a byte size nor the `generic` marker ----
626 //
627 // Uses `$($rest:tt)+` rather than `$inner:ty` so it can never itself
628 // hard-error on a malformed tail; the message names what was written.
629 ($(#[$attr:meta])* $vis:vis $name:ident, $($rest:tt)+) => {
630 ::core::compile_error!(::core::concat!(
631 "fixed_newtype!: no form matches `",
632 ::core::stringify!($($rest)+),
633 "`. The accepted forms are `N`, `N, \"doc\"`, `N, derive: [..]` and \
634 `N, \"doc\", derive: [..]`, where N is the length of the `[u8; N]` \
635 array being wrapped; and the same four with `generic <type>` in place \
636 of N, for an inner type that is not a byte array \
637 (`fixed_newtype!(pub K, generic [i16; 256])`). A literal array there \
638 also gets `SecretLen` and the declaration-site `N = 0` guard; any other \
639 inner type takes the reduced API (RevealSecret, RevealSecretMut, Debug, \
640 Zeroize, `new`, `new_with`) on purpose. `generic [u8; N]` is not one of \
641 those: write the size literal. \
642 If the length or the marker looks right, the problem is in what follows \
643 it: a `derive:` list needs brackets, and accepts only ConstantTimeEq, \
644 Deserialize, FromWrapper, IntoWrapper and WrapperAccess."
645 ));
646 };
647}
648
649/// Rejects `generic [u8; N]`, which is strictly dominated by the size-literal arm.
650#[doc(hidden)]
651#[macro_export]
652macro_rules! __sg_fixed_generic_is_bytes {
653 ($n:literal) => {
654 ::core::compile_error!(::core::concat!(
655 "fixed_newtype!: write `",
656 ::core::stringify!($n),
657 "`, not `generic [u8; ",
658 ::core::stringify!($n),
659 "]`. The size-literal arm is the full API for a byte array (`new_with`, \
660 `SecretLen`, `From`, `TryFrom`, the encoders, `from_random`). The \
661 `generic` arm emits only the shape-independent surface, so this \
662 spelling is the same payload with strictly less API. `generic` is \
663 for an inner type that is not `[u8; N]`."
664 ));
665 };
666}