6
6
7
7
from __future__ import annotations
8
8
9
+ import abc
9
10
import argparse
10
11
import sys
11
12
from hashlib import sha256 , sha512
12
- from typing import BinaryIO
13
+ from typing import BinaryIO , Type
13
14
14
15
from cryptography .exceptions import InvalidSignature
15
- from cryptography .hazmat .primitives import hashes
16
- from cryptography .hazmat .primitives import serialization
17
- from cryptography .hazmat .primitives .asymmetric import ec
18
- from cryptography .hazmat .primitives .asymmetric import ed25519
16
+ from cryptography .hazmat .primitives import hashes , serialization
17
+ from cryptography .hazmat .primitives .asymmetric import ec , ed25519
19
18
from cryptography .hazmat .primitives .serialization import load_pem_private_key
20
19
21
20
@@ -62,44 +61,53 @@ def generate_legal_key_for_ed25519():
62
61
return key
63
62
64
63
65
- class EllipticCurveKeysGenerator :
66
- """Generate private and public keys for Elliptic Curve cryptography."""
64
+ class KeysGeneratorBase (abc .ABC ):
67
65
68
66
def __init__ (self , infile : BinaryIO | None = None ) -> None :
69
67
"""
70
68
:param infile: A file-like object to read the private key.
71
69
"""
72
70
if infile is None :
73
- self .private_key = generate_legal_key_for_elliptic_curve ()
71
+ self .private_key = self . _generate_private_key ()
74
72
else :
75
73
self .private_key = load_pem_private_key (infile .read (), password = None )
76
74
self .public_key = self .private_key .public_key ()
77
75
78
76
@property
77
+ @abc .abstractmethod
79
78
def private_key_pem (self ) -> bytes :
80
- return self .private_key .private_bytes (
81
- encoding = serialization .Encoding .PEM ,
82
- format = serialization .PrivateFormat .PKCS8 ,
83
- encryption_algorithm = serialization .NoEncryption (),
84
- )
79
+ pass
80
+
81
+ @property
82
+ @abc .abstractmethod
83
+ def public_key_pem (self ) -> bytes :
84
+ pass
85
+
86
+ @abc .abstractmethod
87
+ def _generate_private_key (self ):
88
+ pass
89
+
90
+ @staticmethod
91
+ @abc .abstractmethod
92
+ def sign_message (private_key , message : bytes ) -> bytes :
93
+ pass
94
+
95
+ @staticmethod
96
+ @abc .abstractmethod
97
+ def verify_signature (
98
+ public_key , message : bytes , signature : bytes
99
+ ) -> bool :
100
+ pass
85
101
86
102
def write_private_key_pem (self , outfile : BinaryIO ) -> bytes :
87
103
"""
88
104
Write private key pem to file and return it.
89
105
90
106
:param outfile: A file-like object to write the private key.
91
107
"""
92
- if outfile is not None :
93
- outfile .write (self .private_key_pem )
108
+ outfile .write (self .private_key_pem )
94
109
return self .private_key_pem
95
110
96
- @property
97
- def public_key_pem (self ) -> bytes :
98
- return self .public_key .public_bytes (
99
- encoding = serialization .Encoding .PEM ,
100
- format = serialization .PublicFormat .SubjectPublicKeyInfo ,
101
- )
102
-
103
111
def write_public_key_pem (self , outfile : BinaryIO ) -> bytes :
104
112
"""
105
113
Write public key pem to file and return it.
@@ -109,31 +117,48 @@ def write_public_key_pem(self, outfile: BinaryIO) -> bytes:
109
117
outfile .write (self .public_key_pem )
110
118
return self .public_key_pem
111
119
120
+
121
+ class EllipticCurveKeysGenerator (KeysGeneratorBase ):
122
+ """Generate private and public keys for Elliptic Curve cryptography."""
123
+
124
+ def _generate_private_key (self ):
125
+ return generate_legal_key_for_elliptic_curve ()
126
+
127
+ @property
128
+ def private_key_pem (self ) -> bytes :
129
+ return self .private_key .private_bytes (
130
+ encoding = serialization .Encoding .PEM ,
131
+ format = serialization .PrivateFormat .PKCS8 ,
132
+ encryption_algorithm = serialization .NoEncryption (),
133
+ )
134
+
135
+ @property
136
+ def public_key_pem (self ) -> bytes :
137
+ return self .public_key .public_bytes (
138
+ encoding = serialization .Encoding .PEM ,
139
+ format = serialization .PublicFormat .SubjectPublicKeyInfo ,
140
+ )
141
+
112
142
@staticmethod
113
- def verify_signature (public_key , message : bytes , signature : bytes ) -> bool :
143
+ def verify_signature (
144
+ public_key : ec .EllipticCurvePublicKey , message : bytes , signature : bytes
145
+ ) -> bool :
114
146
try :
115
147
public_key .verify (signature , message , ec .ECDSA (hashes .SHA256 ()))
116
148
return True
117
149
except InvalidSignature :
118
150
return False
119
151
120
152
@staticmethod
121
- def sign_message (private_key , message : bytes ) -> bytes :
153
+ def sign_message (private_key : ec . EllipticCurvePrivateKey , message : bytes ) -> bytes :
122
154
return private_key .sign (message , ec .ECDSA (hashes .SHA256 ()))
123
155
124
156
125
- class Ed25519KeysGenerator :
157
+ class Ed25519KeysGenerator ( KeysGeneratorBase ) :
126
158
"""Generate private and public keys for ED25519 cryptography."""
127
159
128
- def __init__ (self , infile : BinaryIO | None = None ) -> None :
129
- """
130
- :param infile: A file-like object to read the private key.
131
- """
132
- if infile is None :
133
- self .private_key : ed25519 .Ed25519PrivateKey = generate_legal_key_for_ed25519 ()
134
- else :
135
- self .private_key = load_pem_private_key (infile .read (), password = None ) # type: ignore[assignment]
136
- self .public_key : ed25519 .Ed25519PublicKey = self .private_key .public_key ()
160
+ def _generate_private_key (self ):
161
+ return generate_legal_key_for_ed25519 ()
137
162
138
163
@property
139
164
def private_key_pem (self ) -> bytes :
@@ -143,50 +168,40 @@ def private_key_pem(self) -> bytes:
143
168
encryption_algorithm = serialization .NoEncryption ()
144
169
)
145
170
146
- def write_private_key_pem (self , outfile : BinaryIO ) -> bytes :
147
- """
148
- Write private key pem to file and return it.
149
-
150
- :param outfile: A file-like object to write the private key.
151
- """
152
- outfile .write (self .private_key_pem )
153
- return self .private_key_pem
154
-
155
171
@property
156
172
def public_key_pem (self ) -> bytes :
157
173
return self .public_key .public_bytes (
158
174
encoding = serialization .Encoding .PEM ,
159
175
format = serialization .PublicFormat .SubjectPublicKeyInfo
160
176
)
161
177
162
- def write_public_key_pem (self , outfile : BinaryIO ) -> bytes :
163
- """
164
- Write public key pem to file and return it.
165
-
166
- :param outfile: A file-like object to write the public key.
167
- """
168
- if outfile is not None :
169
- outfile .write (self .public_key_pem )
170
- return self .public_key_pem
171
-
172
178
@staticmethod
173
- def verify_signature (public_key : ed25519 .Ed25519PublicKey , message : bytes , signature : bytes ) -> bool :
179
+ def verify_signature (
180
+ public_key : ed25519 .Ed25519PublicKey , message : bytes , signature : bytes
181
+ ) -> bool :
174
182
try :
175
183
public_key .verify (signature , message )
176
184
return True
177
185
except InvalidSignature :
178
186
return False
179
187
180
188
@staticmethod
181
- def sign_message (private_key , message : bytes ) -> bytes :
189
+ def sign_message (private_key : ed25519 . Ed25519PrivateKey , message : bytes ) -> bytes :
182
190
return private_key .sign (message )
183
191
184
192
193
+ ALGORITHMS : dict [str , Type [KeysGeneratorBase ]] = {
194
+ "ed25519" : Ed25519KeysGenerator ,
195
+ "ec" : EllipticCurveKeysGenerator ,
196
+ }
197
+
198
+
185
199
def main (argv = None ) -> int :
186
200
parser = argparse .ArgumentParser (
187
201
description = 'Generate PEM file.' ,
188
202
formatter_class = argparse .RawDescriptionHelpFormatter ,
189
- allow_abbrev = False )
203
+ allow_abbrev = False
204
+ )
190
205
191
206
priv_pub_group = parser .add_mutually_exclusive_group (required = True )
192
207
priv_pub_group .add_argument ('--private' , required = False , action = 'store_true' ,
@@ -207,19 +222,15 @@ def main(argv=None) -> int:
207
222
208
223
args = parser .parse_args (argv )
209
224
210
- if args .algorithm == 'ed25519' :
211
- ed25519_generator = Ed25519KeysGenerator (args .infile )
212
- if args .private :
213
- ed25519_generator .write_private_key_pem (args .out )
214
- if args .public :
215
- ed25519_generator .write_public_key_pem (args .out )
216
- else :
217
- ec_generator = EllipticCurveKeysGenerator (args .infile )
218
- if args .private :
219
- ec_generator .write_private_key_pem (args .out )
220
- elif args .public :
221
- ec_generator .write_public_key_pem (args .out )
225
+ try :
226
+ generator = ALGORITHMS [args .algorithm ](args .infile )
227
+ except KeyError :
228
+ sys .exit (f'Unknown algorithm { args .algorithm } .' )
222
229
230
+ if args .private :
231
+ generator .write_private_key_pem (args .out )
232
+ if args .public :
233
+ generator .write_public_key_pem (args .out )
223
234
return 0
224
235
225
236
0 commit comments