Wir empfehlen die Primitiven Prehash und SignPrehash mit einem ML_DSA_44-Schlüssel, wenn sich der privaten Schlüssel an einem Ort befindet, an dem die Nachricht nicht gesendet werden kann.
Manchmal kann oder sollte die Partei, die den Signaturschlüssel besitzt, die Nachricht selbst nicht empfangen: Der Schlüssel befindet sich in einem HSM oder KMS oder die Nachricht ist größer als die Größenbeschränkung für Anfragen des Unterzeichners.
Die Primitiven „Prehash“ und „SignPrehash“ lösen dieses Problem, indem die Signierung in zwei Schritte unterteilt wird. Sie berechnen einen kurzen Prehash-Wert für die Nachricht nur mit dem öffentlichen Schlüssel und senden ihn an den Unterzeichner. Der Unterzeichner wandelt sie mit dem privaten Schlüssel in eine Signatur um, ohne die Nachricht jemals zu sehen. Bei ML-DSA im External Mu-Modus, den der Algorithmus Tink unterstützt, beträgt der Prehash-Wert 69 Byte, unabhängig von der Größe der Nachricht.
Die zurückgegebene Signatur ist eine normale Signatur für die ursprüngliche Nachricht: Prüfer verwenden das reguläre Digital Signature-Primitive und müssen nicht wissen, dass zwei Schritte erforderlich waren.
Hinweis
Erstellen Sie einen ML-DSA-Schlüsselsatz, dessen Schlüssel eine ID-Anforderung haben – entweder die TINK-Variante oder NO_PREFIX_WITH_PREHASH_ID, wenn Sie kein Ausgabepräfix für die resultierende Signatur wünschen. Geben Sie dem Unterzeichner den privaten Schlüsselsatz und der Prehashing-Seite den entsprechenden öffentlichen Schlüsselsatz. Der Unterzeichner sollte für jede Schlüssel-ID, die auf der Prehashing-Seite generiert werden kann, einen aktivierten Schlüssel haben. Weitere Informationen finden Sie unter Schlüsselsätze.
Schritt 1: Prehash-Wert berechnen
Führen Sie diesen Code überall dort aus, wo sich die Nachricht befindet. Es wird nur der öffentliche Schlüsselsatz benötigt.
C++
#include "tink/keyset_handle.h" #include "tink/signature/config_2026.h" #include "tink/signature/prehash.h" absl::StatusOr<std::unique_ptr<crypto::tink::Prehash>> prehasher = public_handle.GetPrimitive<crypto::tink::Prehash>( crypto::tink::ConfigSignature2026()); if (!prehasher.ok()) return prehasher.status(); absl::StatusOr<std::string> prehash = (*prehasher)->Compute(message); if (!prehash.ok()) return prehash.status();
Go
import "github.com/tink-crypto/tink-go/v2/signprehash" prehasher, err := signprehash.NewPrehash(publicHandle) if err != nil { return err } prehash, err := prehasher.ComputePrehash(message) if err != nil { return err }
Python
from tink import signature # Due to the internal structure of Tink Python, Prehash and SignPrehash # are an exception where calling signature.register() is not necessary. # Do not expect the same for other primitives (such as PublicKeyVerify in # Step 4). prehasher = public_handle.primitive(signature.Prehash) prehash = prehasher.compute(message)
Schritt 2: Prehash-Wert an den Unterzeichner senden
Senden Sie den Prehash-Wert an die Stelle, an der sich der private Schlüssel befindet. Es ist nicht geheim, aber Sie müssen seine Integrität während der Übertragung schützen: Ein Angreifer, der es während der Übertragung ändern kann, kontrolliert, was signiert wird.
Schritt 3: Prehash-Wert signieren
Führen Sie diesen Befehl dort aus, wo sich der private Schlüssel befindet.
C++
#include "tink/keyset_handle.h" #include "tink/signature/config_2026.h" #include "tink/signature/sign_prehash.h" absl::StatusOr<std::unique_ptr<crypto::tink::SignPrehash>> signer = private_handle.GetPrimitive<crypto::tink::SignPrehash>( crypto::tink::ConfigSignature2026()); if (!signer.ok()) return signer.status(); absl::StatusOr<std::string> signature = (*signer)->Sign(prehash); if (!signature.ok()) return signature.status();
Go
import "github.com/tink-crypto/tink-go/v2/signprehash" signer, err := signprehash.NewPrehashSigner(privateHandle) if err != nil { return err } sig, err := signer.SignPrehash(prehash) if err != nil { return err }
Python
from tink import signature # Due to the internal structure of Tink Python, Prehash and SignPrehash # are an exception where calling signature.register() is not necessary. # Do not expect the same for other primitives (such as PublicKeyVerify in # Step 4). signer = private_handle.primitive(signature.SignPrehash) sig = signer.sign(prehash)
Schritt 4: Signatur überprüfen
Die Bestätigung erfolgt über den normalen Ablauf für digitale Signaturen für die Originalnachricht und nicht für den Prehash-Wert.
C++
absl::StatusOr<std::unique_ptr<crypto::tink::PublicKeyVerify>> verifier = public_handle.GetPrimitive<crypto::tink::PublicKeyVerify>( crypto::tink::ConfigSignature2026()); if (!verifier.ok()) return verifier.status(); absl::Status verified = (*verifier)->Verify(signature, message);
Go
import "github.com/tink-crypto/tink-go/v2/signature" verifier, err := signature.NewVerifier(publicHandle) if err != nil { return err } if err := verifier.Verify(sig, message); err != nil { return err }
Python
from tink import signature signature.register() verifier = public_handle.primitive(signature.PublicKeyVerify) verifier.verify(sig, message)
Prehash und SignPrehash
Die Primitiven „Prehash“ und „SignPrehash“ teilen die Berechnung einer digitalen Signatur in zwei Schritte auf:
- Für Prehash ist nur der öffentliche Schlüssel erforderlich. Sie wandelt eine Nachricht beliebiger Länge in einen kurzen Prehash-Wert mit fester Größe um.
- Für SignPrehash ist der private-Schlüssel erforderlich. Sie wandelt einen Prehash-Wert in eine Signatur um.
Die resultierende Signatur ist eine gewöhnliche Signatur für die ursprüngliche Nachricht. Sie wird mit dem regulären Digital Signature-Primitiv PublicKeyVerify überprüft. Prüfer müssen nicht wissen oder sich darum kümmern, dass die Signatur in zwei Schritten erstellt wurde.
Behandeln Sie den Prehash-Wert als undurchsichtige Bytes. Es hat eine feste Größe und enthält die ID des Schlüssels, für den es berechnet wurde. Das Layout ist jedoch Teil des Wire-Formats von Tink. Sie sollten es nicht selbst parsen oder erstellen. Wenn Sie Tink portieren oder Details auf Byte-Ebene benötigen, lesen Sie den Abschnitt Tink-Wire-Format.
Verwenden Sie dieses Primitive-Paar, wenn:
- Der Signierschlüssel befindet sich an einem anderen Ort, z. B. in einem HSM, in einem KMS oder hinter einer RPC-Grenze, und Sie möchten nicht die gesamte Nachricht über diese Grenze hinweg übertragen.
- Die Nachricht ist groß und der Remote-Signierer erzwingt eine Beschränkung der Anfragengröße.
Wenn keines dieser Szenarien zutrifft, verwenden Sie stattdessen das einfache Digital Signature-Primitive, da es einfacher und schwieriger zu missbrauchen ist.
Schlüsselsätze
Die beiden Primitiven wählen Schlüssel unterschiedlich aus, so wie das Signieren und die Überprüfung für das Digital Signature-Primitiv funktionieren:
Prehash.Computeverwendet immer den Primärschlüssel des öffentlichen Schlüsselsatzes und zeichnet die ID dieses Schlüssels im Prehash-Wert auf. Es ist die Seite, die auswählt, mit welchem Schlüssel die Signatur erstellt wird.SignPrehash.Signliest die Schlüssel-ID aus dem Prehash-Wert und signiert mit dem entsprechenden aktivierten Schlüssel des privaten Schlüsselsets. Es ist die Seite, die folgt, wenn jemand anderes bereits eine Auswahl getroffen hat. Wenn kein aktivierter Schlüssel im Keyset diese ID hat, schlägt der Aufruf fehl.
Jeder Schlüssel in einem SignPrehash-Keyset muss eine ID-Anforderung haben. Andernfalls schlägt das Erstellen des Primitivs fehl.
Minimale Sicherheitsgarantien
- Die resultierende Signatur hat dieselben Eigenschaften wie eine Signatur, die mit dem Primitiv Digitale Signatur mit demselben Schlüsseltyp erstellt wurde.
- Tink stellt dem Prehash-Wert 5 Bytes voran, die einen speziellen reservierten Wert und die ID des Schlüssels enthalten, für den er berechnet wurde.
SignPrehashsigniert den Wert nur mit diesem Schlüssel. Ob der Wert kryptografisch an diesen Schlüssel gebunden ist, hängt vom Algorithmus ab. Bei External Mu ML-DSA ist das der Fall. Weitere Informationen finden Sie unter Tink-Wire-Format. - Nachrichten können beliebig lang sein.
Wichtige Hinweise
- Der Unterzeichner kann nicht prüfen, was er unterschreibt. Jeder, der
SignPrehashanrufen kann, kann eine beliebige Nachricht signieren lassen. Der Unterzeichner hat keine Möglichkeit, eine Richtlinie auf den Inhalt der Nachricht anzuwenden. Schützen Sie den Zugriff aufSignPrehashgenauso wie den Zugriff aufPublicKeySign. - Prehash-Wert bei der Übertragung schützen Es ist nicht geheim, aber ein Angreifer, der es während der Übertragung ändern kann, steuert, was signiert wird.
Schlüsseltyp auswählen
ML-DSA im External Mu-Modus, wie in RFC 9881 beschrieben, ist der einzige Algorithmus, den Tink für Prehash und SignPrehash unterstützt. Sie sollten einen standardmäßigen ML-DSA-Schlüssel mit diesen Primitiven verwenden.
Wir empfehlen ML_DSA_44 für die meisten Anwendungsfälle.
Der Schlüssel muss eine ID-Anforderung haben, da jeder Prehash-Wert mit einem Präfix beginnt, das die ID des Schlüssels enthält, für den er berechnet wurde. Die folgenden Varianten sind zulässig:
TINK– Die resultierende Signatur beginnt mit dem üblichen 5‑Byte-Ausgabepräfix von Tink.NO_PREFIX_WITH_PREHASH_ID: Die resultierende Signatur hat kein Ausgabepräfix, während der Schlüssel weiterhin die ID hat, die für den Prehash-Wert erforderlich ist.
Schlüssel, für die die NO_PREFIX-Variante (raw) verwendet wird, werden nicht unterstützt, da sie keine Schlüssel-ID haben.