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
//! Implementation for onion service descriptors.
//!
//! An onion service descriptor is a document generated by an onion service and
//! uploaded to one or more HsDir nodes for clients to later download. It tells
//! the onion service client where to find the current introduction points for
//! the onion service, and how to connect to them.
//!
//! An onion service descriptor is more complicated than most other
//! documentation types, because it is partially encrypted.
#![allow(dead_code, unused_variables, clippy::missing_panics_doc)] // TODO hs: remove.
mod desc_enc;
use std::time::SystemTime;
use crate::Result;
pub use desc_enc::DecryptionError;
use tor_checkable::{signed, timed};
use tor_hscrypto::pk::{
BlindedOnionId, ClientDescAuthKey, ClientDescAuthSecretKey, IntroPtAuthKey, IntroPtEncKey,
OnionId,
};
use tor_linkspec::LinkSpec;
use tor_llcrypto::pk::curve25519;
/// Metadata about an onion service descriptor, as stored at an HsDir.
///
/// This object is parsed from the outermost layer of an onion service
/// descriptor, and used on the HsDir to maintain its index. It does not
/// include the inner layers' information about introduction points, since the
/// HsDir cannot decrypt those without knowing the onion service's un-blinded
/// identity.
///
/// The HsDir caches this value, along with the original text of the descriptor.
pub struct StoredHsDescMeta {
/// The blinded onion identity for this descriptor. (This is the only
/// identity that the HsDesc knows.)
blinded_id: BlindedOnionId,
/// Information about the expiration and revision counter for this
/// descriptor.
idx_info: IndexInfo,
}
/// An unchecked StoredHsDescMeta: parsed, but not checked for liveness or validity.
pub type UncheckedStoredHsDescMeta =
timed::TimerangeBound<signed::SignatureGated<StoredHsDescMeta>>;
/// Information about how long to hold a given onion service descriptor, and
/// when to replace it.
struct IndexInfo {
/// The lifetime in minutes that this descriptor should be held after it is
/// received.
desc_lifetime: u16,
/// The expiration time on the signing key certificate included in this
/// descriptor.
signing_cert_expires: SystemTime,
/// The revision counter on this descriptor: higher values should replace
/// older ones.
revision: u64,
}
/// A decrypted, decoded onion service descriptor.
///
/// This object includes information from both the outer (plaintext) layer of
/// the descriptor, and the inner (encrypted) layers. It tells the client the
/// information it needs to contact the onion service, including necessary
/// introduction points and public keys.
pub struct HsDesc {
/// The real onion identity for this onion service.
id: OnionId,
/// Information about the expiration and revision counter for this
/// descriptor.
idx_info: IndexInfo,
/// The public key (if any) for the private key that we used to decrypt this descriptor.
decrypted_with_id: Option<ClientDescAuthKey>,
/// A list of recognized CREATE handshakes that this onion service supports.
// TODO hs: this should probably be an enum, not a string
create2_formats: Vec<String>,
/// A list of authentication types that this onion service supports.
// TODO hs: this should probably be an enum, not a string
auth_required: Vec<String>, // TODO hs
/// If true, this a "single onion service" and is not trying to keep its own location private.
is_single_onion_service: bool,
/// One or more introduction points used to contact the onion service.
intro_points: Vec<IntroPointDesc>,
}
/// An unchecked HsDesc: parsed, but not checked for liveness or validity.
pub type UncheckedHsDesc = timed::TimerangeBound<signed::SignatureGated<HsDesc>>;
/// Information in an onion service descriptor about a single
/// introduction point.
pub struct IntroPointDesc {
/// A list of link specifiers needed to extend a circuit to the introduction point.
///
/// These can include public keys and network addresses.
//
// TODO hs: perhaps we should make certain link specifiers mandatory? That
// would make it possible for IntroPointDesc to implement CircTarget.
link_specifiers: Vec<LinkSpec>,
/// The key used to extand a circuit to the introduction point, using the
/// ntor or ntor3 handshakes.
ntor_onion_key: curve25519::PublicKey,
/// A key used to identify the onion service at this introduction point.
auth_key: IntroPtAuthKey,
/// The key used to encrypt a handshake _to the onion service_ when using this
/// introdution point.
hs_enc_key: IntroPtEncKey,
}
/// An onion service after it has been parsed by the client, but not yet decrypted.
pub struct EncryptedHsDesc {
/// The real onion identity for this onion service.
id: OnionId,
/// Information about the expiration and revision counter for this
/// descriptor.
idx_info: IndexInfo,
/// An encrypted string describing the actual introduction points for this onion service.
encrypted: Vec<u8>,
}
/// An unchecked HsDesc: parsed, but not checked for liveness or validity.
pub type UncheckedEncryptedHsDesc = timed::TimerangeBound<signed::SignatureGated<EncryptedHsDesc>>;
impl StoredHsDescMeta {
// TODO hs: needs accessor functions too. (Let's not use public fields; we
// are likely to want to mess with the repr of these types.)
/// Parse the outermost layer of the descryptor in `input`, and return the
/// resulting metadata (if possible).
pub fn parse(input: &str) -> Result<UncheckedStoredHsDescMeta> {
todo!() // TODO hs
}
}
impl HsDesc {
// TODO hs: needs accessor functions too. (Let's not use public fields; we
// are likely to want to mess with the repr of these types.)
/// Parse the outermost layer of the descriptor in `input`, and validate
/// that its identity is consistent with `blinded_onion_id`.
///
/// On success, the caller will get a wrapped object which they must
/// validate and then decrypt.
pub fn parse(
input: &str,
blinded_onion_id: &BlindedOnionId,
) -> Result<UncheckedEncryptedHsDesc> {
todo!() // TODO hs
}
}
impl EncryptedHsDesc {
/// Attempt to decrypt both layers of encryption in this onion service
/// descriptor.
///
/// If `using_key` is provided, we use it to decrypt the inner layer;
/// otherwise, we require that the inner layer is encrypted using the "no
/// client authorization" method.
//
// TODO hs: I'm not sure that taking `using_key` as an argument is correct. Instead, maybe
// we should take a keystore trait? Or a function from &ClientDescAuthKey to &ClientDescAuthSecretKey?
pub fn decrypt(
self,
using_key: Option<(&ClientDescAuthKey, &ClientDescAuthSecretKey)>,
) -> Result<UncheckedHsDesc> {
todo!() // TODO hs desc
}
}
// TODO hs: Define a HsDescBuilder structure, but it should not create an HsDesc directly.
// Instead, it should make something that is _like_ an HsDesc but with extra client keys,
// full certificates and so on. Then, define a function taking the correct set of private
// keys and using them to encode, encrypt, and sign the built HsDesc.