Browse the docs

silk.tls.https_identity.

Check that a server certificate is for the host name or IP address in an HTTPS URL (RFC 6125 and RFC 9110).

When to use#

After certificate path validation succeeds, call HttpsIdentity.reference with the host of the URL, and then HttpsIdentity.verify with the subject alternative names of the leaf certificate. silk.tls.client does this for you.

Details#

DNS names compare without regard to ASCII case. A wildcard is allowed only as the full leftmost label of a certificate name, and it matches exactly one label: *.example.com matches api.example.com, but not example.com or a.b.example.com. An IP address matches only an IP SAN with the same bytes. All SANs are checked before a match is returned, and nothing allocates.

Errors#

  • IdentityError if the host is not a valid DNS name, a SAN is not valid, a limit is exceeded, or no SAN matches (NoMatch).

Gotchas#

Use the host from the URL, before DNS resolution. Never use a resolved address or a CNAME target. The common name (CN) is never used. Names with a trailing dot and Unicode names are rejected: convert an internationalized name to its xn-- form first. A match does not prove that the chain is trusted.

Examples#

Match a host against a wildcard certificate#

silk
import silk.tls.https_identity {
  CertificateIdentities,
  HttpsIdentity,
  IdentityError,
  IdentityLimits,
  IdentityMatch,
  OriginHost,
  PresentedIdentity,
  ReferenceIdentity
}
import silk.core.result { Result }

