Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 11 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "darkbio-crypto"
version = "0.11.11"
version = "0.12.0"
authors = ["Péter Szilágyi <peter@dark.bio>"]
description = "Cryptography primitives and wrappers"
repository = "https://github.com/dark-bio/crypto-rs"
Expand All @@ -22,7 +22,7 @@ exclude = ["fuzz"]
default = []
argon2 = ["dep:argon2"]
cbor = ["dep:thiserror", "dep:darkbio-crypto-cbor-derive"]
cose = ["cbor", "xhpke"]
cose = ["cbor", "xdsa", "xhpke"]
eddsa = [
"pem",
"dep:der",
Expand All @@ -47,18 +47,21 @@ mldsa = [
]
pem = ["dep:base64"]
rand = []
rsa = ["pem", "dep:rsa", "dep:serde", "dep:signature"]
rsa = ["pem", "dep:rsa", "dep:serde"]
stream = ["dep:age-core", "dep:chacha20poly1305", "dep:pin-project", "dep:zeroize"]
x509 = ["xdsa", "dep:chrono", "dep:const-oid", "dep:sha1", "dep:x509-cert"]
xdsa = ["pem", "eddsa", "mldsa", "x509", "dep:x509-parser"]
x509 = ["xdsa", "pem", "dep:const-oid", "dep:sha1", "dep:thiserror", "dep:x509-cert", "dep:x509-parser"]
xdsa = ["pem", "eddsa", "mldsa"]
xhpke = [
"pem",
"xdsa",
"dep:generic-array",
"dep:hpke",
"dep:pkcs8",
"dep:rand",
"dep:rand_chacha",
"dep:serde",
"dep:sha2",
"dep:spki",
"dep:subtle",
"dep:x-wing",
]

Expand All @@ -69,7 +72,7 @@ base64 = { version = "0.22.1", optional = true }
chacha20poly1305 = { version = "0.10.1", optional = true }
chrono = { version = "0.4.41", optional = true }
const-oid = { version = "0.9.6", optional = true }
darkbio-crypto-cbor-derive = { version = "0.11.11", path = "src/cbor/derive", optional = true }
darkbio-crypto-cbor-derive = { version = "0.12.0", path = "src/cbor/derive", optional = true }
der = { version = "0.7.10", features = ["derive"], optional = true }
ed25519-dalek = { version = "2.2.0", features = ["pem", "rand_core"], optional = true }
generic-array = { version = "0.14.7", optional = true }
Expand Down Expand Up @@ -102,4 +105,4 @@ hex = "0.4"

[[example]]
name = "demo"
required-features = ["cose"]
required-features = ["cose", "x509"]
100 changes: 55 additions & 45 deletions examples/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,16 @@ fn main() {
let alice_xdsa_public = alice_xdsa_secret.public_key();

// Create a certificate for Alice's xDSA key, signed by the root (intermediate CA)
let alice_xdsa_params = x509::Params {
subject_name: "Alice Identity",
issuer_name: "Root",
let alice_xdsa_template = x509::Certificate {
subject: x509::Name::new().cn("Alice Identity"),
issuer: x509::Name::new().cn("Root"),
not_before: now,
not_after: end,
is_ca: true,
path_len: Some(0),
role: x509::Role::Authority { path_len: Some(0) },
..Default::default()
};
let alice_xdsa_cert = alice_xdsa_public
.to_cert_pem(&root_secret, &alice_xdsa_params)
.unwrap();
let alice_xdsa_cert =
xdsa::issue_cert_pem(&alice_xdsa_public, &root_secret, &alice_xdsa_template).unwrap();
println!(
" Alice xDSA fingerprint: {}",
hex::encode(alice_xdsa_public.fingerprint().to_bytes())
Expand All @@ -67,17 +66,16 @@ fn main() {
let bob_xdsa_public = bob_xdsa_secret.public_key();

// Create a certificate for Bob's xDSA key, signed by the root (intermediate CA)
let bob_xdsa_params = x509::Params {
subject_name: "Bob Identity",
issuer_name: "Root",
let bob_xdsa_template = x509::Certificate {
subject: x509::Name::new().cn("Bob Identity"),
issuer: x509::Name::new().cn("Root"),
not_before: now,
not_after: end,
is_ca: true,
path_len: Some(0),
role: x509::Role::Authority { path_len: Some(0) },
..Default::default()
};
let bob_xdsa_cert = bob_xdsa_public
.to_cert_pem(&root_secret, &bob_xdsa_params)
.unwrap();
let bob_xdsa_cert =
xdsa::issue_cert_pem(&bob_xdsa_public, &root_secret, &bob_xdsa_template).unwrap();
println!(
" Bob xDSA fingerprint: {}",
hex::encode(bob_xdsa_public.fingerprint().to_bytes())
Expand All @@ -91,17 +89,20 @@ fn main() {
let alice_xhpke_public = alice_xhpke_secret.public_key();

// Create a certificate for Alice's HPKE key, signed by her xDSA key
let alice_xhpke_params = x509::Params {
subject_name: "Alice Encryption",
issuer_name: "Alice",
let alice_xhpke_template = x509::Certificate {
subject: x509::Name::new().cn("Alice Encryption"),
issuer: x509::Name::new().cn("Alice Identity"),
not_before: now,
not_after: end,
is_ca: false,
path_len: None,
role: x509::Role::Leaf,
..Default::default()
};
let alice_xhpke_cert = alice_xhpke_public
.to_cert_pem(&alice_xdsa_secret, &alice_xhpke_params)
.unwrap();
let alice_xhpke_cert = xhpke::issue_cert_pem(
&alice_xhpke_public,
&alice_xdsa_secret,
&alice_xhpke_template,
)
.unwrap();
println!(
" Alice xHPKE fingerprint: {}",
hex::encode(alice_xhpke_public.fingerprint().to_bytes())
Expand All @@ -115,17 +116,16 @@ fn main() {
let bob_xhpke_public = bob_xhpke_secret.public_key();

// Create a certificate for Bob's HPKE key, signed by his xDSA key
let bob_xhpke_params = x509::Params {
subject_name: "Bob Encryption",
issuer_name: "Bob",
let bob_xhpke_template = x509::Certificate {
subject: x509::Name::new().cn("Bob Encryption"),
issuer: x509::Name::new().cn("Bob Identity"),
not_before: now,
not_after: end,
is_ca: false,
path_len: None,
role: x509::Role::Leaf,
..Default::default()
};
let bob_xhpke_cert = bob_xhpke_public
.to_cert_pem(&bob_xdsa_secret, &bob_xhpke_params)
.unwrap();
let bob_xhpke_cert =
xhpke::issue_cert_pem(&bob_xhpke_public, &bob_xdsa_secret, &bob_xhpke_template).unwrap();
println!(
" Bob xHPKE fingerprint: {}",
hex::encode(bob_xhpke_public.fingerprint().to_bytes())
Expand All @@ -137,15 +137,20 @@ fn main() {
println!("\n6. Alice verifies Bob's identity chain...");

// Alice verifies Bob's xDSA certificate against the root
let (verified_bob_xdsa, _, _) =
xdsa::PublicKey::from_cert_pem(&bob_xdsa_cert, root_public.clone())
let verified_bob_xdsa =
xdsa::verify_cert_pem(&bob_xdsa_cert, &root_public, x509::ValidityCheck::Now)
.expect("Failed to verify Bob's xDSA cert against root");
println!(" ✓ Bob's xDSA cert verified against root");

// Alice verifies Bob's xHPKE certificate against Bob's xDSA key
let (verified_bob_xhpke, _, _) =
xhpke::PublicKey::from_cert_pem(&bob_xhpke_cert, verified_bob_xdsa.clone())
.expect("Failed to verify Bob's xHPKE cert against his xDSA");
// Alice verifies Bob's xHPKE certificate against Bob's xDSA certificate
// (enforcing issuer authorization semantics: CA + keyCertSign/cRLSign).
let verified_bob_xhpke = xhpke::verify_cert_pem_with_issuer(
&bob_xhpke_cert,
&verified_bob_xdsa,
x509::ValidityCheck::Now,
)
.expect("Failed to verify Bob's xHPKE cert against his xDSA")
.public_key;
println!(" ✓ Bob's xHPKE cert verified against his xDSA");

// =========================================================================
Expand All @@ -154,15 +159,20 @@ fn main() {
println!("\n7. Bob verifies Alice's identity chain...");

// Bob verifies Alice's xDSA certificate against the root
let (verified_alice_xdsa, _, _) =
xdsa::PublicKey::from_cert_pem(&alice_xdsa_cert, root_public.clone())
let verified_alice_xdsa =
xdsa::verify_cert_pem(&alice_xdsa_cert, &root_public, x509::ValidityCheck::Now)
.expect("Failed to verify Alice's xDSA cert against root");
println!(" ✓ Alice's xDSA cert verified against root");

// Bob verifies Alice's xHPKE certificate against Alice's xDSA key
let (_verified_alice_xhpke, _, _) =
xhpke::PublicKey::from_cert_pem(&alice_xhpke_cert, verified_alice_xdsa.clone())
.expect("Failed to verify Alice's xHPKE cert against her xDSA");
// Bob verifies Alice's xHPKE certificate against Alice's xDSA certificate
// (enforcing issuer authorization semantics: CA + keyCertSign/cRLSign).
let _verified_alice_xhpke = xhpke::verify_cert_pem_with_issuer(
&alice_xhpke_cert,
&verified_alice_xdsa,
x509::ValidityCheck::Now,
)
.expect("Failed to verify Alice's xHPKE cert against her xDSA")
.public_key;
println!(" ✓ Alice's xHPKE cert verified against her xDSA");

// =========================================================================
Expand Down Expand Up @@ -197,7 +207,7 @@ fn main() {
&ciphertext,
&(),
&bob_xhpke_secret,
&verified_alice_xdsa,
&verified_alice_xdsa.public_key,
b"demo-crypto-domain",
None,
)
Expand Down
2 changes: 1 addition & 1 deletion src/cbor/derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "darkbio-crypto-cbor-derive"
version = "0.11.11"
version = "0.12.0"
authors = ["Péter Szilágyi <peter@dark.bio>"]
description = "CBOR derive macros for darkbio-crypto"
repository = "https://github.com/dark-bio/crypto-rs"
Expand Down
42 changes: 42 additions & 0 deletions src/x509/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// crypto-rs: cryptography primitives and wrappers
// Copyright 2026 Dark Bio AG. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

use std::error::Error as StdError;
use thiserror::Error;

/// Result type used by x509 APIs.
pub type Result<T> = std::result::Result<T, Error>;

/// Error type used by x509 APIs.
#[derive(Debug, Error)]
pub enum Error {
#[error("certificate {field} DN must not be empty")]
EmptyDistinguishedName { field: &'static str },
#[error("not_before must be before not_after")]
InvalidValidity,
#[error("custom extension OID under 2.5.29 is reserved")]
ReservedExtensionOid,
#[error("duplicate extension OID in certificate: {oid}")]
DuplicateExtensionOid { oid: String },
#[error("key type can only be end-entity certificate")]
MustBeLeaf,
#[error("invalid key usage: {details}")]
InvalidKeyUsage { details: &'static str },
#[error("certificate is not valid at requested time")]
ExpiredCertificate,
#[error("X.509 parse error: {details}")]
X509Parse { details: String },
#[error("invalid path length constraint: {details}")]
InvalidPathLen { details: &'static str },
#[error("invalid issuer: {details}")]
InvalidIssuer { details: &'static str },
#[error(transparent)]
Der(#[from] der::Error),
#[error(transparent)]
Oid(#[from] const_oid::Error),
#[error(transparent)]
External(#[from] Box<dyn StdError>),
}
Loading