# Extensions
## Accessing extensions
Certificate extensions are stored as `Option<RawDer<'a>>` — a zero-copy span
of the SEQUENCE OF Extension DER bytes. Access them via the certificate:
```rust,ignore
use synta_certificate::Certificate;
let extensions = &cert.tbs_certificate.extensions; // Option<RawDer<'a>>
if let Some(exts) = extensions {
let raw_bytes: &[u8] = exts.as_bytes();
// Walk manually or use get_extension_value_der()
}
```
The `Certificate` struct provides `get_extension_value_der` to find an extension
by OID and return its raw `extnValue` bytes:
```rust,ignore
use synta_certificate::{Certificate, oids};
// Returns Option<Vec<u8>> — the raw DER bytes of the extension value
let san_value = cert.get_extension_value_der(&oids::SUBJECT_ALT_NAME);
let bc_value = cert.get_extension_value_der(&oids::BASIC_CONSTRAINTS);
```
## Encoding extension values
`synta-certificate` provides four helper functions that return the raw DER bytes
for the most common extension values. These bytes go inside the OCTET STRING
wrapper in the `Extension` SEQUENCE.
### BasicConstraints
```rust,ignore
use synta_certificate::encode_basic_constraints;
// End-entity certificate (cA absent)
let ee_bc = encode_basic_constraints(false, None).unwrap();
assert_eq!(ee_bc, &[0x30, 0x00]);
// CA certificate with pathLen = 0
let ca_bc = encode_basic_constraints(true, Some(0)).unwrap();
assert_eq!(ca_bc, &[0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00]);
```
### KeyUsage
```rust,ignore
use synta_certificate::{
encode_key_usage,
KEY_USAGE_DIGITAL_SIGNATURE,
KEY_USAGE_KEY_CERT_SIGN,
KEY_USAGE_C_RLSIGN,
};
(1 << KEY_USAGE_KEY_CERT_SIGN) | (1 << KEY_USAGE_C_RLSIGN)
).unwrap();
assert_eq!(ku, &[0x03, 0x02, 0x01, 0x06]);
```
Key usage bit positions:
| `KEY_USAGE_DIGITAL_SIGNATURE` | 0 |
| `KEY_USAGE_CONTENT_COMMITMENT` | 1 |
| `KEY_USAGE_KEY_ENCIPHERMENT` | 2 |
| `KEY_USAGE_DATA_ENCIPHERMENT` | 3 |
| `KEY_USAGE_KEY_AGREEMENT` | 4 |
| `KEY_USAGE_KEY_CERT_SIGN` | 5 |
| `KEY_USAGE_C_RLSIGN` | 6 |
| `KEY_USAGE_ENCIPHER_ONLY` | 7 |
| `KEY_USAGE_DECIPHER_ONLY` | 8 |
### SubjectKeyIdentifier
```rust,ignore
use synta_certificate::{
encode_subject_key_identifier,
KeyIdMethod, OpensslKeyIdHasher,
};
let ski = encode_subject_key_identifier(
spki_der,
KeyIdMethod::Rfc5280Sha1,
&OpensslKeyIdHasher,
)?;
```
### AuthorityKeyIdentifier
```rust,ignore
use synta_certificate::{
encode_authority_key_identifier,
KeyIdMethod, OpensslKeyIdHasher,
};
// RFC 7093 method 1: SHA-256 of subjectPublicKey BIT STRING value, first 20 bytes
let aki = encode_authority_key_identifier(
issuer_spki_der,
KeyIdMethod::Rfc7093Method1Sha256,
&OpensslKeyIdHasher,
)?;
```
### KeyIdMethod variants
| `Rfc5280Sha1` | SHA-1 | BIT STRING value of `subjectPublicKey` | 20 bytes | RFC 5280 §4.2.1.2 |
| `Rfc7093Method1Sha256` | SHA-256 | BIT STRING value | first 20 bytes | RFC 7093 m1 |
| `Rfc7093Method2Sha384` | SHA-384 | BIT STRING value | first 20 bytes | RFC 7093 m2 |
| `Rfc7093Method3Sha512` | SHA-512 | BIT STRING value | first 20 bytes | RFC 7093 m3 |
| `Rfc7093Method4 { algorithm_oid }` | configurable | full `SubjectPublicKeyInfo` DER | full hash | RFC 7093 m4 |
### Custom KeyIdHasher
```rust,ignore
use synta_certificate::{KeyIdHasher, KeyIdMethod};
struct MyHasher;
impl KeyIdHasher for MyHasher {
type Error = std::convert::Infallible;
fn hash(&self, algorithm_oid: &[u32], data: &[u8]) -> Result<Vec<u8>, Self::Error> {
// dispatch on algorithm_oid and compute digest
todo!()
}
}
```
### NameConstraints
```rust,ignore
use synta_certificate::NameConstraintsBuilder;
// A leading dot constrains the entire subtree; no dot → exact host only.
.permit_dns(".example.com")
.permit_rfc822(".example.com")
.permit_ip(&[192, 168, 0, 0, 255, 255, 0, 0]) // 192.168.0.0/16
.exclude_dns(".legacy.example.com")
.exclude_ip(&[10, 0, 0, 0, 255, 0, 0, 0]) // 10.0.0.0/8
.build()
.unwrap();
// The outer SEQUENCE content starts with [0] IMPLICIT (permitted)
// and optionally [1] IMPLICIT (excluded) GeneralSubtrees, per RFC 5280 §4.2.1.10.
assert_eq!(nc_der[0], 0x30); // outer SEQUENCE
```
Available methods — permit subtrees:
| `permit_dns(dns)` | `dNSName [2]` |
| `permit_rfc822(email)` | `rfc822Name [1]` |
| `permit_ip(addr_and_mask)` | `iPAddress [7]` |
| `permit_uri(uri)` | `uniformResourceIdentifier [6]` |
| `permit_directory_name(name_der)` | `directoryName [4]` |
Excluded subtrees: `exclude_dns`, `exclude_rfc822`, `exclude_ip`, `exclude_uri`,
`exclude_directory_name` — same signatures.
When embedded in a CA certificate the extension **must** be marked critical
(RFC 5280 §4.2.1.10).
### CRLDistributionPoints
Builds a `CRLDistributionPoints` extension value (OID 2.5.29.31, RFC 5280 §4.2.1.13).
Each `full_name_*` call appends one `DistributionPoint` entry to the SEQUENCE OF.
```rust,ignore
use synta_certificate::CRLDistributionPointsBuilder;
let cdp_der = CRLDistributionPointsBuilder::new()
.full_name_uri("http://crl.example.com/ca.crl")
.build()
.unwrap();
// Multiple endpoints — HTTP primary, LDAP fallback.
let cdp_der = CRLDistributionPointsBuilder::new()
.full_name_uri("http://crl.example.com/ca.crl")
.full_name_uri("ldap://ldap.example.com/cn=CA,dc=example,dc=com?certificateRevocationList")
.build()
.unwrap();
```
Available methods:
| `full_name_uri(uri)` | `uniformResourceIdentifier [6]` |
| `full_name_dns(dns)` | `dNSName [2]` |
| `full_name_directory(name_der)` | `directoryName [4]` |
The `reasons` and `cRLIssuer` fields are not supported by this builder.
### IssuerAlternativeName
Builds an `IssuerAltName` extension value (OID 2.5.29.18, RFC 5280 §4.2.1.8).
The DER structure is a `GeneralNames` SEQUENCE — identical to `SubjectAltName`
but stored under the issuer OID.
```rust,ignore
use synta_certificate::IssuerAlternativeNameBuilder;
let ian_der = IssuerAlternativeNameBuilder::new()
.rfc822_name("ca@example.com")
.uri("http://www.example.com")
.build()
.unwrap();
```
Available methods: `dns_name`, `rfc822_name`, `uri`, `ip_address`,
`directory_name`, `registered_id` — same signatures as
`SubjectAlternativeNameBuilder`, minus `other_name`.
### IssuingDistributionPoint
Builds an `IssuingDistributionPoint` extension value (OID 2.5.29.28,
RFC 5280 §5.2.5). This extension belongs in CRL structures, not in certificates.
It must be marked critical when present.
```rust,ignore
use synta_certificate::IssuingDistributionPointBuilder;
let idp_der = IssuingDistributionPointBuilder::new()
.full_name_uri("http://crl.example.com/ca.crl")
.only_contains_user_certs(true)
.build()
.unwrap();
```
Available methods:
| `full_name_uri(uri)` | Set the distribution point to a URI full name. |
| `full_name_dns(dns)` | Set the distribution point to a DNS name full name. |
| `only_contains_user_certs(bool)` | Limit CRL scope to end-entity certificate revocations. |
| `only_contains_cacerts(bool)` | Limit CRL scope to CA certificate revocations. |
| `indirect_crl(bool)` | Indicate that the CRL may cover certificates from other CAs. |
| `only_contains_attribute_certs(bool)` | Limit CRL scope to attribute certificate revocations. |
All boolean flags default to `false` (omitted in DER per DEFAULT FALSE semantics).
### CertificatePolicies
Builds a `CertificatePolicies` extension value (OID 2.5.29.32, RFC 5280 §4.2.1.4).
Each call appends one `PolicyInformation` entry to the SEQUENCE OF. Only
`id-qt-cps` (CPS URI, `1.3.6.1.5.5.7.2.1`) qualifiers are supported.
```rust,ignore
use synta_certificate::CertificatePoliciesBuilder;
// Add two policies: one without qualifiers, one with a CPS URI.
let cp_der = CertificatePoliciesBuilder::new()
.add_policy(&[2, 23, 140, 1, 2, 1]) // 2.23.140.1.2.1 — BR DV
.add_policy_cps(
&[2, 23, 140, 1, 2, 2], // 2.23.140.1.2.2 — BR OV
"https://www.example.com/cps",
)
.build()
.unwrap();
```
The `cps_uri` argument to `add_policy_cps` must be ASCII; non-ASCII characters
cause `.build()` to return an error.
## CMS EncryptedData (openssl feature)
```rust
use synta_certificate::{
CmsEncryptor as _, CmsDecryptor as _,
OpensslEncryptor, OpensslDecryptor,
pkcs7_types, pkcs12_types,
};
let key: [u8; 16] = *b"0123456789abcdef";
let plaintext = b"secret payload";
// Encrypt → DER-encoded EncryptedData SEQUENCE
let der = OpensslEncryptor
.create_encrypted_data(
pkcs7_types::ID_DATA,
pkcs12_types::ID_AES128_CBC,
plaintext,
&key,
)
.expect("encryption failed");
```
## New protocol and extension builders
`synta-certificate` includes fluent builders for several additional RFC-defined
structures. Each builder follows the same pattern: set required and optional
fields, then call `.build()` which returns `Result<Vec<u8>, String>`.
### TimeStampReqBuilder (RFC 3161)
```rust,ignore
use synta_certificate::{TimeStampReqBuilder, oids};
let hash = sha256_of(data);
let req_der = TimeStampReqBuilder::new()
.message_imprint(oids::ID_SHA256, &hash)
.nonce(&nonce_bytes)
.cert_req(true)
.build()
.unwrap();
```
Use `message_imprint_with_alg_der` when you have a pre-encoded
`AlgorithmIdentifier` DER blob instead of an OID component slice.
### ESS builders (RFC 2634)
```rust,ignore
use synta_certificate::{SigningCertificateBuilder, ReceiptRequestBuilder, ESSSecurityLabelBuilder};
// SigningCertificate — identifies signer cert by SHA-1 hash
let cert_sha1 = [0u8; 20]; // SHA-1 of the signer's DER certificate
let sc_der = SigningCertificateBuilder::new()
.add_cert_id(&cert_sha1, None)
.build()
.unwrap();
// ReceiptRequest — requests signed receipts
let rr_der = ReceiptRequestBuilder::new()
.signed_content_identifier(b"\x01\x02\x03\x04")
.receipts_from_all()
.add_receipt_to_email("receipts@example.com")
.build()
.unwrap();
// ESSSecurityLabel — information security label
let label_der = ESSSecurityLabelBuilder::new()
.security_policy(&[1, 3, 6, 1, 5, 5, 7, 13, 1])
.classification(4) // SECRET
.privacy_mark_utf8("SECRET")
.build()
.unwrap();
```
### PKCS#5 builders (RFC 8018)
```rust,ignore
use synta_certificate::{Pbkdf2ParamsBuilder, Pbes2ParamsBuilder, pkcs5_types};
let kdf_der = Pbkdf2ParamsBuilder::new()
.salt(&salt)
.iteration_count(600_000)
.key_length(32)
.prf(pkcs5_types::ID_HMAC_WITH_SHA256_P5)
.build_as_algorithm_identifier(pkcs5_types::ID_PBKDF2)
.unwrap();
let pbes2_der = Pbes2ParamsBuilder::new()
.key_derivation_func(&kdf_der)
.encryption_scheme_from_oid(pkcs5_types::AES256_CBC_PAD, &iv)
.build()
.unwrap();
```
### LogotypeExtnBuilder (RFC 9399)
```rust,ignore
use synta_certificate::{LogotypeExtnBuilder, LogotypeDetailsSpec, oids};
let extn_der = LogotypeExtnBuilder::new()
.subject_logo_direct(LogotypeDetailsSpec {
media_type: "image/png",
hash_alg_oid: oids::ID_SHA256,
hash_value: &img_hash,
uri: "https://www.example.com/logo.png",
})
.build()
.unwrap();
```
### AuthenticationContextsBuilder (RFC 7773)
```rust,ignore
use synta_certificate::AuthenticationContextsBuilder;
let extn_der = AuthenticationContextsBuilder::new()
.add("urn:id:skatteverket:2:1.0", None)
.add("urn:id:skatteverket:1:1.0", Some("https://www.skatteverket.se/ac/context.xml"))
.build()
.unwrap();
```