def test_pyuecc(self): (pub1, pri1) = pyuecc.make_key() logging.debug('pub key %s' % pub1) logging.debug('pri key %s' % pri1) assert len(pub1) == 64 assert len(pri1) == 64 (pub2, pri2) = pyuecc.make_key() spri1 = pyuecc.shared_secret(pub1, pri2) spri2 = pyuecc.shared_secret(pub2, pri1) logging.debug('shared key %s' % spri1) assert spri1 == spri2 cpub1 = pyuecc.compress(pub1) logging.debug('compressed key %s' % cpub1) assert len(cpub1) == 33 _pub1 = pyuecc.decompress(cpub1) logging.debug('decompressed key %s' % _pub1) assert _pub1 == pub1 msg = b'test' sig1 = pyuecc.sign(pri1, msg) ok = pyuecc.verify(pub1, msg, sig1) logging.debug('signature %s' % sig1) assert ok ok = pyuecc.verify(pub2, msg, sig1) assert not ok
def encrypt_handshake(self, msg, remote_permanent_public_b64): """ encrypt handshake message structured: KEY - 33 bytes, the sender's ephemeral exchange public key in compressed format IV - 4 bytes, a random but unique value determined by the sender INNER - the AES-256-CTR encrypted inner packet ciphertext HMAC - 4 bytes, the calculated HMAC of all of the previous KEY+IV+INNER bytes :param bytes msg: handshake message :param bytes remote_permanent_public_b64: remote node pubkey :return: encrypted bytes data """ remote_permanent_public = \ base64.b64decode(remote_permanent_public_b64) compressed_pkey = pyuecc.compress(self.remote_ephemeral_public) shared_key = get_sha256( pyuecc.shared_secret(remote_permanent_public, self.remote_ephemeral_private) ) iv = pack('I', self.seq) self.seq += 1 aes = AES.new(shared_key, AES.MODE_CTR, counter=IVCounter(iv)) enc_msg = aes.encrypt(msg.encode()) hmac_key = pyuecc.shared_secret(remote_permanent_public, self.mynode.private) + iv sig = fold3(hmac.new(hmac_key, enc_msg, hashlib.sha256).digest()) self.token = fold1(get_sha256(compressed_pkey[0:16])) return compressed_pkey + iv + enc_msg + sig
def decrypt_handshake(self, msg): """ decrypt handshake message structured: decoded msg must be json str and key 'public_key' must be included. :param bytes msg: encrypted handshake message :return: decrypted message """ received_public = pyuecc.decompress(msg[0:33]) self.create_channel_keys(received_public) shared_key = get_sha256( pyuecc.shared_secret(received_public, self.mynode.private) ) aes = AES.new(shared_key, AES.MODE_CTR, counter=IVCounter(msg[33:37])) dec_msg = aes.encrypt(msg[37:-4]).decode() try: jmsg = json.loads(dec_msg) except Exception: logging.error('msg is not json') return None if 'public_key' not in jmsg: logging.error('public_key not included') return None remote_permanent_public = base64.b64decode(jmsg['public_key']) hmac_key = pyuecc.shared_secret(remote_permanent_public, self.mynode.private) + msg[33:37] sig = fold3(hmac.new(hmac_key, msg[37:-4], hashlib.sha256).digest()) if sig != msg[-4:]: logging.error('signature unmatched') return None return dec_msg
def create_channel_keys(self, received_key): """ create channel key by encryption key: SHA256(secret, sent-KEY, received-KEY) / 2 decryption key: SHA256(secret, received-KEY, sent-KEY) / 2 where secret is derived from ECDH from ephemeral keys. :param bytes received_key: recived epheram key. """ shared_key = pyuecc.shared_secret(received_key, self.remote_ephemeral_private) t = shared_key + self.remote_ephemeral_public + received_key self.channel_enckey = get_sha256(t) t = shared_key + received_key + self.remote_ephemeral_public self.channel_deckey = get_sha256(t)