Exemple #1
0
    def verify_mac(self, mac_value: bytes, data: bytes) -> None:
        if len(mac_value) <= crypto_format.NON_RAW_PREFIX_SIZE:
            # This also rejects raw MAC with size of 4 bytes or fewer. Those MACs are
            # clearly insecure, thus should be discouraged.
            raise tink_error.TinkError('tag too short')
        prefix = mac_value[:crypto_format.NON_RAW_PREFIX_SIZE]
        mac_no_prefix = mac_value[crypto_format.NON_RAW_PREFIX_SIZE:]
        for entry in self._primitive_set.primitive_from_identifier(prefix):
            try:
                if entry.output_prefix_type == tink_pb2.LEGACY:
                    entry.primitive.verify_mac(
                        mac_no_prefix, data + crypto_format.LEGACY_START_BYTE)
                else:
                    entry.primitive.verify_mac(mac_no_prefix, data)
                # If there is no exception, the MAC is valid and we can return.
                return
            except tink_error.TinkError as e:
                logging.info('tag prefix matches a key, but cannot verify: %s',
                             e)

        # No 'non-raw' key matched, so let's try the raw keys (if any exist).
        for entry in self._primitive_set.raw_primitives():
            try:
                entry.primitive.verify_mac(mac_value, data)
                # If there is no exception, the MAC is valid and we can return.
                return
            except tink_error.TinkError as e:
                pass
        raise tink_error.TinkError('invalid MAC')
Exemple #2
0
    def register_key_manager(cls,
                             key_manager: km_module.KeyManager,
                             new_key_allowed: bool = True) -> None:
        """Tries to register a key_manager for the given key_manager.key_type().

    Args:
      key_manager: A KeyManager object
      new_key_allowed: If new_key_allowed is true, users can generate new keys
        with this manager using Registry.new_key()
    """
        key_managers = cls._key_managers
        type_url = key_manager.key_type()
        primitive_class = key_manager.primitive_class()

        if not key_manager.does_support(type_url):
            raise tink_error.TinkError(
                'The manager does not support its own type {}.'.format(
                    type_url))

        if type_url in key_managers:
            existing, existing_new_key = key_managers[type_url]
            if (type(existing) != type(key_manager) or  # pylint: disable=unidiomatic-typecheck
                    existing.primitive_class() != primitive_class):
                raise tink_error.TinkError(
                    'A manager for type {} has been already registered.'.
                    format(type_url))
            else:
                if not existing_new_key and new_key_allowed:
                    raise tink_error.TinkError(
                        ('A manager for type {} has been already registered '
                         'with forbidden new key operation.').format(type_url))
                key_managers[type_url] = (existing, new_key_allowed)
        else:
            key_managers[type_url] = (key_manager, new_key_allowed)
Exemple #3
0
def _validate_key(key: tink_pb2.Keyset.Key):
  """Raises tink_error.TinkError if key is not valid."""
  if not key.HasField('key_data'):
    raise tink_error.TinkError('key {} has no key data'.format(key.key_id))
  if key.output_prefix_type == tink_pb2.UNKNOWN_PREFIX:
    raise tink_error.TinkError('key {} has unknown prefix'.format(key.key_id))
  if key.status == tink_pb2.UNKNOWN_STATUS:
    raise tink_error.TinkError('key {} has unknown status'.format(key.key_id))
Exemple #4
0
 def read_encrypted(self) -> tink_pb2.EncryptedKeyset:
     if not self._serialized_keyset:
         raise tink_error.TinkError('No keyset found')
     try:
         encrypted_keyset = tink_pb2.EncryptedKeyset()
         encrypted_keyset.ParseFromString(self._serialized_keyset)
         return encrypted_keyset
     except message.DecodeError as e:
         raise tink_error.TinkError(e)
Exemple #5
0
 def public_key_data(
         cls, private_key_data: tink_pb2.KeyData) -> tink_pb2.KeyData:
     """Generates a new key for the specified key_template."""
     if (private_key_data.key_material_type !=
             tink_pb2.KeyData.ASYMMETRIC_PRIVATE):
         raise tink_error.TinkError('The keyset contains a non-private key')
     key_mgr = cls.key_manager(private_key_data.type_url)
     if not isinstance(key_mgr, km_module.PrivateKeyManager):
         raise tink_error.TinkError(
             'manager for key type {} is not a PrivateKeyManager'.format(
                 private_key_data.type_url))
     return key_mgr.public_key_data(private_key_data)
