2015-12-24 13 views
6

Ho bisogno di firmare un hash di 256 bit con ECDSA usando una chiave privata di 256 bit, proprio come fa bitcoin, e sto raggiungendo la disperazione a causa della mancanza di documentazione di ecdsa in python.Come firmare e verificare la firma con ecdsa in python

Ho trovato molti codici su Internet, ma non c'era niente di semplice come solo ecdsa.sign(msg, privkey) o simile, tutto quello che ho trovato è un sacco di codice di cose matematiche che non capisco, ma ancora usano la libreria ecdsa (Non so perché non aggiungerebbero una funzione di firma in una libreria che verrà utilizzata per firmare roba, invece è necessaria una pagina di codice quando si utilizza la libreria?).

Questo è il codice migliore che ho trovato finora:

def ecdsa_sign(val, secret_exponent): 
    """Return a signature for the provided hash, using the provided 
    random nonce. It is absolutely vital that random_k be an unpredictable 
    number in the range [1, self.public_key.point.order()-1]. If 
    an attacker can guess random_k, he can compute our private key from a 
    single signature. Also, if an attacker knows a few high-order 
    bits (or a few low-order bits) of random_k, he can compute our private 
    key from many signatures. The generation of nonces with adequate 
    cryptographic strength is very difficult and far beyond the scope 
    of this comment. 

    May raise RuntimeError, in which case retrying with a new 
    random value k is in order. 
    """ 
    G = ecdsa.SECP256k1 
    n = G.order() 
    k = deterministic_generate_k(n, secret_exponent, val) 
    p1 = k * G 
    r = p1.x() 
    if r == 0: raise RuntimeError("amazingly unlucky random number r") 
    s = (ecdsa.numbertheory.inverse_mod(k, n) * (val + (secret_exponent * r) % n)) % n 
    if s == 0: raise RuntimeError("amazingly unlucky random number s") 

    return signature_to_der(r, s) 

def deterministic_generate_k(generator_order, secret_exponent, val, hash_f=hashlib.sha256): 
    """ 
    Generate K value according to https://tools.ietf.org/html/rfc6979 
    """ 
    n = generator_order 
    order_size = (bit_length(n) + 7) // 8 
    hash_size = hash_f().digest_size 
    v = b'\x01' * hash_size 
    k = b'\x00' * hash_size 
    priv = intbytes.to_bytes(secret_exponent, length=order_size) 
    shift = 8 * hash_size - bit_length(n) 
    if shift > 0: 
     val >>= shift 
    if val > n: 
     val -= n 
    h1 = intbytes.to_bytes(val, length=order_size) 
    k = hmac.new(k, v + b'\x00' + priv + h1, hash_f).digest() 
    v = hmac.new(k, v, hash_f).digest() 
    k = hmac.new(k, v + b'\x01' + priv + h1, hash_f).digest() 
    v = hmac.new(k, v, hash_f).digest() 

    while 1: 
     t = bytearray() 

     while len(t) < order_size: 
      v = hmac.new(k, v, hash_f).digest() 
      t.extend(v) 

     k1 = intbytes.from_bytes(bytes(t)) 

     k1 >>= (len(t)*8 - bit_length(n)) 
     if k1 >= 1 and k1 < n: 
      return k1 

     k = hmac.new(k, v + b'\x00', hash_f).digest() 
     v = hmac.new(k, v, hash_f).digest() 

Ma io non posso fidarmi di un codice del genere perché non ho idea di quello che fa. Inoltre, i commenti in ecdsa_sign dicono che restituisce una firma data il valore, l'esponente segreto, e un nonce. Dice che è molto importante avere un nonce, ma non riesco proprio a capire dove sia quel nonce.

C'è un modo semplice, a una sola riga per firmare e verificare le firme ECDSA utilizzando qualsiasi libreria attendibile in python su Windows?

+0

non puoi firmarlo usando openssl? –

+0

Quali sono i vantaggi dell'uso di openssl su ecdsa? – Jorky10

+0

Immagino che la libreria ecdsa usi openssl quindi non c'è differenza, avrai accesso a qualsiasi cosa ti serva usando openssl https://www.openssl.org/docs/manmaster/crypto/ecdsa.html https: //www.guyrutenberg .com/2013/12/28/creation-self-signed-ecdsa-ssl-certificate-using-openssl/ –

risposta

5

Si può provare a utilizzare il pacchetto ECDSA python, utilizzando python3:

pip3 install ecdsa 

Usage:

from ecdsa import SigningKey 

# SECP256k1 is the Bitcoin elliptic curve 
sk = SigningKey.generate(curve=ecdsa.SECP256k1) 
vk = sk.get_verifying_key() 
sig = sk.sign(b"message") 
vk.verify(sig, b"message") # True 

Per verificare una firma esistente con una chiave pubblica:

from ecdsa import VerifyingKey 

message = b"message" 
public_key = '98cedbb266d9fc38e41a169362708e0509e06b3040a5dfff6e08196f8d9e49cebfb4f4cb12aa7ac34b19f3b29a17f4e5464873f151fd699c2524e0b7843eb383' 
sig = '740894121e1c7f33b174153a7349f6899d0a1d2730e9cc59f674921d8aef73532f63edb9c5dba4877074a937448a37c5c485e0d53419297967e95e9b1bef630d' 

vk = VerifyingKey.from_string(bytes.fromhex(public_key), curve=ecdsa.SECP256k1) 
vk.verify(bytes.fromhex(sig), message) # True 

Il il pacchetto è compatibile anche con Python 2

MODIFICA: la risposta precedente richiedeva l'importazione di SECP256k1, che è stato corretto. Grazie a @ chengcheng-zhang per indicarlo

+0

È anche possibile utilizzare sign_digest e verify_digest per i dati digestati, ad esempio, se si passa un hash sha256 in binario. –

+0

C'è un modo per rendere le chiavi più piccole? Ad esempio, definisci la loro dimensione massima? –

+0

@Nuclear_Man_D Credo che la dimensione della chiave sia fissa. – k26dr

Problemi correlati