Aller au contenu
ecdsa.com
C · Ligne de commande

NSS (Network Security Services)

NSS, la bibliothèque cryptographique et TLS de Mozilla sur laquelle reposent Firefox et Thunderbird : deux couches de signature en désaccord sur l’encodage ECDSA (DER via cryptohi, r‖s brut via PK11), de P-256 à P-521 uniquement, Ed25519 et ML-KEM.
Licence
MPL-2.0
Version vérifiée
3.124 ·
Code source
hg.mozilla.org/projects/nss
Documentation
firefox-source-docs.mozilla.org/security/nss/index.html

Compatibilité en un coup d’œil

ECDSAEdDSAPost-quantique

Courbes ECDSA
P-256P-384P-521
EdDSA
Ed25519

Network Security Services est la bibliothèque sur laquelle reposent Firefox, Thunderbird et plusieurs composants système de Linux, y compris l’implémentation WebCrypto de Firefox. Elle fonctionne via PKCS#11 : les clés résident dans un jeton (le softoken intégré, une carte à puce ou un HSM), et tout ce qui se trouve au-dessus appelle les fonctions PK11_*. Les faits présentés ici ont été vérifiés sur le paquet Alpine 3.24.2, NSS 3.124, initialisé sans base de clés (NSS_NoDB_Init).

Pour ECDSA, deux couches produisent des encodages différents. Dans cryptohi, SEC_SignData et VFY_VerifyData hachent le message et utilisent DER, la forme qu’attendent X.509, CMS et OpenSSL. PK11_Sign et PK11_Verify prennent un condensat et utilisent la forme r‖s brute, 64 octets sur P-256. Les mélanger est l’erreur classique avec NSS ; convertissez avec le convertisseur DER ⇄ brut. PK11_Sign a aussi accepté un condensat de 64 octets sur P-256 et en a signé les 32 premiers octets : passez donc un condensat adapté à la courbe.

Les nonces sont aléatoires (deux signatures du même message différaient) et s n’est pas normalisé ; une signature high-S a passé la vérification. La signature et la vérification ECDSA avec SHA-256 ont réussi sur une clé P-384 : la fonction de hachage n’est donc pas liée à la courbe. Les échecs se distinguent : après que VFY_VerifyData a renvoyé SECFailure, PORT_GetError() signale SEC_ERROR_BAD_DER pour une signature brute ou tronquée et SEC_ERROR_BAD_SIGNATURE pour une signature valide portant sur d’autres données.

