blob: 69a15a6f74178677b903bc6a5e29267443b47f2b [file] [log] [blame]
# 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
#
# 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.
"""A container class for a set of primitives."""
from __future__ import absolute_import
from __future__ import division
from __future__ import google_type_annotations
from __future__ import print_function
import collections
from typing import Generic, List, Type, TypeVar
from tink.proto import tink_pb2
from tink.python.core import crypto_format
from tink.python.core import tink_error
P = TypeVar('P')
Entry = collections.namedtuple(
'Entry', 'primitive, identifier, status, output_prefix_type')
def new_primitive_set(primitive_class):
return PrimitiveSet(primitive_class)
class PrimitiveSet(Generic[P]):
"""A container class for a set of primitives.
PrimitiveSet is an auxiliary class used for supporting key rotation:
primitives in a set correspond to keys in a keyset. Users will usually work
with primitive instances, which essentially wrap primitive sets. For example
an instance of an Aead-primitive for a given keyset holds a set of
Aead-primitives corresponding to the keys in the keyset, and uses the set
members to do the actual crypto operations: to encrypt data the primary
Aead-primitive from the set is used, and upon decryption the ciphertext's
prefix determines the id of the primitive from the set.
"""
def __init__(self, primitive_class: Type[P]):
# map from identifier to a list of Entry
self._primitives = {}
self._primary = None
self._primitive_class = primitive_class
def primitive_class(self) -> Type[P]:
return self._primitive_class
def primitive_from_identifier(self, identifier: bytes) -> List[P]:
"""Returns a copy of the list of entries for a given identifier."""
# Copy the list so that if the user modifies the list, it does not affect
# the internal data structure.
return self._primitives.get(identifier, [])[:]
def primitive(self, key: tink_pb2.Keyset.Key) -> List[P]:
"""Returns a copy of the list of primitives for a given identifier."""
return self.primitive_from_identifier(crypto_format.output_prefix(key))
def raw_primitives(self) -> List[P]:
"""Returns a copy of the list of primitives for a given identifier."""
# All raw keys have the same identifier, which is just b''.
return self.primitive_from_identifier(crypto_format.RAW_PREFIX)
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
def set_primary(self, entry: Entry) -> None:
self._primary = entry
def primary(self) -> Entry:
return self._primary