# CMP Messages
`synta.cmp` provides RFC 9810 Certificate Management Protocol v3 types.
```python
import synta.cmp as cmp
```
## CMPMessage
```python
class CMPMessage:
@staticmethod
def from_der(data: bytes) -> CMPMessage: ...
def to_der(self) -> bytes: ...
pvno: int # CMP protocol version (2 = cmp2000, 3 = cmp2021)
body_type: str # Active PKIBody arm in lowercase: "ir", "ip", "cr", "cp",
# "rr", "rp", "pkiconf", "error", "genm", "genp", etc.
body_der: bytes | None # Raw DER bytes of the PKIBody content, or None for "pkiconf"
sender_der: bytes # DER bytes of the sender GeneralName field
recipient_der: bytes # DER bytes of the recipient GeneralName field
transaction_id: bytes | None
sender_nonce: bytes | None
recip_nonce: bytes | None
protection_alg_oid: ObjectIdentifier | None # OID of protectionAlg, or None
message_time: str | None # GeneralizedTime string, or None
```
### Body type dispatch
```python
if msg.body_type == "ir" or msg.body_type == "cr":
# body_der is a CertReqMessages SEQUENCE OF
import synta.crmf as crmf
cert_reqs = crmf.CertReqMessages.from_der(msg.body_der)
for req in cert_reqs:
print(f"req id={req.cert_req_id}")
```
## CMPMessageBuilder
Fluent builder for a CMP PKIMessage (RFC 9810). Set exactly one sender and one recipient
GeneralName before calling `build()`.
```python
class CMPMessageBuilder:
def __init__(self) -> None: ...
def pvno(self, pvno: int) -> CMPMessageBuilder: ...
# 2 = cmp2000, 3 = cmp2021
# Sender (exactly one must be set)
def sender_rfc822(self, email: str) -> CMPMessageBuilder: ...
def sender_dns(self, host: str) -> CMPMessageBuilder: ...
def sender_uri(self, uri: str) -> CMPMessageBuilder: ...
def sender_directory_name(self, name_der: bytes) -> CMPMessageBuilder: ...
# Recipient (exactly one must be set)
def recipient_rfc822(self, email: str) -> CMPMessageBuilder: ...
def recipient_dns(self, host: str) -> CMPMessageBuilder: ...
def recipient_uri(self, uri: str) -> CMPMessageBuilder: ...
def recipient_directory_name(self, name_der: bytes) -> CMPMessageBuilder: ...
def transaction_id(self, data: bytes) -> CMPMessageBuilder: ...
def sender_nonce(self, data: bytes) -> CMPMessageBuilder: ...
def recip_nonce(self, data: bytes) -> CMPMessageBuilder: ...
# Body (set exactly one)
def body_pkiconf(self) -> CMPMessageBuilder: ... # [19] pkiConf (NULL)
def body_ir(self, cert_req_messages_der: bytes) -> CMPMessageBuilder: ... # [0]
def body_cr(self, cert_req_messages_der: bytes) -> CMPMessageBuilder: ... # [2]
def body_kur(self, cert_req_messages_der: bytes) -> CMPMessageBuilder: ... # [7]
def body_p10cr(self, csr_der: bytes) -> CMPMessageBuilder: ... # [4]
def body_genm(self, gen_msg_der: bytes) -> CMPMessageBuilder: ... # [21]
def build(self) -> CMPMessage: ...
# Raises ValueError if sender or recipient are not set, or if any GeneralName
# data is invalid.
```
## OID constants
| `ID_PASSWORD_BASED_MAC` | Password-based MAC algorithm |
| `ID_DHBASED_MAC` | DH-based MAC algorithm |
| `ID_KEM_BASED_MAC` | KEM-based MAC algorithm |
| `ID_KP_CM_KGA` | CMP key-generation authority key purpose |
| `ID_REG_CTRL_ALT_CERT_TEMPLATE` | Alternate certificate template control |
| `ID_REG_CTRL_ALG_ID` | Algorithm ID control |
| `ID_REG_CTRL_RSA_KEY_LEN` | RSA key length control |
## Usage
```python
import synta.cmp as cmp
import synta.crmf as crmf
# Parse an incoming CMP message
msg = cmp.CMPMessage.from_der(data)
print(f"pvno: {msg.pvno}, body: {msg.body_type}")
# Build a CMP ir (Initialization Request) with a CRMF batch
crmf_der = batch.to_der() # from CertReqMessagesBuilder.build()
ir_msg = (
cmp.CMPMessageBuilder()
.pvno(2)
.sender_rfc822("alice@example.com")
.recipient_directory_name(ca_name_der)
.transaction_id(os.urandom(16))
.sender_nonce(os.urandom(16))
.body_ir(crmf_der)
.build()
)
ir_der = ir_msg.to_der()
```
See also [CRMF Messages](crmf.md) for the request structures carried in CMP bodies.