Exemple #6
0
 def _key_manager_internal(
         cls, type_url: Text) -> Tuple[km_module.KeyManager, bool]:
     """Returns a key manager, new_key_allowed pair for the given type_url."""
     if type_url not in cls._key_managers:
         raise tink_error.TinkError(
             'No manager for type {} has been registered.'.format(type_url))
     return cls._key_managers[type_url]
Exemple #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 tink_error.TinkError('cannot encrypt keyset: %s != %s' %
                                       (keyset, keyset2))
    except message.DecodeError:
        raise tink_error.TinkError('invalid keyset, corrupted key material')
    return tink_pb2.EncryptedKeyset(encrypted_keyset=encrypted_keyset,
                                    keyset_info=_keyset_info(keyset))
Exemple #8
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 tink_error.TinkError('keyset contains secret key material')
 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 tink_error.TinkError('failed to decrypt ciphertext ' +
                                    ciphertext.decode())
     return data[0]
Exemple #10
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 tink_error.TinkError('empty keyset')
  if num_primary_keys > 1:
    raise tink_error.TinkError('keyset contains multiple primary keys')
  if num_primary_keys == 0 and num_non_public_key_material > 0:
    raise tink_error.TinkError('keyset does not contain a valid primary key')
Exemple #11
0
 def write_encrypted(self, encrypted_keyset: tink_pb2.EncryptedKeyset) -> None:
   if not isinstance(encrypted_keyset, tink_pb2.EncryptedKeyset):
     raise tink_error.TinkError('invalid encrypted keyset.')
   json_keyset = json_format.MessageToJson(encrypted_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()
Exemple #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 tink_error.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 tink_error.TinkError(
                'Number of written bytes was greater than length of bytes given'
            )

        return written
Exemple #13
0
 def write(self, keyset: tink_pb2.Keyset) -> None:
     if not isinstance(keyset, tink_pb2.Keyset):
         raise tink_error.TinkError('invalid keyset.')
     json_keyset = json_format.MessageToJson(keyset)
     # 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()
    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) <= 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 tink_error.TinkError('signature too short')

        key_id = signature[:crypto_format.NON_RAW_PREFIX_SIZE]
        raw_sig = signature[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 + crypto_format.LEGACY_START_BYTE)
                else:
                    entry.primitive.verify(raw_sig, data)
                # Signature is valid, we can return
                return
            except tink_error.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 tink_error.TinkError:
                pass

        raise tink_error.TinkError('invalid signature')
Exemple #15
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 tink_error.TinkError('invalid keyset, corrupted key material')
Exemple #16
0
    def new_key_data(cls,
                     key_template: tink_pb2.KeyTemplate) -> tink_pb2.KeyData:
        """Generates a new key for the specified key_template."""
        key_mgr, new_key_allowed = cls._key_manager_internal(
            key_template.type_url)

        if not new_key_allowed:
            raise tink_error.TinkError(
                'KeyManager for type {} does not allow for creation of new keys.'
                .format(key_template.type_url))

        return key_mgr.new_key_data(key_template)
Exemple #17
0
def output_prefix(key: tink_pb2.Keyset.Key) -> bytes:
    """Generates the prefix for the outputs handled by the specified key."""
    if key.output_prefix_type == tink_pb2.TINK:
        return struct.pack('>cL', TINK_START_BYTE, key.key_id)
    elif (key.output_prefix_type == tink_pb2.CRUNCHY
          or key.output_prefix_type == tink_pb2.LEGACY):
        return struct.pack('>cL', LEGACY_START_BYTE, key.key_id)
    elif key.output_prefix_type == tink_pb2.RAW:
        return b''
    else:
        raise tink_error.TinkError(
            'The given key has invalid OutputPrefixType {}.'.format(
                key.output_prefix_type))
Exemple #18
0
    def add_primitive(self, primitive: P, key: tink_pb2.Keyset.Key) -> Entry:
        """Adds a new primitive and key entry to the set, and returns the entry."""
        if not isinstance(primitive, self._primitive_class):
            raise tink_error.TinkError(
                'The primitive is not an instance of {}'.format(
                    self._primitive_class))
        identifier = crypto_format.output_prefix(key)

        entry = Entry(primitive, identifier, key.status,
                      key.output_prefix_type)
        entries = self._primitives.setdefault(identifier, [])
        entries.append(entry)
        return entry
