def decode_authentication( ciphertext: bytes, privkey: datatypes.PrivateKey ) -> Tuple[datatypes.PublicKey, bytes, datatypes.PublicKey]: """ Decrypts and decodes the ciphertext msg. Returns the initiator's ephemeral pubkey, nonce, and pubkey. """ if len(ciphertext) < ENCRYPTED_AUTH_MSG_LEN: raise DecryptionError(f"Auth msg too short: {len(ciphertext)}") elif len(ciphertext) == ENCRYPTED_AUTH_MSG_LEN: sig, initiator_pubkey, initiator_nonce, _ = decode_auth_plain( ciphertext, privkey ) else: sig, initiator_pubkey, initiator_nonce, _ = decode_auth_eip8( ciphertext, privkey ) # recover initiator ephemeral pubkey from sig # S(ephemeral-privk, ecdh-shared-secret ^ nonce) shared_secret = ecies.ecdh_agree(privkey, initiator_pubkey) ephem_pubkey = sig.recover_public_key_from_msg_hash( sxor(shared_secret, initiator_nonce) ) return ephem_pubkey, initiator_nonce, initiator_pubkey
def decrypt_raw_bytes(self, data: bytes, size: int) -> bytes: """ same as decrypt_body() but no roundup """ if len(data) < size + MAC_LEN: raise ValueError( "Insufficient body length; Got {}, wanted {} + {}".format( len(data), size, MAC_LEN ) ) frame_ciphertext = data[:size] frame_mac = data[size : size + MAC_LEN] self.ingress_mac.update(frame_ciphertext) fmac_seed = self.ingress_mac.digest()[:MAC_LEN] self.ingress_mac.update(sxor(self.mac_enc(fmac_seed), fmac_seed)) expected_frame_mac = self.ingress_mac.digest()[:MAC_LEN] if not bytes_eq(expected_frame_mac, frame_mac): raise DecryptionError( "Invalid frame mac: expected {}, got {}".format( expected_frame_mac, frame_mac ) ) return self.aes_dec.update(frame_ciphertext)[:size]
def decrypt(data: bytes, privkey: datatypes.PrivateKey, shared_mac_data: bytes = b"") -> bytes: """Decrypt data with ECIES method using the given private key 1) generate shared-secret = kdf( ecdhAgree(myPrivKey, msg[1:65]) ) 2) verify tag 3) decrypt ecdhAgree(r, recipientPublic) == ecdhAgree(recipientPrivate, R) [where R = r*G, and recipientPublic = recipientPrivate*G] """ if data[:1] != b"\x04": raise DecryptionError("wrong ecies header") # 1) generate shared-secret = kdf( ecdhAgree(myPrivKey, msg[1:65]) ) shared = data[1:1 + PUBKEY_LEN] key_material = ecdh_agree(privkey, keys.PublicKey(shared)) key = kdf(key_material) key_enc, key_mac = key[:KEY_LEN // 2], key[KEY_LEN // 2:] key_mac = sha256(key_mac).digest() tag = data[-KEY_LEN:] # 2) Verify tag expected_tag = hmac_sha256(key_mac, data[1 + PUBKEY_LEN:-KEY_LEN] + shared_mac_data) if not bytes_eq(expected_tag, tag): raise DecryptionError("Failed to verify tag") # 3) Decrypt algo = CIPHER(key_enc) blocksize = algo.block_size // 8 iv = data[1 + PUBKEY_LEN:1 + PUBKEY_LEN + blocksize] ciphertext = data[1 + PUBKEY_LEN + blocksize:-KEY_LEN] ctx = Cipher(algo, MODE(iv), default_backend()).decryptor() return ctx.update(ciphertext) + ctx.finalize()
def decrypt_header(self, data: bytes) -> bytes: if len(data) != HEADER_LEN + MAC_LEN: raise ValueError( f"Unexpected header length: {len(data)}, expected {HEADER_LEN} + {MAC_LEN}" ) header_ciphertext = data[:HEADER_LEN] header_mac = data[HEADER_LEN:] mac_secret = self.ingress_mac.digest()[:HEADER_LEN] aes = self.mac_enc(mac_secret)[:HEADER_LEN] self.ingress_mac.update(sxor(aes, header_ciphertext)) expected_header_mac = self.ingress_mac.digest()[:HEADER_LEN] if not bytes_eq(expected_header_mac, header_mac): raise DecryptionError( f"Invalid header mac: expected {expected_header_mac}, got {header_mac}" ) return self.aes_dec.update(header_ciphertext)
def decrypt_body(self, data: bytes, body_size: int) -> bytes: read_size = roundup_16(body_size) if len(data) < read_size + MAC_LEN: raise ValueError( f"Insufficient body length; Got {len(data)}, wanted {read_size} + {MAC_LEN}" ) frame_ciphertext = data[:read_size] frame_mac = data[read_size:read_size + MAC_LEN] self.ingress_mac.update(frame_ciphertext) fmac_seed = self.ingress_mac.digest()[:MAC_LEN] self.ingress_mac.update(sxor(self.mac_enc(fmac_seed), fmac_seed)) expected_frame_mac = self.ingress_mac.digest()[:MAC_LEN] if not bytes_eq(expected_frame_mac, frame_mac): raise DecryptionError( f"Invalid frame mac: expected {expected_frame_mac}, got {frame_mac}" ) return self.aes_dec.update(frame_ciphertext)[:body_size]