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#
IdentityErrorif 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#
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#
pub enum UnsupportedFormA reference form that HTTPS identity verification does not support.
IpvFuture#
IpvFutureAn IPvFuture reference is unsupported.
ZoneIdentifier#
ZoneIdentifierA scoped address reference is unsupported.
UriId#
UriIdA URI-ID reference is unsupported for HTTPS.
SrvId#
SrvIdAn SRV-ID reference is unsupported for HTTPS.
Implementation Debug for UnsupportedForm#
impl Debug for UnsupportedFormOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as IpvFuture.
OriginHost#
pub union OriginHost<'name>The original HTTPS host, classified by the caller before DNS resolution.
Dns#
DnsBorrowed ASCII DNS bytes without a trailing dot.
Field bytes#
pub bytes: &'name [u8]The exact bytes. No normalization or text conversion is implicit.
Ipv4#
Ipv4Four owned network-order address octets.
Field bytes#
pub bytes: [u8; 4]The exact bytes. No normalization or text conversion is implicit.
Ipv6#
Ipv6Sixteen owned network-order address octets.
Field bytes#
pub bytes: [u8; 16]The exact bytes. No normalization or text conversion is implicit.
Unsupported#
UnsupportedAn explicitly unsupported reference form.
Field form#
pub form: UnsupportedFormThe unsupported reference form.
ReferenceIdentity#
pub union ReferenceIdentity<'name>The host to check, from HttpsIdentity.reference. Values that you build yourself are checked
again by HttpsIdentity.verify.
Dns#
DnsBorrowed ASCII DNS bytes without a trailing dot.
Field bytes#
pub bytes: &'name [u8]The exact bytes. No normalization or text conversion is implicit.
Ipv4#
Ipv4Four owned network-order address octets.
Field bytes#
pub bytes: [u8; 4]The exact bytes. No normalization or text conversion is implicit.
Ipv6#
Ipv6Sixteen owned network-order address octets.
Field bytes#
pub bytes: [u8; 16]The exact bytes. No normalization or text conversion is implicit.
PresentedIdentity#
pub union PresentedIdentity<'cert>One untrusted SAN descriptor in original certificate order.
Dns#
DnsBorrowed ASCII DNS bytes without a trailing dot.
Field bytes#
pub bytes: &'cert [u8]The exact bytes. No normalization or text conversion is implicit.
Ip#
IpBorrowed network-order address octets. Only lengths four and sixteen are valid.
Field bytes#
pub bytes: &'cert [u8]The exact bytes. No normalization or text conversion is implicit.
Other#
OtherAn uninterpreted GeneralName with an alternative number other than two or seven.
Field tag#
pub tag: u8The decoded GeneralName alternative number, from zero through eight except two and seven.
Field bytes#
pub bytes: &'cert [u8]The complete alternative content octets, counted even when this form is ignored.
CertificateStructureError#
pub enum CertificateStructureErrorA structural failure reported by a complete SAN decoder.
InvalidDer#
InvalidDerThe SAN input violates DER framing.
InvalidGeneralName#
InvalidGeneralNameA GeneralName violates its structural encoding.
DuplicateSanExtension#
DuplicateSanExtensionThe certificate contains more than one SAN extension.
EmptySanExtension#
EmptySanExtensionA present SAN extension has no GeneralNames.
Implementation Debug for CertificateStructureError#
impl Debug for CertificateStructureErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as InvalidDer.
CertificateIdentities#
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#
AbsentThe certificate has no SAN extension. Common names are never consulted.
Decoded#
DecodedAll SAN descriptors in source order, with no filtered entries.
Field entries#
pub entries: &'cert [PresentedIdentity<'cert>]The complete shared descriptor slice and payloads, bounded by the same lifetime.
Malformed#
MalformedA structural failure that must prevent a match.
Field reason#
pub reason: CertificateStructureErrorThe failed rule.
NameError#
pub enum NameErrorThe first failed DNS admission rule under the documented precedence.
EmptyName#
EmptyNameThe name has no octets.
NameTooLong#
NameTooLongThe name exceeds 253 octets.
EmptyLabel#
EmptyLabelA label has no octets.
LabelTooLong#
LabelTooLongA label exceeds 63 octets.
Nul#
NulThe name contains an embedded zero octet.
NonAscii#
NonAsciiThe name contains an octet above 127.
InvalidCharacter#
InvalidCharacterA label contains an octet outside ASCII letters, digits and hyphens.
EdgeHyphen#
EdgeHyphenA label starts or ends with a hyphen.
TrailingDot#
TrailingDotThe name ends with a dot.
WildcardReference#
WildcardReferenceA reference contains an asterisk.
NumericFinalLabel#
NumericFinalLabelThe final label contains decimal digits only.
MissingWildcardSuffix#
MissingWildcardSuffixA wildcard name has no suffix.
Implementation Debug for NameError#
impl Debug for NameErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as EmptyName.
CertificateIdentityError#
pub enum CertificateIdentityErrorAn invalid SAN descriptor form or binary address size.
InvalidIpLength#
InvalidIpLengthAn IP SAN has neither four nor sixteen octets.
WrongGeneralNameTag#
WrongGeneralNameTagAn Other descriptor uses tag two, seven, or a number above eight.
Implementation Debug for CertificateIdentityError#
impl Debug for CertificateIdentityErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as InvalidIpLength.
LimitKind#
pub enum LimitKindThe exhausted matcher budget.
SanCount#
SanCountThe total descriptor count exceeds the configured budget.
SanBytes#
SanBytesThe sum of payload lengths exceeds the configured budget.
Implementation Debug for LimitKind#
impl Debug for LimitKindOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as SanCount.
IdentityError#
pub union IdentityErrorAn identity failure that is independent of certificate path trust.
MalformedReference#
MalformedReferenceThe reference violates DNS admission rules.
Field reason#
pub reason: NameErrorThe failed rule.
MalformedCertificateStructure#
MalformedCertificateStructureThe complete SAN structure is malformed.
Field reason#
pub reason: CertificateStructureErrorThe failed rule.
MalformedCertificateName#
MalformedCertificateNameA DNS SAN violates name admission rules.
Field index#
pub index: usizeThe original zero-based SAN index.
Field reason#
pub reason: NameErrorThe failed rule.
MalformedCertificateIdentity#
MalformedCertificateIdentityA SAN descriptor has an invalid form.
Field index#
pub index: usizeThe original zero-based SAN index.
Field reason#
pub reason: CertificateIdentityErrorThe failed rule.
Unsupported#
UnsupportedAn explicitly unsupported reference form.
Field form#
pub form: UnsupportedFormThe unsupported reference form.
LimitExceeded#
LimitExceededA count or byte budget was exceeded.
Field kind#
pub kind: LimitKindThe exhausted resource budget.
NoMatch#
NoMatchNo eligible SAN matches the reference.
Implementation Debug for IdentityError#
impl Debug for IdentityErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant and its fields, such as MalformedReference { ... }.
IdentityLimits#
pub struct IdentityLimitsThe limits for one HttpsIdentity.verify call. Each limit is inclusive, and 0 allows nothing.
Field maxSanCount#
pub maxSanCount: usizeMaximum number of descriptors, including ignored and unsupported forms.
Field maxSanBytes#
pub maxSanBytes: usizeMaximum sum of all payload lengths, counting each alias occurrence.
IdentityMatch#
pub struct IdentityMatchThe first matching SAN index after validation of the entire SAN set.
Field sanIndex#
pub sanIndex: usizeThe original zero-based index of the first matching SAN.
HttpsIdentity#
pub struct HttpsIdentityChecks 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>#
impl<'a> Copy for OriginHost<'a>Implementation Copy for ReferenceIdentity<'a>#
impl<'a> Copy for ReferenceIdentity<'a>Implementation Copy for PresentedIdentity<'a>#
impl<'a> Copy for PresentedIdentity<'a>Implementation Copy for CertificateIdentities<'a>#
impl<'a> Copy for CertificateIdentities<'a>Implementation Copy for IdentityError#
impl Copy for IdentityErrorImplementation Copy for IdentityLimits#
impl Copy for IdentityLimitsImplementation Copy for IdentityMatch#
impl Copy for IdentityMatchImplementation IdentityLimits#
impl IdentityLimitsAssociated function standard#
pub fn standard() -> IdentityLimitsReturns inclusive limits of 256 SAN descriptors and 65536 payload octets.
Implementation HttpsIdentity#
impl HttpsIdentityAssociated function exactDnsNameError#
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#
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#
impl HttpsIdentityAssociated function reference#
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#
MalformedReferencewith the first rule that the name breaks.Unsupportedfor 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#
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#
NoMatchif no SAN matches, the certificate has no SAN, or it has only other name forms.MalformedCertificateStructure,MalformedCertificateName, orMalformedCertificateIdentityif a SAN is not valid.LimitExceededif the SANs go overlimits.MalformedReferenceifreferenceis 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.