Skip to content

About

A C++, header-only DID:x509 resolver

Resources

Code of conduct

Security policy

Stars

18 stars

Watchers

5 watching

Forks

Repository files navigation

didx509cpp

A header-only C++ library for verification of did:x509 identifiers. The method is registered within the W3C DID Extensions registry and listed in the W3C DID Extensions Methods registry.

Continuous Integration CodeQL

Requirements

  • C++20.
  • OpenSSL 3.3 or newer, including development headers and libcrypto. Older OpenSSL versions are not supported.
  • CMake 3.18 or newer for CMake-based builds.

For a non-system OpenSSL installation, pass -DOPENSSL_ROOT_DIR=<prefix> when configuring CMake.

Usage

#include <didx509cpp.h>

std::string pem_chain = ...;
std::string did = "did:x509:0:sha256:WE4P5dd8DnLHSkyHaIjhp4udlkF9LqoKwCvu9gl38jk::eku:1.3.6.1.4.1.311.10.3.13";

try {
    std::string doc = resolve(pem_chain, did);
} catch (...)
{...}
    
// Or when resolving a historical did, for example for audit purposes
    
try {
    std::string doc = resolve(pem_chain, did, true /* Ignore time */));
} catch (...)
{...}

Fulcio identities

The method-version-0 fulcio predicate selects a registered standalone Fulcio extension on the leaf certificate. For Issuer V2 (1.3.6.1.4.1.57264.1.8), use the full percent-encoded issuer, including its scheme:

::fulcio:issuer:https%3A%2F%2Ftoken.actions.githubusercontent.com

All 17 registered fields are supported, including source/build metadata, deployment-environment, and token-subject. Each predicate requires exactly one literal field and one nonempty percent-encoded UTF-8 value. Multiple predicates are ANDed.

Fulcio username identities use the registered OtherName type inside the SAN extension, independently of standalone Fulcio extensions:

::san:othername:1.3.6.1.4.1.57264.1.7:alice%21example.com

The type OID must be literal, and the complete username SAN (alice!example.com here) is compared exactly, not reconstructed from token-subject. Scalar values are decoded once, without case folding, URI rewriting, or Unicode normalization.

The existing fulcio-issuer predicate still selects only the legacy raw UTF-8 extension .1, with https:// omitted from the predicate value. It never falls back to .8, and fulcio:issuer never falls back to .1. DID URL fragments are excluded from predicate matching and document IDs; percent-escape spelling in the DID is preserved. DID URL paths and queries are rejected. Slash and question-mark characters in identity values must be percent-encoded; they remain permitted inside fragments. All scalar predicates, including existing SAN, subject, and legacy issuer forms, require nonempty, correctly percent-encoded UTF-8 values. The eku predicate value must be a canonical dotted-decimal OID. Subject names that repeat an attribute type are not supported by the specification's certificate model and are rejected rather than matched against any one of their values.

Registered OtherName values and standalone .8-.24 extensions require a complete, minimally encoded DER UTF8String. Malformed or unsupported SAN entries and malformed registered Fulcio extensions fail resolution even when unselected or present on a CA certificate. Standalone Fulcio extensions must be noncritical; normal certificate path validation is not bypassed. Email, DNS, and URI SANs must contain valid ASCII IA5Strings. The supplied certificates must match the verified leaf-first chain exactly, so path building cannot silently discard malformed or duplicate evidence.

DID document shape

Resolution produces the DID document defined by the specification: the @context is https://www.w3.org/ns/cid/v1, the single verification method <DID>#0 has type JsonWebKey, and verification relationships are derived from the leaf certificate's Key Usage as described below. This only affects callers of resolve() that read @context or type from the document, which previously used JsonWebKey2020 with the DID Core and JWS 2020 contexts. The JWK itself, and therefore resolve_jwk(), is unchanged.

Supported public keys

The resolver exports RSA, EC (P-256, P-384, and P-521), and Ed25519 public keys. Ed25519 keys are exported as an RFC 8037 JWK with kty: "OKP", crv: "Ed25519", and the base64url-encoded 32-byte public key in x. The encoding must be canonical per RFC 8032 section 5.1.3: a key whose y-coordinate is not less than the field prime is rejected, even though OpenSSL accepts it.

Ed25519 keys are signing-only: the DID document includes authentication and assertionMethod, never keyAgreement. If the leaf certificate has a Key Usage extension, it must include digitalSignature and, following RFC 8410 section 5, must not include keyAgreement, keyEncipherment, dataEncipherment, encipherOnly, or decipherOnly. Without Key Usage, the key is still treated as signing-only.

RSA and EC keys include authentication and assertionMethod when Key Usage is absent or includes digitalSignature, and keyAgreement when Key Usage is absent or includes keyAgreement. A Key Usage extension with neither is rejected.

Contributing

To run clang-tidy locally, configure with Clang 18 and enable the opt-in target:

cmake -S . -B build/clang-tidy \
  -DCMAKE_CXX_COMPILER=clang++-18 \
  -DCLANG_TIDY=ON \
  -DTESTS=ON
cmake --build build/clang-tidy --target clang-tidy

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.

About

A C++, header-only DID:x509 resolver

Resources

Code of conduct

Security policy

Stars

18 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages