# Copyright 2020 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 # # http://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. """Python implementation of a KeysetBuilder.""" import io import random from tink.proto import tink_pb2 import tink from tink import cleartext_keyset_handle _MAX_INT32 = 4294967295 # 2^32-1 def _new_key_data(key_template: tink_pb2.KeyTemplate) -> tink_pb2.KeyData: return tink.core.Registry.new_key_data(key_template) def _generate_unused_key_id(keyset: tink_pb2.Keyset) -> int: while True: key_id = random.randint(1, _MAX_INT32) if key_id not in {key.key_id for key in keyset.key}: return key_id def raw_template(template: tink_pb2.KeyTemplate) -> tink_pb2.KeyTemplate: template_copy = tink_pb2.KeyTemplate() template_copy.CopyFrom(template) template_copy.output_prefix_type = tink_pb2.RAW return template_copy def legacy_template(template: tink_pb2.KeyTemplate) -> tink_pb2.KeyTemplate: template_copy = tink_pb2.KeyTemplate() template_copy.CopyFrom(template) template_copy.output_prefix_type = tink_pb2.LEGACY return template_copy class KeysetBuilder: """A KeysetBuilder provides convenience methods for managing Keysets. It provides methods for adding, disabling, enabling, or deleting keys. The validity of the keyset is checked when creating a keyset_handle. """ def __init__(self, keyset_proto: tink_pb2.Keyset): self._keyset = keyset_proto def keyset_handle(self) -> tink.KeysetHandle: keyset_copy = tink_pb2.Keyset() keyset_copy.CopyFrom(self._keyset) return cleartext_keyset_handle.from_keyset(keyset_copy) def keyset(self) -> bytes: return self._keyset.SerializeToString() def public_keyset(self) -> bytes: public_handle = self.keyset_handle().public_keyset_handle() public_keyset = io.BytesIO() writer = tink.BinaryKeysetWriter(public_keyset) cleartext_keyset_handle.write(writer, public_handle) return public_keyset.getvalue() def add_new_key(self, key_template: tink_pb2.KeyTemplate) -> int: """Generates a new key, adds it to the keyset, and returns its ID.""" new_key = self._keyset.key.add() new_key.key_data.CopyFrom(_new_key_data(key_template)) new_key.status = tink_pb2.ENABLED new_key_id = _generate_unused_key_id(self._keyset) new_key.key_id = new_key_id new_key.output_prefix_type = key_template.output_prefix_type return new_key_id def set_primary_key(self, key_id: int) -> None: """Sets a key as primary.""" for key in self._keyset.key: if key.key_id == key_id: self._keyset.primary_key_id = key_id return raise tink.TinkError('key not found: %d' % key_id) def enable_key(self, key_id: int) -> None: """Enables a key.""" for key in self._keyset.key: if key.key_id == key_id: key.status = tink_pb2.ENABLED return raise tink.TinkError('key not found: %d' % key_id) def disable_key(self, key_id: int) -> None: """Disables a key.""" for key in self._keyset.key: if key.key_id == key_id: key.status = tink_pb2.DISABLED return raise tink.TinkError('key not found: %d' % key_id) def delete_key(self, key_id: int) -> None: """Deletes a key.""" for key in self._keyset.key: if key.key_id == key_id: self._keyset.key.remove(key) return raise tink.TinkError('key not found: %d' % key_id) def from_keyset(keyset: bytes) -> KeysetBuilder: """Return a KeysetBuilder for a Keyset copied from a KeysetHandle.""" keyset_proto = tink_pb2.Keyset.FromString(keyset) return KeysetBuilder(keyset_proto) def from_keyset_handle(keyset_handle: tink.KeysetHandle) -> KeysetBuilder: """Return a KeysetBuilder for a Keyset copied from a KeysetHandle.""" keyset_buffer = io.BytesIO() cleartext_keyset_handle.write( tink.BinaryKeysetWriter(keyset_buffer), keyset_handle) return from_keyset(keyset_buffer.getvalue()) def new_keyset_builder() -> KeysetBuilder: return KeysetBuilder(tink_pb2.Keyset())