-
Notifications
You must be signed in to change notification settings - Fork 14
/
verify.py
139 lines (113 loc) · 4.72 KB
/
verify.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# This Software (Dioptra) is being made available as a public service by the
# National Institute of Standards and Technology (NIST), an Agency of the United
# States Department of Commerce. This software was developed in part by employees of
# NIST and in part by NIST contractors. Copyright in portions of this software that
# were developed by NIST contractors has been licensed or assigned to NIST. Pursuant
# to Title 17 United States Code Section 105, works of NIST employees are not
# subject to copyright protection in the United States. However, NIST may hold
# international copyright in software created by its employees and domestic
# copyright (or licensing rights) in portions of software that were assigned or
# licensed to NIST. To the extent that NIST holds copyright in this software, it is
# being made available under the Creative Commons Attribution 4.0 International
# license (CC BY 4.0). The disclaimers of the CC BY 4.0 license apply to all parts
# of the software developed or licensed by NIST.
#
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
#
# The load_public_key, load_signature, and verify_payload functions are adapted from the
# following source:
#
# ErikusMaximus (https://stackoverflow.com/users/3508142/erikusmaximus), How to
# verify a signed file in python, URL (version: 2019-07-02):
# https://stackoverflow.com/q/51331461
from __future__ import annotations
import base64
import click
import structlog
from structlog.stdlib import BoundLogger
from mitre.securingai.sdk.exceptions import CryptographyDependencyError
from mitre.securingai.sdk.utilities.decorators import require_package
from .common import load_payload
LOGGER: BoundLogger = structlog.stdlib.get_logger()
try:
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
from cryptography.hazmat.primitives.serialization import load_pem_public_key
except ImportError: # pragma: nocover
LOGGER.warn(
"Unable to import one or more optional packages, functionality may be reduced",
package="cryptography",
)
@require_package("cryptography", exc_type=CryptographyDependencyError)
def load_public_key(filepath: str) -> RSAPublicKey:
"""Load the public RSA key from a file"""
with open(filepath, "rb") as f:
public_key: RSAPublicKey = load_pem_public_key(f.read(), default_backend())
return public_key
def load_signature(filepath: str) -> bytes:
"""Load the signature"""
with open(filepath, "rb") as f:
signature: bytes = base64.b64decode(f.read())
return signature
@require_package("cryptography", exc_type=CryptographyDependencyError)
def verify_payload(payload: bytes, signature: bytes, public_key: RSAPublicKey) -> bool:
"""Verify the payload signature"""
try:
public_key.verify(
signature,
payload,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
except InvalidSignature:
raise InvalidSignature(
"Payload and/or signature files failed verification"
) from None
return True
@click.command()
@click.option(
"--public-key-file",
type=click.Path(
exists=True, file_okay=True, dir_okay=False, resolve_path=True, readable=True
),
required=True,
help="File with public key to use for signing",
)
@click.option(
"--payload-file",
type=click.Path(
exists=True, file_okay=True, dir_okay=False, resolve_path=True, readable=True
),
required=True,
help="Payload to verify with signature file",
)
@click.option(
"--signature-file",
type=click.Path(
exists=True, file_okay=True, dir_okay=False, resolve_path=True, readable=True
),
required=True,
help="File with the payload signature",
)
def verify(public_key_file: str, payload_file: str, signature_file: str) -> bool:
public_key: RSAPublicKey = load_public_key(filepath=public_key_file)
payload: bytes = load_payload(filepath=payload_file)
signature: bytes = load_signature(filepath=signature_file)
try:
verification: bool = verify_payload(
payload=payload, signature=signature, public_key=public_key
)
click.echo("OK")
except InvalidSignature:
click.echo("ERROR - Payload and/or signature files failed verification")
verification = False
return verification
if __name__ == "__main__":
_ = verify()