Aller au contenu
ecdsa.com
Go

Go crypto/ecdsa (standard library)

Les packages de signature de la bibliothèque standard de Go (crypto/ecdsa avec crypto/x509, ed25519, mlkem et mldsa) : signatures DER sur un condensat que vous fournissez, nonces aléatoires qui ignorent votre Reader, courbes NIST uniquement.
Licence
BSD-3-Clause
Version vérifiée
1.27.3 ·
Code source
github.com/golang/go
Documentation
pkg.go.dev/crypto/ecdsa

Compatibilité en un coup d’œil

ECDSAEdDSAPost-quantique

Courbes ECDSA
P-224P-256P-384P-521
EdDSA
Ed25519Ed25519phEd25519ctx
Formats de clé publique
spki-derx509sec1-uncompressed
Formats de clé privée
pkcs8-dersec1-derraw-scalar

Go fournit sa cryptographie dans la bibliothèque standard, et depuis Go 1.24 les primitives de base résident dans le Go Cryptographic Module, conçu pour FIPS 140-3. crypto/ecdsa signe et vérifie, crypto/x509 lit les clés et les certificats, et Go 1.27.3 ajoute crypto/mlkem (ML-KEM-768/1024) et crypto/mldsa (ML-DSA-44/65/87) à côté de crypto/ed25519. Il n’y a pas de secp256k1 : le code Ethereum et Bitcoin utilise les packages de go-ethereum ou de decred.

Tout, dans crypto/ecdsa, opère sur un condensat que vous avez calculé : le package ne hache jamais. Pire qu’une signature fausse, une entrée longue est tronquée silencieusement à la taille de la courbe : une signature sur une entrée de 46 octets a été vérifiée pour une autre entrée ayant les mêmes 32 premiers octets. Hachez toujours d’abord, comme le fait l’exemple. SignASN1, VerifyASN1 et l’interface crypto.Signer utilisent le DER ASN.1 ; WebCrypto, JWS et les autres pairs en r‖s brut ont besoin de Sign/Verify avec des entiers complétés par FillBytes, car Bytes() supprime les octets nuls de tête : tout r ou s qui commence par un octet nul ressort avec un octet de moins.

Les signatures sont aléatoires et, depuis Go 1.26, l’io.Reader que vous passez est ignoré, sauf si GODEBUG=cryptocustomrand=1 est défini : deux signatures faites avec un Reader ne renvoyant que des zéros étaient différentes. Les tests qui comptaient sur un Reader fixe pour obtenir des signatures reproductibles ont cessé de fonctionner ; le remplacement documenté est testing/cryptotest.SetGlobalRandom. s n’est pas normalisé et VerifyASN1 accepte les deux moitiés : pour des vérificateurs low-S stricts, il faut remplacer s par n − s dans les signatures.

L’analyse des clés privilégie le DER. x509.ParsePKIXPublicKey appliqué à du texte PEM échoue avec asn1: structure error: tags don't match : appelez d’abord pem.Decode. ecdsa.ParseUncompressedPublicKey et ecdsa.ParseRawPrivateKey lisent le point SEC1 et le scalaire nus ; les points compressés sont refusés, et aucune fonction ne lit le PKCS#8 chiffré par phrase de passe. Une signature mal formée fait renvoyer false à VerifyASN1, jamais une erreur.

