|
1 | 1 | """Implementation of Edwards Digital Signature Algorithm."""
|
2 | 2 |
|
| 3 | +import hashlib |
| 4 | +from ._sha3 import shake_256 |
3 | 5 | from . import ellipticcurve
|
4 |
| -from ._compat import remove_whitespace |
| 6 | +from ._compat import ( |
| 7 | + remove_whitespace, |
| 8 | + bit_length, |
| 9 | + bytes_to_int, |
| 10 | + int_to_bytes, |
| 11 | + compat26_str, |
| 12 | +) |
5 | 13 |
|
6 | 14 | # edwards25519, defined in RFC7748
|
7 | 15 | _p = 2 ** 255 - 19
|
|
28 | 36 | )
|
29 | 37 | _r = 2 ** 252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED
|
30 | 38 |
|
31 |
| -curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h) |
| 39 | + |
| 40 | +def _sha512(data): |
| 41 | + return hashlib.new("sha512", compat26_str(data)).digest() |
| 42 | + |
| 43 | + |
| 44 | +curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512) |
32 | 45 | generator_ed25519 = ellipticcurve.PointEdwards(
|
33 | 46 | curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r
|
34 | 47 | )
|
|
56 | 69 | )
|
57 | 70 | _r = 2 ** 446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D
|
58 | 71 |
|
59 |
| -curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h) |
| 72 | + |
| 73 | +def _shake256(data): |
| 74 | + return shake_256(data, 114) |
| 75 | + |
| 76 | + |
| 77 | +curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256) |
60 | 78 | generator_ed448 = ellipticcurve.PointEdwards(
|
61 | 79 | curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r
|
62 | 80 | )
|
| 81 | + |
| 82 | + |
| 83 | +class PublicKey(object): |
| 84 | + """Public key for the Edwards Digital Signature Algorithm.""" |
| 85 | + |
| 86 | + def __init__(self, generator, public_key, public_point=None): |
| 87 | + self.generator = generator |
| 88 | + self.curve = generator.curve() |
| 89 | + self.__encoded = public_key |
| 90 | + # plus one for the sign bit and round up |
| 91 | + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 |
| 92 | + if len(public_key) != self.baselen: |
| 93 | + raise ValueError( |
| 94 | + "Incorrect size of the public key, expected: {0} bytes".format( |
| 95 | + self.baselen |
| 96 | + ) |
| 97 | + ) |
| 98 | + if public_point: |
| 99 | + self.__point = public_point |
| 100 | + else: |
| 101 | + self.__point = ellipticcurve.PointEdwards.from_bytes( |
| 102 | + self.curve, public_key |
| 103 | + ) |
| 104 | + |
| 105 | + def public_point(self): |
| 106 | + return self.__point |
| 107 | + |
| 108 | + def public_key(self): |
| 109 | + return self.__encoded |
| 110 | + |
| 111 | + def verify(self, data, signature): |
| 112 | + """Verify a Pure EdDSA signature over data.""" |
| 113 | + if len(signature) != 2 * self.baselen: |
| 114 | + raise ValueError( |
| 115 | + "Invalid signature length, expected: {0} bytes".format( |
| 116 | + 2 * self.baselen |
| 117 | + ) |
| 118 | + ) |
| 119 | + R = ellipticcurve.PointEdwards.from_bytes( |
| 120 | + self.curve, signature[: self.baselen] |
| 121 | + ) |
| 122 | + S = bytes_to_int(signature[self.baselen :], "little") |
| 123 | + if S >= self.generator.order(): |
| 124 | + raise ValueError("Invalid signature") |
| 125 | + |
| 126 | + dom = bytearray() |
| 127 | + if self.curve == curve_ed448: |
| 128 | + dom = bytearray(b"SigEd448" + b"\x00\x00") |
| 129 | + |
| 130 | + k = bytes_to_int( |
| 131 | + self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), |
| 132 | + "little", |
| 133 | + ) |
| 134 | + |
| 135 | + if self.generator * S != self.__point * k + R: |
| 136 | + raise ValueError("Invalid signature") |
| 137 | + |
| 138 | + return True |
| 139 | + |
| 140 | + |
| 141 | +class PrivateKey(object): |
| 142 | + """Private key for the Edwards Digital Signature Algorithm.""" |
| 143 | + |
| 144 | + def __init__(self, generator, private_key): |
| 145 | + self.generator = generator |
| 146 | + self.curve = generator.curve() |
| 147 | + # plus one for the sign bit and round up |
| 148 | + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 |
| 149 | + if len(private_key) != self.baselen: |
| 150 | + raise ValueError( |
| 151 | + "Incorrect size of private key, expected: {0} bytes".format( |
| 152 | + self.baselen |
| 153 | + ) |
| 154 | + ) |
| 155 | + self.__private_key = private_key |
| 156 | + self.__h = bytearray(self.curve.hash_func(private_key)) |
| 157 | + self.__public_key = None |
| 158 | + |
| 159 | + a = self.__h[: self.baselen] |
| 160 | + a = self._key_prune(a) |
| 161 | + scalar = bytes_to_int(a, "little") |
| 162 | + self.__s = scalar |
| 163 | + |
| 164 | + def _key_prune(self, key): |
| 165 | + # make sure the key is not in a small subgroup |
| 166 | + h = self.curve.cofactor() |
| 167 | + if h == 4: |
| 168 | + h_log = 2 |
| 169 | + elif h == 8: |
| 170 | + h_log = 3 |
| 171 | + else: |
| 172 | + raise ValueError("Only cofactor 4 and 8 curves supported") |
| 173 | + key[0] &= ~((1 << h_log) - 1) |
| 174 | + |
| 175 | + # ensure the highest bit is set but no higher |
| 176 | + l = bit_length(self.curve.p()) |
| 177 | + if l % 8 == 0: |
| 178 | + key[-1] = 0 |
| 179 | + key[-2] |= 0x80 |
| 180 | + else: |
| 181 | + key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1 |
| 182 | + return key |
| 183 | + |
| 184 | + def public_key(self): |
| 185 | + """Generate the public key based on the included private key""" |
| 186 | + if self.__public_key: |
| 187 | + return self.__public_key |
| 188 | + |
| 189 | + public_point = self.generator * self.__s |
| 190 | + |
| 191 | + self.__public_key = PublicKey( |
| 192 | + self.generator, public_point.to_bytes(), public_point |
| 193 | + ) |
| 194 | + |
| 195 | + return self.__public_key |
| 196 | + |
| 197 | + def sign(self, data): |
| 198 | + """Perform a Pure EdDSA signature over data.""" |
| 199 | + A = self.public_key().public_key() |
| 200 | + |
| 201 | + prefix = self.__h[self.baselen :] |
| 202 | + |
| 203 | + dom = bytearray() |
| 204 | + if self.curve == curve_ed448: |
| 205 | + dom = bytearray(b"SigEd448" + b"\x00\x00") |
| 206 | + |
| 207 | + r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") |
| 208 | + R = (self.generator * r).to_bytes() |
| 209 | + |
| 210 | + k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") |
| 211 | + k %= self.generator.order() |
| 212 | + |
| 213 | + S = (r + k * self.__s) % self.generator.order() |
| 214 | + |
| 215 | + return R + int_to_bytes(S, self.baselen, "little") |
0 commit comments