-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathm45.py
executable file
·128 lines (94 loc) · 3.97 KB
/
m45.py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python3
"""DSA parameter tampering"""
from Crypto.Random.random import randint
import m28
import m39
import m43
import m44
def sign_relaxed(m: bytes, x: int, p: int, q: int, g: int) -> m43.DSASignature:
"""Sign message without checking constraints on r, s"""
# The original implementation checks this and falls into
# an infinite loop.
h_m = m39.to_int(m28.SHA1(m).digest())
k = randint(1, q - 1)
k_inv = m39.invmod(k, q)
r = pow(g, k, p) % q
s = (k_inv * (h_m + x * r)) % q
return m43.DSASignature(r, s)
def verify_relaxed(m: bytes, signature: m43.DSASignature, y: int,
p: int, q: int, g: int) -> bool:
"""Verify DSA signature without checking constraints on r, s"""
r, s = signature
w = m39.invmod(s, q)
h_m = m39.to_int(m28.SHA1(m).digest())
u_1 = h_m * w % q
u_2 = r * w % q
v = pow(g, u_1, p) * pow(y, u_2, p) % p % q
return v == r
def magic_signature_generator(y: int, p: int, q: int) -> m43.DSASignature:
"""Generate a magic signature, assuming verification with g = 1 mod p"""
# See https://infoscience.epfl.ch/record/99390/files/Vau96c.ps §4.
#
# The trick is to realise at what point we assume g = 1 mod p. Here, it's
# after key generation but before validation, i,e. the value of g changes
# mid-way. This means we have to work with some given public key y != 1.
#
# The last validation calculation is v = g^u_1 y^u_2 mod p mod q. At this
# point, g = 1 mod p so this reduces to
#
# v = g^u_1 y^u_2 mod p mod q
# = y^u_2 mod p mod q
# = y^(rw) mod p mod q
# = y^(r / s) mod p mod q.
#
# Since validation requires r = v, we solve
#
# r = y^(r / s) mod p mod q.
#
# Note that r necessarily depends on y. r = y^z mod p mod q and
# s = r / z mod q is a solution, for arbitrary z != 0 mod q.
z = randint(1, q - 1)
z_inv = m39.invmod(z, q)
r = pow(y, z, p) % q
s = z_inv * r % q
return m43.DSASignature(r, s)
def main() -> None:
parameters = m44.get_parameters()
p, q, _ = parameters.values()
m_1 = b"Hello, world"
m_2 = b"Goodbye, world"
# Using bad generator g = 0 mod p, we get y = 0. This sets r = 0
# for all signatures. Verifiers that don't check r > 0 will
# return false positives for all message and signature pairs.
g = 0
keypair = m43.keygen(p, q, g)
signature_1 = sign_relaxed(m_1, keypair.x, p, q, g)
signature_2 = sign_relaxed(m_2, keypair.x, p, q, g)
assert verify_relaxed(m_1, signature_1, keypair.y, p, q, g)
assert verify_relaxed(m_2, signature_2, keypair.y, p, q, g)
assert verify_relaxed(m_1, signature_2, keypair.y, p, q, g)
assert verify_relaxed(m_2, signature_1, keypair.y, p, q, g)
# Using bad generator g = 1 mod p, we get y = 1. This sets r = 1
# for all signatures. The last validation step is calculating
# v = g^u_1 g^u_2 mod p mod q, but if g mod p = 1 then v = 1.
# Thus v = r and the signature passes validation.
g = p + 1
keypair = m43.keygen(p, q, g)
signature_1 = m43.sign(m_1, keypair.x, p, q, g)
signature_2 = m43.sign(m_2, keypair.x, p, q, g)
assert m43.verify(m_1, signature_1, keypair.y, p, q, g)
assert m43.verify(m_2, signature_2, keypair.y, p, q, g)
assert m43.verify(m_1, signature_2, keypair.y, p, q, g)
assert m43.verify(m_2, signature_1, keypair.y, p, q, g)
assert m43.verify(b"Ice ice baby", signature_1, keypair.y, p, q, g)
# Previous signatures were generated for public key y = 1.
# Given an arbitrary public key y, we can generate validating
# signatures for any message assuming validation occurs with
# g = 1 mod p.
keypair = m43.keygen(**parameters)
magic_signature = magic_signature_generator(keypair.y, p, q)
assert m43.verify(m_1, magic_signature, keypair.y, p, q, g)
assert m43.verify(m_2, magic_signature, keypair.y, p, q, g)
print(magic_signature)
if __name__ == "__main__":
main()