Exemplo n.º 1
0
    def encrypt(self,
                data: bytes,
                raw_pubkey: bytes,
                shared_mac_data: bytes = b'') -> bytes:
        """
        ECIES Encrypt, where P = recipient public key is:
        1) generate r = random value
        2) generate shared-secret = kdf( ecdhAgree(r, P) )
        3) generate R = rG [same op as generating a public key]
        4) send 0x04 || R || AES(shared-secret, plaintext) || tag
        """
        enc_key, mac_key, ephem_pubkey = \
            self.key_manager.get_ephem_key(raw_pubkey)

        iv = pyelliptic.Cipher.gen_IV(self.CIPHERNAME)
        assert len(iv) == 16

        cipher = pyelliptic.Cipher(enc_key, iv, 1, self.CIPHERNAME)
        ciphertext = cipher.ciphering(data)
        assert len(ciphertext) == len(data)

        msg = ephem_pubkey + iv + ciphertext

        tag = pyelliptic.hmac_sha256(mac_key, msg[1 + 64:] + shared_mac_data)
        assert len(tag) == 32

        msg += tag
        assert len(msg) == self.OVERHEAD + len(data)

        return msg
Exemplo n.º 2
0
    def ecies_encrypt(cls, data, raw_pubkey, shared_mac_data=''):
        """
        ECIES Encrypt, where P = recipient public key is:
        1) generate r = random value
        2) generate shared-secret = kdf( ecdhAgree(r, P) )
        3) generate R = rG [same op as generating a public key]
        4) send 0x04 || R || AsymmetricEncrypt(shared-secret, plaintext) || tag


        currently used by go:
        ECIES_AES128_SHA256 = &ECIESParams{
            Hash: sha256.New,
            hashAlgo: crypto.SHA256,
            Cipher: aes.NewCipher,
            BlockSize: aes.BlockSize,
            KeyLen: 16,
            }

        """
        # 1) generate r = random value
        ephem = cls(None)

        # 2) generate shared-secret = kdf( ecdhAgree(r, P) )
        key_material = ephem.raw_get_ecdh_key(pubkey_x=raw_pubkey[:32],
                                              pubkey_y=raw_pubkey[32:])
        assert len(key_material) == 32
        key = eciesKDF(key_material, 32)
        assert len(key) == 32
        key_enc, key_mac = key[:16], key[16:]

        key_mac = hashlib.sha256(key_mac).digest()  # !!!
        assert len(key_mac) == 32
        # 3) generate R = rG [same op as generating a public key]
        ephem_pubkey = ephem.raw_pubkey

        # encrypt
        iv = pyelliptic.Cipher.gen_IV(cls.ecies_ciphername)
        assert len(iv) == 16
        ctx = pyelliptic.Cipher(key_enc, iv, 1, cls.ecies_ciphername)
        ciphertext = ctx.ciphering(data)
        assert len(ciphertext) == len(data)

        # 4) send 0x04 || R || AsymmetricEncrypt(shared-secret, plaintext)
        #    || tag
        msg = rlp_utils.ascii_chr(0x04) + ephem_pubkey + iv + ciphertext

        # the MAC of a message (called the tag) as per SEC 1, 3.5.
        tag = pyelliptic.hmac_sha256(
            key_mac, msg[1 + 64:] + rlp_utils.str_to_bytes(shared_mac_data))
        assert len(tag) == 32
        msg += tag

        assert len(msg) == 1 + 64 + 16 + 32 + len(data) == 113 + len(data)
        assert len(msg) - cls.ecies_encrypt_overhead_length == len(data)
        return msg
