blob: 0bda9c34526de0cfd910942bea4f0cb61ccc6e2d [file] [log] [blame]
C.J. Collier37141e42020-02-13 13:49:49 -08001# Copyright 2016 Google LLC
Jon Wayne Parrott9281ca02017-08-11 14:36:42 -07002#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Base classes for cryptographic signers and verifiers."""
16
17import abc
Danny Hermes1cd83902018-02-08 15:41:51 -080018import io
19import json
Jon Wayne Parrott9281ca02017-08-11 14:36:42 -070020
Jon Wayne Parrott9281ca02017-08-11 14:36:42 -070021
Bu Sun Kim9eec0912019-10-21 17:04:21 -070022_JSON_FILE_PRIVATE_KEY = "private_key"
23_JSON_FILE_PRIVATE_KEY_ID = "private_key_id"
Danny Hermes1cd83902018-02-08 15:41:51 -080024
25
Tres Seaver560cf1e2021-08-03 16:35:54 -040026class Verifier(object, metaclass=abc.ABCMeta):
Jon Wayne Parrott9281ca02017-08-11 14:36:42 -070027 """Abstract base class for crytographic signature verifiers."""
28
29 @abc.abstractmethod
30 def verify(self, message, signature):
31 """Verifies a message against a cryptographic signature.
32
33 Args:
34 message (Union[str, bytes]): The message to verify.
35 signature (Union[str, bytes]): The cryptography signature to check.
36
37 Returns:
38 bool: True if message was signed by the private key associated
39 with the public key that this object was constructed with.
40 """
41 # pylint: disable=missing-raises-doc,redundant-returns-doc
42 # (pylint doesn't recognize that this is abstract)
Bu Sun Kim9eec0912019-10-21 17:04:21 -070043 raise NotImplementedError("Verify must be implemented")
Jon Wayne Parrott9281ca02017-08-11 14:36:42 -070044
45
Tres Seaver560cf1e2021-08-03 16:35:54 -040046class Signer(object, metaclass=abc.ABCMeta):
Jon Wayne Parrott9281ca02017-08-11 14:36:42 -070047 """Abstract base class for cryptographic signers."""
48
49 @abc.abstractproperty
50 def key_id(self):
51 """Optional[str]: The key ID used to identify this private key."""
Bu Sun Kim9eec0912019-10-21 17:04:21 -070052 raise NotImplementedError("Key id must be implemented")
Jon Wayne Parrott9281ca02017-08-11 14:36:42 -070053
54 @abc.abstractmethod
55 def sign(self, message):
56 """Signs a message.
57
58 Args:
59 message (Union[str, bytes]): The message to be signed.
60
61 Returns:
62 bytes: The signature of the message.
63 """
64 # pylint: disable=missing-raises-doc,redundant-returns-doc
65 # (pylint doesn't recognize that this is abstract)
Bu Sun Kim9eec0912019-10-21 17:04:21 -070066 raise NotImplementedError("Sign must be implemented")
Danny Hermes1cd83902018-02-08 15:41:51 -080067
68
Tres Seaver560cf1e2021-08-03 16:35:54 -040069class FromServiceAccountMixin(object, metaclass=abc.ABCMeta):
Danny Hermes1cd83902018-02-08 15:41:51 -080070 """Mix-in to enable factory constructors for a Signer."""
71
72 @abc.abstractmethod
73 def from_string(cls, key, key_id=None):
74 """Construct an Signer instance from a private key string.
75
76 Args:
77 key (str): Private key as a string.
78 key_id (str): An optional key id used to identify the private key.
79
80 Returns:
81 google.auth.crypt.Signer: The constructed signer.
82
83 Raises:
84 ValueError: If the key cannot be parsed.
85 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -070086 raise NotImplementedError("from_string must be implemented")
Danny Hermes1cd83902018-02-08 15:41:51 -080087
88 @classmethod
89 def from_service_account_info(cls, info):
90 """Creates a Signer instance instance from a dictionary containing
91 service account info in Google format.
92
93 Args:
94 info (Mapping[str, str]): The service account info in Google
95 format.
96
97 Returns:
98 google.auth.crypt.Signer: The constructed signer.
99
100 Raises:
101 ValueError: If the info is not in the expected format.
102 """
103 if _JSON_FILE_PRIVATE_KEY not in info:
104 raise ValueError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700105 "The private_key field was not found in the service account " "info."
106 )
Danny Hermes1cd83902018-02-08 15:41:51 -0800107
108 return cls.from_string(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700109 info[_JSON_FILE_PRIVATE_KEY], info.get(_JSON_FILE_PRIVATE_KEY_ID)
110 )
Danny Hermes1cd83902018-02-08 15:41:51 -0800111
112 @classmethod
113 def from_service_account_file(cls, filename):
114 """Creates a Signer instance from a service account .json file
115 in Google format.
116
117 Args:
118 filename (str): The path to the service account .json file.
119
120 Returns:
121 google.auth.crypt.Signer: The constructed signer.
122 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700123 with io.open(filename, "r", encoding="utf-8") as json_file:
Danny Hermes1cd83902018-02-08 15:41:51 -0800124 data = json.load(json_file)
125
126 return cls.from_service_account_info(data)