Pour comparer une signature Go avec une autre pile, convertissez-la dans le convertisseur DER ⇄ brut et contrôlez-la dans le vérificateur de signatures ; les problèmes d’analyse de certificats se voient le plus facilement dans le décodeur de certificats.

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/go-crypto-ecdsa/run.sh a exécuté Go crypto/ecdsa (standard library) 1.27.3 dans l’image de conteneur golang:1.27.3-trixie 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

    SignASN1, VerifyASN1 et PrivateKey.Sign (la voie crypto.Signer) utilisent le DER ASN.1 ; Sign et Verify prennent r et s sous forme de *big.Int.

    Relevé par la vérification
    fact ecdsa-signature-encoding = der
  • Nonce ECDSA

    random

    Nonce aléatoire : deux signatures d’un même condensat diffèrent, comme l’indique la documentation du package (l’entropie est mélangée à la clé et au message).

    Relevé par la vérification
    fact ecdsa-nonce = random
    evidence ecdsa-nonce: two signatures with an all-zero Reader are identical: false
  • Argument de source d’aléa

    ignored

    Depuis Go 1.26, l’argument io.Reader est ignoré : deux signatures faites avec un Reader ne renvoyant que des zéros étaient différentes. GODEBUG=cryptocustomrand=1 rétablit l’ancien comportement, pour l’instant.

    Relevé par la vérification
    fact go-rand-reader-argument = ignored
  • 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
  • High-S à la vérification

    accepts-high-s

    VerifyASN1 a accepté la signature dont s a été remplacé par n − s.

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

    takes-digest

    Toutes les fonctions prennent un condensat ; rien dans crypto/ecdsa ne hache le message.

    Relevé par la vérification
    fact ecdsa-prehash = takes-digest
  • Condensat plus long que l’ordre de la courbe

    truncated

    Une entrée plus longue que l’ordre de la courbe est tronquée : une signature sur une entrée de 46 octets a été vérifiée pour une autre entrée partageant ses 32 premiers octets.

    Relevé par la vérification
    fact ecdsa-long-input = truncated
    evidence ecdsa-long-input: a signature over a 46-byte input verified for a different input with the same first 32 bytes: true
  • Signature ECDSA mal formée

    returns-false

    VerifyASN1 a renvoyé false pour une signature r‖s brute et pour un blob de 3 octets.

    Relevé par la vérification
    fact ecdsa-malformed-signature = returns-false
  • Courbes ECDSA

    P-224P-256P-384P-521

    P-224, P-256, P-384 et P-521 ; crypto/elliptic ne propose ni secp256k1 ni courbes Brainpool.

    Relevé par la vérification
    fact ecdsa-curves = P-224, P-256, P-384, P-521
    evidence ecdsa-curves: crypto/elliptic exposes P224, P256, P384 and P521 only
  • EdDSA

    Ed25519Ed25519phEd25519ctx

    Ed25519, ainsi que les variantes Ed25519ph et Ed25519ctx de la RFC 8032 via ed25519.Options.

    Relevé par la vérification
    fact eddsa = Ed25519, Ed25519ph, Ed25519ctx
  • Structure de la clé privée Ed25519

    expanded-64

    ed25519.PrivateKey fait 64 octets, la graine suivie de la clé publique ; NewKeyFromSeed la construit à partir de la graine de 32 octets.

    Relevé par la vérification
    fact ed25519-private-key = expanded-64
  • Algorithmes post-quantiques

    ML-KEMML-DSA

    Encapsulation et décapsulation réussies avec ML-KEM-768 et ML-KEM-1024, signature et vérification réussies avec ML-DSA-44/65/87, dans Go 1.27.3.

    Relevé par la vérification
    fact pqc = ML-KEM, ML-DSA
    evidence pqc: crypto/mlkem offers ML-KEM-768 and ML-KEM-1024; crypto/mldsa offers ML-DSA-44, ML-DSA-65 and ML-DSA-87
  • Formats de clé publique

    spki-derx509sec1-uncompressed

    SPKI DER via x509.ParsePKIXPublicKey, certificats, et points non compressés SEC1 via ecdsa.ParseUncompressedPublicKey ; le PEM nécessite d’abord pem.Decode, et les points compressés sont refusés.

    Relevé par la vérification
    fact public-key-formats = spki-der, x509, sec1-uncompressed
    evidence public-key-formats: ParsePKIXPublicKey on PEM text: asn1: structure error: tags don't match (16 vs {class:0 tag:13 length:45 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} publicKeyInfo @2
    evidence public-key-formats: ParseUncompressedPublicKey on a compressed point: ecdsa: invalid uncompressed public key
    evidence public-key-formats: ecdh.P256().NewPublicKey on a compressed point: crypto/ecdh: invalid public key
  • Formats de clé privée

    pkcs8-dersec1-derraw-scalar

    PKCS#8 DER, SEC1 DER et le scalaire brut via ecdsa.ParseRawPrivateKey ; il n’existe pas d’analyseur pour le PKCS#8 chiffré par phrase de passe.

    Relevé par la vérification
    fact private-key-formats = pkcs8-der, sec1-der, raw-scalar
    evidence private-key-formats: no parser for passphrase-encrypted PKCS#8 in the standard library

Utilisation

Hacher d’abord, signer en DER pour les pairs de type OpenSSL, et construire un r‖s brut correctement complété pour WebCrypto et JOSE.
package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"fmt"
	"math/big"
)

func main() {
	key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	// crypto/ecdsa never hashes: pass a digest, never the message itself.
	digest := sha256.Sum256([]byte("order #1042 approved"))

	der, _ := ecdsa.SignASN1(rand.Reader, key, digest[:]) // ASN.1 DER for OpenSSL, Java, .NET peers
	fmt.Println("DER verified:", ecdsa.VerifyASN1(&key.PublicKey, digest[:], der))

	// Raw r||s for WebCrypto and JOSE: FillBytes pads to 32 bytes; Bytes() would not.
	r, s, _ := ecdsa.Sign(rand.Reader, key, digest[:])
	raw := make([]byte, 64)
	r.FillBytes(raw[:32])
	s.FillBytes(raw[32:])
	ok := ecdsa.Verify(&key.PublicKey, digest[:], new(big.Int).SetBytes(raw[:32]), new(big.Int).SetBytes(raw[32:]))
	fmt.Println("raw verified:", ok, len(raw))
}

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

Commande
go run .
La sortie doit correspondre à cette expression régulière
DER verified: true\nraw verified: true 64

Bibliothèques