ma-did 0.5.1

Rust implementation of the 間 (did:ma) DID method for secure identities and messaging
Documentation

ma-did

A Rust implementation of the 間 (did:ma) DID method — a modern, lean decentralized identifier method providing secure identities as a foundation for secure messaging.

What It Provides

  • DID parsing and validationdid:ma:<ipns> with optional #fragment for sub-identities within a namespace
  • DID documentsDocument, VerificationMethod, and Proof types conforming to W3C DID v1.1
  • Cryptographic key types — Ed25519 signing keys (SigningKey) and X25519 encryption keys (EncryptionKey) with Multikey encoding
  • Multiformat pipeline — multibase (Base58btc) + multicodec encoding/decoding for public keys and signatures
  • Identity generation — one-call generate_identity() or generate_identity_from_secret() produces keys, verification methods, and a signed document
  • Document proofsMultiformatSignature2023 proof type (BLAKE3 + Ed25519 over CBOR)
  • Signed messagesMessage with BLAKE3 content hashing, Ed25519 signature, TTL, and replay-window freshness checks
  • Encrypted envelopesEnvelope using ephemeral X25519 key agreement with XChaCha20-Poly1305 AEAD
  • Serialization — JSON (marshal/unmarshal) and CBOR (to_cbor/from_cbor) for both documents and messages
  • Method-specific extension — optional ma namespace on documents for application-defined fields
  • WASM support — compiles to wasm32-unknown-unknown with JS time sources

Specification

This crate implements the formal did:ma method specification documents at bahner/ma-spec:

References

  • W3C DID Core v1.1 — Decentralized Identifiers specification
  • Nano ID — DID URL fragment generation and validation ([A-Za-z0-9_-]+)
  • Multibase / Multicodec — key and signature encoding
  • BLAKE3 — content hashing for proofs and messages
  • IPNS — DID method-specific identifier

Project Layout

  • src/constants.rs: method name, version, BLAKE3 labels
  • src/did.rs: DID model and validation
  • src/doc.rs: DID document, proof, and verification method model
  • src/error.rs: crate error types
  • src/identity.rs: key + document generation helper
  • src/key.rs: key generation, multibase encoding, Ed25519/X25519 key types
  • src/lib.rs: public exports
  • src/msg.rs: message, headers, envelope, and replay guard
  • src/multiformat.rs: multibase/multicodec encoding and decoding pipeline

Build

cargo build
cargo test

Usage

[dependencies]
ma-did = "0.5"

Release Notes

0.5.0

  • Improved API safety with #[must_use] on commonly dropped return values.
  • Fixed message timestamp tests and aligned examples with current behavior.
  • Internal multiformat encoding cleanup for clearer error boundaries.

Migration Notes (0.4 -> 0.5)

  • If your project uses -D warnings, new #[must_use] attributes may surface ignored-result warnings. Update call sites to use, store, or explicitly discard return values as needed.
  • Public crate API remains source-compatible for normal ma_did usage. The multiformat module is internal (mod multiformat) and not exported from crate root.

Identity and documents

use ma_did::{generate_identity_from_secret, Did};

// Generate a complete identity from an application-managed secret
let secret = [7u8; 32];
let identity = generate_identity_from_secret(secret).unwrap();

// The document is already signed and valid
identity.document.verify().unwrap();
identity.document.validate().unwrap();

// Serialize to JSON or CBOR
let json = identity.document.marshal().unwrap();
let cbor = identity.document.to_cbor().unwrap();

If you already have an IPNS identifier from elsewhere, you can still call generate_identity(ipns) directly as the explicit lower-level path. If not, let the application supply the secret and use generate_identity_from_secret(secret) to derive it.

DID validation

use ma_did::Did;

// Validate any DID (bare or URL)
Did::validate("did:ma:k51qzi5uqu5abc").unwrap();
Did::validate("did:ma:k51qzi5uqu5abc#lobby").unwrap();

// Validate specifically a bare DID identity (no fragment)
Did::validate_identity("did:ma:k51qzi5uqu5abc").unwrap();

// Validate specifically a DID URL (requires fragment)
Did::validate_url("did:ma:k51qzi5uqu5abc#lobby").unwrap();

Messages

use ma_did::{generate_identity_from_secret, Message, SigningKey, Did};

let alice = generate_identity_from_secret([1u8; 32]).unwrap();
let bob = generate_identity_from_secret([2u8; 32]).unwrap();

// Reconstruct signing key from stored private key bytes
let alice_sign_url = Did::new_url(&alice.subject_url.ipns, None::<String>).unwrap();
let alice_signing_key = SigningKey::from_private_key_bytes(
    alice_sign_url,
    hex::decode(&alice.signing_private_key_hex).unwrap().try_into().unwrap(),
).unwrap();

// Create a signed message
let msg = Message::new(
    alice.document.id.clone(),
    bob.document.id.clone(),
    "text/plain",
    b"hello".to_vec(),
    &alice_signing_key,
).unwrap();

// Verify message signature against sender's document
msg.verify_with_document(&alice.document).unwrap();

// Encrypt for recipient as an Envelope
let envelope = msg.enclose_for(&bob.document).unwrap();

License

GPL-3.0-only