|
64 | 64 | NACL = True
|
65 | 65 | NO_NACL_MSG = "ed25519 key support requires the nacl library"
|
66 | 66 | try:
|
67 |
| - from nacl.encoding import RawEncoder |
68 |
| - from nacl.signing import (SigningKey, VerifyKey) |
69 |
| - # avoid conflicts with own exceptions of same name |
70 |
| - from nacl import exceptions as nacl_exceptions |
| 67 | + from nacl.encoding import RawEncoder |
| 68 | + from nacl.signing import SigningKey, VerifyKey |
| 69 | + |
| 70 | + # avoid conflicts with own exceptions of same name |
| 71 | + from nacl import exceptions as nacl_exceptions |
71 | 72 | except ImportError:
|
72 | 73 | NACL = False
|
73 | 74 |
|
|
84 | 85 | # modules are currently supported in the creation of 'ed25519' signatures.
|
85 | 86 | # Previously, a distinction was made between signatures made by the pure Python
|
86 | 87 | # implementation and PyNaCl.
|
87 |
| -_SUPPORTED_ED25519_SIGNING_SCHEMES = ['ed25519'] |
| 88 | +_SUPPORTED_ED25519_SIGNING_SCHEMES = ["ed25519"] |
88 | 89 |
|
89 | 90 |
|
90 | 91 | def generate_public_and_private():
|
91 |
| - """ |
92 |
| - <Purpose> |
93 |
| - Generate a pair of ed25519 public and private keys with PyNaCl. The public |
94 |
| - and private keys returned conform to |
95 |
| - 'securesystemslib.formats.ED25519PUBLIC_SCHEMA' and |
96 |
| - 'securesystemslib.formats.ED25519SEED_SCHEMA', respectively. |
| 92 | + """ |
| 93 | + <Purpose> |
| 94 | + Generate a pair of ed25519 public and private keys with PyNaCl. The public |
| 95 | + and private keys returned conform to |
| 96 | + 'securesystemslib.formats.ED25519PUBLIC_SCHEMA' and |
| 97 | + 'securesystemslib.formats.ED25519SEED_SCHEMA', respectively. |
97 | 98 |
|
98 |
| - An ed25519 seed key is a random 32-byte string. Public keys are also 32 |
99 |
| - bytes. |
| 99 | + An ed25519 seed key is a random 32-byte string. Public keys are also 32 |
| 100 | + bytes. |
100 | 101 |
|
101 |
| - >>> public, private = generate_public_and_private() |
102 |
| - >>> securesystemslib.formats.ED25519PUBLIC_SCHEMA.matches(public) |
103 |
| - True |
104 |
| - >>> securesystemslib.formats.ED25519SEED_SCHEMA.matches(private) |
105 |
| - True |
| 102 | + >>> public, private = generate_public_and_private() |
| 103 | + >>> securesystemslib.formats.ED25519PUBLIC_SCHEMA.matches(public) |
| 104 | + True |
| 105 | + >>> securesystemslib.formats.ED25519SEED_SCHEMA.matches(private) |
| 106 | + True |
106 | 107 |
|
107 |
| - <Arguments> |
108 |
| - None. |
| 108 | + <Arguments> |
| 109 | + None. |
109 | 110 |
|
110 |
| - <Exceptions> |
111 |
| - securesystemslib.exceptions.UnsupportedLibraryError, if the PyNaCl ('nacl') |
112 |
| - module is unavailable. |
| 111 | + <Exceptions> |
| 112 | + securesystemslib.exceptions.UnsupportedLibraryError, if the PyNaCl ('nacl') |
| 113 | + module is unavailable. |
113 | 114 |
|
114 |
| - NotImplementedError, if a randomness source is not found by 'os.urandom'. |
| 115 | + NotImplementedError, if a randomness source is not found by 'os.urandom'. |
115 | 116 |
|
116 |
| - <Side Effects> |
117 |
| - The ed25519 keys are generated by first creating a random 32-byte seed |
118 |
| - with os.urandom() and then calling PyNaCl's nacl.signing.SigningKey(). |
| 117 | + <Side Effects> |
| 118 | + The ed25519 keys are generated by first creating a random 32-byte seed |
| 119 | + with os.urandom() and then calling PyNaCl's nacl.signing.SigningKey(). |
119 | 120 |
|
120 |
| - <Returns> |
121 |
| - A (public, private) tuple that conform to |
122 |
| - 'securesystemslib.formats.ED25519PUBLIC_SCHEMA' and |
123 |
| - 'securesystemslib.formats.ED25519SEED_SCHEMA', respectively. |
124 |
| - """ |
| 121 | + <Returns> |
| 122 | + A (public, private) tuple that conform to |
| 123 | + 'securesystemslib.formats.ED25519PUBLIC_SCHEMA' and |
| 124 | + 'securesystemslib.formats.ED25519SEED_SCHEMA', respectively. |
| 125 | + """ |
125 | 126 |
|
126 |
| - if not NACL: # pragma: no cover |
127 |
| - raise exceptions.UnsupportedLibraryError(NO_NACL_MSG) |
| 127 | + if not NACL: # pragma: no cover |
| 128 | + raise exceptions.UnsupportedLibraryError(NO_NACL_MSG) |
128 | 129 |
|
129 |
| - # Generate ed25519's seed key by calling os.urandom(). The random bytes |
130 |
| - # returned should be suitable for cryptographic use and is OS-specific. |
131 |
| - # Raise 'NotImplementedError' if a randomness source is not found. |
132 |
| - # ed25519 seed keys are fixed at 32 bytes (256-bit keys). |
133 |
| - # http://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ |
134 |
| - seed = os.urandom(32) |
135 |
| - public = None |
| 130 | + # Generate ed25519's seed key by calling os.urandom(). The random bytes |
| 131 | + # returned should be suitable for cryptographic use and is OS-specific. |
| 132 | + # Raise 'NotImplementedError' if a randomness source is not found. |
| 133 | + # ed25519 seed keys are fixed at 32 bytes (256-bit keys). |
| 134 | + # http://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ |
| 135 | + seed = os.urandom(32) |
| 136 | + public = None |
136 | 137 |
|
137 |
| - # Generate the public key. PyNaCl (i.e., 'nacl' module) performs the actual |
138 |
| - # key generation. |
139 |
| - nacl_key = SigningKey(seed) |
140 |
| - public = nacl_key.verify_key.encode(encoder=RawEncoder()) |
141 |
| - |
142 |
| - return public, seed |
| 138 | + # Generate the public key. PyNaCl (i.e., 'nacl' module) performs the actual |
| 139 | + # key generation. |
| 140 | + nacl_key = SigningKey(seed) |
| 141 | + public = nacl_key.verify_key.encode(encoder=RawEncoder()) |
143 | 142 |
|
| 143 | + return public, seed |
144 | 144 |
|
145 | 145 |
|
146 | 146 | def create_signature(public_key, private_key, data, scheme):
|
147 |
| - """ |
| 147 | + """ |
148 | 148 | <Purpose>
|
149 | 149 | Return a (signature, scheme) tuple, where the signature scheme is 'ed25519'
|
150 | 150 | and is always generated by PyNaCl (i.e., 'nacl'). The signature returned
|
@@ -203,52 +203,52 @@ def create_signature(public_key, private_key, data, scheme):
|
203 | 203 | returned.
|
204 | 204 | """
|
205 | 205 |
|
206 |
| - if not NACL: # pragma: no cover |
207 |
| - raise exceptions.UnsupportedLibraryError(NO_NACL_MSG) |
| 206 | + if not NACL: # pragma: no cover |
| 207 | + raise exceptions.UnsupportedLibraryError(NO_NACL_MSG) |
208 | 208 |
|
209 |
| - # Does 'public_key' have the correct format? |
210 |
| - # This check will ensure 'public_key' conforms to |
211 |
| - # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 |
212 |
| - # bytes. Raise 'securesystemslib.exceptions.FormatError' if the check fails. |
213 |
| - formats.ED25519PUBLIC_SCHEMA.check_match(public_key) |
| 209 | + # Does 'public_key' have the correct format? |
| 210 | + # This check will ensure 'public_key' conforms to |
| 211 | + # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 |
| 212 | + # bytes. Raise 'securesystemslib.exceptions.FormatError' if the check fails. |
| 213 | + formats.ED25519PUBLIC_SCHEMA.check_match(public_key) |
214 | 214 |
|
215 |
| - # Is 'private_key' properly formatted? |
216 |
| - formats.ED25519SEED_SCHEMA.check_match(private_key) |
| 215 | + # Is 'private_key' properly formatted? |
| 216 | + formats.ED25519SEED_SCHEMA.check_match(private_key) |
217 | 217 |
|
218 |
| - # Is 'scheme' properly formatted? |
219 |
| - formats.ED25519_SIG_SCHEMA.check_match(scheme) |
| 218 | + # Is 'scheme' properly formatted? |
| 219 | + formats.ED25519_SIG_SCHEMA.check_match(scheme) |
220 | 220 |
|
221 |
| - # Signing the 'data' object requires a seed and public key. |
222 |
| - # nacl.signing.SigningKey.sign() generates the signature. |
223 |
| - signature = None |
| 221 | + # Signing the 'data' object requires a seed and public key. |
| 222 | + # nacl.signing.SigningKey.sign() generates the signature. |
| 223 | + signature = None |
224 | 224 |
|
225 |
| - # An if-clause is not strictly needed here, since 'ed25519' is the only |
226 |
| - # currently supported scheme. Nevertheless, include the conditional |
227 |
| - # statement to accommodate schemes that might be added in the future. |
228 |
| - if scheme == 'ed25519': |
229 |
| - try: |
230 |
| - nacl_key = SigningKey(private_key) |
231 |
| - nacl_sig = nacl_key.sign(data) |
232 |
| - signature = nacl_sig.signature |
233 |
| - |
234 |
| - except (ValueError, TypeError, nacl_exceptions.CryptoError) as e: |
235 |
| - raise exceptions.CryptoError('An "ed25519" signature' |
236 |
| - ' could not be created with PyNaCl.' + str(e)) |
237 |
| - |
238 |
| - # This is a defensive check for a valid 'scheme', which should have already |
239 |
| - # been validated in the check_match() above. |
240 |
| - else: #pragma: no cover |
241 |
| - raise exceptions.UnsupportedAlgorithmError('Unsupported' |
242 |
| - ' signature scheme is specified: ' + repr(scheme)) |
243 |
| - |
244 |
| - return signature, scheme |
| 225 | + # An if-clause is not strictly needed here, since 'ed25519' is the only |
| 226 | + # currently supported scheme. Nevertheless, include the conditional |
| 227 | + # statement to accommodate schemes that might be added in the future. |
| 228 | + if scheme == "ed25519": |
| 229 | + try: |
| 230 | + nacl_key = SigningKey(private_key) |
| 231 | + nacl_sig = nacl_key.sign(data) |
| 232 | + signature = nacl_sig.signature |
245 | 233 |
|
| 234 | + except (ValueError, TypeError, nacl_exceptions.CryptoError) as e: |
| 235 | + raise exceptions.CryptoError( |
| 236 | + 'An "ed25519" signature' |
| 237 | + " could not be created with PyNaCl." + str(e) |
| 238 | + ) |
246 | 239 |
|
| 240 | + # This is a defensive check for a valid 'scheme', which should have already |
| 241 | + # been validated in the check_match() above. |
| 242 | + else: # pragma: no cover |
| 243 | + raise exceptions.UnsupportedAlgorithmError( |
| 244 | + "Unsupported" " signature scheme is specified: " + repr(scheme) |
| 245 | + ) |
247 | 246 |
|
| 247 | + return signature, scheme |
248 | 248 |
|
249 | 249 |
|
250 | 250 | def verify_signature(public_key, scheme, signature, data):
|
251 |
| - """ |
| 251 | + """ |
252 | 252 | <Purpose>
|
253 | 253 | Determine whether the private key corresponding to 'public_key' produced
|
254 | 254 | 'signature'. verify_signature() will use the public key, the 'scheme' and
|
@@ -299,58 +299,64 @@ def verify_signature(public_key, scheme, signature, data):
|
299 | 299 | Boolean. True if the signature is valid, False otherwise.
|
300 | 300 | """
|
301 | 301 |
|
302 |
| - # Does 'public_key' have the correct format? |
303 |
| - # This check will ensure 'public_key' conforms to |
304 |
| - # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 |
305 |
| - # bytes. Raise 'securesystemslib.exceptions.FormatError' if the check fails. |
306 |
| - formats.ED25519PUBLIC_SCHEMA.check_match(public_key) |
307 |
| - |
308 |
| - # Is 'scheme' properly formatted? |
309 |
| - formats.ED25519_SIG_SCHEMA.check_match(scheme) |
310 |
| - |
311 |
| - # Is 'signature' properly formatted? |
312 |
| - formats.ED25519SIGNATURE_SCHEMA.check_match(signature) |
313 |
| - |
314 |
| - # Verify 'signature'. Before returning the Boolean result, ensure 'ed25519' |
315 |
| - # was used as the signature scheme. |
316 |
| - public = public_key |
317 |
| - valid_signature = False |
318 |
| - |
319 |
| - if scheme in _SUPPORTED_ED25519_SIGNING_SCHEMES: |
320 |
| - if NACL: |
321 |
| - try: |
322 |
| - nacl_verify_key = VerifyKey(public) |
323 |
| - nacl_verify_key.verify(data, signature) |
324 |
| - valid_signature = True |
325 |
| - |
326 |
| - except nacl_exceptions.BadSignatureError: |
327 |
| - pass |
328 |
| - |
329 |
| - # Verify 'ed25519' signature with the pure Python implementation. |
330 |
| - else: |
331 |
| - try: |
332 |
| - python_ed25519.checkvalid(signature, data, public) |
333 |
| - valid_signature = True |
334 |
| - |
335 |
| - # The pure Python implementation raises 'Exception' if 'signature' is |
336 |
| - # invalid. |
337 |
| - except Exception: |
338 |
| - pass |
339 |
| - |
340 |
| - # This is a defensive check for a valid 'scheme', which should have already |
341 |
| - # been validated in the ED25519_SIG_SCHEMA.check_match(scheme) above. |
342 |
| - else: #pragma: no cover |
343 |
| - message = 'Unsupported ed25519 signature scheme: ' + repr(scheme) + '.\n' + \ |
344 |
| - 'Supported schemes: ' + repr(_SUPPORTED_ED25519_SIGNING_SCHEMES) + '.' |
345 |
| - raise exceptions.UnsupportedAlgorithmError(message) |
346 |
| - |
347 |
| - return valid_signature |
348 |
| - |
349 |
| - |
350 |
| - |
351 |
| -if __name__ == '__main__': |
352 |
| - # The interactive sessions of the documentation strings can |
353 |
| - # be tested by running 'ed25519_keys.py' as a standalone module. |
354 |
| - # python -B ed25519_keys.py |
355 |
| - import doctest |
356 |
| - doctest.testmod() |
| 302 | + # Does 'public_key' have the correct format? |
| 303 | + # This check will ensure 'public_key' conforms to |
| 304 | + # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 |
| 305 | + # bytes. Raise 'securesystemslib.exceptions.FormatError' if the check fails. |
| 306 | + formats.ED25519PUBLIC_SCHEMA.check_match(public_key) |
| 307 | + |
| 308 | + # Is 'scheme' properly formatted? |
| 309 | + formats.ED25519_SIG_SCHEMA.check_match(scheme) |
| 310 | + |
| 311 | + # Is 'signature' properly formatted? |
| 312 | + formats.ED25519SIGNATURE_SCHEMA.check_match(signature) |
| 313 | + |
| 314 | + # Verify 'signature'. Before returning the Boolean result, ensure 'ed25519' |
| 315 | + # was used as the signature scheme. |
| 316 | + public = public_key |
| 317 | + valid_signature = False |
| 318 | + |
| 319 | + if scheme in _SUPPORTED_ED25519_SIGNING_SCHEMES: |
| 320 | + if NACL: |
| 321 | + try: |
| 322 | + nacl_verify_key = VerifyKey(public) |
| 323 | + nacl_verify_key.verify(data, signature) |
| 324 | + valid_signature = True |
| 325 | + |
| 326 | + except nacl_exceptions.BadSignatureError: |
| 327 | + pass |
| 328 | + |
| 329 | + # Verify 'ed25519' signature with the pure Python implementation. |
| 330 | + else: |
| 331 | + try: |
| 332 | + python_ed25519.checkvalid(signature, data, public) |
| 333 | + valid_signature = True |
| 334 | + |
| 335 | + # The pure Python implementation raises 'Exception' if 'signature' is |
| 336 | + # invalid. |
| 337 | + except Exception: |
| 338 | + pass |
| 339 | + |
| 340 | + # This is a defensive check for a valid 'scheme', which should have already |
| 341 | + # been validated in the ED25519_SIG_SCHEMA.check_match(scheme) above. |
| 342 | + else: # pragma: no cover |
| 343 | + message = ( |
| 344 | + "Unsupported ed25519 signature scheme: " |
| 345 | + + repr(scheme) |
| 346 | + + ".\n" |
| 347 | + + "Supported schemes: " |
| 348 | + + repr(_SUPPORTED_ED25519_SIGNING_SCHEMES) |
| 349 | + + "." |
| 350 | + ) |
| 351 | + raise exceptions.UnsupportedAlgorithmError(message) |
| 352 | + |
| 353 | + return valid_signature |
| 354 | + |
| 355 | + |
| 356 | +if __name__ == "__main__": |
| 357 | + # The interactive sessions of the documentation strings can |
| 358 | + # be tested by running 'ed25519_keys.py' as a standalone module. |
| 359 | + # python -B ed25519_keys.py |
| 360 | + import doctest |
| 361 | + |
| 362 | + doctest.testmod() |
0 commit comments