///|
/// An OpenPGP transferable private key: v4 RSA primary key, one User ID
/// with its self-certification, and an RSA encryption subkey with its
/// binding signature. Fields are read-only from outside the package —
/// obtain keys through `generate_key` / `from_armored` / `from_bytes` and
/// mutate them through `lock` / `unlock` so the cross-field invariants hold.
pub struct PrivateKey {
primary : @packet.SecretKeyPacket
/// All certified User IDs, in wire order (M5). A bare v6 key carries
/// none and is self-signed by its Direct Key signature instead.
user_ids : Array[UserIdBinding]
/// v6 keys: the Direct Key signature (type 0x1F) over the primary key,
/// required by RFC 9580 §5.2.3.10 before a v6 key may be used.
direct_signature : @packet.SignaturePacket?
/// Every bound subkey in wire order (N2). Import guarantees each entry's
/// binding signature verifies over (primary, subkey) — a subkey without
/// a binding is rejected rather than trusted (RFC 9580 §10.1).
subkeys : Array[SecretSubkeyCert]
/// Primary-key revocation signatures (type 0x20) found on import;
/// verified lazily by `is_revoked` (their validity is time-dependent).
revocations : Array[@packet.SignaturePacket]
} derive(Debug)
///|
/// The public half of a transferable key. Read-only like `PrivateKey`;
/// obtain values via `to_public` / `from_armored` / `from_bytes`.
pub struct PublicKey {
primary : @packet.PublicKeyPacket
/// All certified User IDs, in wire order (M5).
user_ids : Array[UserIdBinding]
direct_signature : @packet.SignaturePacket?
/// Every bound subkey in wire order (N2); see `PrivateKey::subkeys`.
subkeys : Array[SubkeyCert]
/// Primary-key revocation signatures (type 0x20) found on import.
revocations : Array[@packet.SignaturePacket]
} derive(Debug)
///|
/// One bound subkey of a public transferable key: the public key packet,
/// the SubkeyBinding signature by the primary (import-verified), and the
/// SubkeyRevocation signature when the wire carried one.
pub(all) struct SubkeyCert {
key : @packet.PublicKeyPacket
binding : @packet.SignaturePacket
revocation : @packet.SignaturePacket?
} derive(Debug)
///|
/// The private-key half of one bound subkey: the secret key packet plus
/// the same binding/revocation signatures as its public twin.
pub(all) struct SecretSubkeyCert {
secret : @packet.SecretKeyPacket
binding : @packet.SignaturePacket
revocation : @packet.SignaturePacket?
} derive(Debug)
///|
/// One certified User ID: the identifier plus the self-signature that
/// binds it to the primary key.
pub(all) struct UserIdBinding {
user_id : String
self_signature : @packet.SignaturePacket
} derive(Debug)
///|
pub extend UserIdBinding with @moonbitlang/core/debug.Debug::{to_repr}
///|
/// 8-octet key id of the primary key (low 64 bits of the v4 fingerprint).
pub fn PrivateKey::key_id(self : PrivateKey) -> Bytes raise {
self.primary.public.key_id()
}
///|
/// 20-octet v4 fingerprint of the primary key.
pub fn PrivateKey::fingerprint(self : PrivateKey) -> Bytes raise {
self.primary.public.fingerprint()
}
///|
/// The primary (first) certified User ID string; empty for a bare v6 key.
pub fn PrivateKey::user_id(self : PrivateKey) -> String {
match self.user_ids {
[] => ""
[first, ..] => first.user_id
}
}
///|
/// The key-level self-signature: the first User ID certification, or the
/// Direct Key signature of a bare v6 key. Import guarantees one of the
/// two exists, so this never aborts on a key obtained through
/// `from_bytes`/`from_armored`/`generate_*`.
pub fn PrivateKey::self_signature(self : PrivateKey) -> @packet.SignaturePacket {
match self.user_ids {
[first, ..] => first.self_signature
[] =>
match self.direct_signature {
Some(d) => d
None => abort("api: key has no self-signature")
}
}
}
///|
/// 8-octet key id of the primary key.
pub fn PublicKey::key_id(self : PublicKey) -> Bytes raise {
self.primary.key_id()
}
///|
/// 20-octet v4 fingerprint of the primary key.
pub fn PublicKey::fingerprint(self : PublicKey) -> Bytes raise {
self.primary.fingerprint()
}
///|
/// The primary (first) certified User ID string; empty for a bare v6 key.
pub fn PublicKey::user_id(self : PublicKey) -> String {
match self.user_ids {
[] => ""
[first, ..] => first.user_id
}
}
///|
/// The key-level self-signature: the first User ID certification, or the
/// Direct Key signature of a bare v6 key. Import guarantees one of the
/// two exists, so this never aborts on a key obtained through
/// `from_bytes`/`from_armored`/`generate_*`.
pub fn PublicKey::self_signature(self : PublicKey) -> @packet.SignaturePacket {
match self.user_ids {
[first, ..] => first.self_signature
[] =>
match self.direct_signature {
Some(d) => d
None => abort("api: key has no self-signature")
}
}
}
///|
/// The public key packet of an encryption-capable key, structurally (no
/// clock): the last subkey whose binding flags permit encryption (absent
/// flags count as capable), otherwise the RSA primary key. The
/// lifecycle-aware choice is `encryption_packet_at`.
pub fn PublicKey::encryption_key(self : PublicKey) -> @packet.PublicKeyPacket? {
let mut chosen : @packet.PublicKeyPacket? = None
for c in self.subkeys {
match governing_flags(Some(c.binding)) {
Some(f) => if (f & FLAG_ENCRYPT) != 0 { chosen = Some(c.key) }
None => chosen = Some(c.key)
}
}
match chosen {
Some(k) => return Some(k)
None => ()
}
if self.primary.algo().is_rsa() {
Some(self.primary)
} else {
None
}
}
///|
/// Strip the secret material, producing a transferable public key.
pub fn PrivateKey::to_public(self : PrivateKey) -> PublicKey {
let subkeys : Array[SubkeyCert] = []
for c in self.subkeys {
subkeys.push(SubkeyCert::{
key: c.secret.public,
binding: c.binding,
revocation: c.revocation,
})
}
PublicKey::{
primary: self.primary.public,
user_ids: self.user_ids,
direct_signature: self.direct_signature,
subkeys,
revocations: self.revocations,
}
}
///|
pub extend PrivateKey with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend PublicKey with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend SubkeyCert with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend SecretSubkeyCert with @moonbitlang/core/debug.Debug::{to_repr}