Le primitive Prehash e SignPrehash dividono il calcolo di una firma digitale in due passaggi:
- Prehash richiede solo la chiave pubblica. Trasforma un messaggio di lunghezza arbitraria in un valore pre-hash breve e di dimensioni fisse.
- SignPrehash richiede la chiave privata. Trasforma un valore pre-hash in una firma.
La firma risultante è una firma ordinaria sul messaggio originale. La verifica viene eseguita con la primitiva firma digitale PublicKeyVerify e i verificatori non devono sapere, né interessarsi, che la firma è stata prodotta in due passaggi.
Considera il valore pre-hash come byte opachi. Ha una dimensione fissa e contiene l'ID della chiave per cui è stato calcolato, ma il suo layout fa parte del formato di trasferimento di Tink e non devi analizzarlo o costruirne uno autonomamente. Se stai trasferendo Tink o hai bisogno dei dettagli a livello di byte, consulta Formato di Tink.
Utilizza questa coppia di primitive quando:
- La chiave di firma si trova altrove, ad esempio in un modulo HSM, in un KMS o dietro un limite RPC, e non vuoi inviare l'intero messaggio oltre questo limite.
- Il messaggio è grande e il firmatario remoto impone un limite alle dimensioni della richiesta.
Se nessuno di questi casi si applica, utilizza la primitiva Firma digitale: è più semplice e più difficile da usare in modo improprio.
Set di chiavi
Le due primitive selezionano le chiavi in modo diverso, proprio come la firma e la verifica per la primitiva Firma digitale:
Prehash.Computeutilizza sempre la chiave primaria del keyset pubblico e registra l'ID della chiave nel valore pre-hash. È la parte che sceglie con quale chiave verrà creata la firma.SignPrehash.Signlegge l'ID chiave dal valore pre-hash e firma con la chiave abilitata corrispondente del set di chiavi private. È la parte che segue una scelta già fatta da qualcun altro. Se nessuna chiave abilitata nel keyset ha questo ID, la chiamata non va a buon fine.
Ogni chiave in un keyset SignPrehash deve avere un requisito ID, altrimenti la creazione della primitiva non va a buon fine.
Garanzie di sicurezza minime
- La firma risultante ha le stesse proprietà di una firma prodotta dalla primitiva Firma digitale con lo stesso tipo di chiave.
- Tink antepone al valore prehash 5 byte contenenti un valore speciale riservato e l'ID della chiave per cui è stato calcolato.
SignPrehashfirma il valore solo con questa chiave. Se il valore è associato crittograficamente a questa chiave dipende dall'algoritmo; per External Mu ML-DSA lo è, vedi Formato di Tink. - I messaggi possono avere una lunghezza arbitraria.
Aspetti a cui prestare attenzione
- Il firmatario non può ispezionare ciò che firma. Chiunque possa chiamare
SignPrehashpuò ottenere la firma di un messaggio arbitrario e il firmatario non ha modo di applicare un criterio ai contenuti del messaggio. Proteggi l'accesso aSignPrehashesattamente come proteggeresti l'accesso aPublicKeySign. - Proteggere il valore pre-hash in transito. Non è un segreto, ma un malintenzionato che può modificarlo durante il trasferimento controlla cosa viene firmato.
Scegli un tipo di chiave
ML-DSA in modalità External Mu, come descritto in RFC 9881, è l'unico algoritmo supportato da Tink per Prehash e SignPrehash. Devi utilizzare una chiave ML-DSA standard con queste primitive.
Consigliamo ML_DSA_65 per la maggior parte dei casi d'uso.
La chiave deve avere un requisito di ID, perché ogni valore pre-hash inizia con un prefisso che contiene l'ID della chiave per cui è stato calcolato. Sono accettate le seguenti varianti:
TINK: la firma risultante inizia con il prefisso di output di 5 byte abituale di Tink.NO_PREFIX_WITH_PREHASH_ID: la firma risultante non ha un prefisso di output, mentre la chiave ha ancora l'ID necessario per il valore prehash.
Le chiavi che utilizzano la variante NO_PREFIX (non elaborata) non sono supportate perché
non hanno un ID chiave.
Esempi
Gli esempi seguenti firmano un messaggio in due passaggi e poi verificano la firma risultante con la primitiva Firma digitale ordinaria.
Vengono mostrati come un unico blocco per facilitare la lettura. In un'implementazione reale, i due passaggi vengono eseguiti in posizioni diverse. Per una procedura dettagliata passo passo, consulta Voglio firmare i dati con un firmatario remoto.
C++
#include "tink/keyset_handle.h" #include "tink/public_key_verify.h" #include "tink/signature/config_2026.h" #include "tink/signature/prehash.h" #include "tink/signature/sign_prehash.h" using ::crypto::tink::ConfigSignature2026; using ::crypto::tink::Prehash; using ::crypto::tink::PublicKeyVerify; using ::crypto::tink::SignPrehash; // 1. Wherever the message is: compute the prehash value. This needs only // the public keyset. absl::StatusOr<std::unique_ptr<Prehash>> prehasher = public_handle.GetPrimitive<Prehash>(ConfigSignature2026()); if (!prehasher.ok()) return prehasher.status(); absl::StatusOr<std::string> prehash = (*prehasher)->Compute(message); if (!prehash.ok()) return prehash.status(); // 2. Wherever the private key is: turn the prehash value into a signature. absl::StatusOr<std::unique_ptr<SignPrehash>> signer = private_handle.GetPrimitive<SignPrehash>(ConfigSignature2026()); if (!signer.ok()) return signer.status(); absl::StatusOr<std::string> signature = (*signer)->Sign(*prehash); if (!signature.ok()) return signature.status(); // 3. Anywhere: verify the signature over the original message. absl::StatusOr<std::unique_ptr<PublicKeyVerify>> verifier = public_handle.GetPrimitive<PublicKeyVerify>(ConfigSignature2026()); if (!verifier.ok()) return verifier.status(); absl::Status verified = (*verifier)->Verify(*signature, message);
Go
import ( "github.com/tink-crypto/tink-go/v2/signature" "github.com/tink-crypto/tink-go/v2/signprehash" ) // 1. Wherever the message is: compute the prehash value. This needs only // the public keyset. prehasher, err := signprehash.NewPrehash(publicHandle) if err != nil { return err } prehash, err := prehasher.ComputePrehash(message) if err != nil { return err } // 2. Wherever the private key is: turn the prehash value into a signature. signer, err := signprehash.NewPrehashSigner(privateHandle) if err != nil { return err } sig, err := signer.SignPrehash(prehash) if err != nil { return err } // 3. Anywhere: verify the signature over the original message. verifier, err := signature.NewVerifier(publicHandle) if err != nil { return err } if err := verifier.Verify(sig, message); err != nil { return err }
Argomenti correlati
- Voglio firmare i dati con un firmatario remoto: una procedura dettagliata del flusso in due passaggi.
- Firma digitale: firma e verifica in un unico passaggio, quando il firmatario può vedere il messaggio.
- Tipi di chiavi primitive supportati per lingua
- Formato wire Tink: il layout a livello di byte di un valore pre-hash, per il porting di Tink in un'altra lingua.