Exemplo n.º 3
0
    def ecies_decrypt(self, data, shared_mac_data=b''):
        """
        Decrypt data with ECIES method using the local private key

        ECIES Decrypt (performed by recipient):
        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 exceptions.DecryptionError("wrong ecies header")

        #  1) generate shared-secret = kdf( ecdhAgree(myPrivKey, msg[1:65]) )
        _shared = data[1:1 + 64]
        # FIXME, check that _shared_pub is a valid one (on curve)

        key_material = self.raw_get_ecdh_key(pubkey_x=_shared[:32],
                                             pubkey_y=_shared[32:])
        assert len(key_material) == 32
        key = eciesKDF(key_material, 32)
        assert len(key) == 32
        key_enc, key_mac = key[:16], key[16:]

        key_mac = hashlib.sha256(key_mac).digest()
        assert len(key_mac) == 32

        tag = data[-32:]
        assert len(tag) == 32

        # 2) verify tag
        hmaced_data = pyelliptic.hmac_sha256(
            key_mac, data[1 + 64:-32] + shared_mac_data)
        if not pyelliptic.equals(hmaced_data, tag):
            raise exceptions.DecryptionError("Fail to verify data")

        # 3) decrypt
        blocksize = pyelliptic.OpenSSL.get_cipher(
            self.ecies_ciphername).get_blocksize()  # noqa
        iv = data[1 + 64:1 + 64 + blocksize]
        assert len(iv) == 16
        ciphertext = data[1 + 64 + blocksize:-32]
        assert 1 + len(_shared) + len(iv) + len(ciphertext) + len(tag) == len(
            data)  # noqa
        ctx = pyelliptic.Cipher(key_enc, iv, 0, self.ecies_ciphername)
        return ctx.ciphering(ciphertext)
Exemplo n.º 4
0
    def decrypt(self, data, raw_privkey, shared_mac_data=b''):
        """
        Decrypt data with ECIES method using the local private key

        ECIES Decrypt (performed by recipient):
        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 exceptions.DecryptionError("Wrong ECIES header")

        # 1) generate shared-secret
        ephem_pubkey = data[:1 + 64]
        key_material = self.key_manager.ecdh(HashablePrivateKey(raw_privkey),
                                             ephem_pubkey)
        assert len(key_material) == 32

        enc_key, mac_key = self.key_manager.get_derived_keys(key_material)

        tag = data[-32:]
        assert len(tag) == 32

        # 2) verify tag
        hmaced_data = pyelliptic.hmac_sha256(
            mac_key, data[1 + 64:-32] + shared_mac_data)
        if not pyelliptic.equals(hmaced_data, tag):
            raise exceptions.DecryptionError("Tag verification failed")

        # 3) decrypt
        iv = data[1 + 64:1 + 64 + self.BLOCK_SIZE]
        assert len(iv) == 16

        ciphertext = data[1 + 64 + self.BLOCK_SIZE:-32]
        assert len(ephem_pubkey) + len(iv) + len(ciphertext) + len(tag) \
            == len(data)

        cipher = pyelliptic.Cipher(enc_key, iv, 0, self.CIPHERNAME)
        msg = cipher.ciphering(ciphertext)
        assert len(msg) == len(ciphertext)
        return msg
Exemplo n.º 5
0
    def encrypt(cls, data, raw_public_key, shared_mac_data=""):
        """
        ECIES Encrypt, where P = recipient public key is:
        1) generate r = random value
        2) generate shared-secret = kdf( ecdhAgree(r, P) )
        3) generate R = rG [same op as generating a public key]
        4) send 0x04 || R || AsymmetricEncrypt(shared-secret, plaintext) || tag


        currently used by go:
        ECIES_AES128_SHA256 = &ECIESParams{
            Hash: sha256.New,
            hashAlgo: crypto.SHA256,
            Cipher: aes.NewCipher,
            BlockSize: aes.BlockSize,
            KeyLen: 16,
            }

        :param data: data to encrypt
        :param raw_public_key: public key used of encryption
        :param shared_mac_data: shared mac
        :return: encrypted data
        """

        if not data:
            raise ValueError("Data is required")

        if len(raw_public_key) != eth_common_constants.PUBLIC_KEY_LEN:
            raise ValueError(
                "Public key of len {0} is expected but was {1}".format(
                    eth_common_constants.PUBLIC_KEY_LEN, len(raw_public_key)))

        # 1) generate r = random value
        ephem = ECCx()

        # 2) generate shared-secret = kdf( ecdhAgree(r, P) )
        pubkey_x = raw_public_key[:eth_common_constants.PUBLIC_KEY_X_Y_LEN]
        pubkey_y = raw_public_key[eth_common_constants.PUBLIC_KEY_X_Y_LEN:]
        key_material = ephem.raw_get_ecdh_key(pubkey_x=pubkey_x,
                                              pubkey_y=pubkey_y)

        key = crypto_utils.ecies_kdf(key_material,
                                     eth_common_constants.SHARED_KEY_LEN)
        assert len(key) == eth_common_constants.SHARED_KEY_LEN
        key_enc, key_mac = key[:eth_common_constants.ENC_KEY_LEN], key[
            eth_common_constants.ENC_KEY_LEN:]

        key_mac = hashlib.sha256(key_mac).digest()

        # 3) generate R = rG [same op as generating a public key]
        ephem_pubkey = ephem.get_raw_public_key()

        # encrypt
        iv = pyelliptic.Cipher.gen_IV(eth_common_constants.ECIES_CIPHER_NAME)
        assert len(iv) == eth_common_constants.IV_LEN

        ctx = pyelliptic.Cipher(key_enc, iv,
                                eth_common_constants.CIPHER_ENCRYPT_DO,
                                eth_common_constants.ECIES_CIPHER_NAME)
        cipher_text = ctx.ciphering(data)
        assert len(cipher_text) == len(data)

        # 4) send 0x04 || R || AsymmetricEncrypt(shared-secret, plaintext) || tag
        msg = rlp_utils.ascii_chr(eth_common_constants.ECIES_HEADER
                                  ) + ephem_pubkey + iv + cipher_text

        # the MAC of a message (called the tag) as per SEC 1, 3.5.
        tag = pyelliptic.hmac_sha256(
            key_mac, msg[eth_common_constants.ECIES_HEADER_LEN +
                         eth_common_constants.PUBLIC_KEY_LEN:] +
            rlp_utils.str_to_bytes(shared_mac_data))
        assert len(tag) == eth_common_constants.MAC_LEN
        msg += tag

        assert len(
            msg) - eth_common_constants.ECIES_ENCRYPT_OVERHEAD_LENGTH == len(
                data)
        return msg
