Saltar al contenido
ecdsa.com
C · Línea de comandos

NSS (Network Security Services)

NSS, la biblioteca criptográfica y TLS de Mozilla en la que se basan Firefox y Thunderbird: dos capas de firma que discrepan en la codificación ECDSA (DER con cryptohi, r‖s en bruto con PK11), solo de P-256 a P-521, Ed25519 y ML-KEM.
Licencia
MPL-2.0
Versión comprobada
3.124 ·
Código fuente
hg.mozilla.org/projects/nss
Documentación
firefox-source-docs.mozilla.org/security/nss/index.html

Compatibilidad de un vistazo

ECDSAEdDSACriptografía poscuántica

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

Network Security Services es la biblioteca que hay debajo de Firefox, Thunderbird y varios componentes del sistema en Linux, incluida la implementación de WebCrypto de Firefox. Funciona a través de PKCS#11: las claves residen en un token (el softoken integrado, o una tarjeta inteligente o un HSM), y todo lo que está por encima llama a las funciones PK11_*. Los datos de esta página se comprobaron con el paquete de Alpine 3.24.2, NSS 3.124, inicializado sin base de datos de claves (NSS_NoDB_Init).

ECDSA tiene dos capas que producen codificaciones distintas. SEC_SignData y VFY_VerifyData, en cryptohi, calculan el hash del mensaje y usan DER, la forma que esperan X.509, CMS y OpenSSL. PK11_Sign y PK11_Verify reciben un resumen y usan la forma r‖s en bruto, de 64 bytes en P-256. Mezclarlas es el error clásico con NSS; convierte con el conversor DER ⇄ en bruto. PK11_Sign también aceptó un resumen de 64 bytes en P-256 y firmó sus primeros 32 bytes, así que pasa un resumen que corresponda a la curva.

Los nonces son aleatorios —dos firmas del mismo mensaje fueron distintas— y s no se normaliza; una firma high-S se verificó. Con una clave P-384 se firmó y se verificó con ECDSA y SHA-256, así que el hash no está ligado a la curva. Los fallos se pueden distinguir: después de que VFY_VerifyData devuelva SECFailure, PORT_GetError() devuelve SEC_ERROR_BAD_DER para una firma en bruto o truncada y SEC_ERROR_BAD_SIGNATURE para una firma válida sobre otros datos.

El softoken solo generó claves en P-256, P-384 y P-521; P-224 y secp256k1 fallaron con SEC_ERROR_INVALID_KEY. Ed25519 funciona mediante CKM_EC_EDWARDS_KEY_PAIR_GEN y CKM_EDDSA; Ed448, no. La encapsulación ML-KEM-768 supera la prueba de ida y vuelta (PK11_Encapsulate, PK11_Decapsulate), mientras que los archivos de cabecera declaran mecanismos ML-DSA que el softoken de esta compilación no ofrece. Compara el KEM con la herramienta de ML-KEM.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/nss/run.sh ejecutó NSS (Network Security Services) 3.124 en la imagen de contenedor alpine:3.24.2 el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Codificación de la firma ECDSA

    der-or-raw

    SEC_SignData devolvió DER estricto; PK11_Sign devolvió 64 bytes de r‖s en bruto en P-256.

    Registrado por la comprobación
    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 de ECDSA

    random

    Dos firmas SEC_SignData de un mismo mensaje fueron distintas.

    Registrado por la comprobación
    fact ecdsa-nonce = random
    evidence ecdsa-nonce: two SEC_SignData signatures of one message are different
  • Low-S al firmar

    either

    s no se normaliza: aparecieron valores high-S entre 64 firmas.

    Registrado por la comprobación
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: signatures with s > n/2 among 64: yes
  • High-S al verificar

    accepts-high-s

    Una firma DER con s sustituido por n − s se verificó con VFY_VerifyData.

    Registrado por la comprobación
    fact ecdsa-low-s-verify = accepts-high-s
  • De qué calcula el hash la llamada de firma

    hashes-message-or-digest

    SEC_SignData calcula el hash del mensaje; PK11_Sign sobre el SHA-256 del mensaje produjo una firma que VFY_VerifyData aceptó.

    Registrado por la comprobación
    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
  • Resumen más largo que el orden de la curva

    truncated

    PK11_Sign aceptó un resumen de 64 bytes en P-256; la firma se verificó sobre sus primeros 32 bytes.

    Registrado por la comprobación
    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
  • Hash vinculado a la curva

    none

    Con una clave P-384 se firmó y se verificó con ECDSA y SHA-256.

    Registrado por la comprobación
    fact ecdsa-hash-curve-binding = none
    evidence ecdsa-hash-curve-binding: ECDSA with SHA-256 on a P-384 key: sign ok, verify ok
  • Firma ECDSA mal formada

    returns-error

    VFY_VerifyData falló con SEC_ERROR_BAD_DER para r‖s en bruto y para 3 bytes, y con SEC_ERROR_BAD_SIGNATURE para un mensaje incorrecto.

    Registrado por la comprobación
    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
  • Curvas ECDSA

    P-256P-384P-521

    P-256, P-384 y P-521; la generación de claves P-224 y secp256k1 devolvió SEC_ERROR_INVALID_KEY.

    Registrado por la comprobación
    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 firmó, verificó y produjo dos veces la misma firma; la generación de claves Ed448 devolvió SEC_ERROR_INVALID_KEY.

    Registrado por la comprobación
    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
  • Algoritmos poscuánticos

    ML-KEM

    La encapsulación ML-KEM-768 superó la prueba de ida y vuelta con un texto cifrado de 1088 bytes; la generación de claves ML-DSA-65 devolvió SEC_ERROR_INVALID_ALGORITHM, y el softoken no ofrece SLH-DSA.

    Registrado por la comprobación
    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

Uso

Genera una clave P-256 en el softoken, firma con `SEC_SignData` y verifica la firma DER con `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;
}

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor alpine:3.24.2, y la ejecución falla si la salida no coincide.

Configuración
apk add --no-cache -q build-base nss-dev=3.124-r0 >/dev/null
Comando
cc -o usage usage.c $(pkg-config --cflags --libs nss) && ./usage
La salida debe coincidir (expresión regular)
DER tag: 0x30, verified: yes

Bibliotecas