-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathdsa.py
More file actions
110 lines (93 loc) · 3.87 KB
/
dsa.py
File metadata and controls
110 lines (93 loc) · 3.87 KB
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
import sys
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
from ..exception import BadSignature
from ..exception import Unsupported
from . import Signer
class ECDSASigner(Signer):
def __init__(self, algorithm="ES256"):
if algorithm == "ES256":
self.hash_algorithm = hashes.SHA256
self.curve_name = "secp256r1"
elif algorithm == "ES256K":
self.hash_algorithm = hashes.SHA256
self.curve_name = "secp256k1"
elif algorithm == "ES384":
self.hash_algorithm = hashes.SHA384
self.curve_name = "secp384r1"
elif algorithm == "ES512":
self.hash_algorithm = hashes.SHA512
self.curve_name = "secp521r1"
else:
raise Unsupported("algorithm: {}".format(algorithm))
self.algorithm = algorithm
def sign(self, msg, key):
"""
Create a signature over a message as defined in RFC7515 using an
Elliptic curve key
:param msg: The message
:param key: An ec.EllipticCurvePrivateKey instance
:return:
"""
if not isinstance(key, ec.EllipticCurvePrivateKey):
raise TypeError("The private key must be an instance of " "ec.EllipticCurvePrivateKey")
self._cross_check(key.public_key())
num_bits = key.curve.key_size
num_bytes = (num_bits + 7) // 8
asn1sig = key.sign(msg, ec.ECDSA(self.hash_algorithm()))
# Cryptography returns ASN.1-encoded signature data; decode as JWS
# uses raw signatures (r||s)
(r, s) = decode_dss_signature(asn1sig)
return int.to_bytes(r, num_bytes, "big") + int.to_bytes(s, num_bytes, "big")
def verify(self, msg, sig, key):
"""
Verify a message signature
:param msg: The message
:param sig: A signature
:param key: A ec.EllipticCurvePublicKey to use for the verification.
:raises: BadSignature if the signature can't be verified.
:return: True
"""
if not isinstance(key, ec.EllipticCurvePublicKey):
raise TypeError("The public key must be an instance of " "ec.EllipticCurvePublicKey")
self._cross_check(key)
num_bits = key.curve.key_size
num_bytes = (num_bits + 7) // 8
if len(sig) != 2 * num_bytes:
raise ValueError("Invalid signature")
try:
# cryptography uses ASN.1-encoded signature data; split JWS
# signature (r||s) and encode before verification
(r, s) = self._split_raw_signature(sig)
asn1sig = encode_dss_signature(r, s)
key.verify(asn1sig, msg, ec.ECDSA(self.hash_algorithm()))
except InvalidSignature as err:
raise BadSignature(err)
else:
return True
def _cross_check(self, pub_key):
"""
In Ecdsa, both the key and the algorithm define the curve.
Therefore, we must crosscheck them to make sure they're the same.
:param key:
:raises: ValueError is the curves are not the same
"""
if self.curve_name != pub_key.curve.name:
raise ValueError(
"The curve in private key {} and in algorithm {} don't "
"match".format(pub_key.curve.name, self.curve_name)
)
@staticmethod
def _split_raw_signature(sig):
"""
Split raw signature into components
:param sig: The signature
:return: A 2-tuple
"""
c_length = len(sig) // 2
r = int.from_bytes(sig[:c_length], byteorder="big")
s = int.from_bytes(sig[c_length:], byteorder="big")
return r, s