Ejemplo n.º 1
0
 def decrypt(self, ciphertext: bytes, associated_data: bytes) -> bytes:
   data = ciphertext.split(b'|')
   if (len(data) < 3 or data[1] != associated_data or
       data[2] != self._name.encode()):
     raise core.TinkError('failed to decrypt ciphertext ' +
                          ciphertext.decode())
   return data[0]
Ejemplo n.º 2
0
 def write_encrypted(self,
                     encrypted_keyset: tink_pb2.EncryptedKeyset) -> None:
     if not isinstance(encrypted_keyset, tink_pb2.EncryptedKeyset):
         raise core.TinkError('invalid encrypted keyset.')
     json_keyset = json_format.MessageToJson(encrypted_keyset)
     self._io_stream.write(json_keyset)
     self._io_stream.flush()
Ejemplo n.º 3
0
def _validate_primitive_set(pset: core.PrimitiveSet):
    # TODO(juerg): also validate that there is a primary
    for entries in pset.all():
        for entry in entries:
            if (entry.output_prefix_type != tink_pb2.RAW
                    and entry.output_prefix_type != tink_pb2.TINK):
                raise core.TinkError('unsupported OutputPrefixType')
Ejemplo n.º 4
0
    def verify_mac_and_decode(
            self, compact: Text, validator: _jwt_validator.JwtValidator
    ) -> _verified_jwt.VerifiedJwt:
        """Verifies, validates and decodes a MACed compact JWT token.

    Args:
      compact: A MACed token encoded in the JWS compact serialization format.
      validator: A JwtValidator that validates the token.

    Returns:
      A VerifiedJwt.
    Raises:
      tink.TinkError if the operation fails.
    """
        interesting_error = None
        for entry in self._primitive_set.raw_primitives():
            try:
                return entry.primitive.verify_mac_and_decode(
                    compact, validator)
            except core.TinkError as e:
                if isinstance(e, _jwt_error.JwtInvalidError):
                    interesting_error = e
                pass
        if interesting_error:
            raise interesting_error
        raise core.TinkError('invalid MAC')
Ejemplo n.º 5
0
 def read(self, size: int = -1) -> Optional[bytes]:
   if self._error:
     raise self._error
   if not self._bytes_io:
     # read to EOF, and don't stop when None is returned.
     while True:
       try:
         d = self._ciphertext_source.read()
         if d is None:
           return None
       except BlockingIOError:
         # There is currently no data available. This error may be raised by a
         # BufferedIOBase source. For RawIOBase, we have to return None in this
         # case.
         return None
       if not d:
         # d == b'', which means EOF
         break
       self._data.extend(d)
     data = bytes(self._data).split(b'|')
     if (len(data) < 3 or data[1] != self._associated_data or
         data[0] != self._name.encode()):
       self._error = core.TinkError('error occured.')
       raise self._error
     self._bytes_io = io.BytesIO(data[2])
   return self._bytes_io.read(size)
Ejemplo n.º 6
0
def _assert_no_secret_key_material(keyset: tink_pb2.Keyset):
    for key in keyset.key:
        if key.key_data.key_material_type in (
                tink_pb2.KeyData.UNKNOWN_KEYMATERIAL,
                tink_pb2.KeyData.SYMMETRIC,
                tink_pb2.KeyData.ASYMMETRIC_PRIVATE):
            raise core.TinkError('keyset contains secret key material')
Ejemplo n.º 7
0
def _encrypt(keyset: tink_pb2.Keyset,
             master_key_primitive: aead.Aead) -> tink_pb2.EncryptedKeyset:
    """Encrypts a Keyset and returns an EncryptedKeyset."""
    encrypted_keyset = master_key_primitive.encrypt(keyset.SerializeToString(),
                                                    b'')
    # Check if we can decrypt, to detect errors
    try:
        keyset2 = tink_pb2.Keyset.FromString(
            master_key_primitive.decrypt(encrypted_keyset, b''))
        if keyset != keyset2:
            raise core.TinkError('cannot encrypt keyset: %s != %s' %
                                 (keyset, keyset2))
    except message.DecodeError:
        raise core.TinkError('invalid keyset, corrupted key material')
    return tink_pb2.EncryptedKeyset(encrypted_keyset=encrypted_keyset,
                                    keyset_info=_keyset_info(keyset))
