Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Remarque
Certaines informations concernent un produit de préversion qui peut être sensiblement modifié avant sa publication commerciale. Microsoft n’offre aucune garantie, expresse ou implicite, en ce qui concerne les informations fournies ici. La fonctionnalité décrite dans cette rubrique est disponible dans les versions préliminaires du Windows Insider Preview.
Cet article fournit un guide pour implémenter le flux de travail de bout en bout pour créer et vérifier des signatures numériques à l’aide de l’algorithme ML-DSA avec l’API CNG de Microsoft.
Exemple de code ML-DSA à l’aide de BCrypt
Découvrez comment utiliser l’algorithme ML-DSA avec l’API CNG de Microsoft pour les signatures numériques. L’exemple BCrypt inclut des fonctions pour signer un message et vérifier la signature, ainsi que l’exportation de la clé publique. Le code est conçu pour être facile à suivre et à comprendre, ce qui permet aux développeurs qui cherchent à implémenter des signatures numériques post-quantiques dans leurs applications.
Génération et vérification de signature avec ML-DSA
Cet exemple de code illustre le flux de travail de bout en bout pour créer et vérifier des signatures numériques à l’aide de l’algorithme ML-DSA avec l’API CNG de Microsoft. Il met en évidence l’importance de la correspondance de contexte pendant la signature et la vérification, ainsi que la gestion minutieuse des descripteurs de chiffrement et de la mémoire. Les algorithmes de signature numérique post-quantique sont équivalents à un niveau élevé aux algorithmes de signature numérique existants que nous utilisons aujourd’hui (RSA-PSS, ECDSA, etc.), où une partie génère une paire de clés publique/privée et signe un message (ou sa valeur de hachage) avec la clé privée pour produire une signature. La signature peut être vérifiée par toute personne disposant de la clé publique associée. Une différence est que les algorithmes de signature numérique PQ prennent en charge les variantes de pré-hachage, qui signent ensuite des données similaires aux algorithmes de signature traditionnels, ainsi que des variantes pures, qui signent toutes les données d’entrée de longueur arbitraire. Cet exemple utilise la variante pure ML-DSA et décrit comment utiliser ML-DSA de pré-hachage. En suivant ces étapes, les développeurs peuvent implémenter des signatures numériques post-quantiques sécurisées dans leurs applications.
Configurer des handles d’algorithme et des paires de clés
Vous allez suivre ces étapes pour configurer la gestion des algorithmes et les paires de clés pour ML-DSA :
Utilisez BCryptOpenAlgorithmProvider pour l’algorithme ML-DSA, en choisissant le fournisseur primitif Microsoft par défaut ou un autre fournisseur HSM. Cette étape configure le contexte de chiffrement pour les opérations suivantes.
status = BCryptOpenAlgorithmProvider( &hAlg, BCRYPT_MLDSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Utilisez BCryptGenerateKeyPair pour créer des clés privées et publiques pour l’algorithme choisi.
status = BCryptGenerateKeyPair(hAlg, &hKeyPair, 0, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Les paramètres sont les suivants :
- hAlg : handle pour le fournisseur d’algorithmes, obtenu à partir de BCryptOpenAlgorithmProvider.
- hKeyPair : la poignée du couple de clés.
-
0: indique la taille de clé par défaut pour ML-DSA. -
NULL: aucun indicateur n’est défini pour cette opération.
Utilisez BCryptSetProperty pour spécifier le jeu de paramètres à utiliser pour ML-DSA, qui ont des compromis pour la force et les performances. Dans cet exemple, le jeu de paramètres ML-DSA-44 est choisi.
status = BCryptSetProperty(&hKeyPair, BCRYPT_PARAMETER_SET_NAME, (PUCHAR)BCRYPT_MLDSA_PARAMETER_SET_44, sizeof(BCRYPT_MLDSA_PARAMETER_SET_44), 0); if (!NT_SUCCESS(status)) { goto cleanup; }Le paramètre BCRYPT_PARAMETER_SET_NAME indique quel ensemble de paramètres utiliser (par exemple, ML-DSA-44).
Utilisez BCryptFinalizeKeyPair pour que la clé publique et privée soit prête à être utilisée.
status = BCryptFinalizeKeyPair(hKeyPair, NULL); if (!NT_SUCCESS(status)) { goto cleanup; } // Public/Private key pair is generated at this point
Signer le message
Pour signer un message avec la paire de clés générée, le code utilise BCryptSignHash.
Le code détermine d’abord la taille de mémoire tampon nécessaire pour la signature en appelant BCryptSignHash avec
NULLla sortie. Il alloue ensuite de la mémoire pour la signature.// Get the signature size status = BCryptSignHash(hKeyPair, NULL, NULL, 0, NULL, 0, &cbSignature, NULL); if (!NT_SUCCESS(status)) { goto cleanup; } pbSignature = (PBYTE)LocalAlloc(LMEM_FIXED, cbSignature); if (pbSignature == NULL) { status = STATUS_NO_MEMORY; goto cleanup; }Ensuite, il configure une structure BCRYPT_PQDSA_PADDING_INFO :
// Sign with pure ML-DSA // // Pure variants sign arbitrary length messages whereas // the pre-hash variants sign a hash value of the input. // To sign with pure ML-DSA or pure SLH-DSA, pszPrehashAlgId must be set // to NULL if BCRYPT_PQDSA_PADDING_INFO is provided to the sign/verify // functions. // For pre-hash signing, the hash algorithm used in creating // the hash of the input must be specified using the pszPrehashAlgId // field of BCRYPT_PQDSA_PADDING_INFO. This hash algorithm information // is required in generating and verifying the signature as the OID of // the hash algorithm becomes a prefix of the input to be signed. // padinfo.pbCtx = (PUCHAR)ctx; padinfo.cbCtx = sizeof(ctx); padinfo.pszPrehashAlgId = NULL;La structure BCRYPT_PQDSA_PADDING_INFO contient les champs suivants :
- pbCtx et cbCtx : pointeur et taille pour une chaîne de contexte facultative (données authentifiées supplémentaires).
-
pszPrehashAlgId : défini sur
NULLla signature « pure » (le message est signé directement, pas un hachage).
Enfin, la signature réelle est générée avec un autre appel à BCryptSignHash. Le handle de clé, les informations de remplissage, le message et la mémoire tampon de signature allouée sont passés à l’appel. L’indicateur BCRYPT_PAD_PQDSA spécifie le remplissage PQDSA.
status = BCryptSignHash( hKeyPair, &padinfo, (PUCHAR)msg, sizeof(msg), pbSignature, cbSignature, &cbWritten, BCRYPT_PAD_PQDSA); if (!NT_SUCCESS(status)) { goto cleanup; }
Exporter la clé publique
Dans cette section, la clé publique est exportée afin que d’autres puissent vérifier les signatures.
BCryptExportKey est d’abord appelé avec
NULLdes paramètres et le format BCRYPT_PQDSA_PUBLIC_BLOB pour obtenir la taille de mémoire tampon requise :// Get the public key blob size status = BCryptExportKey( hKeyPair, NULL, BCRYPT_PQDSA_PUBLIC_BLOB, NULL, 0, &cbPublicKeyBlob, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }La mémoire tampon est allouée et BCryptExportKey est appelée à nouveau pour exporter la clé publique :
pbPublicKeyBlob = (PBYTE)LocalAlloc(LMEM_FIXED, cbPublicKeyBlob); if (pbPublicKeyBlob == NULL) { status = STATUS_NO_MEMORY; goto cleanup; } // Export the public key status = BCryptExportKey( hKeyPair, NULL, BCRYPT_PQDSA_PUBLIC_BLOB, pbPublicKeyBlob, cbPublicKeyBlob, &cbWritten, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }
La clé exportée est au format BCRYPT_PQDSA_PUBLIC_BLOB .
Nettoyer les ressources
Au cours de l’étape de nettoyage, les ressources allouées, telles que les mémoires tampons et les descripteurs, sont libérées, ce qui garantit qu'il n'y ait pas de fuite de mémoire ni de descripteurs danglants.
cleanup:
if (pbPublicKeyBlob)
{
LocalFree(pbPublicKeyBlob);
}
if (pbSignature)
{
LocalFree(pbSignature);
}
if (hKeyPair != NULL)
{
BCryptDestroyKey(hKeyPair);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
Vérifier la signature
Pour vérifier une signature, le code utilise BCryptVerifySignature.
- La fonction MLDSAVerifySample dans l’exemple de code ci-dessous reçoit l’objet blob de clé publique exporté, le message, le contexte et la signature.
- BCryptImportKeyPair importe le bloc de clé publique, ce qui permet de créer un handle de clé utilisable par l'API.
- Là encore, BCRYPT_PQDSA_PADDING_INFO est préparé avec le contexte (il doit correspondre à celui utilisé lors de la signature). Les champs suivants sont définis :
- pbCtx/cbCtx (contexte) : utilisé pour fournir des données supplémentaires, ce qui peut aider à lier des signatures à une application ou un cas d’usage spécifique. La signature et la vérification doivent utiliser le même contexte.
-
pszPrehashAlgId : réglé sur
NULLpour un ML-DSA « pur ». Pour les variantes de pré-hachage, elle spécifie l’ID de l’algorithme de hachage. - BCRYPT_PAD_PQDSA : spécifie l’utilisation du remplissage PQDSA pour la sécurité post-quantique.
- BCryptVerifySignature est appelé avec le descripteur de clé, les infos de padding, le message d’origine et la signature. Si la fonction retourne la réussite, la signature est valide ; sinon, ce n’est pas le cas.
Les étapes ci-dessus sont annotées dans l’exemple de code ci-dessous :
//
// This function takes as input an ML-DSA-44 public key blob,
// and a signature generated by the same algorithm along with
// the message the signature belongs to. It verifies whether the
// signature is valid or not.
//
// STEP 1: Receive the public key blob, message, context, and signature
NTSTATUS MLDSAVerifySample(
PCBYTE pbPublicKeyBlob,
ULONG cbPublicKeyBlob,
PCBYTE pbMsg,
ULONG cbMsg,
PCBYTE pbContext,
ULONG cbContext,
PCBYTE pbSignature,
ULONG cbSignature)
{
NTSTATUS status = STATUS_SUCCESS;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BCRYPT_PQDSA_PADDING_INFO padinfo = { 0 };
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_MLDSA_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// STEP 2: Import the public key
// Import the public key
status = BCryptImportKeyPair(
hAlg,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
&hKey,
(PUCHAR)pbPublicKeyBlob,
cbPublicKeyBlob,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// STEP 3: Prepare the padding info
// Verify the signature
// Assuming the signature is generated with pure ML-DSA.
// Otherwise pszPrehashAlgId must be set to the identifier
// of the hash algorithm used in pre-hashing the input.
padinfo.pbCtx = (PUCHAR)pbContext;
padinfo.cbCtx = cbContext;
padinfo.pszPrehashAlgId = NULL;
// STEP 4: Verify the signature
status = BCryptVerifySignature(
hKey,
&padinfo,
(PUCHAR)pbMsg, // pbHash
cbMsg, // cbHash
(PUCHAR)pbSignature,
cbSignature,
BCRYPT_PAD_PQDSA);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
cleanup:
if (hKey != NULL)
{
BCryptDestroyKey(hKey);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
return status;
}
Dans le cleanup: bloc, le code utilise BCryptDestroyKey pour supprimer le handle de clé de la mémoire et BCryptCloseAlgorithmProvider pour supprimer le handle d’algorithme de la mémoire.
Passez en revue l’exemple de code complet
L’exemple de code suivant illustre le processus complet de génération et de vérification d’une signature numérique à l’aide de l’algorithme ML-DSA avec l’API CNG de Microsoft :
//
// Sample ML-DSA code
//
// This function creates an ML-DSA-44 private key, signs
// a message with it, and exports the public-key. The receiver
// can use the public key, message, and the signature to verify
// that they are generated by the same party who owns the private key.
//
#define SAMPLE_MESSAGE "message"
#define SAMPLE_CONTEXT "context"
NTSTATUS MLDSASignSample()
{
NTSTATUS status = STATUS_SUCCESS;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKeyPair = NULL;
BCRYPT_PQDSA_PADDING_INFO padinfo = { 0 };
PBYTE pbSignature = NULL;
ULONG cbSignature, cbWritten;
const BYTE msg[] = SAMPLE_MESSAGE;
const BYTE ctx[] = SAMPLE_CONTEXT;
PBYTE pbPublicKeyBlob = NULL;
ULONG cbPublicKeyBlob;
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_MLDSA_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
status = BCryptGenerateKeyPair(hAlg, &hKeyPair, 0, NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
status = BCryptSetProperty(&hKeyPair, BCRYPT_PARAMETER_SET_NAME, (PUCHAR)BCRYPT_MLDSA_PARAMETER_SET_44, sizeof(BCRYPT_MLDSA_PARAMETER_SET_44), 0);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
status = BCryptFinalizeKeyPair(hKeyPair, NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// Public/Private key pair is generated at this point
// Get the signature size
status = BCryptSignHash(hKeyPair, NULL, NULL, 0, NULL, 0, &cbSignature, NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
pbSignature = (PBYTE)LocalAlloc(LMEM_FIXED, cbSignature);
if (pbSignature == NULL) {
status = STATUS_NO_MEMORY;
goto cleanup;
}
//
// Sign with pure ML-DSA
//
// Pure variants sign arbitrary length messages whereas
// the pre-hash variants sign a hash value of the input.
// To sign with pure ML-DSA or pure SLH-DSA, pszPrehashAlgId must be set
// to NULL if BCRYPT_PQDSA_PADDING_INFO is provided to the sign/verify
// functions.
// For pre-hash signing, the hash algorithm used in creating
// the hash of the input must be specified using the pszPrehashAlgId
// field of BCRYPT_PQDSA_PADDING_INFO. This hash algorithm information
// is required in generating and verifying the signature as the OID of
// the hash algorithm becomes a prefix of the input to be signed.
//
padinfo.pbCtx = (PUCHAR)ctx;
padinfo.cbCtx = sizeof(ctx);
padinfo.pszPrehashAlgId = NULL;
status = BCryptSignHash(
hKeyPair,
&padinfo,
(PUCHAR)msg,
sizeof(msg),
pbSignature,
cbSignature,
&cbWritten,
BCRYPT_PAD_PQDSA);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
//
// Export the public key
//
// Get the public key blob size
status = BCryptExportKey(
hKeyPair,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
NULL,
0,
&cbPublicKeyBlob,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
pbPublicKeyBlob = (PBYTE)LocalAlloc(LMEM_FIXED, cbPublicKeyBlob);
if (pbPublicKeyBlob == NULL) {
status = STATUS_NO_MEMORY;
goto cleanup;
}
// Export the public key
status = BCryptExportKey(
hKeyPair,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
pbPublicKeyBlob,
cbPublicKeyBlob,
&cbWritten,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
cleanup:
if (pbPublicKeyBlob)
{
LocalFree(pbPublicKeyBlob);
}
if (pbSignature)
{
LocalFree(pbSignature);
}
if (hKeyPair != NULL)
{
BCryptDestroyKey(hKeyPair);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
return status;
}
//
// This function takes as input an ML-DSA-44 public key blob,
// and a signature generated by the same algorithm along with
// the message the signature belongs to. It verifies whether the
// signature is valid or not.
//
NTSTATUS MLDSAVerifySample(
PCBYTE pbPublicKeyBlob,
ULONG cbPublicKeyBlob,
PCBYTE pbMsg,
ULONG cbMsg,
PCBYTE pbContext,
ULONG cbContext,
PCBYTE pbSignature,
ULONG cbSignature)
{
NTSTATUS status = STATUS_SUCCESS;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BCRYPT_PQDSA_PADDING_INFO padinfo = { 0 };
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_MLDSA_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// Import the public key
status = BCryptImportKeyPair(
hAlg,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
&hKey,
(PUCHAR)pbPublicKeyBlob,
cbPublicKeyBlob,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// Verify the signature
// Assuming the signature is generated with pure ML-DSA.
// Otherwise pszPrehashAlgId must be set to the identifier
// of the hash algorithm used in pre-hashing the input.
padinfo.pbCtx = (PUCHAR)pbContext;
padinfo.cbCtx = cbContext;
padinfo.pszPrehashAlgId = NULL;
status = BCryptVerifySignature(
hKey,
&padinfo,
(PUCHAR)pbMsg, // pbHash
cbMsg, // cbHash
(PUCHAR)pbSignature,
cbSignature,
BCRYPT_PAD_PQDSA);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
cleanup:
if (hKey != NULL)
{
BCryptDestroyKey(hKey);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
return status;
}
Contenu connexe
- Utilisation de ML-KEM avec CNG
- Vue d’ensemble de la programmation CNG classique
- fonctions de stockage de clés CNG