source: trunk/src/allmydata/crypto/ed25519.py

Last change on this file was 6cf3bc7, checked in by Itamar Turner-Trauring <itamar@…>, at 2022-07-18T15:09:19Z

Some Python 3 cleanups.

  • Property mode set to 100644
File size: 5.0 KB
Line 
1'''
2Ed25519 keys and helpers.
3
4Key Formatting
5--------------
6
7- in base32, keys are 52 chars long (both signing and verifying keys)
8- in base62, keys is 43 chars long
9- in base64, keys is 43 chars long
10
11We can't use base64 because we want to reserve punctuation and preserve
12cut-and-pasteability. The base62 encoding is shorter than the base32 form,
13but the minor usability improvement is not worth the documentation and
14specification confusion of using a non-standard encoding. So we stick with
15base32.
16'''
17
18from cryptography.exceptions import (
19    InvalidSignature,
20)
21from cryptography.hazmat.primitives.asymmetric.ed25519 import (
22    Ed25519PrivateKey,
23    Ed25519PublicKey,
24)
25from cryptography.hazmat.primitives.serialization import (
26    Encoding,
27    PrivateFormat,
28    NoEncryption,
29    PublicFormat,
30)
31
32from allmydata.crypto.util import remove_prefix
33from allmydata.crypto.error import BadSignature
34
35from allmydata.util.base32 import (
36    a2b,
37    b2a,
38)
39
40PRIVATE_KEY_PREFIX = b'priv-v0-'
41PUBLIC_KEY_PREFIX = b'pub-v0-'
42
43
44def create_signing_keypair():
45    """
46    Creates a new ed25519 keypair.
47
48    :returns: 2-tuple of (private_key, public_key)
49    """
50    private_key = Ed25519PrivateKey.generate()
51    return private_key, private_key.public_key()
52
53
54def verifying_key_from_signing_key(private_key):
55    """
56    :returns: the public key associated to the given `private_key`
57    """
58    _validate_private_key(private_key)
59    return private_key.public_key()
60
61
62def sign_data(private_key, data: bytes) -> bytes:
63    """
64    Sign the given data using the given private key
65
66    :param private_key: the private part returned from
67        `create_signing_keypair` or from
68        `signing_keypair_from_string`
69
70    :param bytes data: the data to sign
71
72    :returns: bytes representing the signature
73    """
74
75    _validate_private_key(private_key)
76    if not isinstance(data, bytes):
77        raise ValueError('data must be bytes')
78    return private_key.sign(data)
79
80
81def string_from_signing_key(private_key):
82    """
83    Encode a private key to a string of bytes
84
85    :param private_key: the private part returned from
86        `create_signing_keypair` or from
87        `signing_keypair_from_string`
88
89    :returns: byte-string representing this key
90    """
91    _validate_private_key(private_key)
92    raw_key_bytes = private_key.private_bytes(
93        Encoding.Raw,
94        PrivateFormat.Raw,
95        NoEncryption(),
96    )
97    return PRIVATE_KEY_PREFIX + b2a(raw_key_bytes)
98
99
100def signing_keypair_from_string(private_key_bytes: bytes):
101    """
102    Load a signing keypair from a string of bytes (which includes the
103    PRIVATE_KEY_PREFIX)
104
105    :returns: a 2-tuple of (private_key, public_key)
106    """
107
108    if not isinstance(private_key_bytes, bytes):
109        raise ValueError('private_key_bytes must be bytes')
110
111    private_key = Ed25519PrivateKey.from_private_bytes(
112        a2b(remove_prefix(private_key_bytes, PRIVATE_KEY_PREFIX))
113    )
114    return private_key, private_key.public_key()
115
116
117def verify_signature(public_key, alleged_signature: bytes, data: bytes):
118    """
119    :param public_key: a verifying key
120
121    :param bytes alleged_signature: the bytes of the alleged signature
122
123    :param bytes data: the data which was allegedly signed
124
125    :raises: BadSignature if the signature is bad
126    :returns: None (or raises an exception).
127    """
128
129    if not isinstance(alleged_signature, bytes):
130        raise ValueError('alleged_signature must be bytes')
131
132    if not isinstance(data, bytes):
133        raise ValueError('data must be bytes')
134
135    _validate_public_key(public_key)
136    try:
137        public_key.verify(alleged_signature, data)
138    except InvalidSignature:
139        raise BadSignature()
140
141
142def verifying_key_from_string(public_key_bytes):
143    """
144    Load a verifying key from a string of bytes (which includes the
145    PUBLIC_KEY_PREFIX)
146
147    :returns: a public_key
148    """
149    if not isinstance(public_key_bytes, bytes):
150        raise ValueError('public_key_bytes must be bytes')
151
152    return Ed25519PublicKey.from_public_bytes(
153        a2b(remove_prefix(public_key_bytes, PUBLIC_KEY_PREFIX))
154    )
155
156
157def string_from_verifying_key(public_key) -> bytes:
158    """
159    Encode a public key to a string of bytes
160
161    :param public_key: the public part of a keypair
162
163    :returns: byte-string representing this key
164    """
165    _validate_public_key(public_key)
166    raw_key_bytes = public_key.public_bytes(
167        Encoding.Raw,
168        PublicFormat.Raw,
169    )
170    return PUBLIC_KEY_PREFIX + b2a(raw_key_bytes)
171
172
173def _validate_public_key(public_key: Ed25519PublicKey):
174    """
175    Internal helper. Verify that `public_key` is an appropriate object
176    """
177    if not isinstance(public_key, Ed25519PublicKey):
178        raise ValueError('public_key must be an Ed25519PublicKey')
179    return None
180
181
182def _validate_private_key(private_key: Ed25519PrivateKey):
183    """
184    Internal helper. Verify that `private_key` is an appropriate object
185    """
186    if not isinstance(private_key, Ed25519PrivateKey):
187        raise ValueError('private_key must be an Ed25519PrivateKey')
188    return None
Note: See TracBrowser for help on using the repository browser.