Ejemplo n.º 8
0
    def verify_mac_and_decode(
            self, compact: str, validator: _jwt_validator.JwtValidator
    ) -> _verified_jwt.VerifiedJwt:
        """Verifies, validates and decodes a MACed compact JWT token.

    Args:
      compact: A MACed token encoded in the JWS compact serialization format.
      validator: A JwtValidator that validates the token.

    Returns:
      A VerifiedJwt.
    Raises:
      tink.TinkError if the operation fails.
    """
        interesting_error = None
        for entries in self._primitive_set.all():
            for entry in entries:
                try:
                    kid = _jwt_format.get_kid(entry.key_id,
                                              entry.output_prefix_type)
                    return entry.primitive.verify_mac_and_decode_with_kid(
                        compact, validator, kid)
                except core.TinkError as e:
                    if isinstance(e, _jwt_error.JwtInvalidError):
                        interesting_error = e
                    pass
        if interesting_error:
            raise interesting_error
        raise core.TinkError('invalid MAC')
Ejemplo n.º 9
0
    def decrypt(self, ciphertext: bytes, associated_data: bytes) -> bytes:
        try:
            response = self.client.decrypt(self.key_name, ciphertext,
                                           associated_data)
        except (GoogleAPICallError, RetryError, ValueError):
            raise core.TinkError("Decryption failed inside GCP client.")

        return response.plaintext
Ejemplo n.º 10
0
 def write(self, keyset: tink_pb2.Keyset) -> None:
     if not isinstance(keyset, tink_pb2.Keyset):
         raise core.TinkError('invalid keyset.')
     json_keyset = json_format.MessageToJson(keyset)
     # TODO(b/141106504) Needed for python 2.7 compatibility. StringIO expects
     # unicode, but MessageToJson outputs UTF-8.
     if isinstance(json_keyset, bytes):
         json_keyset = json_keyset.decode('utf-8')
     self._io_stream.write(json_keyset)
     self._io_stream.flush()
Ejemplo n.º 11
0
def _validate_keyset(keyset: tink_pb2.Keyset):
    """Raises tink_error.TinkError if keyset is not valid."""
    for key in keyset.key:
        if key.status != tink_pb2.DESTROYED:
            _validate_key(key)
    num_non_destroyed_keys = sum(1 for key in keyset.key
                                 if key.status != tink_pb2.DESTROYED)
    num_non_public_key_material = sum(
        1 for key in keyset.key if
        key.key_data.key_material_type != tink_pb2.KeyData.ASYMMETRIC_PUBLIC)
    num_primary_keys = sum(1 for key in keyset.key
                           if key.status == tink_pb2.ENABLED
                           and key.key_id == keyset.primary_key_id)
    if num_non_destroyed_keys == 0:
        raise core.TinkError('empty keyset')
    if num_primary_keys > 1:
        raise core.TinkError('keyset contains multiple primary keys')
    if num_primary_keys == 0 and num_non_public_key_material > 0:
        raise core.TinkError('keyset does not contain a valid primary key')
