Pular para o conteúdo
ecdsa.com
C · Linha de comando

NSS (Network Security Services)

O NSS, a biblioteca de criptografia e TLS da Mozilla por trás do Firefox e do Thunderbird: duas camadas de assinatura que divergem na codificação ECDSA (DER pelo cryptohi, r‖s bruto pelo PK11), apenas P-256 a P-521, Ed25519 e ML-KEM.
Licença
MPL-2.0
Versão verificada
3.124 ·
Código-fonte
hg.mozilla.org/projects/nss
Documentação
firefox-source-docs.mozilla.org/security/nss/index.html

Visão geral da compatibilidade

ECDSAEdDSAPós-quântica

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

O Network Security Services é a biblioteca por baixo do Firefox, do Thunderbird e de vários componentes de sistema do Linux, incluindo o WebCrypto do Firefox. Ele funciona por meio do PKCS#11: as chaves ficam em um token (o softoken embutido, um smart card ou um HSM), e tudo acima dele chama as funções PK11_*. Os fatos desta página foram verificados no pacote do Alpine 3.24.2, NSS 3.124, inicializado sem banco de dados de chaves (NSS_NoDB_Init).

O ECDSA tem duas camadas que produzem codificações diferentes. SEC_SignData e VFY_VerifyData, no cryptohi, calculam o hash da mensagem e usam DER, a forma que X.509, CMS e OpenSSL esperam. PK11_Sign e PK11_Verify recebem um digest e usam a forma r‖s bruta, 64 bytes na P-256. Misturar as duas é o erro clássico com o NSS; converta com o conversor DER ⇄ bruto. PK11_Sign também aceitou um digest de 64 bytes na P-256 e assinou os primeiros 32 bytes dele, então passe um digest compatível com a curva.

Os nonces são aleatórios — duas assinaturas da mesma mensagem foram diferentes — e o s não é normalizado; uma assinatura high-S passou na verificação. O ECDSA com SHA-256 assinou e verificou com uma chave P-384, então o hash não está vinculado à curva. As falhas podem ser distinguidas: depois que VFY_VerifyData retorna SECFailure, PORT_GetError() informa SEC_ERROR_BAD_DER para uma assinatura bruta ou truncada e SEC_ERROR_BAD_SIGNATURE para uma assinatura válida sobre outros dados.

O softoken gerou chaves apenas na P-256, na P-384 e na P-521; P-224 e secp256k1 falharam com SEC_ERROR_INVALID_KEY. O Ed25519 funciona por meio de CKM_EC_EDWARDS_KEY_PAIR_GEN e CKM_EDDSA; o Ed448, não. O encapsulamento ML-KEM-768 faz o ciclo completo de ida e volta (PK11_Encapsulate, PK11_Decapsulate), enquanto os arquivos de cabeçalho declaram mecanismos ML-DSA que o softoken deste build não oferece. Compare o KEM na ferramenta de ML-KEM.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/nss/run.sh executou NSS (Network Security Services) 3.124 na imagem de contêiner alpine:3.24.2 em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Codificação da assinatura ECDSA

    der-or-raw

    SEC_SignData retornou DER estrito; PK11_Sign retornou 64 bytes de r‖s bruto na P-256.

    Registrado pela verificação
    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 do ECDSA

    random

    Duas assinaturas de SEC_SignData sobre a mesma mensagem foram diferentes.

    Registrado pela verificação
    fact ecdsa-nonce = random
    evidence ecdsa-nonce: two SEC_SignData signatures of one message are different
  • Low-S ao assinar

    either

    O s não é normalizado: valores high-S apareceram entre 64 assinaturas.

    Registrado pela verificação
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: signatures with s > n/2 among 64: yes
  • High-S ao verificar

    accepts-high-s

    Uma assinatura DER com s substituído por n − s passou na verificação de VFY_VerifyData.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Entrada do hash na chamada de assinatura

    hashes-message-or-digest

    SEC_SignData calcula o hash da mensagem; PK11_Sign sobre o SHA-256 da mensagem produziu uma assinatura que VFY_VerifyData aceitou.

    Registrado pela verificação
    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
  • Digest mais longo que a ordem da curva

    truncated

    PK11_Sign aceitou um digest de 64 bytes na P-256; a assinatura foi verificada sobre os primeiros 32 bytes dele.

    Registrado pela verificação
    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 à curva

    none

    O ECDSA com SHA-256 assinou e verificou com uma chave P-384.

    Registrado pela verificação
    fact ecdsa-hash-curve-binding = none
    evidence ecdsa-hash-curve-binding: ECDSA with SHA-256 on a P-384 key: sign ok, verify ok
  • Assinatura ECDSA malformada

    returns-error

    VFY_VerifyData falhou com SEC_ERROR_BAD_DER para r‖s bruto e para 3 bytes, e com SEC_ERROR_BAD_SIGNATURE para uma mensagem errada.

    Registrado pela verificação
    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 e P-521; a geração de chaves P-224 e secp256k1 retornou SEC_ERROR_INVALID_KEY.

    Registrado pela verificação
    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

    O Ed25519 assinou, verificou e produziu duas vezes assinaturas idênticas; a geração de chaves Ed448 retornou SEC_ERROR_INVALID_KEY.

    Registrado pela verificação
    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 pós-quânticos

    ML-KEM

    O encapsulamento ML-KEM-768 fez o ciclo completo de ida e volta com um texto cifrado de 1.088 bytes; a geração de chaves ML-DSA-65 retornou SEC_ERROR_INVALID_ALGORITHM, e o softoken não oferece SLH-DSA.

    Registrado pela verificação
    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

Gere uma chave P-256 no softoken, assine com `SEC_SignData` e verifique a assinatura DER com `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;
}

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner alpine:3.24.2, e a execução falha se a saída não corresponder.

Configuração
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
A saída deve corresponder a (expressão regular)
DER tag: 0x30, verified: yes

Bibliotecas