====== std.pgp — OpenPGP (RFC 4880) ======
→ [[lyx_-_programmiersprache:units|Zurück zur Unit-Übersicht]]
Das ''std.pgp''-Paket implementiert **OpenPGP-Format-Parsing und ASCII-Armor** nach RFC 4880. Es ist eine reine **Parser- und Codec-Bibliothek**: kein Schlüssel-Erzeugen, kein Ver- oder Entschlüsseln, kein Signieren, kein Verifizieren — nur das Lesen und Kodieren von PGP-Datenstrukturen.
Typische Einsatzfälle: ''.asc''-Dateien dekodieren, öffentliche Schlüssel aus Keyrings lesen, Key-IDs und Fingerprints extrahieren, Paketströme inspizieren.
**Autor:** Andreas Röne\\
**Copyright:** 2025–2026 Andreas Röne\\
**Quelle:** ''std/pgp/''
Der frühere Pufferüberlauf in ''PgpArmorEncode'' (#1454) ist **behoben**: Bei zu kleinem Ausgabepuffer kommt jetzt ''-1'' zurück, und der Nachbarspeicher bleibt unberührt (nachgeprüft mit einem Wächterpuffer). Als Faustregel für die Größe weiterhin ''(dataLen * 4 / 3) + 200'' Bytes.
===== Units =====
^ Unit ^ Beschreibung ^
| ''std.pgp.core'' | Konstanten (Tags, Algorithmen, Signaturtypen, Armor-Typen), Paket-Struct-Offsets, ''PgpCrc24'' |
| ''std.pgp.armor'' | ASCII-Armor kodieren, dekodieren, Typ erkennen |
| ''std.pgp.packet'' | Paket-Iteration über binäre PGP-Daten |
| ''std.pgp.key'' | Public-Key-Paket-Analyse: Version, Algorithmus, Zeitstempel, Fingerprint, Key-ID |
Die Units bauen aufeinander auf: ''key'' und ''packet'' setzen ''core'' voraus, ''key'' zusätzlich ''std.crypto.sha1''. In der Praxis importiert man alle vier plus ''std.alloc''.
----
===== Funktionsreferenz =====
==== std.pgp.core ====
^ Signatur ^ Beschreibung ^
| ''PgpCrc24(data: int64, len: int64): int64'' | CRC-24 nach RFC 4880 §6.1 (Init ''0xB704CE'', Polynom ''0x1864CFB''). Ergebnis sind die unteren 24 Bit |
==== std.pgp.armor ====
^ Signatur ^ Beschreibung ^
| ''PgpArmorEncode(data, dataLen, tp, out, outMax): int64'' | Kodiert Rohbytes als Armor-Block. ''tp'' ist eine ''PGP_ARMOR_*''-Konstante. Rückgabe: geschriebene Zeichen ohne NUL, oder ''-1'' wenn ''outMax'' nicht reicht |
| ''PgpArmorDecode(arm, armLen, out, outMax): int64'' | Dekodiert einen Armor-Block und prüft die CRC-24. Rückgabe: Bytezahl, oder ''-1'' bei CRC-Fehler, ungültigem Block oder zu kleinem ''out'' |
| ''PgpArmorType(arm, armLen): int64'' | Erkennt den Blocktyp allein an der BEGIN-Zeile, ohne zu dekodieren. Rückgabe: ''PGP_ARMOR_*'' |
==== std.pgp.packet ====
^ Signatur ^ Beschreibung ^
| ''PgpPacketFirst(buf, len, pkt): int64'' | Liest das erste Paket ab Offset 0 in den Struct ''pkt''. 1 = Paket gefunden, 0 = leer oder ungültig |
| ''PgpPacketNext(buf, len, pkt): int64'' | Liest das Paket am gespeicherten Folge-Offset. 1 = weiteres Paket, 0 = Ende |
==== std.pgp.key ====
Alle Funktionen arbeiten auf dem **Paket-Body** — dem Bereich, den ''PGP_PKT_OFF_BODY'' / ''PGP_PKT_OFF_BLEN'' beschreiben, **ohne** Tag-Byte und Längenfeld.
^ Signatur ^ Beschreibung ^
| ''PgpKeyVersion(body, blen): int64'' | Versionsbyte (3 oder 4), 0 bei zu kurzem Body |
| ''PgpKeyAlgo(body, blen): int64'' | Public-Key-Algorithmus (''PGP_ALG_*''); berücksichtigt die unterschiedliche Feldlage bei v3 und v4 |
| ''PgpKeyTimestamp(body, blen): int64'' | Erzeugungszeit als Unix-Sekunden (4 Bytes big-endian ab Offset 1) |
| ''PgpKeyFingerprint(body, blen, out): int64'' | 20-Byte-SHA-1-Fingerprint nach §12.2 in ''out''. 1 = Erfolg, 0 bei v3 oder Body > 65535 Bytes |
| ''PgpKeyId(body, blen, out): int64'' | 8-Byte-Key-ID (Bytes 12–19 des Fingerprints) in ''out''. 1 = Erfolg |
----
===== Konstanten (std.pgp.core) =====
==== Paket-Tags (RFC 4880 §4.3) ====
^ Konstante ^ Wert ^ Bedeutung ^
| ''PGP_TAG_PKESK'' | 1 | Public-Key Encrypted Session Key |
| ''PGP_TAG_SIG'' | 2 | Signatur |
| ''PGP_TAG_SKESK'' | 3 | Symmetric-Key Encrypted Session Key |
| ''PGP_TAG_OPS'' | 4 | One-Pass Signature |
| ''PGP_TAG_SECKEY'' | 5 | Geheimer Schlüssel |
| ''PGP_TAG_PUBKEY'' | 6 | Öffentlicher Schlüssel |
| ''PGP_TAG_SECSUBKEY'' | 7 | Geheimer Unterschlüssel |
| ''PGP_TAG_COMPRESSED'' | 8 | Komprimierte Daten |
| ''PGP_TAG_SYM_ENC'' | 9 | Symmetrisch verschlüsselte Daten |
| ''PGP_TAG_MARKER'' | 10 | Marker-Paket |
| ''PGP_TAG_LITERAL'' | 11 | Literal-Daten (die eigentlichen Nutzdaten) |
| ''PGP_TAG_TRUST'' | 12 | Trust-Paket (lokal, nicht exportiert) |
| ''PGP_TAG_UID'' | 13 | User-ID |
| ''PGP_TAG_PUBSUBKEY'' | 14 | Öffentlicher Unterschlüssel |
| ''PGP_TAG_UAT'' | 17 | User-Attribute (z. B. Foto) |
| ''PGP_TAG_SEIPD'' | 18 | Symmetrisch verschlüsselt, integritätsgeschützt |
| ''PGP_TAG_MDC'' | 19 | Modification Detection Code |
==== Public-Key-Algorithmen (RFC 4880 §9.1) ====
^ Konstante ^ Wert ^ Algorithmus ^
| ''PGP_ALG_RSA'' | 1 | RSA (Encrypt + Sign) |
| ''PGP_ALG_RSA_E'' | 2 | RSA (nur Encrypt) |
| ''PGP_ALG_RSA_S'' | 3 | RSA (nur Sign) |
| ''PGP_ALG_ELGAMAL'' | 16 | ElGamal (nur Encrypt) |
| ''PGP_ALG_DSA'' | 17 | DSA (nur Sign) |
| ''PGP_ALG_ECDH'' | 18 | ECDH (nur Encrypt) |
| ''PGP_ALG_ECDSA'' | 19 | ECDSA (nur Sign) |
| ''PGP_ALG_EDDSA'' | 22 | EdDSA (nur Sign, z. B. Ed25519) |
==== Hash-Algorithmen (RFC 4880 §9.4) ====
^ Konstante ^ Wert ^ Algorithmus ^
| ''PGP_HASH_MD5'' | 1 | MD5 |
| ''PGP_HASH_SHA1'' | 2 | SHA-1 |
| ''PGP_HASH_RIPEMD160'' | 3 | RIPEMD-160 |
| ''PGP_HASH_SHA256'' | 8 | SHA-256 |
| ''PGP_HASH_SHA384'' | 9 | SHA-384 |
| ''PGP_HASH_SHA512'' | 10 | SHA-512 |
| ''PGP_HASH_SHA224'' | 11 | SHA-224 |
==== Symmetrische Algorithmen (RFC 4880 §9.2) ====
^ Konstante ^ Wert ^ Algorithmus ^
| ''PGP_SYM_PLAINTEXT'' | 0 | Klartext (unverschlüsselt) |
| ''PGP_SYM_IDEA'' | 1 | IDEA |
| ''PGP_SYM_3DES'' | 2 | Triple-DES |
| ''PGP_SYM_CAST5'' | 3 | CAST5 |
| ''PGP_SYM_BLOWFISH'' | 4 | Blowfish |
| ''PGP_SYM_AES128'' | 7 | AES-128 |
| ''PGP_SYM_AES192'' | 8 | AES-192 |
| ''PGP_SYM_AES256'' | 9 | AES-256 |
| ''PGP_SYM_TWOFISH'' | 10 | Twofish |
==== Signatur-Typen (RFC 4880 §5.2.1) ====
^ Konstante ^ Wert ^ Bedeutung ^
| ''PGP_SIG_BINARY'' | 0x00 | Binärdokument |
| ''PGP_SIG_TEXT'' | 0x01 | Textdokument (CRLF-normalisiert) |
| ''PGP_SIG_STANDALONE'' | 0x02 | Eigenständige Signatur |
| ''PGP_SIG_CERT_GENERIC'' | 0x10 | Generische Zertifizierung |
| ''PGP_SIG_CERT_PERSONA'' | 0x11 | Zertifizierung ohne Prüfung |
| ''PGP_SIG_CERT_CASUAL'' | 0x12 | Zertifizierung nach oberflächlicher Prüfung |
| ''PGP_SIG_CERT_POSITIVE'' | 0x13 | Zertifizierung nach gründlicher Prüfung |
| ''PGP_SIG_SUBKEY_BIND'' | 0x18 | Unterschlüssel-Bindung |
| ''PGP_SIG_PRIMARY_BIND'' | 0x19 | Rückbindung an den Hauptschlüssel |
| ''PGP_SIG_DIRECT'' | 0x1F | Direkte Schlüsselsignatur |
| ''PGP_SIG_REVOKE_KEY'' | 0x20 | Schlüssel-Widerruf |
| ''PGP_SIG_REVOKE_SUBKEY'' | 0x28 | Unterschlüssel-Widerruf |
| ''PGP_SIG_REVOKE_CERT'' | 0x30 | Zertifizierungs-Widerruf |
| ''PGP_SIG_TIMESTAMP'' | 0x40 | Zeitstempel |
| ''PGP_SIG_THIRD_PARTY'' | 0x50 | Drittbestätigung |
==== Armor-Typen ====
^ Konstante ^ Wert ^ BEGIN-Header ^
| ''PGP_ARMOR_UNKNOWN'' | 0 | Unbekannt / kein Armor |
| ''PGP_ARMOR_MESSAGE'' | 1 | ''BEGIN PGP MESSAGE'' |
| ''PGP_ARMOR_PUBLIC_KEY'' | 2 | ''BEGIN PGP PUBLIC KEY BLOCK'' |
| ''PGP_ARMOR_PRIVATE_KEY'' | 3 | ''BEGIN PGP PRIVATE KEY BLOCK'' |
| ''PGP_ARMOR_SIGNATURE'' | 4 | ''BEGIN PGP SIGNATURE'' |
==== Paket-Struct-Offsets ====
Der Aufrufer alloziert einen ''PGP_PKT_SIZE'' Bytes großen Puffer und liest die Felder mit ''peek64'':
^ Konstante ^ Offset ^ Inhalt ^
| ''PGP_PKT_OFF_TAG'' | 0 | Paket-Typ (''PGP_TAG_*'') |
| ''PGP_PKT_OFF_BODY'' | 8 | Absoluter Zeiger auf die Body-Bytes im Quellpuffer |
| ''PGP_PKT_OFF_BLEN'' | 16 | Body-Länge in Bytes |
| ''PGP_PKT_OFF_NEXT'' | 24 | Offset des nächsten Pakets im Quellpuffer |
| ''PGP_PKT_SIZE'' | 32 | Gesamtgröße des Structs |
Die Iteration kopiert nichts: ''PGP_PKT_OFF_BODY'' zeigt direkt in den Quellpuffer. Der muss also gültig bleiben, solange mit den Bodies gearbeitet wird.
----
===== Beispiele =====
Alle Beispiele sind gegen einen mit ''gpg --quick-generate-key'' erzeugten Testschlüssel geprüft; die gezeigten Ausgaben stammen aus dem tatsächlichen Lauf.
==== Schlüsseldatei auslesen ====
Der vollständige Weg: Datei laden → Armor-Typ prüfen → dekodieren → Pakete durchlaufen → Schlüsseldaten lesen.
import std.pgp.core;
import std.pgp.armor;
import std.pgp.packet;
import std.pgp.key;
import std.alloc;
import std.fs;
fn HexOut(p: int64, n: int64): void {
var hex: pchar := "0123456789ABCDEF";
var i: int64 := 0;
while (i < n) {
var b: int64 := peek8(p + i);
var s: int64 := alloc(3);
poke8(s, StrCharAt(hex, (b >> 4) & 15));
poke8(s + 1, StrCharAt(hex, b & 15));
poke8(s + 2, 0);
Print(s as pchar);
free(s, 3);
i := i + 1;
}
}
fn main(): int64 {
var path: pchar := "key.asc";
var fsize: int64 := FileSize(path);
PrintLn("Armor-Datei: ", IntToStr(fsize), " Bytes");
var arm: int64 := alloc(fsize + 1);
ReadFile(path, arm as pchar, fsize);
PrintLn("PgpArmorType = ", IntToStr(PgpArmorType(arm, fsize)),
" (PGP_ARMOR_PUBLIC_KEY = ", IntToStr(PGP_ARMOR_PUBLIC_KEY), ")");
var raw: int64 := alloc(fsize);
var rawLen: int64 := PgpArmorDecode(arm, fsize, raw, fsize);
PrintLn("PgpArmorDecode -> ", IntToStr(rawLen), " Bytes binaer");
if (rawLen < 0) { return 1; }
var pkt: int64 := alloc(PGP_PKT_SIZE);
var fp: int64 := alloc(20);
var kid: int64 := alloc(8);
var more: int64 := PgpPacketFirst(raw, rawLen, pkt);
while (more != 0) {
var tag: int64 := peek64(pkt + PGP_PKT_OFF_TAG);
var body: int64 := peek64(pkt + PGP_PKT_OFF_BODY);
var blen: int64 := peek64(pkt + PGP_PKT_OFF_BLEN);
PrintLn("Paket: Tag ", IntToStr(tag), ", ", IntToStr(blen), " Bytes Body");
if (tag == PGP_TAG_PUBKEY) {
PrintLn(" Version = ", IntToStr(PgpKeyVersion(body, blen)));
PrintLn(" Algorithmus = ", IntToStr(PgpKeyAlgo(body, blen)));
PrintLn(" Zeitstempel = ", IntToStr(PgpKeyTimestamp(body, blen)));
if (PgpKeyFingerprint(body, blen, fp) != 0) {
Print(" Fingerprint = "); HexOut(fp, 20); PrintLn("");
}
if (PgpKeyId(body, blen, kid) != 0) {
Print(" Key-ID = "); HexOut(kid, 8); PrintLn("");
}
}
more := PgpPacketNext(raw, rawLen, pkt);
}
return 0;
}
Ausgabe:
Armor-Datei: 961 Bytes
PgpArmorType = 2 (PGP_ARMOR_PUBLIC_KEY = 2)
PgpArmorDecode -> 649 Bytes binaer
Paket: Tag 6, 269 Bytes Body
Version = 4
Algorithmus = 1
Zeitstempel = 1786643838
Fingerprint = 85C6D42C76D20716A4DDF55EBBF229737744C1D3
Key-ID = BBF229737744C1D3
Paket: Tag 13, 35 Bytes Body
Paket: Tag 2, 337 Bytes Body
Gegenprobe mit ''gpg --list-keys --with-colons'' auf demselben Schlüssel:
fpr:::::::::85C6D42C76D20716A4DDF55EBBF229737744C1D3:
Fingerprint und Key-ID stimmen byteweise überein. Algorithmus 1 ist RSA, Tag 6 der Hauptschlüssel, Tag 13 die User-ID, Tag 2 deren Selbstsignatur.
==== Paketstruktur eines Schlüsselbunds ====
Bei einem Schlüssel mit Unterschlüssel kommen zwei weitere Pakete dazu:
import std.pgp.core;
import std.pgp.armor;
import std.pgp.packet;
import std.alloc;
import std.fs;
fn TagName(t: int64): pchar {
if (t == PGP_TAG_PUBKEY) { return "Hauptschluessel"; }
if (t == PGP_TAG_UID) { return "User-ID"; }
if (t == PGP_TAG_SIG) { return "Signatur"; }
if (t == PGP_TAG_PUBSUBKEY) { return "Unterschluessel"; }
return "sonstiges";
}
fn scan(path: pchar): void {
var fsize: int64 := FileSize(path);
var arm: int64 := alloc(fsize + 1);
ReadFile(path, arm as pchar, fsize);
PrintLn("--- ", path, ": Typ ", IntToStr(PgpArmorType(arm, fsize)), ", ", IntToStr(fsize), " Bytes Armor");
var raw: int64 := alloc(fsize);
var rawLen: int64 := PgpArmorDecode(arm, fsize, raw, fsize);
PrintLn(" dekodiert: ", IntToStr(rawLen), " Bytes");
var pkt: int64 := alloc(PGP_PKT_SIZE);
var more: int64 := PgpPacketFirst(raw, rawLen, pkt);
var cnt: int64 := 0;
while (more != 0) {
var tag: int64 := peek64(pkt + PGP_PKT_OFF_TAG);
PrintLn(" Tag ", IntToStr(tag), " (", TagName(tag), "), Body ", IntToStr(peek64(pkt + PGP_PKT_OFF_BLEN)));
cnt := cnt + 1;
more := PgpPacketNext(raw, rawLen, pkt);
}
PrintLn(" Pakete gesamt: ", IntToStr(cnt));
}
fn main(): int64 {
scan("key2.asc");
scan("sig.asc");
return 0;
}
Ausgabe:
--- key2.asc: Typ 2, 1753 Bytes Armor
dekodiert: 1234 Bytes
Tag 6 (Hauptschluessel), Body 269
Tag 13 (User-ID), Body 35
Tag 2 (Signatur), Body 337
Tag 14 (Unterschluessel), Body 269
Tag 2 (Signatur), Body 310
Pakete gesamt: 5
--- sig.asc: Typ 4, 516 Bytes Armor
dekodiert: 332 Bytes
Tag 2 (Signatur), Body 329
Pakete gesamt: 1
Dieselbe Datei durch ''gpg --list-packets'' ergibt exakt dieselbe Folge — Hauptschlüssel, User-ID, Signatur, Unterschlüssel, Signatur.
==== Armor kodieren und wieder dekodieren ====
import std.pgp.core;
import std.pgp.armor;
import std.alloc;
fn main(): int64 {
var text: pchar := "OpenPGP Rohdaten-Test 1234567890";
var len: int64 := StrLen(text);
var outMax: int64 := (len * 4 / 3) + 200;
var arm: int64 := alloc(outMax);
var n: int64 := PgpArmorEncode(text as int64, len, PGP_ARMOR_MESSAGE, arm, outMax);
PrintLn("PgpArmorEncode -> ", IntToStr(n), " Zeichen");
PrintLn(arm as pchar);
PrintLn("Typ erkannt: ", IntToStr(PgpArmorType(arm, n)), " (PGP_ARMOR_MESSAGE = ", IntToStr(PGP_ARMOR_MESSAGE), ")");
var back: int64 := alloc(len + 16);
var m: int64 := PgpArmorDecode(arm, n, back, len + 16);
PrintLn("PgpArmorDecode -> ", IntToStr(m), " Bytes (Original: ", IntToStr(len), ")");
poke8(back + m, 0);
PrintLn("Inhalt: ", back as pchar);
return 0;
}
Ausgabe:
PgpArmorEncode -> 106 Zeichen
-----BEGIN PGP MESSAGE-----
T3BlblBHUCBSb2hkYXRlbi1UZXN0IDEyMzQ1Njc4OTA=
=WWrf
-----END PGP MESSAGE-----
Typ erkannt: 1 (PGP_ARMOR_MESSAGE = 1)
PgpArmorDecode -> 32 Bytes (Original: 32)
Inhalt: OpenPGP Rohdaten-Test 1234567890
Der Aufbau entspricht RFC 4880 §6.2: BEGIN-Zeile, Leerzeile, Base64-Block, Prüfsummenzeile ''=WWrf'', END-Zeile. Base64-Zeilen sind **76 Zeichen** lang (57 Eingangsbytes je Zeile) — RFC 4880 erlaubt bis 76, GnuPG selbst schreibt 64. Beide Formen sind gültig und werden von ''PgpArmorDecode'' gelesen.
==== CRC-Prüfung ====
''PgpArmorDecode'' verifiziert die Prüfsumme und meldet Manipulation:
// aus dem obigen Programm: ein Zeichen der '='-Zeile kippen
var c: int64 := peek8(arm + crcPos + 1);
if (c == 65) { poke8(arm + crcPos + 1, 66); } else { poke8(arm + crcPos + 1, 65); }
var bad: int64 := PgpArmorDecode(arm, n, back, len + 16);
PrintLn("Decode mit falscher CRC -> ", IntToStr(bad));
Decode mit falscher CRC -> -1
''-1'' ist der einzige Fehlerwert und deckt drei Fälle ab: CRC-Fehler, fehlender oder unvollständiger BEGIN/END-Rahmen und zu kleiner Ausgabepuffer. Welcher davon vorlag, ist am Rückgabewert nicht erkennbar.
==== CRC-24 direkt ====
import std.pgp.core;
fn main(): int64 {
var data: pchar := "Hallo OpenPGP";
PrintLn("CRC-24 = ", IntToStr(PgpCrc24(data as int64, StrLen(data))));
PrintLn("CRC-24 leer = ", IntToStr(PgpCrc24(data as int64, 0)));
return 0;
}
CRC-24 = 6658813
CRC-24 leer = 11994318
Bei Länge 0 kommt der Initialwert ''0xB704CE'' = 11994318 zurück — das ist RFC-konform. ''PgpArmorEncode'' ruft ''PgpCrc24'' selbst auf; im normalen Armor-Ablauf braucht man die Funktion nicht direkt.
----
===== Fallstricke =====
==== Die Puffergröße muss weiterhin selbst berechnet werden ====
''PgpArmorEncode'' meldet seit dem Fix von #1454 einen zu kleinen Puffer mit ''-1'', statt darüber hinaus zu schreiben:
PgpArmorEncode(200 Bytes, outMax=40) -> -1
überschriebene Wächter-Bytes: 0 von 64
Der Rückgabewert ist damit zu prüfen — sonst bleibt der Puffer stillschweigend leer. Für die Größe gilt ''(dataLen * 4 / 3) + 200'' Bytes; das deckt Base64-Aufblähung, Zeilenumbrüche, Rahmen und Prüfsummenzeile ab. Mit ausreichendem Puffer liefert der Rundlauf ''Encode → Decode'' die 200 Ausgangsbytes zurück.
==== Der Body zeigt in den Quellpuffer ====
''PGP_PKT_OFF_BODY'' ist ein Zeiger in den dekodierten Puffer, keine Kopie. Zwei Folgen:
* Der Quellpuffer darf nicht freigegeben werden, solange Bodies in Gebrauch sind.
* Ein Body ist **nicht** nullterminiert. Eine User-ID (Tag 13) direkt als ''pchar'' auszugeben, liest über das Paketende hinaus. Wer sie als String braucht, kopiert ''blen'' Bytes in einen eigenen Puffer und terminiert dort.
Das früher an dieser Stelle gezeigte ''poke8(body + blen, 0)'' überschreibt das erste Byte des Folgepakets und zerstört die weitere Iteration — bitte nicht verwenden.
==== Fingerprints nur für v4 ====
''PgpKeyFingerprint'' und ''PgpKeyId'' liefern 0, wenn das Versionsbyte nicht 4 ist oder der Body größer als 65535 Bytes wäre. v3-Schlüssel geben nur Version, Algorithmus und Zeitstempel her. Der Rückgabewert ist zu prüfen — bei 0 bleibt der Ausgabepuffer uninitialisiert.
==== Partial-Body-Pakete werden nicht zusammengeführt ====
Neuformat-Pakete mit Partial-Body-Header (Längenbyte 224–254) behandelt der Iterator als vollständiges Paket der jeweiligen Teillänge. Die Folgestücke erscheinen als eigene, aus Sicht des Formats falsch interpretierte Pakete. Das betrifft in der Praxis große komprimierte oder verschlüsselte Datenpakete, nicht die üblichen Schlüsseldateien.
----
===== Einschränkungen =====
^ Was ^ Einschränkung ^
| Schlüsselerzeugung | Nicht vorhanden — kein RSA/DSA/ECDSA/EdDSA-KeyGen |
| Ver- und Entschlüsseln | Nicht vorhanden |
| Signieren und Verifizieren | Nicht vorhanden — Signaturpakete werden erkannt, aber nicht geprüft |
| v3-Fingerprint | Nicht unterstützt |
| Partial-Body-Pakete | Werden nicht zusammengeführt |
| Compressed Data (Tag 8) | Wird als Paket erkannt, aber nicht entpackt |
| Signatur-Subpakete | Werden nicht zerlegt — der Signaturbody bleibt roh |
Für kryptografische Operationen sind die Bausteine in [[lyx_-_programmiersprache:units:crypto:rsa|std.crypto.rsa]], [[lyx_-_programmiersprache:units:crypto:sha256|std.crypto.sha256]] und Verwandten vorhanden; die Verbindung zum OpenPGP-Format muss man selbst herstellen.
----
===== Quelldateien =====
^ Datei ^ Inhalt ^
| ''std/pgp/core.lyx'' | Alle Konstanten, Paket-Struct-Offsets, ''PgpCrc24'' |
| ''std/pgp/armor.lyx'' | ''PgpArmorEncode'', ''PgpArmorDecode'', ''PgpArmorType'' samt Base64-Codec |
| ''std/pgp/packet.lyx'' | ''PgpPacketFirst'', ''PgpPacketNext''; Alt- und Neuformat-Parser |
| ''std/pgp/key.lyx'' | ''PgpKeyVersion'', ''PgpKeyAlgo'', ''PgpKeyTimestamp'', ''PgpKeyFingerprint'', ''PgpKeyId'' |
----
===== Verwandte Units =====
* [[lyx_-_programmiersprache:units:base64|std.base64]] — allgemeine Base64-Kodierung
* [[lyx_-_programmiersprache:units:crypto:sha1|std.crypto.sha1]] — SHA-1, Basis der v4-Fingerprints
* [[lyx_-_programmiersprache:units:fs|std.fs]] — ''FileSize'', ''ReadFile'' zum Laden von ''.asc''-Dateien
Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen).
----
Letzte Aktualisierung: 2026-08-30 (Dateistand)