Ejemplo n.º 12
0
    def write(self, b: bytes) -> int:
        """Write the given buffer to the stream.

    May use multiple calls to the underlying file object's write() method.

    Returns:
      The number of bytes written, which will always be the length of b in
      bytes.

    Raises:
      BlockingIOError: if the write could not be fully completed, with
        characters_written set to the number of bytes successfully written.
      TinkError: if there was a permanent error.

    Args:
      b: The buffer to write.
    """
        self._check_not_closed()

        if not isinstance(b, (bytes, memoryview, bytearray)):
            raise TypeError('a bytes-like object is required, not {}'.format(
                type(b).__name__))

        # One call to OutputStreamAdapter.write() may call next() multiple times
        # on the C++ EncryptingStream, but will perform a partial write if there is
        # a temporary write error. Permanent write errors will bubble up as
        # exceptions.
        written = self._output_stream_adapter.write(b)
        if written < 0:
            raise core.TinkError('Number of written bytes was negative')

        self._bytes_written += written

        if written < len(b):
            raise io.BlockingIOError(
                errno.EAGAIN, 'Write could not complete without blocking.',
                written)
        elif written > len(b):
            raise core.TinkError(
                'Number of written bytes was greater than length of bytes given'
            )

        return written
Ejemplo n.º 13
0
    def verify(self, signature: bytes, data: bytes):
        """Verifies that signature is a digital signature for data.

    Args:
      signature: The signature bytes to be checked.
      data: The data bytes to be checked.

    Raises:
      tink_error.TinkError if the verification fails.
    """
        if len(signature) <= core.crypto_format.NON_RAW_PREFIX_SIZE:
            # This also rejects raw signatures with size of 4 bytes or fewer.
            # We're not aware of any schemes that output signatures that small.
            raise core.TinkError('signature too short')

        key_id = signature[:core.crypto_format.NON_RAW_PREFIX_SIZE]
        raw_sig = signature[core.crypto_format.NON_RAW_PREFIX_SIZE:]

        for entry in self._primitive_set.primitive_from_identifier(key_id):
            try:
                if entry.output_prefix_type == tink_pb2.LEGACY:
                    entry.primitive.verify(
                        raw_sig, data + core.crypto_format.LEGACY_START_BYTE)
                else:
                    entry.primitive.verify(raw_sig, data)
                # Signature is valid, we can return
                return
            except core.TinkError as err:
                logging.info(
                    'signature prefix matches a key, but cannot verify: %s',
                    err)

        # No matching key succeeded with verification, try all RAW keys
        for entry in self._primitive_set.raw_primitives():
            try:
                entry.primitive.verify(signature, data)
                # Signature is valid, we can return
                return
            except core.TinkError:
                pass

        raise core.TinkError('invalid signature')
Ejemplo n.º 14
0
def _decrypt(encrypted_keyset: tink_pb2.EncryptedKeyset,
             master_key_aead: aead.Aead) -> tink_pb2.Keyset:
    """Decrypts an EncryptedKeyset and returns a Keyset."""
    try:
        keyset = tink_pb2.Keyset.FromString(
            master_key_aead.decrypt(encrypted_keyset.encrypted_keyset, b''))
        # Check emptiness here too, in case the encrypted keys unwrapped to nothing?
        _assert_enough_key_material(keyset)
        return keyset
    except message.DecodeError:
        raise core.TinkError('invalid keyset, corrupted key material')
Ejemplo n.º 15
0
    def compute_mac_and_encode(self, raw_jwt: _raw_jwt.RawJwt) -> Text:
        """Computes a MAC and encodes the token.

    Args:
      raw_jwt: The RawJwt token to be MACed and encoded.

    Returns:
      The MACed token encoded in the JWS compact serialization format.
    Raises:
      tink.TinkError if the operation fails.
    """
        primary = self._primitive_set.primary()
        if primary.output_prefix_type != tink_pb2.RAW:
            raise core.TinkError('unexpected output prefix type')
        return primary.primitive.compute_mac_and_encode(raw_jwt)
