std.crypto.ecc — secp256k1 ECDSA
import std.crypto.ecc;
ECDSA (Elliptic Curve Digital Signature Algorithm) auf der Kurve secp256k1 — dieselbe Kurve wie Bitcoin/Ethereum. Alle 256-Bit-Werte als 32-Byte Little-Endian-Puffer (Byte 0 = LSB). Jacobian-Koordinaten intern, Ausgabe in affiner Form.
Abhängigkeit: std.crypto.rand (für Schlüsselgenerierung und Signing).
→ std.crypto Paket · std.crypto.rand · std.crypto.sha256
Datenformat
| Typ | Größe | Format |
|---|---|---|
| Privater Schlüssel | 32 Bytes | Little-Endian, Byte 0 = LSB |
| Öffentlicher Schlüssel X | 32 Bytes | Little-Endian |
| Öffentlicher Schlüssel Y | 32 Bytes | Little-Endian |
| Signatur r | 32 Bytes | Little-Endian |
| Signatur s | 32 Bytes | Little-Endian |
| Hash-Eingabe | 32 Bytes | Beliebig (typisch SHA-256-Digest) |
Little-Endian-Besonderheit: Die Standard-Darstellung für secp256k1 in anderen Bibliotheken (OpenSSL, libsecp256k1) ist Big-Endian. Beim Austausch mit externen Systemen muss byte-reversed werden.
Funktionen
| Funktion | Signatur | Beschreibung |
|---|---|---|
ECCGenKey | (privk: int64, pubx: int64, puby: int64): void | Generiert zufälliges Schlüsselpaar; schreibt privaten Schlüssel und öffentlichen Schlüssel (X/Y) |
ECCPubFromPriv | (privk: int64, pubx: int64, puby: int64): void | Berechnet öffentlichen Schlüssel aus privatem; pubkey = privk × G |
ECDSASign | (hash: int64, privk: int64, r_out: int64, s_out: int64): int64 | Signiert 32-Byte-Hash mit privatem Schlüssel; gibt immer 1 zurück |
ECDSAVerify | (hash: int64, pubx: int64, puby: int64, r: int64, s: int64): int64 | Verifiziert Signatur; gibt 1 wenn gültig, 0 wenn ungültig |
Verwendung
Schlüsselpaar generieren
import std.crypto.ecc;
import std.alloc;
fn main(): int64 {
var privk: int64 := alloc(32);
var pubx: int64 := alloc(32);
var puby: int64 := alloc(32);
ECCGenKey(privk, pubx, puby);
// privk: 32-Byte privater Schlüssel (geheim halten!)
// pubx/puby: öffentlicher Schlüssel (teilbar)
free(privk, 32);
free(pubx, 32);
free(puby, 32);
return 0;
}
Nachricht signieren und verifizieren
import std.crypto.ecc;
import std.crypto.sha256;
import std.alloc;
fn SignAndVerify(msg: pchar, msgLen: int64): int64 {
var privk: int64 := alloc(32);
var pubx: int64 := alloc(32);
var puby: int64 := alloc(32);
var hash: int64 := alloc(32);
var sig_r: int64 := alloc(32);
var sig_s: int64 := alloc(32);
// 1. Schlüsselpaar erzeugen
ECCGenKey(privk, pubx, puby);
// 2. Nachricht hashen
SHA256(msg as int64, msgLen, hash);
// 3. Signieren
ECDSASign(hash, privk, sig_r, sig_s);
// 4. Verifizieren
var ok: int64 := ECDSAVerify(hash, pubx, puby, sig_r, sig_s);
free(privk, 32); free(pubx, 32); free(puby, 32);
free(hash, 32); free(sig_r, 32); free(sig_s, 32);
return ok; // 1 = gültig
}
Öffentlichen Schlüssel aus bekanntem privaten Schlüssel
import std.crypto.ecc;
import std.alloc;
fn RestorePubKey(privk: int64, pubx: int64, puby: int64): void {
// Nützlich wenn nur der private Schlüssel gespeichert wurde
ECCPubFromPriv(privk, pubx, puby);
}
Hinweise
- Nonce-Sicherheit:
ECDSASigngeneriert den Noncekintern zufällig viastd.crypto.rand. Ein wiederverwendeter Nonce legt den privaten Schlüssel vollständig offen — das ist durch die interne Implementierung verhindert, aber bei eigenem ECDSA-Code kritisch. - Little-Endian: Alle 32-Byte-Werte sind Little-Endian (LSB zuerst). Für Interoperabilität mit OpenSSL oder RFC-Formaten muss byte-reversed werden.
- secp256k1 (nicht secp256r1): Dies ist die Bitcoin/Ethereum-Kurve, nicht die häufiger in TLS verwendete NIST-Kurve P-256 (secp256r1).
- Keine ASN.1-Kodierung:
ECDSASigngibt r und s als rohe 32-Byte-Werte zurück, kein DER-Format. Für TLS/X.509 muss DER-Encoding selbst implementiert werden. - Performance: Schulmethode (schoolbook multiplication). Für produktiven Hochdurchsatz-Einsatz sollte eine optimierte ECDSA-Bibliothek per
@externeingebunden werden.
Letzte Aktualisierung: 2026-06-05
