Nous recommandons les primitives Prehash et SignPrehash avec une clé ML_DSA_44 lorsque la clé privée se trouve à un emplacement où le message ne peut pas être envoyé.
Il arrive parfois que la partie qui détient la clé de signature ne puisse pas (ou ne doive pas) recevoir le message lui-même : la clé se trouve dans un HSM ou un KMS, ou le message est plus volumineux que la limite de taille de la requête du signataire.
Les primitives Prehash et SignPrehash résolvent ce problème en divisant la signature en deux étapes. Vous calculez une courte valeur de préhash là où se trouve le message, en utilisant uniquement la clé publique, et vous l'envoyez au signataire. Le signataire la transforme en signature à l'aide de la clé privée, sans jamais voir le message. Avec ML-DSA en mode Mu externe (l'algorithme que Tink prend en charge ici), la valeur de préhachage est de 69 octets, quelle que soit la taille du message.
La signature que vous obtenez est une signature ordinaire sur le message d'origine : les vérificateurs utilisent la primitive Signature numérique habituelle et n'ont pas besoin de savoir que deux étapes ont été nécessaires.
Avant de commencer
Créez un keyset ML-DSA dont les clés ont une exigence d'ID : la variante TINK ou NO_PREFIX_WITH_PREHASH_ID si vous ne souhaitez pas de préfixe de sortie sur la signature résultante. Donnez au signataire le keyset de clé privée et au côté préhachage le keyset de clé publique correspondant. Le signataire doit disposer d'une clé activée pour chaque ID de clé que le côté préhachage peut produire. Pour en savoir plus, consultez Ensembles de clés.
Étape 1 : Calculez la valeur pré-hachée
Exécutez-le là où se trouve le message. Il n'a besoin que de la collection de clés publiques.
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)
Étape 2 : Envoyer la valeur préhachée au signataire
Envoyez la valeur préhachée à l'entité qui détient la clé privée. Il n'est pas secret, mais vous devez protéger son intégrité en transit : un pirate informatique qui peut le modifier en vol contrôle ce qui est signé.
Étape 3 : Signer la valeur préhachée
Exécutez cette commande à l'emplacement de la clé privée.
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)
Étape 4 : Vérifiez la signature
La validation correspond au flux de signature numérique habituel sur le message d'origine, et non sur la valeur préhachée.
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 et SignPrehash
Les primitives Prehash et SignPrehash divisent le calcul d'une signature numérique en deux étapes :
- Prehash n'a besoin que de la clé publique. Elle transforme un message de longueur arbitraire en une valeur préhachée courte et de taille fixe.
- SignPrehash a besoin de la clé privée. Elle transforme une valeur préhachée en signature.
La signature obtenue est une signature ordinaire sur le message d'origine. Vous le validez avec la primitive Digital Signature PublicKeyVerify habituelle, et les validateurs n'ont pas besoin de connaître ni de se soucier du fait que la signature a été produite en deux étapes.
Traitez la valeur préhachée comme des octets opaques. Il a une taille fixe et porte l'ID de la clé pour laquelle il a été calculé, mais sa mise en page fait partie du format filaire de Tink. Vous ne devez pas l'analyser ni le construire vous-même. Si vous portez Tink ou avez besoin d'informations au niveau des octets, consultez Format filaire Tink.
Utilisez cette paire de primitives lorsque :
- La clé de signature se trouve ailleurs, par exemple dans un HSM, un KMS ou derrière une limite RPC, et vous ne souhaitez pas envoyer le message complet au-delà de cette limite.
- Le message est volumineux et le signataire à distance applique une limite de taille de requête.
Si aucune de ces situations ne s'applique, utilisez plutôt la primitive Signature numérique simple, qui est plus facile à utiliser et moins sujette aux erreurs.
Collections de clés
Les deux primitives sélectionnent les clés différemment, de la même manière que la signature et la validation le font pour la primitive Signature numérique :
Prehash.Computeutilise toujours la clé primaire du keyset public et enregistre l'ID de cette clé dans la valeur préhachée. C'est le côté qui choisit la clé avec laquelle la signature sera effectuée.SignPrehash.Signlit l'ID de clé à partir de la valeur préhachée et signe avec la clé activée correspondante du keyset de clés privées. Il s'agit du côté qui suit un choix déjà fait par quelqu'un d'autre. Si aucune clé activée dans le trousseau de clés ne possède cet ID, l'appel échoue.
Chaque clé d'un keyset SignPrehash doit avoir une exigence d'ID. Sinon, la création de la primitive échoue.
Garanties de sécurité minimales
- La signature obtenue présente les mêmes propriétés qu'une signature produite par la primitive Digital Signature avec le même type de clé.
- Tink ajoute au début de la valeur préhachée cinq octets contenant une valeur spéciale réservée et l'ID de la clé pour laquelle elle a été calculée.
SignPrehashsigne la valeur uniquement avec cette clé. La question de savoir si la valeur est cryptographiquement liée à cette clé dépend de l'algorithme. Pour External Mu ML-DSA, c'est le cas. Pour en savoir plus, consultez Format fil Tink. - Les messages peuvent avoir une longueur arbitraire.
Éléments à surveiller
- Le signataire ne peut pas inspecter ce qu'il signe. Toute personne pouvant appeler
SignPrehashpeut obtenir un message arbitraire signé, et le signataire n'a aucun moyen d'appliquer une règle au contenu du message. Protégez l'accès àSignPrehashexactement comme vous le feriez pourPublicKeySign. - Protégez la valeur du préhash en transit. Il ne s'agit pas d'un secret, mais un pirate informatique qui peut le modifier en transit contrôle ce qui est signé.
Choisir un type de clé
ML-DSA en mode External Mu, tel que décrit dans la RFC 9881, est le seul algorithme que Tink prend en charge pour Prehash et SignPrehash. Vous devez utiliser une clé ML-DSA standard avec ces primitives.
Nous recommandons ML_DSA_44 pour la plupart des cas d'utilisation.
La clé doit avoir une exigence d'ID, car chaque valeur préhachée commence par un préfixe contenant l'ID de la clé pour laquelle elle a été calculée. Les variantes suivantes sont acceptées :
TINK: la signature obtenue commence par le préfixe de sortie habituel de Tink (5 octets).NO_PREFIX_WITH_PREHASH_ID: la signature obtenue ne comporte aucun préfixe de sortie, tandis que la clé conserve l'ID dont la valeur préhachée a besoin.
Les clés qui utilisent la variante NO_PREFIX (brute) ne sont pas acceptées, car elles n'ont pas d'ID de clé.