Le softoken n’a généré de clés que sur P-256, P-384 et P-521 ; P-224 et secp256k1 ont échoué avec SEC_ERROR_INVALID_KEY. Ed25519 fonctionne via CKM_EC_EDWARDS_KEY_PAIR_GEN et CKM_EDDSA, mais pas Ed448. L’encapsulation ML-KEM-768 fonctionne en aller-retour (PK11_Encapsulate, PK11_Decapsulate), tandis que les en-têtes déclarent des mécanismes ML-DSA que le softoken de ce build ne propose pas. Comparez le KEM avec l’outil ML-KEM.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/nss/run.sh a exécuté NSS (Network Security Services) 3.124 dans l’image de conteneur alpine:3.24.2 le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Encodage des signatures ECDSA

    der-or-raw

    SEC_SignData a renvoyé du DER strict ; PK11_Sign a renvoyé 64 octets de r‖s brut sur P-256.

    Relevé par la vérification
    fact ecdsa-signature-encoding = der-or-raw
    evidence ecdsa-signature-encoding: SEC_SignData output parses as strict DER: yes; PK11_Sign: 64 bytes (P-256 raw r||s is 64)
  • Nonce ECDSA

    random

    Deux signatures SEC_SignData d’un même message étaient différentes.

    Relevé par la vérification
    fact ecdsa-nonce = random
    evidence ecdsa-nonce: two SEC_SignData signatures of one message are different
  • Low-S à la signature

    either

    s n’est pas normalisé : des valeurs high-S sont apparues parmi 64 signatures.

    Relevé par la vérification
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: signatures with s > n/2 among 64: yes
  • High-S à la vérification

    accepts-high-s

    Une signature DER dont s avait été remplacé par n − s a passé la vérification avec VFY_VerifyData.

    Relevé par la vérification
    fact ecdsa-low-s-verify = accepts-high-s
  • Ce que l’appel de signature hache

    hashes-message-or-digest

    SEC_SignData hache le message ; PK11_Sign appliqué au SHA-256 du message a produit une signature que VFY_VerifyData a acceptée.

    Relevé par la vérification
    fact ecdsa-prehash = hashes-message-or-digest
    evidence ecdsa-prehash: SEC_SignData hashes the message; PK11_Sign over SHA-256(message) verified with VFY_VerifyData: yes
  • Condensat plus long que l’ordre de la courbe

    truncated

    PK11_Sign a accepté un condensat de 64 octets sur P-256 ; la signature a été vérifiée sur ses 32 premiers octets.

    Relevé par la vérification
    fact ecdsa-long-input = truncated
    evidence ecdsa-long-input: PK11_Sign accepted a 64-byte digest; the signature verifies over its first 32 bytes: yes
  • Hachage lié à la courbe

    none

    La signature et la vérification ECDSA avec SHA-256 ont réussi sur une clé P-384.

    Relevé par la vérification
    fact ecdsa-hash-curve-binding = none
    evidence ecdsa-hash-curve-binding: ECDSA with SHA-256 on a P-384 key: sign ok, verify ok
  • Signature ECDSA mal formée

    returns-error

    VFY_VerifyData a échoué avec SEC_ERROR_BAD_DER pour du r‖s brut et pour 3 octets, et avec SEC_ERROR_BAD_SIGNATURE pour un mauvais message.

    Relevé par la vérification
    fact ecdsa-malformed-signature = returns-error
    evidence ecdsa-malformed-signature: VFY_VerifyData returned SECFailure with: raw r||s SEC_ERROR_BAD_DER, 3 bytes SEC_ERROR_BAD_DER, wrong message SEC_ERROR_BAD_SIGNATURE
  • Courbes ECDSA

    P-256P-384P-521

    P-256, P-384 et P-521 ; la génération de clés P-224 et secp256k1 a renvoyé SEC_ERROR_INVALID_KEY.

    Relevé par la vérification
    fact ecdsa-curves = P-256, P-384, P-521
    evidence ecdsa-curves: P-224: key generation SEC_ERROR_INVALID_KEY
    evidence ecdsa-curves: secp256k1: key generation SEC_ERROR_INVALID_KEY
  • EdDSA

    Ed25519

    Ed25519 a signé, vérifié et produit deux fois des signatures identiques ; la génération de clés Ed448 a renvoyé SEC_ERROR_INVALID_KEY.

    Relevé par la vérification
    fact eddsa = Ed25519
    evidence eddsa: Ed25519: keygen ok, sign and verify ok, deterministic yes
    evidence eddsa: Ed448 (OID 1.3.101.113): SEC_ERROR_INVALID_KEY
  • Algorithmes post-quantiques

    ML-KEM

    L’encapsulation ML-KEM-768 a fonctionné en aller-retour avec un texte chiffré de 1088 octets ; la génération de clés ML-DSA-65 a renvoyé SEC_ERROR_INVALID_ALGORITHM, et le softoken ne propose pas SLH-DSA.

    Relevé par la vérification
    fact pqc = ML-KEM
    evidence pqc: ML-KEM-768: ciphertext 1088 bytes, shared secrets match: yes
    evidence pqc: ML-DSA-65 key generation failed: SEC_ERROR_INVALID_ALGORITHM; CKM_ML_DSA_KEY_PAIR_GEN in the internal slot: no
    evidence pqc: CKM_SLH_DSA in the internal slot: no

Utilisation

Générer une clé P-256 dans le softoken, signer avec `SEC_SignData` et vérifier la signature DER avec `VFY_VerifyData`.
#include <cryptohi.h>
#include <keyhi.h>
#include <nss.h>
#include <pk11pub.h>
#include <secoid.h>
#include <stdio.h>
#include <string.h>

int main(void) {
    NSS_NoDB_Init(NULL);
    /* EC key generation takes the curve OID, DER-encoded, as its parameter. */
    SECOidData *curve = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PRIME256V1);
    unsigned char oid[18] = {0x06, (unsigned char)curve->oid.len};
    memcpy(oid + 2, curve->oid.data, curve->oid.len);
    SECItem params = {siBuffer, oid, curve->oid.len + 2};
    PK11SlotInfo *slot = PK11_GetInternalSlot();
    SECKEYPublicKey *pub = NULL;
    SECKEYPrivateKey *priv = PK11_GenerateKeyPair(slot, CKM_EC_KEY_PAIR_GEN, &params, &pub, PR_FALSE, PR_FALSE, NULL);

    /* SEC_SignData hashes the message and returns DER; PK11_Sign would return raw r||s over a digest. */
    unsigned char msg[] = "order #1042 approved";
    SECItem sig = {siBuffer, NULL, 0};
    SEC_SignData(&sig, msg, sizeof msg - 1, priv, SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
    SECStatus rv = VFY_VerifyData(msg, sizeof msg - 1, pub, &sig, SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, NULL);
    printf("DER tag: 0x%02x, verified: %s\n", sig.data[0], rv == SECSuccess ? "yes" : "no");

    SECITEM_FreeItem(&sig, PR_FALSE);
    SECKEY_DestroyPrivateKey(priv);
    SECKEY_DestroyPublicKey(pub);
    PK11_FreeSlot(slot);
    return NSS_Shutdown() == SECSuccess ? 0 : 1;
}

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur alpine:3.24.2, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
apk add --no-cache -q build-base nss-dev=3.124-r0 >/dev/null
Commande
cc -o usage usage.c $(pkg-config --cflags --libs nss) && ./usage
La sortie doit correspondre à cette expression régulière
DER tag: 0x30, verified: yes

Bibliothèques