atproto-attestation
Utilities for creating and verifying AT Protocol record attestations using the CID-first workflow.
Overview
A Rust library implementing the CID-first attestation specification for AT Protocol records. This crate provides cryptographic signature creation and verification for records, supporting both inline attestations (signatures embedded directly in records) and remote attestations (separate proof records with strongRef references).
The attestation workflow ensures deterministic signing payloads and prevents replay attacks by:
- Automatically preparing records with
$sigmetadata containing$typeandrepositoryfields - Generating content identifiers (CIDs) using DAG-CBOR serialization
- Signing CID bytes with elliptic curve cryptography (for inline attestations)
- Normalizing signatures to low-S form to prevent malleability attacks
- Embedding signatures or creating proof records with strongRef references
Critical Security Feature: The repository field in $sig metadata binds attestations to specific repositories, preventing replay attacks where an attacker might attempt to clone records from one repository into their own.
Features
- Inline attestations: Embed cryptographic signatures directly in record structures
- Remote attestations: Create separate proof records with CID-based strongRef references
- CID-first workflow: Deterministic signing based on content identifiers
- Multi-curve support: Full support for P-256, P-384, and K-256 elliptic curves
- Signature normalization: Automatic low-S normalization for ECDSA signatures to prevent malleability
- Flexible input types: Accept records as JSON strings, JSON values, or typed lexicons
- Repository binding: Automatic prevention of replay attacks
CLI Tools
The following command-line tools are available when built with the clap and tokio features:
atproto-attestation-sign: Sign AT Protocol records with inline or remote attestationsatproto-attestation-verify: Verify cryptographic signatures on AT Protocol records
Library Usage
Creating Inline Attestations
Inline attestations embed the signature bytes directly in the record:
use ;
use ;
use json;
The resulting record will have a signatures array:
Creating Remote Attestations
Remote attestations create a separate proof record that must be stored in a repository:
use ;
use json;
Verifying Signatures
Verify all signatures in a record:
use ;
use IdentityDocumentKeyResolver;
use HttpRecordResolver;
async
Command Line Usage
Signing Records
Inline Attestation
# Sign with inline attestation (signature embedded in record)
# Using JSON strings instead of files
# Read record from stdin
|
Remote Attestation
# Create remote attestation (generates proof record + strongRef)
# This outputs TWO JSON objects:
# 1. Proof record (store this in the attestor's repository)
# 2. Source record with strongRef attestation
Verifying Signatures
# Verify all signatures in a record from file
# Verify from stdin
|
# Verify from inline JSON
# Verify specific attestation against record
Public API
The crate exposes the following public functions:
Attestation Creation
-
create_inline_attestation: Create a signed record with embedded signature- Automatically normalizes signatures to low-S form
- Binds attestation to repository DID
- Returns signed record with
signaturesarray
-
create_remote_attestation: Create separate proof record and strongRef- Returns tuple of (attested_record, proof_record)
- Proof record must be stored in attestor's repository
CID Generation
create_cid: Generate CID for a record with$sigmetadatacreate_dagbor_cid: Generate CID for any serializable datacreate_attestation_cid: High-level CID generation with automatic$sigpreparation
Signature Operations
normalize_signature: Normalize raw signature bytes to low-S form- Prevents signature malleability attacks
- Supports P-256, P-384, and K-256 curves
Verification
verify_record: Verify all signatures in a record- Validates repository binding
- Supports both inline and remote attestations
- Requires key and record resolvers
Input Types
AnyInput: Flexible input enum supporting:String: JSON string to parseJson: serde_json::ValueTypedLexicon: Strongly-typed lexicon records
Attestation Specification
This crate implements the CID-first attestation specification, which ensures:
- Deterministic signing: Records are serialized to DAG-CBOR with
$sigmetadata, producing consistent CIDs - Content addressing: Signatures are over CID bytes, not the full record
- Repository binding: Every attestation is bound to a specific repository DID to prevent replay attacks
- Signature normalization: ECDSA signatures are normalized to low-S form to prevent malleability
- Flexible metadata: Custom fields in
$sigare preserved and included in the CID calculation - Multiple attestations: Records can have multiple signatures in the
signaturesarray
Signature Structure
Inline attestation entry:
Remote attestation entry (strongRef):
Error Handling
The crate provides structured error types via AttestationError:
RecordMustBeObject: Input must be a JSON objectMetadataMustBeObject: Attestation metadata must be a JSON objectSigMetadataMissing: No$sigfield found in prepared recordSignatureCreationFailed: Key signing operation failedSignatureValidationFailed: Signature verification failedSignatureNotNormalized: ECDSA signature not in low-S formSignatureLengthInvalid: Signature bytes have incorrect lengthKeyResolutionFailed: Could not resolve verification keyUnsupportedKeyType: Key type not supported for signing/verificationRemoteAttestationFetchFailed: Failed to fetch remote proof record
Security Considerations
Repository Binding and Replay Attack Prevention
The most critical security feature of this attestation framework is repository binding. Every attestation includes the repository DID in the $sig metadata during CID generation, which:
- Prevents replay attacks: An attacker cannot copy a signed record from one repository to another because the signature is bound to the original repository DID
- Ensures context integrity: Attestations are only valid within their intended repository context
- Automatic enforcement: The library automatically adds the repository field during CID generation
Important: Always verify signatures with the correct repository DID. Verifying with a different repository DID will (correctly) fail validation, as this would indicate a potential replay attack.
Signature Normalization
All ECDSA signatures are automatically normalized to low-S form to prevent signature malleability attacks:
- The library enforces low-S normalization during signature creation
- Verification accepts only normalized signatures
- This prevents attackers from creating alternate valid signatures for the same content
Key Management Best Practices
- Private keys: Never log, transmit, or store private keys in plaintext
- Key rotation: Plan for key rotation by using verification method references that can be updated in DID documents
- Key types: The library supports P-256, P-384, and K-256 elliptic curves
- did:key: For testing and simple use cases, did:key identifiers provide self-contained key references
CID Verification
- Always verify against CIDs: Signatures are over CID bytes, not the original record content
- Deterministic generation: The same record with the same
$sigmetadata always produces the same CID - Content integrity: Any modification to the record will produce a different CID and invalidate signatures
Metadata Validation
When creating attestations:
- The
$typefield is always required in metadata to scope the attestation - The
repositoryfield is automatically added and must not be manually set - Custom metadata fields are preserved and included in CID calculation
- The
cidfield is automatically added to inline attestation metadata
Remote Attestation Considerations
- Proof record storage: Store proof records in the attestor's repository with appropriate access controls
- CID matching: Verify that the CID in the proof record matches the computed CID of the attested content
- Record resolution: Use trusted record resolvers when fetching remote proof records
License
MIT License