aboutsummaryrefslogtreecommitdiff
path: root/private_join_and_compute/py/ciphers/ec_cipher.py
blob: 36ae8ec1f7813c352025c55a6fb0d442a26cdd76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# Copyright 2019 Google LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""EC based commutative cipher."""

from typing import Optional

from private_join_and_compute.py.crypto_util import elliptic_curve
from private_join_and_compute.py.crypto_util import supported_hashes

NID_secp224r1 = 713  # pylint: disable=invalid-name
DEFAULT_CURVE_ID = NID_secp224r1
POINT_CONVERSION_COMPRESSED = 2


class EcCipher(object):
  """A commutative cipher based on Elliptic Curves."""

  # key is an address.
  def __init__(
      self,
      curve_id: int = DEFAULT_CURVE_ID,
      private_key_bytes: Optional[bytes] = None,
      hash_type: Optional[supported_hashes.HashType] = None,
  ) -> None:
    """Generate a new EC key pair, if the key is not passed as a parameter.

    The private key is a random value and the private point is the result of
    performing a scalar point multiplication of that value with the curve's
    base point.

    Args:
      curve_id: the id of the curve to use, given as an int value.
      private_key_bytes: an ec key in bytes, if the key has already been
        generated.
      hash_type: the hash to use in order to map a string to the elliptic curve.

    Raises:
      TypeError: If curve_id is not an int.
      Exception: If the key could not be generated.
    """
    self._ec_key = elliptic_curve.ECKey(curve_id, private_key_bytes, hash_type)

  def Encrypt(self, id_bytes: bytes) -> bytes:
    """Hashes the client id to a point on the curve.

    It then encrypts the point by multiplying it with the private key.

    Args:
      id_bytes: a client id encoded as a string/byte value.

    Returns:
      the compressed encoded EC Point in bytes.

    Raises:
      TypeError: If id_bytes is not a str type.
    """
    ec_point = self._ec_key.elliptic_curve.GetPointByHashingToCurve(id_bytes)
    return self.EncryptPoint(ec_point)

  def EncryptPoint(self, ec_point) -> bytes:
    """Encrypts a point on the curve.

    Args:
      ec_point: the point to encrypt.

    Returns:
      the compressed encoded encrypted point in bytes
    """
    ec_point *= self._ec_key.priv_key_bn
    return ec_point.GetAsBytes()

  def ReEncrypt(self, enc_id_bytes: bytes) -> bytes:
    """Re-encrypts the id by multiplying with the private key.

    Args:
      enc_id_bytes: an encrypted client id as a bytes value.

    Returns:
      the compressed encoded re-encrypted EC Point in bytes.

    Raises:
      TypeError: If enc_id_bytes id is not a str type.
    """
    ec_point = self._ec_key.elliptic_curve.GetPointFromBytes(enc_id_bytes)
    return self.EncryptPoint(ec_point)

  @property
  def ec_key(self):
    return self._ec_key

  @property
  def elliptic_curve(self):
    return self._ec_key.elliptic_curve

  def DecryptReEncryptedId(self, reenc_id_bytes: bytes) -> bytes:
    """Decrypts a reencrypted id to its encrypted id form.

    Assuming reenc_id_bytes=E_k1(E_k2(m)) where E(.) is the ec_cipher and k1/k2
    are private keys. This function with decryption key, k1', returns E_k2(m) or
    with decryption key, k2', E_k1(m). Essentially this removes one layer of
    encryption from the reenc_id_bytes.

    This function *cannot* be applied to encrypted ids as the return value would
    be the message one-way hashed to a point on the curve.

    Args:
      reenc_id_bytes: a reencrypted client id, encoded with a key and then
        reencoded with another key.

    Returns:
      An encoded id in bytes.
    """
    ec_point = self._ec_key.elliptic_curve.GetPointFromBytes(reenc_id_bytes)
    ec_point *= self._ec_key.decrypt_key_bignum
    return ec_point.GetAsBytes()