HiddenServiceDescriptorV3

Struct HiddenServiceDescriptorV3 

Source
pub struct HiddenServiceDescriptorV3 {
    pub version: u32,
    pub lifetime: u32,
    pub signing_cert: Option<String>,
    pub revision_counter: u64,
    pub superencrypted: Option<String>,
    pub signature: String,
    /* private fields */
}
Expand description

Version 3 hidden service descriptor.

A v3 hidden service descriptor uses modern Ed25519/X25519 cryptography and provides stronger security than v2 descriptors. The .onion address is 56 characters long (vs 16 for v2).

§Structure

The descriptor contains:

  • version: Always 3 for this type
  • lifetime: How long the descriptor is valid (in minutes)
  • signing_cert: Ed25519 certificate for the descriptor signing key
  • revision_counter: Monotonically increasing counter to prevent replay
  • superencrypted: Encrypted outer layer (contains inner layer)
  • signature: Ed25519 signature over the descriptor

§Encryption Layers

V3 descriptors have two encryption layers:

  1. Superencrypted (outer): Decrypted with blinded key + subcredential
  2. Encrypted (inner): May require client authorization cookie

The decryption process requires:

  • The service’s blinded public key (derived from identity key + time)
  • The subcredential (derived from identity key)
  • Optionally, the client’s authorization cookie

§Address Format

V3 .onion addresses are 56 characters and encode:

  • 32 bytes: Ed25519 public key
  • 2 bytes: Checksum
  • 1 byte: Version (0x03)

Use address_from_identity_key and identity_key_from_address to convert.

§Example

use stem_rs::descriptor::hidden::HiddenServiceDescriptorV3;
use stem_rs::descriptor::Descriptor;

let content = std::fs::read_to_string("v3_descriptor.txt")?;
let desc = HiddenServiceDescriptorV3::parse(&content)?;

println!("Version: {}", desc.version);
println!("Lifetime: {} minutes", desc.lifetime);
println!("Revision: {}", desc.revision_counter);

// Convert identity key to .onion address
let key = [0u8; 32]; // Your 32-byte Ed25519 public key
let address = HiddenServiceDescriptorV3::address_from_identity_key(&key);
println!("Address: {}", address);

§Security

  • The revision_counter prevents replay attacks
  • The signature authenticates the descriptor
  • Introduction points are encrypted and require decryption
  • Client authorization adds an additional encryption layer

Fields§

§version: u32

Hidden service descriptor version (always 3 for this type).

§lifetime: u32

Descriptor validity period in minutes (typically 180).

§signing_cert: Option<String>

Ed25519 certificate for the descriptor signing key (PEM format).

§revision_counter: u64

Monotonically increasing counter to prevent replay attacks.

§superencrypted: Option<String>

Encrypted outer layer containing client auth and inner layer.

§signature: String

Ed25519 signature over the descriptor content.

Implementations§

Source§

impl HiddenServiceDescriptorV3

Source

pub fn address_from_identity_key(key: &[u8]) -> String

Converts an Ed25519 identity key to a v3 .onion address.

The address is computed as:

  1. Compute checksum: SHA3-256(“.onion checksum” || pubkey || version)[0:2]
  2. Concatenate: pubkey || checksum || version
  3. Base32-encode and append “.onion”
§Arguments
  • key - 32-byte Ed25519 public key
§Returns

A 62-character string ending in “.onion” (56 chars + “.onion”).

§Example
use stem_rs::descriptor::hidden::HiddenServiceDescriptorV3;

let key = [0u8; 32];
let address = HiddenServiceDescriptorV3::address_from_identity_key(&key);
assert!(address.ends_with(".onion"));
assert_eq!(address.len(), 62); // 56 + ".onion"
Source

pub fn identity_key_from_address(onion_address: &str) -> Result<Vec<u8>, Error>

Extracts the Ed25519 identity key from a v3 .onion address.

Validates the address format, checksum, and version byte.

§Arguments
  • onion_address - A v3 .onion address (with or without “.onion” suffix)
§Returns

The 32-byte Ed25519 public key on success.

§Errors

Returns Error::Parse if:

  • The address is not valid base32
  • The decoded length is not 35 bytes
  • The version byte is not 3
  • The checksum does not match
§Example
use stem_rs::descriptor::hidden::HiddenServiceDescriptorV3;

let key = [0u8; 32];
let address = HiddenServiceDescriptorV3::address_from_identity_key(&key);
let recovered = HiddenServiceDescriptorV3::identity_key_from_address(&address).unwrap();
assert_eq!(recovered, key.to_vec());
§Security

The checksum prevents typos in addresses from connecting to the wrong service. Always validate addresses before use.

Trait Implementations§

Source§

impl Clone for HiddenServiceDescriptorV3

Source§

fn clone(&self) -> HiddenServiceDescriptorV3

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for HiddenServiceDescriptorV3

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Descriptor for HiddenServiceDescriptorV3

Source§

fn parse(content: &str) -> Result<Self, Error>

Parses a descriptor from its string content. Read more
Source§

fn to_descriptor_string(&self) -> String

Serializes the descriptor to its canonical string format. Read more
Source§

fn digest( &self, hash: DigestHash, encoding: DigestEncoding, ) -> Result<String, Error>

Computes the cryptographic digest of the descriptor. Read more
Source§

fn raw_content(&self) -> &[u8]

Returns the raw bytes of the original descriptor content. Read more
Source§

fn unrecognized_lines(&self) -> &[String]

Returns lines from the descriptor that were not recognized. Read more
Source§

impl Display for HiddenServiceDescriptorV3

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl FromStr for HiddenServiceDescriptorV3

Source§

type Err = Error

The associated error which can be returned from parsing.
Source§

fn from_str(s: &str) -> Result<Self, Self::Err>

Parses a string s to return a value of this type. Read more
Source§

impl PartialEq for HiddenServiceDescriptorV3

Source§

fn eq(&self, other: &HiddenServiceDescriptorV3) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for HiddenServiceDescriptorV3

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.