bug 1190603 - import PyECC library r=gps,gerv
Obtained from https://github.com/amintos/PyECC.git (commit 1bfd3a41410c3650e57d58f7bd016bb0819af250)
This commit is contained in:
320
python/PyECC/ecc/Key.py
Normal file
320
python/PyECC/ecc/Key.py
Normal file
@@ -0,0 +1,320 @@
|
||||
# ====================================================================
|
||||
#
|
||||
# ELLIPTIC CURVE KEY ENCAPSULATION
|
||||
# Version 2011-01-26
|
||||
#
|
||||
# Copyright (c) 2010 - 2011 | Toni Mattis
|
||||
#
|
||||
# ====================================================================
|
||||
|
||||
"""
|
||||
== Elliptic Curve Key Encapsulation ==
|
||||
|
||||
Keypairs
|
||||
--------
|
||||
Keypairs are generated using: Key.generate(bits)
|
||||
|
||||
The number of bits is tied to the NIST-proposed elliptic curves
|
||||
and has to be 192, 224, 256, 384 or 521 (not 512!).
|
||||
The result is a Key object containing public and private key.
|
||||
|
||||
private() is a method for checking whether the Key object is a
|
||||
pure public key or also includes the private part.
|
||||
|
||||
|
||||
Exchange
|
||||
--------
|
||||
Public keys have to be exported using the export()-Method without
|
||||
passing an argument. The result is a string which can be safely
|
||||
transmitted.
|
||||
|
||||
Using Key.decode(<encoded key>) the receiver obtains a new
|
||||
public Key object of the sender.
|
||||
|
||||
|
||||
Storage
|
||||
-------
|
||||
For storing a key, export(True) exports both private and public
|
||||
key as a string. Make sure this information is properly encrypted
|
||||
when stored.
|
||||
|
||||
Key.decode(<encoded key>) obtains the full Key object from the
|
||||
encoded keypair.
|
||||
|
||||
|
||||
Public Keys
|
||||
-----------
|
||||
A public Key object can perform the following cryptographic
|
||||
operations:
|
||||
|
||||
* validate() Checks key integrity, i.e. after loading the
|
||||
key from a file. Returns True if the key is
|
||||
valid. Invalid keys should be discarded.
|
||||
|
||||
* fingerprint() Returns the public key fingerprint used to
|
||||
identify the key. Optional arguments:
|
||||
1. as_hex - True, if output should be formatted
|
||||
as hexadecimal number (default: True).
|
||||
2. hashfunc - The official name of the hash
|
||||
function being used (default: 'sha1')
|
||||
For supported hash functions see below.
|
||||
|
||||
* keyid() Returns a (mostly) unique Key ID, which is
|
||||
shorter than the fingerprint. The result
|
||||
is an integer of max. 64 bits.
|
||||
|
||||
* verify() Verifies whether the given data (argument 1)
|
||||
matches the signature (argument 2) issued
|
||||
by the owner of this key. A falsification
|
||||
can have multiple causes:
|
||||
|
||||
- Data, public key or signature were altered
|
||||
during transmission/storage.
|
||||
- The siganture was not issued by the owner
|
||||
of this key but may be valid with another
|
||||
key.
|
||||
- The signature was issued for different data.
|
||||
- The signature was issued using a different
|
||||
hash function. Another hash function may work.
|
||||
|
||||
Optionally, the name of a hash algorithm
|
||||
can be provided. For hash names see below.
|
||||
|
||||
* encrypt() Encrypts a packet of data destined for the owner
|
||||
of this key*. After encryption only the holder
|
||||
of this Key's private part is able to decrypt
|
||||
the message.
|
||||
|
||||
Private Keys / Keypairs
|
||||
-----------------------
|
||||
|
||||
If the key object is private, then it is a keypair consisting of
|
||||
a public and a private key. Therefore all Public key operations
|
||||
are supported.
|
||||
|
||||
Additional functions:
|
||||
|
||||
* sign() Signs given data using this private key. The
|
||||
result is a signature which can be passed as
|
||||
argument to the verify() function in addition
|
||||
to the data being verified.
|
||||
|
||||
As additional argument the name of the hash
|
||||
function can be provided (defaults to 'sha256').
|
||||
For hash names see below.
|
||||
|
||||
* auth_encrypt() Performs authenticated encryption of data
|
||||
(argument 1) for the holder of the key provided
|
||||
as second argument. Only the receiver whose
|
||||
public key is given is able to derypt and verify
|
||||
the message. The message will be implicitly
|
||||
signed using the own private key. *
|
||||
|
||||
* decrypt() Decrypts a message which has been encrypted
|
||||
using the public key of this keypair*. If
|
||||
decryption yields random data, this can have
|
||||
multiple causes:
|
||||
- You were not the intended receiver, a different
|
||||
private key may be able to decrypt it.
|
||||
- The message was altered.
|
||||
- Your private key is damaged.
|
||||
|
||||
* auth_decrypt() Decrypts a message while verifying whether
|
||||
it has been authentically issued by the holder
|
||||
of the given key (argument 2). When
|
||||
authentication failed, a
|
||||
SecurityViolationException is thrown. Reasons
|
||||
for this to happen are those mentioned with
|
||||
decrypt() and verify(). *
|
||||
|
||||
*) The encryption used here depends on the "eccrypt" module imported
|
||||
by this module. Default implementation should use RABBIT as cipher
|
||||
and do the asymmetric part using an optimized El-Gamal scheme.
|
||||
|
||||
|
||||
|
||||
Hash functions
|
||||
--------------
|
||||
The following hash functions can be passed at the moment:
|
||||
|
||||
name | hash size | security level
|
||||
| (bits, bytes, hex digits)
|
||||
---------+------------------------+----------------
|
||||
'sha1' 160 / 20 / 40 medium
|
||||
'sha224' 224 / 28 / 56 medium-strong
|
||||
'sha256' 256 / 32 / 64 strong
|
||||
'sha384' 384 / 48 / 96 very strong
|
||||
'sha512' 512 / 64 / 128 very strong
|
||||
|
||||
'md5' 128 / 16 / 32 weak (not recommended!)
|
||||
|
||||
|
||||
Curves
|
||||
------
|
||||
According to FIPS 186-3, Appendix D.1.2 there are 5 elliptic
|
||||
curves recommended. All of those are strong, but those with
|
||||
a higher bit number even stronger.
|
||||
|
||||
192 and 224 bits are sufficient for most purposes.
|
||||
256 bits offer an additional magnitude of security.
|
||||
(i.e. for classified / strongly confidential data)
|
||||
384 and 521 bits provide exceptionally strong security. According
|
||||
to current research they most probably keep this level for
|
||||
decades in the future.
|
||||
|
||||
FIPS also recommends curves over polynomial fields but actually
|
||||
only prime fields are implemented here. (Because 2^521-1 is a mersenne
|
||||
prime having great security characteristics, 521 bits are preferred
|
||||
over a constructed 512 bit field.)
|
||||
"""
|
||||
|
||||
from encoding import *
|
||||
from eccrypt import *
|
||||
import ecdsa
|
||||
import hashlib
|
||||
from SecurityViolationException import *
|
||||
|
||||
class Key:
|
||||
|
||||
# --- KEY SETUP ------------------------------------------------------------
|
||||
|
||||
def __init__(self, public_key, private_key = None):
|
||||
'''Create a Key(pair) from numeric keys.'''
|
||||
self._pub = public_key
|
||||
self._priv = private_key
|
||||
self._fingerprint = {}
|
||||
self._id = None
|
||||
|
||||
@staticmethod
|
||||
def generate(bits):
|
||||
'''Generate a new ECDSA keypair'''
|
||||
return Key(*ecdsa.keypair(bits))
|
||||
|
||||
# --- BINARY REPRESENTATION ------------------------------------------------
|
||||
|
||||
def encode(self, include_private = False):
|
||||
'''Returns a strict binary representation of this Key'''
|
||||
e = Encoder().int(self.keyid(), 8)
|
||||
e.int(self._pub[0], 2).point(self._pub[1], 2)
|
||||
if include_private and self._priv:
|
||||
e.long(self._priv[1], 2)
|
||||
else:
|
||||
e.long(0, 2)
|
||||
return e.out()
|
||||
|
||||
def compress(self):
|
||||
'''Returns a compact public key representation'''
|
||||
|
||||
|
||||
@staticmethod
|
||||
def decode(s):
|
||||
'''Constructs a new Key object from its binary representation'''
|
||||
kid, ksize, pub, priv = Decoder(s).int(8).int(2).point(2).long(2).out()
|
||||
k = Key((ksize, pub), (ksize, priv) if priv else None)
|
||||
if kid == k.keyid():
|
||||
return k
|
||||
else:
|
||||
raise ValueError, "Invalid Key ID"
|
||||
|
||||
# --- IDENTIFICATION AND VALIDATION ----------------------------------------
|
||||
|
||||
def private(self):
|
||||
'''Checks whether Key object contains private key'''
|
||||
return bool(self._priv)
|
||||
|
||||
def validate(self):
|
||||
'''Checks key validity'''
|
||||
if ecdsa.validate_public_key(self._pub):
|
||||
if self._priv: # ? validate and match private key
|
||||
return ecdsa.validate_private_key(self._priv) and \
|
||||
ecdsa.match_keys(self._pub, self._priv)
|
||||
else:
|
||||
return True # : everything valid
|
||||
else:
|
||||
return False
|
||||
|
||||
def fingerprint(self, as_hex = True, hashfunc = 'sha1'):
|
||||
'''Get the public key fingerprint'''
|
||||
if hashfunc in self._fingerprint:
|
||||
return self._fingerprint[hashfunc] if not as_hex else \
|
||||
self._fingerprint[hashfunc].encode("hex")
|
||||
else:
|
||||
h = hashlib.new(hashfunc, enc_point(self._pub[1]))
|
||||
d = h.digest()
|
||||
self._fingerprint[hashfunc] = d
|
||||
return d.encode("hex") if as_hex else d
|
||||
|
||||
def keyid(self):
|
||||
'''Get a short, unique identifier'''
|
||||
if not self._id:
|
||||
self._id = dec_long(self.fingerprint(False, 'sha1')[:8])
|
||||
return self._id
|
||||
|
||||
# --- DIGITAL SIGNATURES ---------------------------------------------------
|
||||
|
||||
def sign(self, data, hashfunc = 'sha256'):
|
||||
'''Sign data using the specified hash function'''
|
||||
if self._priv:
|
||||
h = dec_long(hashlib.new(hashfunc, data).digest())
|
||||
s = ecdsa.sign(h, self._priv)
|
||||
return enc_point(s)
|
||||
else:
|
||||
raise AttributeError, "Private key needed for signing."
|
||||
|
||||
def verify(self, data, sig, hashfunc = 'sha256'):
|
||||
'''Verify the signature of data using the specified hash function'''
|
||||
h = dec_long(hashlib.new(hashfunc, data).digest())
|
||||
s = dec_point(sig)
|
||||
return ecdsa.verify(h, s, self._pub)
|
||||
|
||||
# --- HYBRID ENCRYPTION ----------------------------------------------------
|
||||
|
||||
def encrypt(self, data):
|
||||
'''Encrypt a message using this public key'''
|
||||
ctext, mkey = encrypt(data, self._pub)
|
||||
return Encoder().point(mkey).str(ctext, 4).out()
|
||||
|
||||
def decrypt(self, data):
|
||||
'''Decrypt an encrypted message using this private key'''
|
||||
mkey, ctext = Decoder(data).point().str(4).out()
|
||||
return decrypt(ctext, mkey, self._priv)
|
||||
|
||||
# --- AUTHENTICATED ENCRYPTION ---------------------------------------------
|
||||
|
||||
def auth_encrypt(self, data, receiver):
|
||||
'''Sign and encrypt a message'''
|
||||
sgn = self.sign(data)
|
||||
ctext, mkey = encrypt(data, receiver._pub)
|
||||
return Encoder().point(mkey).str(ctext, 4).str(sgn, 2).out()
|
||||
|
||||
def auth_decrypt(self, data, source):
|
||||
'''Decrypt and verify a message'''
|
||||
mkey, ctext, sgn = Decoder(data).point().str(4).str(2).out()
|
||||
text = decrypt(ctext, mkey, self._priv)
|
||||
if source.verify(text, sgn):
|
||||
return text
|
||||
else:
|
||||
raise SecurityViolationException, "Invalid Signature"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import time
|
||||
|
||||
def test_overhead():
|
||||
print "sender", "receiver", "+bytes", "+enctime", "+dectime"
|
||||
for s in [192, 224, 256, 384, 521]:
|
||||
sender = Key.generate(s)
|
||||
for r in [192, 224, 256, 384, 521]:
|
||||
receiver = Key.generate(r)
|
||||
t = time.time()
|
||||
e = sender.auth_encrypt("", receiver)
|
||||
t1 = time.time() - t
|
||||
t = time.time()
|
||||
receiver.auth_decrypt(e, sender)
|
||||
t2 = time.time() - t
|
||||
print s, r, len(e), t1, t2
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user