Exemple #19
0
    def register_primitive_wrapper(
            cls, wrapper: primitive_wrapper.PrimitiveWrapper) -> None:
        """Tries to register a PrimitiveWrapper.

    Args:
      wrapper: A PrimitiveWrapper object.
    Raises:
      Error if a different wrapper has already been registered for the same
      Primitive.
    """
        if (wrapper.primitive_class() in cls._wrappers and type(
                cls._wrappers[wrapper.primitive_class()]) != type(wrapper)):  # pylint: disable=unidiomatic-typecheck
            raise tink_error.TinkError(
                'A wrapper for primitive {} has already been added.'.format(
                    wrapper.primitive_class().__name__))
        wrapped = wrapper.wrap(
            pset_module.PrimitiveSet(wrapper.primitive_class()))
        if not isinstance(wrapped, wrapper.primitive_class()):
            raise tink_error.TinkError(
                'Wrapper for primitive {} generates incompatible primitive of type {}'
                .format(wrapper.primitive_class().__name__,
                        type(wrapped).__name__))
        cls._wrappers[wrapper.primitive_class()] = wrapper
Exemple #20
0
    def wrap(cls,
             primitive_set: pset_module.PrimitiveSet) -> Any:  # -> Primitive
        """Tries to register a PrimitiveWrapper.

    Args:
      primitive_set: A PrimitiveSet object.
    Returns:
      A primitive that wraps the primitives in primitive_set.
    Raises:
      Error if no wrapper for this primitive class is registered.
    """
        if primitive_set.primitive_class() not in cls._wrappers:
            raise tink_error.TinkError(
                'No PrimitiveWrapper registered for primitive {}.'.format(
                    primitive_set.primitive_class().__name__))
        wrapper = cls._wrappers[primitive_set.primitive_class()]
        return wrapper.wrap(primitive_set)
Exemple #21
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 tink_error.TinkError('primary primitive not set')

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

    return primary.identifier + primary.primitive.sign(sign_data)
Exemple #22
0
 def decrypt(self, ciphertext: bytes, context_info: bytes) -> bytes:
   if len(ciphertext) > crypto_format.NON_RAW_PREFIX_SIZE:
     prefix = ciphertext[:crypto_format.NON_RAW_PREFIX_SIZE]
     ciphertext_no_prefix = ciphertext[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 tink_error.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 tink_error.TinkError as e:
       pass
   # nothing works.
   raise tink_error.TinkError('Decryption failed.')
Exemple #23
0
    def primitive(cls, key_data: tink_pb2.KeyData,
                  primitive_class: Type[P]) -> P:
        """Creates a new primitive for the key given in key_data.

    It looks up a KeyManager identified by key_data.type_url,
    and calls manager's primitive(key_data) method.

    Args:
      key_data: KeyData object
      primitive_class: The expected primitive class

    Returns:
      A primitive for the given key_data
    Raises:
      Error if primitive_class does not match the registered primitive class.
    """
        key_mgr = cls.key_manager(key_data.type_url)
        if key_mgr.primitive_class() != primitive_class:
            raise tink_error.TinkError(
                'Wrong primitive class: type {} uses primitive {}, and not {}.'
                .format(key_data.type_url,
                        key_mgr.primitive_class().__name__,
                        primitive_class.__name__))
        return key_mgr.primitive(key_data)
 def verify_mac(self, mac_value: bytes, data: bytes) -> None:
     if mac_value != data + b'|' + self._name.encode():
         raise tink_error.TinkError('invalid mac ' + mac_value.decode())
 def verify(self, signature: bytes, data: bytes):
     if signature != data + b'|' + self._name.encode():
         raise tink_error.TinkError('invalid signature ' +
                                    signature.decode())
Exemple #26
0
def _assert_enough_encrypted_key_material(
        encrypted_keyset: tink_pb2.EncryptedKeyset):
    if not encrypted_keyset or not encrypted_keyset.encrypted_keyset:
        raise tink_error.TinkError('empty keyset')
Exemple #27
0
def _assert_enough_key_material(keyset: tink_pb2.Keyset):
    if not keyset or not keyset.key:
        raise tink_error.TinkError('empty keyset')
Exemple #28
0
 def write(self, keyset: tink_pb2.Keyset) -> None:
     if not isinstance(keyset, tink_pb2.Keyset):
         raise tink_error.TinkError('invalid keyset.')
     self._io_stream.write(keyset.SerializeToString())
     self._io_stream.flush()
Exemple #29
0
 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 tink_error.TinkError(e)
Exemple #30
0
 def write_encrypted(self,
                     encrypted_keyset: tink_pb2.EncryptedKeyset) -> None:
     if not isinstance(encrypted_keyset, tink_pb2.EncryptedKeyset):
         raise tink_error.TinkError('invalid encrypted keyset.')
     self._io_stream.write(encrypted_keyset.SerializeToString())
     self._io_stream.flush()