fn certificateView<'a>(entries: &'a [PresentedIdentity<'a>]) -> CertificateIdentities<'a> {
  return CertificateIdentities<'a>.Decoded { entries: entries }
}

pub fn main() -> i32 {
  let reference = match HttpsIdentity.reference(
    OriginHost<'static>.Dns { bytes: b"API.example.com" }
  ) {
    Result<ReferenceIdentity, IdentityError>.Success { value } => value
    Result<ReferenceIdentity, IdentityError>.Failure { error: _ } => {
      return 1
    }
  }
  let sans: [PresentedIdentity<'static>; 2] = [
    PresentedIdentity<'static>.Dns { bytes: b"example.com" },
    PresentedIdentity<'static>.Dns { bytes: b"*.example.com" }
  ]
  let certificate = certificateView(&sans)
  return match HttpsIdentity.verify(&reference, &certificate, IdentityLimits.standard()) {
    Result<IdentityMatch, IdentityError>.Success { value } => {
      // The wildcard at index 1 matches. Case does not matter.
      if value.sanIndex != 1 {
        return 1
      }
      return 0
    }
    Result<IdentityMatch, IdentityError>.Failure { error: _ } => 1
  }
}

Import as HttpsIdentity with import silk.tls.https_identity { HttpsIdentity }.

Public declarations: 13.

UnsupportedForm#

silk
pub enum UnsupportedForm

A reference form that HTTPS identity verification does not support.

IpvFuture#

silk
IpvFuture

An IPvFuture reference is unsupported.

ZoneIdentifier#

silk
ZoneIdentifier

A scoped address reference is unsupported.

UriId#

silk
UriId

A URI-ID reference is unsupported for HTTPS.

SrvId#

silk
SrvId

An SRV-ID reference is unsupported for HTTPS.

Implementation Debug for UnsupportedForm#

silk
impl Debug for UnsupportedForm

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as IpvFuture.

OriginHost#

silk
pub union OriginHost<'name>

The original HTTPS host, classified by the caller before DNS resolution.

Dns#

silk
Dns

Borrowed ASCII DNS bytes without a trailing dot.

Field bytes#

silk
pub bytes: &'name [u8]

The exact bytes. No normalization or text conversion is implicit.

Ipv4#

silk
Ipv4

Four owned network-order address octets.

Field bytes#

silk
pub bytes: [u8; 4]

The exact bytes. No normalization or text conversion is implicit.

Ipv6#

silk
Ipv6

Sixteen owned network-order address octets.

Field bytes#

silk
pub bytes: [u8; 16]

The exact bytes. No normalization or text conversion is implicit.

Unsupported#

silk
Unsupported

An explicitly unsupported reference form.

Field form#

silk
pub form: UnsupportedForm

The unsupported reference form.

ReferenceIdentity#

silk
pub union ReferenceIdentity<'name>

The host to check, from HttpsIdentity.reference. Values that you build yourself are checked again by HttpsIdentity.verify.

Dns#

silk
Dns

Borrowed ASCII DNS bytes without a trailing dot.

Field bytes#

silk
pub bytes: &'name [u8]

The exact bytes. No normalization or text conversion is implicit.

Ipv4#

silk
Ipv4

Four owned network-order address octets.

Field bytes#

silk
pub bytes: [u8; 4]

The exact bytes. No normalization or text conversion is implicit.

Ipv6#

silk
Ipv6

Sixteen owned network-order address octets.

Field bytes#

silk
pub bytes: [u8; 16]

The exact bytes. No normalization or text conversion is implicit.

PresentedIdentity#

silk
pub union PresentedIdentity<'cert>

One untrusted SAN descriptor in original certificate order.

Dns#

silk
Dns

Borrowed ASCII DNS bytes without a trailing dot.

Field bytes#

silk
pub bytes: &'cert [u8]

The exact bytes. No normalization or text conversion is implicit.

Ip#

silk
Ip

Borrowed network-order address octets. Only lengths four and sixteen are valid.

Field bytes#

silk
pub bytes: &'cert [u8]

The exact bytes. No normalization or text conversion is implicit.

Other#

silk
Other

An uninterpreted GeneralName with an alternative number other than two or seven.

Field tag#

silk
pub tag: u8

The decoded GeneralName alternative number, from zero through eight except two and seven.

Field bytes#

silk
pub bytes: &'cert [u8]

The complete alternative content octets, counted even when this form is ignored.

CertificateStructureError#

silk
pub enum CertificateStructureError

A structural failure reported by a complete SAN decoder.

InvalidDer#

silk
InvalidDer

The SAN input violates DER framing.

InvalidGeneralName#

silk
InvalidGeneralName

A GeneralName violates its structural encoding.

DuplicateSanExtension#

silk
DuplicateSanExtension

The certificate contains more than one SAN extension.

EmptySanExtension#

silk
EmptySanExtension

A present SAN extension has no GeneralNames.

Implementation Debug for CertificateStructureError#

silk
impl Debug for CertificateStructureError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as InvalidDer.

CertificateIdentities#

silk
pub union CertificateIdentities<'cert>

The SAN state of a certificate. A certificate without a SAN extension is different from one with an empty SAN extension.

Absent#

silk
Absent

The certificate has no SAN extension. Common names are never consulted.

Decoded#

silk
Decoded

All SAN descriptors in source order, with no filtered entries.

Field entries#

silk
pub entries: &'cert [PresentedIdentity<'cert>]

The complete shared descriptor slice and payloads, bounded by the same lifetime.

Malformed#

silk
Malformed

A structural failure that must prevent a match.

Field reason#

silk
pub reason: CertificateStructureError

The failed rule.

NameError#

silk
pub enum NameError

The first failed DNS admission rule under the documented precedence.

EmptyName#

silk
EmptyName

The name has no octets.

NameTooLong#

silk
NameTooLong

The name exceeds 253 octets.

EmptyLabel#

silk
EmptyLabel

A label has no octets.

LabelTooLong#

silk
LabelTooLong

A label exceeds 63 octets.

Nul#

silk
Nul

The name contains an embedded zero octet.

NonAscii#

silk
NonAscii

The name contains an octet above 127.

InvalidCharacter#

silk
InvalidCharacter

A label contains an octet outside ASCII letters, digits and hyphens.

EdgeHyphen#

silk
EdgeHyphen

A label starts or ends with a hyphen.

TrailingDot#

silk
TrailingDot