Ejemplo n.º 16
0
  def decrypt(self, ciphertext: bytes, associated_data: bytes) -> bytes:
    if associated_data:
      ad = {'additionalData': associated_data.decode()}

    try:
      if associated_data:
        response = self.client.decrypt(KeyId=self.key_name,
                                       CiphertextBlob=ciphertext,
                                       EncryptionContext=ad)
      else:
        response = self.client.decrypt(KeyId=self.key_name,
                                       CiphertextBlob=ciphertext)
    except ClientError:
      raise core.TinkError('Decryption failed inside AWS client.')

    return response['Plaintext']
Ejemplo n.º 17
0
 def verify_and_decode(
         self, compact: Text, validator: _jwt_validator.JwtValidator
 ) -> _verified_jwt.VerifiedJwt:
     interesting_error = None
     for entries in self._primitive_set.all():
         for entry in entries:
             try:
                 return entry.primitive.verify_and_decode(
                     compact, validator)
             except core.TinkError as e:
                 if isinstance(e, _jwt_error.JwtInvalidError):
                     interesting_error = e
                 pass
     if interesting_error:
         raise interesting_error
     raise core.TinkError('invalid signature')
Ejemplo n.º 18
0
  def encrypt(self, plaintext: bytes, associated_data: bytes) -> bytes:
    if associated_data:
      ad = {'additionalData': associated_data.decode()}

    try:
      if associated_data:
        response = self.client.encrypt(KeyId=self.key_name,
                                       Plaintext=plaintext,
                                       EncryptionContext=ad)
      else:
        response = self.client.encrypt(KeyId=self.key_name,
                                       Plaintext=plaintext)
    except (ValueError, ClientError):
      raise core.TinkError('Encryption failed inside AWS client.')

    return response['CiphertextBlob']
Ejemplo n.º 19
0
  def get_aead(self, key_uri: Text) -> aead.Aead:
    """Returns an Aead-primitive backed by KMS key specified by 'key_uri'.

    Args:
      key_uri: Text, URI of the key which should be used.

    Returns:
      An AEAD primitive which uses the specified key.

    Raises:
      TinkError: If the key_uri is not supported.
    """

    if not self.does_support(key_uri):
      raise core.TinkError('Key URI not supported.')

    key_name = key_uri[len(AWS_KEYURI_PREFIX):]
    return AwsKmsAead(key_name, self.client)
Ejemplo n.º 20
0
 def verify_and_decode(
         self, compact: str, validator: _jwt_validator.JwtValidator
 ) -> _verified_jwt.VerifiedJwt:
     interesting_error = None
     for entries in self._primitive_set.all():
         for entry in entries:
             try:
                 kid = _jwt_format.get_kid(entry.key_id,
                                           entry.output_prefix_type)
                 return entry.primitive.verify_and_decode_with_kid(
                     compact, validator, kid)
             except core.TinkError as e:
                 if isinstance(e, _jwt_error.JwtInvalidError):
                     interesting_error = e
                 pass
     if interesting_error:
         raise interesting_error
     raise core.TinkError('invalid signature')
Ejemplo n.º 21
0
    def read(self, size=-1) -> Optional[bytes]:
        """Read and return up to size bytes, where size is an int.

    Args:
      size: Maximum number of bytes to read. As a convenience, if size is
        unspecified or -1, all bytes until EOF are returned.

    Returns:
      Bytes read. An empty bytes object is returned if the stream is already at
      EOF. None is returned if no data is available at the moment.

    Raises:
      TinkError if there was a permanent error.
      ValueError if the file is closed.
    """
        if self.closed:  # pylint:disable=using-constant-test
            raise ValueError('read on closed file.')
        if size == 0:
            return bytes()
        if self._matching_stream:
            return self._matching_stream.read(size)
        # if self._matching_stream is not set, we are currently reading from
        # self._attempting_stream but no data has been read successfully yet.
        while True:
            try:
                data = self._attempting_stream.read(size)
                if data is None:
                    # No data at the moment. Not clear if decryption was successful.
                    # Try again with the same stream next time.
                    return None
                # Any value other than None means that decryption was successful.
                # (b'' indicates that the plaintext is an empty string.)
                self._matching_stream = self._attempting_stream
                self._attempting_stream = None
                self._ciphertext_source.disable_rewind()
                return data
            except core.TinkError:
                if not self._remaining_primitives:
                    raise core.TinkError(
                        'No matching key found for the ciphertext in the stream'
                    )
                # Try another key.
                self._ciphertext_source.rewind()
                self._attempting_stream = self._next_decrypting_stream()
