def main(): # The initial shared secret key is assumed to be the product of some prior # protocol exchange. The specification recommends the use of the Extended # Triple Diffie-Hellman (X3DH) protocol, which will generate both the # shared secret key and AD, where the value of AD will be derived from the # identifiers for the processes corresponding through the double ratchet. SK = keygen('random', 32) # initial shared secret AD = keygen('random', 32) # random simulated AD value bob_init_key_pair = GENERATE_DH( ) # generate Bob's initial ratchet key pair # get public part bob_init_pubk = bob_init_key_pair.verify_key.encode(encoder=HexEncoder) alice_state = state() # create sender state bob_state = state() # create receiver state RatchetInitAlice(alice_state, SK, bob_init_pubk) # set-up sender state RatchetInitBob(bob_state, SK, bob_init_key_pair) # set-up receiver state msg1 = b'I am a secret.' # some message print('Message 1:', msg1) # Alice encrypts first message, getting back header and ciphertext msg1_header, msg1_ct = RatchetEncrypt(alice_state, msg1, AD) print('Header 1:', msg1_header) print('Encrypted 1:', msg1_ct) # Bob decrypts the first message msg1_pt = RatchetDecrypt(bob_state, msg1_header, msg1_ct, AD) print('Decrypted 1:', msg1_pt) msg2 = b'I am also a secret.' print('Message 2:', msg2) # Bob encrypts second message msg2_header, msg2_ct = RatchetEncrypt(bob_state, msg2, AD) print('Header 2:', msg2_header) print('Encrypted 2:', msg2_ct) # Alice decrypts seconds message msg2_pt = RatchetDecrypt(alice_state, msg2_header, msg2_ct, AD) print('Decrypted 2:', msg2_pt)
def encrypt(k, pt, ad): kd_out = HKDF(k, salt = (b'\0' * 32), key_len = 80, hashmod = SHA256.new(), context = b'kdf_encrypt_info') enc_key = SA.keygen('shared', key_mat = kd_out[:32]) auth_key = SA.keygen('mac', key_mat = kd_out[32:64]) kd_iv = kd_out[64:] ct = SA.encrypt(pt, key = enc_key, iv = kd_iv) data, mac = SA.sign(ad + ct, key = auth_key) return ct + mac
def ENCRYPT(mk, plaintext, associated_data): kd = HKDF(algorithm=hashes.SHA256(), length=80, salt=(b'\0' * 32), info=b'kdf_encrypt_info', backend=default_backend()) kd_out = kd.derive(mk) enc_key = keygen('shared', key_mat=kd_out[:32]) auth_key = keygen('mac', key_mat=kd_out[32:64]) kd_iv = kd_out[64:] ct = encrypt(plaintext, key=enc_key, iv=kd_iv) data, mac = sign(associated_data + ct, key=auth_key) return ct + mac
def decrypt(k, ct, ad): kd_out = HKDF(k, salt = (b'\0' * 32), key_len = 80, hashmod = SHA256.new(), context = b'kdf_encrypt_info') enc_key = SA.keygen('shared', key_mat = kd_out[:32]) auth_key = SA.keygen('mac', key_mat = kd_out[32:64]) kd_iv = kd_out[64:] ct_iv = ct[:16] ct_mac = ct[-32:] ct_ct = ct[:-32] assert kd_iv == ct_iv verdict = SA.verify((b'\0' + ad + ct_ct, ct_mac), key = auth_key) if verdict != None: return SA.decrypt(ct_ct, key = enc_key) else: raise Exception
def DECRYPT(mk, ciphertext, associated_data): kd = HKDF(algorithm=hashes.SHA256(), length=80, salt=(b'\0' * 32), info=b'kdf_encrypt_info', backend=default_backend()) kd_out = kd.derive(mk) enc_key = keygen('shared', key_mat=kd_out[:32]) auth_key = keygen('mac', key_mat=kd_out[32:64]) kd_iv = kd_out[64:] ct_iv = ciphertext[:16] ct_mac = ciphertext[-32:] ct = ciphertext[:-32] assert kd_iv == ct_iv verdict = verify((b'\0' + associated_data + ct, ct_mac), key=auth_key) if verdict != None: return decrypt(ct, key=enc_key) else: raise Exception()
def KDF_CK(ck): mac_key = keygen('mac', key_mat=ck) chain_key = sign(b'\1', key=mac_key)[1] message_key = sign(b'\2', key=mac_key)[1] return chain_key, message_key