synta 0.3.0

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# CMC (Certificate Management over CMS)

The `synta-cmc` crate implements RFC 5272 / RFC 6402 Certificate Management
over CMS.  CMC wraps certificate enrollment requests (PKCS#10 or CRMF) and
responses inside CMS SignedData envelopes with attribute-based controls for
transaction tracking, nonce exchange, and compliance checking.

```rust,ignore
use synta_cmc::builder::{PKIDataBuilder, PKIResponseBuilder};
use synta_cmc::parser::{parse_pki_data, parse_pki_response, unwrap_signed_cmc};
use synta_cmc::controls;
use synta_cmc::oids;
```

## Building a PKIData request

`PKIDataBuilder` constructs a CMC request envelope.  Body part IDs are
auto-allocated; use `add_control_with_id` when an explicit ID is needed.

```rust,ignore
use synta_cmc::builder::PKIDataBuilder;

let der = PKIDataBuilder::new()
    .transaction_id(42)?
    .sender_nonce(&[0x01, 0x02, 0x03, 0x04])?
    .identification("user@example.com")?
    .add_pkcs10_request(&csr_der)?
    .build()?;
```

### CRMF requests

CRMF requests use `add_crmf_request`.  The builder validates the DER input
by decoding it as a `CertReqMsg` before encoding the `TaggedRequest::Crm`
CHOICE alternative:

```rust,ignore
let der = PKIDataBuilder::new()
    .transaction_id(1)?
    .add_crmf_request(&crmf_der)?
    .build()?;
```

### Custom controls

```rust,ignore
use synta::{Integer, ToDer};
use synta_cmc::oids;

let value = Integer::from_i64(99).to_der()?;

// Auto-allocated body part ID:
let builder = PKIDataBuilder::new()
    .add_control(oids::ID_CMC_TRANSACTION_ID, &[&value])?;

// Explicit body part ID:
let builder = PKIDataBuilder::new()
    .add_control_with_id(5, oids::ID_CMC_TRANSACTION_ID, &[&value])?;
```

## Building a PKIResponse

`PKIResponseBuilder` constructs a CMC response envelope with
`CMCStatusInfoV2` controls:

```rust,ignore
use synta_cmc::builder::PKIResponseBuilder;
use synta_cmc::status::CMCFailInfo;

// Success response
let der = PKIResponseBuilder::new()
    .add_status(&[1])?
    .recipient_nonce(&[0xCC, 0xDD])?
    .sender_nonce(&[0xAA, 0xBB])?
    .build()?;

// Failure response
let der = PKIResponseBuilder::new()
    .add_failed(&[1], CMCFailInfo::BadRequest)?
    .build()?;

// Pending response
let der = PKIResponseBuilder::new()
    .add_pending(&[1, 2, 3])?
    .build()?;
```

## Parsing

### PKIData and PKIResponse

`parse_pki_data` and `parse_pki_response` return the generated ASN.1 types
with zero-copy borrowed fields:

```rust,ignore
use synta_cmc::parser::{parse_pki_data, parse_pki_response};

let parsed = parse_pki_data(&der)?;
println!("Controls: {}", parsed.control_sequence.len());
println!("Requests: {}", parsed.req_sequence.len());

let response = parse_pki_response(&response_der)?;
```

### CMS SignedData unwrapping

`unwrap_signed_cmc` extracts the inner eContent and signer certificates
from a CMS SignedData envelope.  Both DER and BER input are accepted:

```rust,ignore
use synta_cmc::parser::unwrap_signed_cmc;

let (econtent, certs) = unwrap_signed_cmc(&signed_data_der)?;
let pki_data = parse_pki_data(&econtent)?;
println!("Signer certificates: {}", certs.len());
```

## Control extraction

The `controls` module provides typed extractors for common control
attributes.  Each function searches a `&[TaggedAttribute]` slice by OID:

```rust,ignore
use synta_cmc::controls;
use synta_cmc::oids;

let parsed = parse_pki_data(&der)?;

// Typed extractors
let txn_id = controls::extract_transaction_id(&parsed.control_sequence);
let nonce  = controls::extract_sender_nonce(&parsed.control_sequence);
let ident  = controls::extract_identification(&parsed.control_sequence);

// Generic lookup by OID
let attr = controls::find_control(&parsed.control_sequence, oids::ID_CMC_DATA_RETURN);
```

## Status enums

`CMCStatus` and `CMCFailInfo` map RFC 5272 ยง6.1.1 status codes to Rust enums
with `Display`, integer conversion, and HTTP status code mapping:

```rust,ignore
use synta_cmc::status::{CMCStatus, CMCFailInfo};

let status = CMCStatus::from_i64(0).unwrap();
assert_eq!(format!("{status}"), "success");

let fail = CMCFailInfo::BadRequest;
assert_eq!(fail.to_i64(), 2);
assert_eq!(fail.http_status(), 400);
println!("{fail}"); // "badRequest"
```

| `CMCStatus` variant | Value | Display |
|---|---|---|
| `Success` | 0 | `success` |
| `Failed` | 2 | `failed` |
| `Pending` | 3 | `pending` |
| `NoSupport` | 4 | `noSupport` |
| `ConfirmRequired` | 5 | `confirmRequired` |
| `PopRequired` | 6 | `popRequired` |
| `Partial` | 7 | `partial` |

| `CMCFailInfo` variant | Value | HTTP | Display |
|---|---|---|---|
| `BadAlg` | 0 | 400 | `badAlg` |
| `BadMessageCheck` | 1 | 400 | `badMessageCheck` |
| `BadRequest` | 2 | 400 | `badRequest` |
| `BadTime` | 3 | 400 | `badTime` |
| `BadCertId` | 4 | 404 | `badCertId` |
| `UnsupportedExt` | 5 | 400 | `unsupportedExt` |
| `MustArchiveKeys` | 6 | 400 | `mustArchiveKeys` |
| `BadIdentity` | 7 | 403 | `badIdentity` |
| `PopRequired` | 8 | 403 | `popRequired` |
| `PopFailed` | 9 | 403 | `popFailed` |
| `NoKeyReuse` | 10 | 400 | `noKeyReuse` |
| `InternalCaError` | 11 | 500 | `internalCAError` |
| `TryLater` | 12 | 503 | `tryLater` |
| `AuthDataFail` | 13 | 403 | `authDataFail` |

## Compliance checking

The `compliance` module validates CMC message structure against RFC 5274
and RFC 5272bis requirements:

```rust,ignore
use synta_cmc::compliance::{check_required_controls, check_5272bis_controls, AgentType};

let parsed = parse_pki_data(&der)?;

// RFC 5274: check controls required for a given agent type
let issues = check_required_controls(
    AgentType::EndEntity,
    &parsed.control_sequence,
);
for issue in &issues {
    eprintln!("[{:?}] {}", issue.severity, issue.message);
}

// RFC 5272bis: check controls with transport authentication context
let issues = check_5272bis_controls(&parsed.control_sequence, false);
```

| Agent type | Required controls |
|---|---|
| `EndEntity` | `transactionId`, `senderNonce` |
| `RegistrationAuthority` | `transactionId`, `senderNonce` |
| `CertificationAuthority` | `transactionId`, `recipientNonce` |

## OID constants

All CMC and CCT OID constants are re-exported from the generated `cmc_types`
module through `synta_cmc::oids`:

| Constant | OID | Description |
|---|---|---|
| `ID_CMC_TRANSACTION_ID` | 1.3.6.1.5.5.7.7.5 | Transaction identifier |
| `ID_CMC_SENDER_NONCE` | 1.3.6.1.5.5.7.7.6 | Sender nonce |
| `ID_CMC_RECIPIENT_NONCE` | 1.3.6.1.5.5.7.7.7 | Recipient nonce |
| `ID_CMC_STATUS_INFO_V2` | 1.3.6.1.5.5.7.7.25 | Status info v2 |
| `ID_CMC_IDENTIFICATION` | 1.3.6.1.5.5.7.7.2 | Identification |
| `ID_CMC_IDENTITY_PROOF` | 1.3.6.1.5.5.7.7.3 | Identity proof |
| `ID_CMC_IDENTITY_PROOF_V2` | 1.3.6.1.5.5.7.7.34 | Identity proof v2 |
| `ID_CMC_REVOKE_REQUEST` | 1.3.6.1.5.5.7.7.17 | Revocation request |
| `ID_CCT_PKI_DATA` | 1.3.6.1.5.5.7.12.2 | CMC PKIData content type |
| `ID_CCT_PKI_RESPONSE` | 1.3.6.1.5.5.7.12.3 | CMC PKIResponse content type |

See `synta_cmc::oids` for the full list.

## Generated types

The `cmc_types` module is generated from `asn1/CMC.asn1` by `synta-codegen`
in the crate's `build.rs`.  Key types:

| Type | Description |
|---|---|
| `PKIData` | Request envelope: controls + requests + CMS + other |
| `PKIResponse` | Response envelope: controls + CMS + other |
| `TaggedRequest` | CHOICE: `Tcr` (PKCS#10), `Crm` (CRMF), `Orm` (other) |
| `TaggedCertificationRequest` | BodyPartID + PKCS#10 CSR |
| `TaggedAttribute` | BodyPartID + OID + attribute values |
| `TaggedContentInfo` | BodyPartID + CMS ContentInfo |
| `CMCStatusInfoV2` | Enhanced status with body part paths |
| `OtherReqMsg` | Extension point for future request types |

## Error handling

All fallible operations return `Result<T, CmcError>`:

```rust,ignore
use synta_cmc::error::CmcError;

match result {
    Err(CmcError::DecodeError(e)) => eprintln!("ASN.1 decode: {e}"),
    Err(CmcError::EncodeError(e)) => eprintln!("ASN.1 encode: {e}"),
    Err(CmcError::DuplicateBodyPartId(id)) => eprintln!("duplicate ID: {id}"),
    Err(CmcError::BuilderError(msg)) => eprintln!("builder: {msg}"),
    Err(CmcError::CmsError(msg)) => eprintln!("CMS: {msg}"),
    Ok(value) => { /* ... */ }
}
```

## See also

- [CRMF Messages]../../python/src/protocols/crmf.md -- certificate request format used inside CMC
- [CMP Messages]../../python/src/protocols/cmp.md -- alternative certificate management protocol
- [CMS Overview]../../python/src/cms/overview.md -- CMS SignedData transport layer