The name ends with a dot.

WildcardReference#

silk
WildcardReference

A reference contains an asterisk.

NumericFinalLabel#

silk
NumericFinalLabel

The final label contains decimal digits only.

MissingWildcardSuffix#

silk
MissingWildcardSuffix

A wildcard name has no suffix.

Implementation Debug for NameError#

silk
impl Debug for NameError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as EmptyName.

CertificateIdentityError#

silk
pub enum CertificateIdentityError

An invalid SAN descriptor form or binary address size.

InvalidIpLength#

silk
InvalidIpLength

An IP SAN has neither four nor sixteen octets.

WrongGeneralNameTag#

silk
WrongGeneralNameTag

An Other descriptor uses tag two, seven, or a number above eight.

Implementation Debug for CertificateIdentityError#

silk
impl Debug for CertificateIdentityError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as InvalidIpLength.

LimitKind#

silk
pub enum LimitKind

The exhausted matcher budget.

SanCount#

silk
SanCount

The total descriptor count exceeds the configured budget.

SanBytes#

silk
SanBytes

The sum of payload lengths exceeds the configured budget.

Implementation Debug for LimitKind#

silk
impl Debug for LimitKind

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as SanCount.

IdentityError#

silk
pub union IdentityError

An identity failure that is independent of certificate path trust.

MalformedReference#

silk
MalformedReference

The reference violates DNS admission rules.

Field reason#

silk
pub reason: NameError

The failed rule.

MalformedCertificateStructure#

silk
MalformedCertificateStructure

The complete SAN structure is malformed.

Field reason#

silk
pub reason: CertificateStructureError

The failed rule.

MalformedCertificateName#

silk
MalformedCertificateName

A DNS SAN violates name admission rules.

Field index#

silk
pub index: usize

The original zero-based SAN index.

Field reason#

silk
pub reason: NameError

The failed rule.

MalformedCertificateIdentity#

silk
MalformedCertificateIdentity

A SAN descriptor has an invalid form.

Field index#

silk
pub index: usize

The original zero-based SAN index.

Field reason#

silk
pub reason: CertificateIdentityError

The failed rule.

Unsupported#

silk
Unsupported

An explicitly unsupported reference form.

Field form#

silk
pub form: UnsupportedForm

The unsupported reference form.

LimitExceeded#

silk
LimitExceeded

A count or byte budget was exceeded.

Field kind#

silk
pub kind: LimitKind

The exhausted resource budget.

NoMatch#

silk
NoMatch

No eligible SAN matches the reference.

Implementation Debug for IdentityError#

silk
impl Debug for IdentityError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant and its fields, such as MalformedReference { ... }.

IdentityLimits#

silk
pub struct IdentityLimits

The limits for one HttpsIdentity.verify call. Each limit is inclusive, and 0 allows nothing.

Field maxSanCount#

silk
pub maxSanCount: usize

Maximum number of descriptors, including ignored and unsupported forms.

Field maxSanBytes#

silk
pub maxSanBytes: usize

Maximum sum of all payload lengths, counting each alias occurrence.

IdentityMatch#

silk
pub struct IdentityMatch

The first matching SAN index after validation of the entire SAN set.

Field sanIndex#

silk
pub sanIndex: usize

The original zero-based index of the first matching SAN.

HttpsIdentity#

silk
pub struct HttpsIdentity

Checks that a server certificate is for the host name or IP address in an HTTPS URL (RFC 6125 and RFC 9110).

When to use#

After certificate path validation succeeds, call HttpsIdentity.reference with the host of the URL, and then HttpsIdentity.verify with the subject alternative names of the leaf certificate. silk.tls.client does this for you.

Details#

DNS names compare without regard to ASCII case. A wildcard is allowed only as the full leftmost label of a certificate name, and it matches exactly one label: *.example.com matches api.example.com, but not example.com or a.b.example.com. An IP address matches only an IP SAN with the same bytes. Nothing allocates.

Gotchas#

