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