Source Code Listings

secretkey.py

  1 # secretkey.py: secret-key cryptographic functions
  2 """
  3 Secret-key functions from chapter 1 of "A Working Introduction to
  4 Cryptography with Python".
  5 """
  6 
  7 import Crypto.Cipher.AES as AES
  8 import Crypto.Hash.HMAC as HMAC
  9 import Crypto.Hash.SHA384 as SHA384
 10 import Crypto.Random.OSRNG.posix as RNG
 11 import pbkdf2
 12 import streql
 13 
 14 
 15 __AES_KEYLEN = 32
 16 __TAG_KEYLEN = 48
 17 __TAG_LEN = __TAG_KEYLEN
 18 KEYSIZE = __AES_KEYLEN + __TAG_KEYLEN
 19 
 20 
 21 def pad_data(data):
 22     """pad_data pads out the data to an AES block length."""
 23     # return data if no padding is required
 24     if len(data) % 16 == 0:
 25         return data
 26 
 27     # subtract one byte that should be the 0x80
 28     # if 0 bytes of padding are required, it means only
 29     # a single \x80 is required.
 30 
 31     padding_required = 15 - (len(data) % 16)
 32 
 33     data = '%s\x80' % data
 34     data = '%s%s' % (data, '\x00' * padding_required)
 35 
 36     return data
 37 
 38 
 39 def unpad_data(data):
 40     """unpad_data removes padding from the data."""
 41     if not data:
 42         return data
 43 
 44     data = data.rstrip('\x00')
 45     if data[-1] == '\x80':
 46         return data[:-1]
 47     else:
 48         return data
 49 
 50 
 51 def generate_nonce():
 52     """Generate a random number used once."""
 53     return RNG.new().read(AES.block_size)
 54 
 55 
 56 def new_tag(ciphertext, key):
 57     """Compute a new message tag using HMAC-SHA-384."""
 58     return HMAC.new(key, msg=ciphertext, digestmod=SHA384).digest()
 59 
 60 
 61 def verify_tag(ciphertext, key):
 62     """Verify the tag on a ciphertext."""
 63     tag_start = len(ciphertext) - __TAG_LEN
 64     data = ciphertext[:tag_start]
 65     tag = ciphertext[tag_start:]
 66     actual_tag = new_tag(data, key)
 67     return streql.equals(actual_tag, tag)
 68 
 69 
 70 def decrypt(ciphertext, key):
 71     """
 72     Decrypt a ciphertext encrypted with AES in CBC mode; assumes the IV
 73     has been prepended to the ciphertext.
 74     """
 75     if len(ciphertext) <= AES.block_size:
 76         return None, False
 77     tag_start = len(ciphertext) - __TAG_LEN
 78     ivec = ciphertext[:AES.block_size]
 79     data = ciphertext[AES.block_size:tag_start]
 80     if not verify_tag(ciphertext, key[__AES_KEYLEN:]):
 81         return None, False
 82     aes = AES.new(key[:__AES_KEYLEN], AES.MODE_CBC, ivec)
 83     data = aes.decrypt(data)
 84     return unpad_data(data), True
 85 
 86 
 87 def encrypt(data, key):
 88     """
 89     Encrypt data using AES in CBC mode. The IV is prepended to the
 90     ciphertext.
 91     """
 92     data = pad_data(data)
 93     ivec = generate_nonce()
 94     aes = AES.new(key[:__AES_KEYLEN], AES.MODE_CBC, ivec)
 95     ctxt = aes.encrypt(data)
 96     tag = new_tag(ivec+ctxt, key[__AES_KEYLEN:])
 97     return ivec + ctxt + tag
 98 
 99 
100 def generate_salt(salt_len):
101     """Generate a salt for use with PBKDF2."""
102     return RNG.new().read(salt_len)
103 
104 
105 def password_key(passphrase, salt=None):
106     """Generate a key from a passphrase. Returns the tuple (salt, key)."""
107     if salt is None:
108         salt = generate_salt(16)
109     passkey = pbkdf2.PBKDF2(passphrase, salt, iterations=16384).read(KEYSIZE)
110     return salt, passkey

publickey.py

 1 # publickey.py: public key cryptographic functions
 2 """
 3 Secret-key functions from chapter 1 of "A Working Introduction to
 4 Cryptography with Python".
 5 """
 6 
 7 import Crypto.Hash.SHA384 as SHA384
 8 import pyelliptic
 9 import secretkey
10 import struct
11 
12 
13 __CURVE = 'secp521r1'
14 
15 
16 def generate_key():
17     """Generate a new elliptic curve keypair."""
18     return pyelliptic.ECC(curve=__CURVE)
19 
20 
21 def sign(priv, msg):
22     """Sign a message with the ECDSA key."""
23     return priv.sign(msg)
24 
25 
26 def verify(pub, msg, sig):
27     """
28     Verify the public key's signature on the message. pub should
29     be a serialised public key.
30     """
31     return pyelliptic.ECC(curve='secp521r1', pubkey=pub).verify(sig, msg)
32 
33 
34 def shared_key(priv, pub):
35     """Generate a new shared encryption key from a keypair."""
36     key = priv.get_ecdh_key(pub)
37     key = key[:32] + SHA384.new(key[32:]).digest()
38     return key
39 
40 
41 def encrypt(pub, msg):
42     """
43     Encrypt the message to the public key using ECIES. The public key
44     should be a serialised public key.
45     """
46     ephemeral = generate_key()
47     key = shared_key(ephemeral, pub)
48     ephemeral_pub = struct.pack('>H', len(ephemeral.get_pubkey()))
49     ephemeral_pub += ephemeral.get_pubkey()
50     return ephemeral_pub+secretkey.encrypt(msg, key)
51 
52 
53 def decrypt(priv, msg):
54     """
55     Decrypt an ECIES-encrypted message with the private key.
56     """
57     ephemeral_len = struct.unpack('>H', msg[:2])[0]
58     ephemeral_pub = msg[2:2+ephemeral_len]
59     key = shared_key(priv, ephemeral_pub)
60     return secretkey.decrypt(msg[2+ephemeral_len:], key)
  1. A certificate is a public key encoded with X.509 and which can have additional informational attributes attached, such as organisation name and country.↩
  2. The extent to which this actually happens varies widely based on the different CAs.↩
  3. There is some question as to whether VeriSign can actually be trusted, but that is another discussion for another day…↩
  4. and GnuPG↩
  5. http://www.rubin.ch/pgp/weboftrust.en.html↩
  6. It is quite often important to distinguish between I know this key belongs to that user and I trust that user. This is especially important with key signatures - if Bob cannot trust Alice to properly check identities, she might sign a key for an identity she hasn’t checked.↩
  7. http://is.gd/Tr0zLP↩
  8. https://secure.wikimedia.org/wikipedia/en/wiki/Internet_Key_Exchange↩