Exemplo n.º 6
0
    def decrypt(self, data, shared_mac_data=b""):
        """
        Decrypt data with ECIES method using the local private key

        ECIES Decrypt (performed by recipient):
        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]

        :param data: data to decrypt
        :param shared_mac_data: shared mac
        :return: decrypted data
        """

        if not data:
            raise ValueError("Data is required")

        if data[:eth_common_constants.
                ECIES_HEADER_LEN] != eth_common_constants.ECIES_HEADER_BYTE:
            raise DecryptionError("Wrong ECIES header")

        #  1) generate shared-secret = kdf( ecdhAgree(myPrivKey, msg[1:65]) )
        shared = data[eth_common_constants.
                      ECIES_HEADER_LEN:eth_common_constants.ECIES_HEADER_LEN +
                      eth_common_constants.PUBLIC_KEY_LEN]

        shared_pubkey_x = shared[:eth_common_constants.PUBLIC_KEY_X_Y_LEN]
        shared_pubkey_y = shared[eth_common_constants.PUBLIC_KEY_X_Y_LEN:]

        key_material = self.raw_get_ecdh_key(pubkey_x=shared_pubkey_x,
                                             pubkey_y=shared_pubkey_y)
        assert len(key_material) == eth_common_constants.KEY_MATERIAL_LEN
        key = crypto_utils.ecies_kdf(key_material,
                                     eth_common_constants.SHARED_KEY_LEN)
        assert len(key) == eth_common_constants.SHARED_KEY_LEN
        key_enc, key_mac = key[:eth_common_constants.ENC_KEY_LEN], key[
            eth_common_constants.ENC_KEY_LEN:]

        key_mac = hashlib.sha256(key_mac).digest()
        assert len(key_mac) == eth_common_constants.MAC_LEN

        tag = data[-eth_common_constants.MAC_LEN:]
        assert len(tag) == eth_common_constants.MAC_LEN

        # 2) verify tag
        header_len = eth_common_constants.ECIES_HEADER_LEN + eth_common_constants.PUBLIC_KEY_LEN

        mac_data = data[header_len:- eth_common_constants.MAC_LEN] + \
                   shared_mac_data
        if not pyelliptic.equals(pyelliptic.hmac_sha256(key_mac, mac_data),
                                 tag):
            raise DecryptionError("Fail to verify data")

        # 3) decrypt
        block_size = pyelliptic.OpenSSL.get_cipher(
            eth_common_constants.ECIES_CIPHER_NAME).get_blocksize()

        iv = data[header_len:header_len + block_size]
        cipher_text = data[header_len +
                           block_size:-eth_common_constants.MAC_LEN]
        assert eth_common_constants.ECIES_HEADER_LEN + len(shared) + len(
            iv) + len(cipher_text) + len(tag) == len(data)
        ctx = pyelliptic.Cipher(key_enc, iv,
                                eth_common_constants.CIPHER_DECRYPT_DO,
                                eth_common_constants.ECIES_CIPHER_NAME)
        return ctx.ciphering(cipher_text)