Use the host from the URL, before DNS resolution. Never use a resolved address or a CNAME target. The common name (CN) is never used. Names with a trailing dot and Unicode names are rejected: convert an internationalized name to its xn-- form first. A match does not prove that the chain is trusted.

Implementation Copy for OriginHost<'a>#

silk
impl<'a> Copy for OriginHost<'a>

Implementation Copy for ReferenceIdentity<'a>#

silk
impl<'a> Copy for ReferenceIdentity<'a>

Implementation Copy for PresentedIdentity<'a>#

silk
impl<'a> Copy for PresentedIdentity<'a>

Implementation Copy for CertificateIdentities<'a>#

silk
impl<'a> Copy for CertificateIdentities<'a>

Implementation Copy for IdentityError#

silk
impl Copy for IdentityError

Implementation Copy for IdentityLimits#

silk
impl Copy for IdentityLimits

Implementation Copy for IdentityMatch#

silk
impl Copy for IdentityMatch

Implementation IdentityLimits#

silk
impl IdentityLimits

Associated function standard#

silk
pub fn standard() -> IdentityLimits

Returns inclusive limits of 256 SAN descriptors and 65536 payload octets.

Implementation HttpsIdentity#

silk
impl HttpsIdentity

Associated function exactDnsNameError#

silk
pub fn exactDnsNameError(bytes: &[u8]) -> Option<NameError>

Returns the shared HTTPS-profile admission error for one exact DNS name, when invalid.

Details#

This applies the canonical 253-octet, LDH-label, edge-hyphen, wildcard-free, and nonnumeric-final-label grammar used by HTTPS reference identities.

Associated function presentedDnsNameError#

silk
pub fn presentedDnsNameError(bytes: &[u8]) -> Option<NameError>

Returns the shared HTTPS-profile admission error for one presented DNS name, when invalid.

Details#

A wildcard is admitted only as the complete leftmost *. label and remains included in the 253-octet total-name bound. All other label policy is identical to exact DNS names.

Implementation HttpsIdentity#

silk
impl HttpsIdentity

Associated function reference#

silk
pub fn reference<'name>(host: OriginHost<'name>) -> Result<
    ReferenceIdentity<'name>,
    IdentityError,
  >

Checks the host of an HTTPS URL and returns it as a reference to verify. It does not allocate.

A DNS name must be 1 to 253 ASCII bytes, with labels of 1 to 63 letters, digits, and hyphens. A label must not start or end with a hyphen, and the last label must not be all digits. An IP address must already be converted to its 4 or 16 bytes.

Errors#

  • MalformedReference with the first rule that the name breaks.

  • Unsupported for an IPvFuture address, a zone identifier, a URI-ID, or an SRV-ID.

Gotchas#

Use the host from the URL, before DNS resolution. Remove brackets and the port first. An xn-- label is not checked as Punycode.

Associated function verify#

silk
pub fn verify<'name, 'cert>(
    reference: &ReferenceIdentity<'name>,
    certificate: &CertificateIdentities<'cert>,
    limits: IdentityLimits,
  ) -> Result<IdentityMatch, IdentityError>

Checks all SANs of certificate, and returns the index of the first one that matches reference.

DNS names compare without regard to ASCII case. A wildcard SAN matches one label. A wildcard in another position is ignored. An IP SAN must have the same 4 or 16 bytes. An IPv4 address does not match its IPv6-mapped form.

Errors#

  • NoMatch if no SAN matches, the certificate has no SAN, or it has only other name forms.

  • MalformedCertificateStructure, MalformedCertificateName, or MalformedCertificateIdentity if a SAN is not valid.

  • LimitExceeded if the SANs go over limits.

  • MalformedReference if reference is not valid.

Gotchas#

One SAN that is not valid fails the call, also when an earlier SAN matches. A match does not check the chain, the dates, or the TLS handshake. The common name (CN) is never used.

Category: TLS and certificates.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.

View the source of this page