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)
- A certificate is a public key encoded with X.509 and which can have additional informational attributes attached, such as organisation name and country.↩
- The extent to which this actually happens varies widely based on the different CAs.↩
- There is some question as to whether VeriSign can actually be trusted, but that is another discussion for another day…↩
- and GnuPG↩
- http://www.rubin.ch/pgp/weboftrust.en.html↩
- 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.↩
- http://is.gd/Tr0zLP↩
- https://secure.wikimedia.org/wikipedia/en/wiki/Internet_Key_Exchange↩