Ejemplo n.º 22
0
 def decrypt(self, ciphertext: bytes, context_info: bytes) -> bytes:
   if len(ciphertext) > core.crypto_format.NON_RAW_PREFIX_SIZE:
     prefix = ciphertext[:core.crypto_format.NON_RAW_PREFIX_SIZE]
     ciphertext_no_prefix = ciphertext[core.crypto_format.NON_RAW_PREFIX_SIZE:]
     for entry in self._primitive_set.primitive_from_identifier(prefix):
       try:
         return entry.primitive.decrypt(ciphertext_no_prefix,
                                        context_info)
       except core.TinkError as e:
         logging.info(
             'ciphertext prefix matches a key, but cannot decrypt: %s', e)
   # Let's try all RAW keys.
   for entry in self._primitive_set.raw_primitives():
     try:
       return entry.primitive.decrypt(ciphertext, context_info)
     except core.TinkError as e:
       pass
   # nothing works.
   raise core.TinkError('Decryption failed.')
Ejemplo n.º 23
0
  def sign(self, data: bytes) -> bytes:
    """Computes the signature for data using the primary primitive.

    Args:
      data: The input data.

    Returns:
      The signature.
    """
    primary = self._primitive_set.primary()

    if not primary:
      raise core.TinkError('primary primitive not set')

    sign_data = data
    if primary.output_prefix_type == tink_pb2.LEGACY:
      sign_data = sign_data + core.crypto_format.LEGACY_START_BYTE

    return primary.identifier + primary.primitive.sign(sign_data)
Ejemplo n.º 24
0
    def write(self, b: bytes) -> int:
        """Write the given buffer to the IO stream.

    Args:
      b: The buffer to write.
    Returns:
      The number of bytes written, which may be less than the length of b in
      bytes.
    Raises:
      TinkError: if there was a permanent error.

    """
        if self.closed:  # pylint:disable=using-constant-test
            raise ValueError('write on closed file')

        if not isinstance(b, (bytes, memoryview, bytearray)):
            raise TypeError('a bytes-like object is required, not {}'.format(
                type(b).__name__))
        written = self._write_to_cc_encrypting_stream(b)
        if written < 0 or written > len(b):
            raise core.TinkError('Incorrect number of bytes written')
        return written
Ejemplo n.º 25
0
 def compute(self, input_data: bytes, output_length: int) -> bytes:
     raise core.TinkError('Invalid Prf')
Ejemplo n.º 26
0
 def verify_mac(self, mac_value: bytes, data: bytes) -> None:
   if mac_value != data + b'|' + self._name.encode():
     raise core.TinkError('invalid mac ' + mac_value.decode())
Ejemplo n.º 27
0
 def verify(self, signature: bytes, data: bytes):
   if signature != data + b'|' + self._name.encode():
     raise core.TinkError('invalid signature ' + signature.decode())
Ejemplo n.º 28
0
 def decrypt(self, plaintext: bytes, associated_data: bytes) -> bytes:
   raise core.TinkError('decrypt failed.')
 def read_encrypted(self) -> tink_pb2.EncryptedKeyset:
     try:
         return json_format.Parse(self._serialized_keyset,
                                  tink_pb2.EncryptedKeyset())
     except json_format.ParseError as e:
         raise core.TinkError(e)
Ejemplo n.º 30
0
 def __new__(cls):
     raise core.TinkError('RawJwt cannot be instantiated directly.')