
firma/verifica ECDSA in pure-python e accordo di chiave ECDH
Questa è un'implementazione facile da usare di ECC (Elliptic Curve Cryptography) con supporto per ECDSA (Elliptic Curve Digital Signature Algorithm), EdDSA (Edwards-curve Digital Signature Algorithm) ed ECDH (Elliptic Curve Diffie-Hellman), implementata interamente in Python, rilasciata sotto la licenza MIT. Con questa libreria puoi creare rapidamente coppie di chiavi (chiave di firma e chiave di verifica), firmare messaggi e verificare le firme. Puoi anche concordare una chiave segreta condivisa basata su chiavi pubbliche scambiate. Le chiavi e le firme sono molto brevi, il che le rende facili da gestire e integrare in altri protocolli.
NOTA: questa libreria non dovrebbe essere usata in contesti di produzione; vedere Sicurezza per maggiori dettagli.
Questa libreria fornisce generazione di chiavi, firma, verifica e derivazione di segreti
condivisi per cinque
curve NIST "Suite B" GF(p) (campo primo) molto diffuse, con lunghezze di chiave di 192,
224, 256, 384 e 521 bit. I "nomi brevi" di queste curve, come noti allo
strumento OpenSSL (openssl ecparam -list_curves), sono: prime192v1,
secp224r1, prime256v1, secp384r1 e secp521r1. Include la curva
a 256 bit secp256k1 usata da Bitcoin. C'è anche il supporto per le
varianti regolari (non twistate) delle curve Brainpool da 160 a 512 bit. I
"nomi brevi" di queste curve sono: brainpoolP160r1, brainpoolP192r1,
brainpoolP224r1, brainpoolP256r1, brainpoolP320r1, brainpoolP384r1,
. Sono incluse anche alcune curve piccole dello standard SEC
(principalmente per velocizzare i test della libreria), e cioè:
, , e .
La generazione delle chiavi, la firma e la verifica sono supportate anche per le curve
Ed25519 e Ed448.
Non sono incluse altre curve, ma non è troppo difficile aggiungere il supporto per ulteriori
curve su campi primi.
Questa libreria usa solo Python e il pacchetto 'six'. È compatibile con Python 2.6, 2.7 e 3.6+. Supporta inoltre l'esecuzione su implementazioni alternative come pypy e pypy3.
Se gmpy2 o gmpy è installato, verranno usati per un'aritmetica più veloce.
Uno dei due può essere installato dopo l'installazione di questa libreria,
python-ecdsa ne rileverà la presenza all'avvio e li userà
automaticamente.
Su Python3 è preferibile usare gmpy2 per prestazioni ottimali.
Per eseguire i test di compatibilità con OpenSSL, lo strumento 'openssl' deve trovarsi nel tuo
PATH. Questa versione è stata testata con successo con OpenSSL 0.9.8o,
1.0.0a, 1.0.2f, 1.1.1d e 3.0.1 (tra gli altri).
Questa libreria è disponibile su PyPI; si consiglia di installarla usando pip:```
pip install ecdsa
Nel caso si desideri una maggiore performance e l'uso di codice nativo non sia un problema, è possibile specificare l'installazione insieme a `gmpy2`:```
pip install ecdsa[gmpy2]
oppure (opzione più lenta, legacy):``` pip install ecdsa[gmpy]
## Velocità
La tabella seguente mostra quanto tempo impiega questa libreria a generare coppie di chiavi
(`keygen`), a firmare dati (`sign`), a verificare tali firme (`verify`),
a derivare un segreto condiviso (`ecdh`), e
a verificare le firme senza precalcolo specifico della chiave (`no PC verify`).
Tutti questi valori sono in secondi.
Per comodità, vengono forniti anche gli inversi di tali valori:
quante chiavi al secondo possono essere generate (`keygen/s`), quante firme
possono essere create al secondo (`sign/s`), quante firme possono essere verificate
al secondo (`verify/s`), quanti segreti condivisi possono essere derivati al secondo
(`ecdh/s`), e quante firme senza precalcolo specifico della chiave
possono essere verificate al secondo (`no PC verify/s`). La dimensione della firma
grezza (generalmente il modo più piccolo
in cui una firma può essere codificata) è fornita anche nella colonna `siglen`.
Usa `tox -e speed` per generare questa tabella sul tuo computer.
Su un Intel Core i7 4790K @ 4.0GHz ottengo le seguenti prestazioni:```
siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s
NIST192p: 48 0.00032s 3134.06 0.00033s 2985.53 0.00063s 1598.36 0.00129s 774.43
NIST224p: 56 0.00040s 2469.24 0.00042s 2367.88 0.00081s 1233.41 0.00170s 586.66
NIST256p: 64 0.00051s 1952.73 0.00054s 1867.80 0.00098s 1021.86 0.00212s 471.27
NIST384p: 96 0.00107s 935.92 0.00111s 904.23 0.00203s 491.77 0.00446s 224.00
NIST521p: 132 0.00210s 475.52 0.00215s 464.16 0.00398s 251.28 0.00874s 114.39
SECP256k1: 64 0.00052s 1921.54 0.00054s 1847.49 0.00105s 948.68 0.00210s 477.01
BRAINPOOLP160r1: 40 0.00025s 4003.88 0.00026s 3845.12 0.00053s 1893.93 0.00105s 949.92
BRAINPOOLP192r1: 48 0.00033s 3043.97 0.00034s 2975.98 0.00063s 1581.50 0.00135s 742.29
BRAINPOOLP224r1: 56 0.00041s 2436.44 0.00043s 2315.51 0.00078s 1278.49 0.00180s 556.16
BRAINPOOLP256r1: 64 0.00053s 1892.49 0.00054s 1846.24 0.00114s 875.64 0.00229s 437.25
BRAINPOOLP320r1: 80 0.00073s 1361.26 0.00076s 1309.25 0.00143s 699.29 0.00322s 310.49
BRAINPOOLP384r1: 96 0.00107s 931.29 0.00111s 901.80 0.00230s 434.19 0.00476s 210.20
BRAINPOOLP512r1: 128 0.00207s 483.41 0.00212s 471.42 0.00425s 235.43 0.00912s 109.61
SECP112r1: 28 0.00015s 6672.53 0.00016s 6440.34 0.00031s 3265.41 0.00056s 1774.20
SECP112r2: 28 0.00015s 6697.11 0.00015s 6479.98 0.00028s 3524.72 0.00058s 1716.16
SECP128r1: 32 0.00018s 5497.65 0.00019s 5272.89 0.00036s 2747.39 0.00072s 1396.16
SECP160r1: 42 0.00025s 3949.32 0.00026s 3894.45 0.00046s 2153.85 0.00102s 985.07
Ed25519: 64 0.00076s 1324.48 0.00042s 2405.01 0.00109s 918.05 0.00344s 290.50
Ed448: 114 0.00176s 569.53 0.00115s 870.94 0.00282s 355.04 0.01024s 97.69
ecdh ecdh/s
NIST192p: 0.00104s 964.89
NIST224p: 0.00134s 748.63
NIST256p: 0.00170s 587.08
NIST384p: 0.00352s 283.90
NIST521p: 0.00717s 139.51
SECP256k1: 0.00154s 648.40
BRAINPOOLP160r1: 0.00082s 1220.70
BRAINPOOLP192r1: 0.00105s 956.75
BRAINPOOLP224r1: 0.00136s 734.52
BRAINPOOLP256r1: 0.00178s 563.32
BRAINPOOLP320r1: 0.00252s 397.23
BRAINPOOLP384r1: 0.00376s 266.27
BRAINPOOLP512r1: 0.00733s 136.35
SECP112r1: 0.00046s 2180.40
SECP112r2: 0.00045s 2229.14
SECP128r1: 0.00054s 1868.15
SECP160r1: 0.00080s 1243.98
Per testare le prestazioni con gmpy2 caricato, usa tox -e speedgmpy2.
Sulla stessa macchina ottengo le seguenti prestazioni con gmpy2:```
siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s
NIST192p: 48 0.00017s 5933.40 0.00017s 5751.70 0.00032s 3125.28 0.00067s 1502.41
NIST224p: 56 0.00021s 4782.87 0.00022s 4610.05 0.00040s 2487.04 0.00089s 1126.90
NIST256p: 64 0.00023s 4263.98 0.00024s 4125.16 0.00045s 2200.88 0.00098s 1016.82
NIST384p: 96 0.00041s 2449.54 0.00042s 2399.96 0.00083s 1210.57 0.00172s 581.43
NIST521p: 132 0.00071s 1416.07 0.00072s 1389.81 0.00144s 692.93 0.00312s 320.40
SECP256k1: 64 0.00024s 4245.05 0.00024s 4122.09 0.00045s 2206.40 0.00094s 1068.32
BRAINPOOLP160r1: 40 0.00014s 6939.17 0.00015s 6681.55 0.00029s 3452.43 0.00057s 1769.81
BRAINPOOLP192r1: 48 0.00017s 5920.05 0.00017s 5774.36 0.00034s 2979.00 0.00069s 1453.19
BRAINPOOLP224r1: 56 0.00021s 4732.12 0.00022s 4622.65 0.00041s 2422.47 0.00087s 1149.87
BRAINPOOLP256r1: 64 0.00024s 4233.02 0.00024s 4115.20 0.00047s 2143.27 0.00098s 1015.60
BRAINPOOLP320r1: 80 0.00032s 3162.38 0.00032s 3077.62 0.00063s 1598.83 0.00136s 737.34
BRAINPOOLP384r1: 96 0.00041s 2436.88 0.00042s 2395.62 0.00083s 1202.68 0.00178s 562.85
BRAINPOOLP512r1: 128 0.00063s 1587.60 0.00064s 1558.83 0.00125s 799.96 0.00281s 355.83
SECP112r1: 28 0.00009s 11118.66 0.00009s 10775.48 0.00018s 5456.00 0.00033s 3020.83
SECP112r2: 28 0.00009s 11322.97 0.00009s 10857.71 0.00017s 5748.77 0.00032s 3094.28
SECP128r1: 32 0.00010s 10078.39 0.00010s 9665.27 0.00019s 5200.58 0.00036s 2760.88
SECP160r1: 42 0.00015s 6875.51 0.00015s 6647.35 0.00029s 3422.41 0.00057s 1768.35
Ed25519: 64 0.00030s 3322.56 0.00018s 5568.63 0.00046s 2165.35 0.00153s 654.02
Ed448: 114 0.00060s 1680.53 0.00039s 2567.40 0.00096s 1036.67 0.00350s 285.62
ecdh ecdh/s
NIST192p: 0.00050s 1985.70
NIST224p: 0.00066s 1524.16
NIST256p: 0.00071s 1413.07
NIST384p: 0.00127s 788.89
NIST521p: 0.00230s 434.85
SECP256k1: 0.00071s 1409.95
BRAINPOOLP160r1: 0.00042s 2374.65 BRAINPOOLP192r1: 0.00051s 1960.01 BRAINPOOLP224r1: 0.00066s 1518.37 BRAINPOOLP256r1: 0.00071s 1399.90 BRAINPOOLP320r1: 0.00100s 997.21 BRAINPOOLP384r1: 0.00129s 777.51 BRAINPOOLP512r1: 0.00210s 475.99 SECP112r1: 0.00022s 4457.70 SECP112r2: 0.00024s 4252.33 SECP128r1: 0.00028s 3589.31 SECP160r1: 0.00043s 2305.02
(c'è anche la versione `gmpy`, eseguila usando `tox -e speedgmpy`)
Per confronto, un'implementazione altamente ottimizzata (che include
assembly specifico per alcune curve), come quella in OpenSSL 1.1.1d, fornisce i
seguenti valori di prestazione sulla stessa macchina.
Esegui `openssl speed ecdsa` e `openssl speed ecdh` per riprodurre i risultati:```
sign verify sign/s verify/s
192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7
224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0
256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6
384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1
521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5
256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2
384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1
512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1
sign verify sign/s verify/s
253 bits EdDSA (Ed25519) 0.0000s 0.0001s 28217.9 10897.7
456 bits EdDSA (Ed448) 0.0003s 0.0005s 3926.5 2147.7
op op/s
192 bits ecdh (nistp192) 0.0002s 4853.4
224 bits ecdh (nistp224) 0.0001s 15252.1
256 bits ecdh (nistp256) 0.0001s 18436.3
384 bits ecdh (nistp384) 0.0008s 1292.7
521 bits ecdh (nistp521) 0.0003s 2884.7
256 bits ecdh (brainpoolP256r1) 0.0003s 3066.5
384 bits ecdh (brainpoolP384r1) 0.0008s 1298.0
512 bits ecdh (brainpoolP512r1) 0.0014s 694.8
Keys and signature can be serialized in different ways (see Usage, below). For a NIST192p key, the three basic representations require strings of the following lengths (in bytes):
to_string: signkey= 24, verifykey= 48, signature=48
compressed: signkey=n/a, verifykey= 25, signature=n/a
DER: signkey=106, verifykey= 80, signature=55
PEM: signkey=278, verifykey=162, (no support for PEM signatures)
Nel 2006, Peter Pearson annunciò la sua implementazione in puro Python di ECDSA in un messaggio su sci.crypt, disponibile dal suo sito di download. Nel 2010, Brian Warner scrisse un wrapper attorno a questo codice, per renderlo un po' più facile e sicuro da usare. Nel 2020, Hubert Kario incluse un'implementazione di crittografia a curve ellittiche che usa internamente coordinate di Jacobi, migliorando le prestazioni di circa 20 volte. Stai guardando il README di questo wrapper.
Per eseguire l'intera suite di test, fare così:
tox -e coverage
Su un Intel Core i7 4790K @ 4.0GHz, i test richiedono circa 18 secondi per essere eseguiti.
La suite di test usa
hypothesis quindi c'è una certa
variabilità intrinseca nei tempi di esecuzione della suite di test.
Una parte di test_pyecdsa.py e test_ecdh.py verifica la compatibilità con
OpenSSL, eseguendo lo strumento CLI "openssl"; assicurati che sia nel tuo PATH se
vuoi testare la compatibilità con esso (se OpenSSL manca, è troppo vecchio o non
supporta tutte le curve supportate nelle release a monte, vedrai dei test saltati
nell'esecuzione di coverage di cui sopra).
Questa libreria non è stata progettata pensando alla sicurezza. Se stai elaborando dati che devono essere protetti, ti suggeriamo di usare un wrapper di qualità attorno a OpenSSL. pyca/cryptography è un esempio di tale wrapper. Il caso d'uso principale di questa libreria è come libreria portabile per test di interoperabilità e come strumento didattico.
Questa libreria non protegge dagli attacchi side-channel.
Non consentire agli attaccanti di misurare quanto tempo impieghi a generare una coppia di chiavi o a firmare un messaggio. Non consentire agli attaccanti di eseguire codice sulla stessa macchina fisica mentre è in corso la generazione della coppia di chiavi o la firma (questo include le macchine virtuali). Non consentire agli attaccanti di misurare quanta energia consuma il tuo computer durante la generazione della coppia di chiavi o la firma di un messaggio. Non consentire agli attaccanti di misurare le interferenze RF provenienti dal tuo computer durante la generazione di una coppia di chiavi o la firma di un messaggio. Nota: il semplice caricamento della chiave privata causerà la generazione della coppia di chiavi. Anche altre operazioni o vettori di attacco possono essere vulnerabili ad attacchi. Per un attaccante sofisticato, osservare una sola operazione con una chiave privata sarà sufficiente per ricostruire completamente la chiave privata.
Si noti inoltre che qualsiasi libreria crittografica in puro Python sarà vulnerabile
agli stessi attacchi side-channel. Questo perché Python non fornisce primitive sicure
contro i side-channel (con l'eccezione di
hmac.compare_digest()), rendendo impossibile una programmazione sicura contro
i side-channel.
Questa libreria dipende da una solida fonte di numeri casuali. Non usarla su un sistema
in cui os.urandom() non fornisce numeri casuali crittograficamente sicuri.
Si inizia creando una SigningKey. Puoi usarla per firmare dati, passando i dati come
stringa di byte e ottenendo indietro la firma (anch'essa una stringa di byte). Puoi anche
chiedere a una SigningKey di darti la corrispondente VerifyingKey. La VerifyingKey
può essere usata per verificare una firma, passandole sia la stringa di dati sia la
stringa di byte della firma: restituisce True oppure solleva BadSignatureError.```python
from ecdsa import SigningKey
sk = SigningKey.generate() # uses NIST192p
vk = sk.verifying_key
signature = sk.sign(b"message")
assert vk.verify(signature, b"message")
Ogni `SigningKey`/`VerifyingKey` è associata a una curva specifica, come
NIST192p (quella predefinita). Le curve più lunghe sono più sicure, ma richiedono più tempo per
essere utilizzate e producono chiavi e firme più lunghe.```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
vk = sk.verifying_key
signature = sk.sign(b"message")
assert vk.verify(signature, b"message")
La SigningKey può essere serializzata in diversi formati: la forma più breve
consiste nel chiamare s=sk.to_string(), e poi ricrearla con
SigningKey.from_string(s, curve). Questa forma breve non registra la
curva, quindi devi assicurarti di passare a from_string() la stessa curva che hai usato
per la chiave originale. La forma breve di una chiave di firma basata su NIST192p è lunga solo 24
byte. Se una codifica di punto non è valida o non giace sulla curva
specificata, from_string() solleverà MalformedPointError.```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
sk_string = sk.to_string()
sk2 = SigningKey.from_string(sk_string, curve=NIST384p)
print(sk_string.hex())
print(sk2.to_string().hex())
Nota: sebbene i metodi siano chiamati `to_string()`, il tipo che restituiscono è in
realtà `bytes`; la parte "string" è un residuo di Python 2.
`sk.to_pem()` e `sk.to_der()` serializzano la chiave di firma negli stessi
formati usati da OpenSSL. Il file PEM ha il familiare formato ASCII-armored
`"-----BEGIN EC PRIVATE KEY-----"` codificato in base64, mentre il formato DER
è una forma binaria più corta degli stessi dati.
`SigningKey.from_pem()/.from_der()` annullano questa serializzazione. Questi
formati includono il nome della curva, quindi non è necessario passare un
identificatore di curva al deserializzatore. Nel caso in cui il file sia
malformato, `from_der()` e `from_pem()` solleveranno `UnexpectedDER` o
`MalformedPointError`.```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
sk_pem = sk.to_pem()
sk2 = SigningKey.from_pem(sk_pem)
# sk and sk2 are the same key
Analogamente, la VerifyingKey può essere serializzata allo stesso modo:
vk.to_string()/VerifyingKey.from_string(), to_pem()/from_pem(), e
to_der()/from_der(). Lo stesso argomento curve= è necessario per
VerifyingKey.from_string().```python
from ecdsa import SigningKey, VerifyingKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
vk = sk.verifying_key
vk_string = vk.to_string()
vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p)
from ecdsa import SigningKey, VerifyingKey, NIST384p sk = SigningKey.generate(curve=NIST384p) vk = sk.verifying_key vk_pem = vk.to_pem() vk2 = VerifyingKey.from_pem(vk_pem)
Esistono un paio di modi diversi per calcolare una firma. In sostanza,
ECDSA prende un numero che rappresenta i dati da firmare e restituisce una
coppia di numeri che rappresenta la firma. L'argomento `hashfunc=` di
`sk.sign()` e `vk.verify()` viene usato per trasformare una stringa arbitraria in un
digest a lunghezza fissa, che viene poi convertito in un numero che ECDSA può firmare,
e sia la firma che la verifica devono usare lo stesso approccio. Il valore predefinito è
`hashlib.sha1`, ma se usi NIST256p o una curva più lunga, puoi usare
`hashlib.sha256` invece.
Ci sono anche diversi modi per rappresentare una firma. I metodi predefiniti
`sk.sign()` e `vk.verify()` la presentano come una stringa breve, per
semplicità e overhead minimo. Per usare uno schema diverso, utilizza gli
argomenti `sk.sign(sigencode=)` e `vk.verify(sigdecode=)`. Ci sono funzioni di supporto
nel modulo `ecdsa.util` che possono essere utili in questo caso.
È anche possibile creare una `SigningKey` da un "seed", che è
deterministico. Questo può essere usato in protocolli in cui si vogliono derivare
chiavi di firma coerenti da un altro segreto, ad esempio quando si vogliono
tre chiavi separate e si vuole memorizzare un unico segreto master. Si dovrebbe
iniziare con un seed non indovinabile e distribuito uniformemente con circa `curve.baselen`
byte di entropia, quindi usare una delle funzioni di supporto in `ecdsa.util` per
convertirlo in un intero nell'intervallo corretto, e infine passarlo
a `SigningKey.from_secret_exponent()`, in questo modo:```python
import os
from ecdsa import NIST384p, SigningKey
from ecdsa.util import randrange_from_seed__trytryagain
def make_key(seed):
secexp = randrange_from_seed__trytryagain(seed, NIST384p.order)
return SigningKey.from_secret_exponent(secexp, curve=NIST384p)
seed = os.urandom(NIST384p.baselen) # or other starting point
sk1a = make_key(seed)
sk1b = make_key(seed)
# note: sk1a and sk1b are the same key
assert sk1a.to_string() == sk1b.to_string()
sk2 = make_key(b"2-"+seed) # different key
assert sk1a.to_string() != sk2.to_string()
Nel caso in cui l'applicazione debba verificare molte firme realizzate con una singola chiave, è possibile precalcolare alcuni dei valori interni per rendere la verifica delle firme significativamente più veloce. Il punto di pareggio si verifica a circa 100 firme verificate.
Per eseguire il precalcolo, è possibile chiamare il metodo precompute() su un'istanza di VerifyingKey:```python
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
vk = sk.verifying_key
vk.precompute()
signature = sk.sign(b"message")
assert vk.verify(signature, b"message")
Una volta chiamato `precompute()`, tutte le verifiche delle firme con questa chiave
verranno eseguite più rapidamente.
## Compatibilità con OpenSSL
Per produrre firme che possano essere verificate dagli strumenti OpenSSL, o per verificare
firme prodotte da tali strumenti, usa:```python
# openssl ecparam -name prime256v1 -genkey -out sk.pem
# openssl ec -in sk.pem -pubout -out vk.pem
# echo "data for signing" > data
# openssl dgst -sha256 -sign sk.pem -out data.sig data
# openssl dgst -sha256 -verify vk.pem -signature data.sig data
# openssl dgst -sha256 -prverify sk.pem -signature data.sig data
import hashlib
from ecdsa import SigningKey, VerifyingKey
from ecdsa.util import sigencode_der, sigdecode_der
with open("vk.pem") as f:
vk = VerifyingKey.from_pem(f.read())
with open("data", "rb") as f:
data = f.read()
with open("data.sig", "rb") as f:
signature = f.read()
assert vk.verify(signature, data, hashlib.sha256, sigdecode=sigdecode_der)
with open("sk.pem") as f:
sk = SigningKey.from_pem(f.read(), hashlib.sha256)
new_signature = sk.sign_deterministic(data, sigencode=sigencode_der)
with open("data.sig2", "wb") as f:
f.write(new_signature)
# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data
Nota: se è necessaria la compatibilità con OpenSSL 1.0.0 o versioni precedenti,
le funzioni sigencode_string e sigdecode_string di ecdsa.util possono essere usate
rispettivamente per scrivere e leggere le firme.
Le chiavi possono inoltre essere scritte in un formato gestibile da openssl:```python from ecdsa import SigningKey, VerifyingKey
with open("sk.pem") as f: sk = SigningKey.from_pem(f.read()) with open("sk.pem", "wb") as f: f.write(sk.to_pem())
with open("vk.pem") as f: vk = VerifyingKey.from_pem(f.read()) with open("vk.pem", "wb") as f: f.write(vk.to_pem())
## Entropia
La creazione di una chiave di firma con `SigningKey.generate()` richiede una qualche forma di
entropia (a differenza di
`from_secret_exponent`/`from_string`/`from_der`/`from_pem`,
che sono deterministici e non richiedono una fonte di entropia). La fonte
predefinita è `os.urandom()`, ma puoi passare qualsiasi altra funzione che si comporti
come `os.urandom` come argomento `entropy=` per fare qualcosa di diverso. Questo
può essere utile nei test unitari, dove vuoi ottenere risultati ripetibili. L'utility
`ecdsa.util.PRNG` è utile in questo caso: prende un seed e produce da esso un
flusso pseudo-casuale robusto:```python
from ecdsa.util import PRNG
from ecdsa import SigningKey
rng1 = PRNG(b"seed")
sk1 = SigningKey.generate(entropy=rng1)
rng2 = PRNG(b"seed")
sk2 = SigningKey.generate(entropy=rng2)
# sk1 and sk2 are the same key
Allo stesso modo, la generazione di una firma ECDSA richiede un numero casuale, e ogni firma deve usarne uno diverso (usare lo stesso numero due volte rivelerà immediatamente la chiave di firma privata). Il metodo sk.sign() accetta un argomento entropy= che si comporta come SigningKey.generate(entropy=).
Se si chiama SigningKey.sign_deterministic(data) invece di .sign(data), il codice genererà una firma deterministica invece di una casuale. Questo utilizza l'algoritmo di RFC6979 per generare in modo sicuro un valore k unico, derivato dalla chiave privata e dal messaggio firmato. Ogni volta che si firma lo stesso messaggio con la stessa chiave, si otterrà la stessa firma (usando lo stesso k).
Questo potrebbe diventare il comportamento predefinito in una versione futura, poiché non è vulnerabile ai guasti della sorgente di entropia.
Crea una coppia di chiavi NIST192p e salva entrambe immediatamente su disco:```python from ecdsa import SigningKey sk = SigningKey.generate() vk = sk.verifying_key with open("private.pem", "wb") as f: f.write(sk.to_pem()) with open("public.pem", "wb") as f: f.write(vk.to_pem())
Carica una chiave di firma dal disco, usala per firmare un messaggio (usando SHA-1), e scrivi
la firma su disco:```python
from ecdsa import SigningKey
with open("private.pem") as f:
sk = SigningKey.from_pem(f.read())
with open("message", "rb") as f:
message = f.read()
sig = sk.sign(message)
with open("signature", "wb") as f:
f.write(sig)
Carica la chiave di verifica, il messaggio e la firma dal disco e verifica la firma (supponendo un hash SHA-1):```python from ecdsa import VerifyingKey, BadSignatureError vk = VerifyingKey.from_pem(open("public.pem").read()) with open("message", "rb") as f: message = f.read() with open("signature", "rb") as f: sig = f.read() try: vk.verify(sig, message) print "good signature" except BadSignatureError: print "BAD SIGNATURE"
Crea una coppia di chiavi NIST521p:```python
from ecdsa import SigningKey, NIST521p
sk = SigningKey.generate(curve=NIST521p)
vk = sk.verifying_key
Crea tre chiavi di firma indipendenti da un seed master:```python from ecdsa import NIST192p, SigningKey from ecdsa.util import randrange_from_seed__trytryagain
def make_key_from_seed(seed, curve=NIST192p): secexp = randrange_from_seed__trytryagain(seed, curve.order) return SigningKey.from_secret_exponent(secexp, curve)
sk1 = make_key_from_seed("1:%s" % seed) sk2 = make_key_from_seed("2:%s" % seed) sk3 = make_key_from_seed("3:%s" % seed)
Load a verifying key from disk and print it using hex encoding in
uncompressed and compressed format (defined in X9.62 and SEC1 standards):```python
from ecdsa import VerifyingKey
with open("public.pem") as f:
vk = VerifyingKey.from_pem(f.read())
print("uncompressed: {0}".format(vk.to_string("uncompressed").hex()))
print("compressed: {0}".format(vk.to_string("compressed").hex()))
Caricare una chiave di verifica da una stringa esadecimale in formato compresso, output non compresso:```python from ecdsa import VerifyingKey, NIST256p
comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p) print(vk.to_string("uncompressed").hex())
Scambio di chiavi ECDH con la controparte remota:```python
from ecdsa import ECDH, NIST256p
ecdh = ECDH(curve=NIST256p)
ecdh.generate_private_key()
local_public_key = ecdh.get_public_key()
#send `local_public_key` to remote party and receive `remote_public_key` from remote party
with open("remote_public_key.pem") as e:
remote_public_key = e.read()
ecdh.load_received_public_key_pem(remote_public_key)
secret = ecdh.generate_sharedsecret_bytes()
brainpoolP512r1secp112r1secp112r2secp128r1secp160r1