-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Python code for DAC using mercurial sigs
- Loading branch information
Showing
5 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from mercurial_signature_scheme import MercurialSignatureScheme, MercurialSignatureDual | ||
|
||
|
||
class DelegatableAnonCredScheme: | ||
def __init__(self, ell): | ||
self.MSS1 = MercurialSignatureScheme() | ||
self.MSS2 = MercurialSignatureDual() | ||
self.ell = ell | ||
self.pk0, self.sk0 = self.MSS2.KeyGen(self.ell) | ||
self.nym0 = (self.pk0, None) | ||
|
||
def KeyGen(self): | ||
pk_even, sk_even = self.MSS2.KeyGen(self.ell) | ||
pk_odd, sk_odd = self.MSS1.KeyGen(self.ell) | ||
return (pk_even, sk_even), (pk_odd, sk_odd) | ||
|
||
def NymGen(self, pk_even, sk_even, pk_odd, sk_odd): | ||
rho_even = self.MSS2.RandomZp() | ||
sk_even = self.MSS2.ConvertSK(sk_even, rho_even) | ||
nym_even = self.MSS2.ConvertPK(pk_even, rho_even) | ||
rho_odd = self.MSS1.RandomZp() | ||
sk_odd = self.MSS1.ConvertSK(sk_odd, rho_odd) | ||
nym_odd = self.MSS1.ConvertPK(pk_odd, rho_odd) | ||
return (nym_even, sk_even), (nym_odd, sk_odd) | ||
|
||
def IssueFirst(self, nym1): | ||
sig1 = self.MSS2.Sign(self.sk0, nym1) | ||
return [nym1], [sig1] | ||
|
||
def IssueNext(self, cred_chain, new_nym, sk): | ||
nym_list, sig_list = cred_chain | ||
assert len(nym_list) == len(sig_list) | ||
rho = self.MSS2.RandomZp() | ||
nym_list[0], sig_list[0] = self.MSS2.ChangeRep( | ||
self.pk0, nym_list[0], sig_list[0], rho | ||
) | ||
assert self.MSS2.Verify(self.pk0, nym_list[0], sig_list[0]) | ||
for i in range(len(nym_list) - 1): | ||
# Note: MSS1 & MSS2 share the same functions RandomZp, ChangeRep, & ConvertSig | ||
MSS = self.MSS1 if i % 2 == 0 else self.MSS2 | ||
sig_tilde = MSS.ConvertSig( | ||
nym_list[i], nym_list[i + 1], sig_list[i + 1], rho | ||
) | ||
rho = MSS.RandomZp() | ||
nym_list[i + 1], sig_list[i + 1] = MSS.ChangeRep( | ||
nym_list[i], nym_list[i + 1], sig_tilde, rho | ||
) | ||
assert MSS.Verify(nym_list[i], nym_list[i + 1], sig_list[i + 1]) | ||
nym_list.append(new_nym) | ||
MSS = self.MSS1 if len(nym_list) % 2 == 0 else self.MSS2 | ||
sk = MSS.ConvertSK(sk, rho) | ||
sig_list.append(MSS.Sign(sk, new_nym)) | ||
assert MSS.Verify(nym_list[-2], nym_list[-1], sig_list[-1]) | ||
return nym_list, sig_list | ||
|
||
def VerifyChain(self, cred_chain): | ||
nym_list, sig_list = cred_chain | ||
assert len(nym_list) == len(sig_list) | ||
if not self.MSS2.Verify(self.pk0, nym_list[0], sig_list[0]): | ||
return False | ||
for i in range(len(nym_list) - 1): | ||
MSS = self.MSS1 if i % 2 == 0 else self.MSS2 | ||
if not MSS.Verify(nym_list[i], nym_list[i + 1], sig_list[i + 1]): | ||
return False | ||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import functools | ||
import hashlib | ||
|
||
from miracl_core_py_bn254 import big | ||
from miracl_core_py_bn254 import curve | ||
from miracl_core_py_bn254 import ecp | ||
from miracl_core_py_bn254 import ecp2 | ||
from miracl_core_py_bn254 import fp12 | ||
from miracl_core_py_bn254 import pair | ||
|
||
|
||
class MercurialSignatureScheme: | ||
def __init__(self): | ||
self.G1 = ecp.ECp() | ||
self.G2 = ecp2.ECp2() | ||
self.GT = fp12.Fp12() | ||
self.P = ecp.generator().copy() | ||
self.Phat = ecp2.generator().copy() | ||
self.e = pair.e | ||
self.curve = curve | ||
|
||
def KeyGen(self, ell): | ||
sk = [] | ||
pk = [] | ||
for _ in range(ell): | ||
x = self.RandomZp() | ||
w = x * self.Phat | ||
sk.append(x) | ||
pk.append(w) | ||
return pk, sk | ||
|
||
def Sign(self, sk, M): | ||
y = self.RandomZp() | ||
Z = y * functools.reduce( | ||
lambda a, b: a.add(b), [xi * Mi for xi, Mi in zip(sk, M)] | ||
) | ||
Y = big.invmodp(y, self.curve.r) * self.P | ||
Yhat = big.invmodp(y, self.curve.r) * self.Phat | ||
return Z, Y, Yhat | ||
|
||
def Verify(self, pk, M, sigma): | ||
Z, Y, Yhat = sigma | ||
q1 = functools.reduce( | ||
lambda a, b: a * b, [self.e(Xi, Mi) for Xi, Mi in zip(pk, M)] | ||
) | ||
return q1 == self.e(Yhat, Z) and self.e(self.Phat, Y) == self.e(Yhat, self.P) | ||
|
||
def ConvertSK(self, sk, rho): | ||
return [rho * xi for xi in sk] | ||
|
||
def ConvertPK(self, pk, rho): | ||
return [rho * Xi for Xi in pk] | ||
|
||
def ConvertSig(self, pk, M, sigma, rho): | ||
Z, Y, Yhat = sigma | ||
psi = self.RandomZp() | ||
return ( | ||
psi * rho * Z, | ||
big.invmodp(psi, self.curve.r) * Y, | ||
big.invmodp(psi, self.curve.r) * Yhat, | ||
) | ||
|
||
def ChangeRep(self, pk, M, sigma, mu): | ||
Z, Y, Yhat = sigma | ||
psi = self.RandomZp() | ||
M0 = [mu * m for m in M] | ||
sigma0 = ( | ||
psi * mu * Z, | ||
big.invmodp(psi, self.curve.r) * Y, | ||
big.invmodp(psi, self.curve.r) * Yhat, | ||
) | ||
return M0, sigma0 | ||
|
||
@staticmethod | ||
def HashMessage(m): | ||
h = hashlib.shake_256() | ||
h.update(bytes(m, "utf-8")) | ||
hm = big.from_bytes(h.digest(curve.EFS)) | ||
HM = ecp.ECp() | ||
while not HM.set(hm): | ||
hm = hm + 1 | ||
HM = curve.CurveCof * HM | ||
return HM | ||
|
||
@staticmethod | ||
def RandomZp(): | ||
return big.rand(curve.r) | ||
|
||
|
||
class MercurialSignatureDual(MercurialSignatureScheme): | ||
def KeyGen(self, ell): | ||
sk = [] | ||
pk = [] | ||
for _ in range(ell): | ||
x = self.RandomZp() | ||
w = x * self.P | ||
sk.append(x) | ||
pk.append(w) | ||
return pk, sk | ||
|
||
def Sign(self, sk, M): | ||
y = self.RandomZp() | ||
Z = y * functools.reduce( | ||
lambda a, b: a.add(b), [Xi * Mi for Xi, Mi in zip(sk, M)] | ||
) | ||
Y = big.invmodp(y, self.curve.r) * self.Phat | ||
Yhat = big.invmodp(y, self.curve.r) * self.P | ||
return Z, Y, Yhat | ||
|
||
def Verify(self, pk, M, sigma): | ||
Z, Y, Yhat = sigma | ||
q1 = functools.reduce( | ||
lambda a, b: a * b, [self.e(Mi, Xi) for Xi, Mi in zip(pk, M)] | ||
) | ||
return q1 == self.e(Z, Yhat) and self.e(Y, self.P) == self.e(self.Phat, Yhat) | ||
|
||
@staticmethod | ||
def HashMessage(m): | ||
# not a real hash but sufficient for testing purposes | ||
return big.rand(curve.r) * ecp2.generator().copy() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[tool.black] | ||
line-length = 88 | ||
py36 = false | ||
exclude = ''' | ||
/( | ||
\.git | ||
| \.idea | ||
| miracl_core_py_bn254 | ||
| miracl_core_cpp_bn254 | ||
| miracl_core_c_bn254 | ||
# The following are specific to Black, you probably don't want those. | ||
| blib2to3 | ||
| tests/data | ||
)/ | ||
''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import unittest | ||
|
||
from delegatable_anon_cred_scheme import DelegatableAnonCredScheme | ||
|
||
|
||
class TestMercurialSignatureScheme(unittest.TestCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
cls.DAC1 = DelegatableAnonCredScheme(2) | ||
cls.DAC2 = DelegatableAnonCredScheme(7) | ||
cls.DAC3 = DelegatableAnonCredScheme(9) | ||
|
||
def test_chain(self): | ||
for DAC in [self.DAC1, self.DAC2, self.DAC3]: | ||
|
||
# user 1 generates keys, nyms, & gets on the credential chain | ||
even_keys1, odd_keys1 = DAC.KeyGen() | ||
(nym_even1, sk_even1), (nym_odd1, sk_odd1) = DAC.NymGen( | ||
*even_keys1, *odd_keys1 | ||
) | ||
cred_chain = DAC.IssueFirst(nym_odd1) | ||
self.assertTrue(DAC.VerifyChain(cred_chain), "user 1 checks out") | ||
|
||
# user 2 generates keys, nyms, & gets on the credential chain | ||
even_keys2, odd_keys2 = DAC.KeyGen() | ||
(nym_even2, sk_even2), (nym_odd2, sk_odd2) = DAC.NymGen( | ||
*even_keys2, *odd_keys2 | ||
) | ||
cred_chain = DAC.IssueNext(cred_chain, nym_even2, sk_odd1) | ||
self.assertTrue(DAC.VerifyChain(cred_chain), "user 2 is a-ok") | ||
|
||
# user 3 generates keys, nyms, & gets on the credential chain | ||
even_keys3, odd_keys3 = DAC.KeyGen() | ||
(nym_even3, sk_even3), (nym_odd3, sk_odd3) = DAC.NymGen( | ||
*even_keys3, *odd_keys3 | ||
) | ||
cred_chain = DAC.IssueNext(cred_chain, nym_odd3, sk_even2) | ||
self.assertTrue(DAC.VerifyChain(cred_chain), "go for user 3") | ||
|
||
# user 4 generates keys, nyms, & gets on the credential chain | ||
even_keys4, odd_keys4 = DAC.KeyGen() | ||
(nym_even4, sk_even4), (nym_odd4, sk_odd4) = DAC.NymGen( | ||
*even_keys4, *odd_keys4 | ||
) | ||
cred_chain = DAC.IssueNext(cred_chain, nym_even4, sk_odd3) | ||
self.assertTrue(DAC.VerifyChain(cred_chain), "go for user 4") | ||
|
||
# user 5 generates keys, nyms, & gets on the credential chain | ||
even_keys5, odd_keys5 = DAC.KeyGen() | ||
(nym_even5, sk_even5), (nym_odd5, sk_odd5) = DAC.NymGen( | ||
*even_keys5, *odd_keys5 | ||
) | ||
cred_chain = DAC.IssueNext(cred_chain, nym_odd5, sk_even4) | ||
self.assertTrue(DAC.VerifyChain(cred_chain), "go for user 5") | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import unittest | ||
|
||
from mercurial_signature_scheme import MercurialSignatureScheme, MercurialSignatureDual | ||
|
||
|
||
class TestMercurialSignatureScheme(unittest.TestCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
cls.MSS1 = MercurialSignatureScheme() | ||
cls.MSS2 = MercurialSignatureDual() | ||
|
||
def test_verify(self): | ||
for MSS in [self.MSS1, self.MSS2]: | ||
pk, sk = MSS.KeyGen(3) | ||
M = [MSS.HashMessage(m) for m in ["this", "is a", "test"]] | ||
sigma = MSS.Sign(sk, M) | ||
self.assertTrue(MSS.Verify(pk, M, sigma), "test message signature verifies") | ||
|
||
def test_convert_sig(self): | ||
for MSS in [self.MSS1, self.MSS2]: | ||
pk, sk = MSS.KeyGen(4) | ||
M = [MSS.HashMessage(m) for m in ["this", "is", "another", "test"]] | ||
sigma = MSS.Sign(sk, M) | ||
rho = MSS.RandomZp() | ||
pk1 = MSS.ConvertPK(pk, rho) | ||
sigma1 = MSS.ConvertSig(pk, M, sigma, rho) | ||
self.assertTrue(MSS.Verify(pk1, M, sigma1), "test conversion verifies") | ||
M[0] = MSS.HashMessage("oh noes") | ||
self.assertFalse(MSS.Verify(pk1, M, sigma1), "forgery does not verify") | ||
|
||
def test_change_rep(self): | ||
for MSS in [self.MSS1, self.MSS2]: | ||
pk, sk = MSS.KeyGen(5) | ||
M = [MSS.HashMessage(m) for m in ["this", "is", "also", "a", "test"]] | ||
sigma = MSS.Sign(sk, M) | ||
mu = MSS.RandomZp() | ||
M0, sigma0 = MSS.ChangeRep(pk, M, sigma, mu) | ||
self.assertTrue(MSS.Verify(pk, M0, sigma0), "change rep verifies") | ||
M0[-1] = MSS.HashMessage("is bad") | ||
self.assertFalse(MSS.Verify(pk, M0, sigma0), "forgery does not verify") | ||
|
||
def test_underlying_groups(self): | ||
for MSS in [self.MSS1]: # same groups for both signature schemes | ||
self.assertEqual( | ||
(MSS.curve.r + 1) * MSS.P, MSS.P, "ecp group order check passes" | ||
) | ||
self.assertEqual( | ||
(MSS.curve.r + 1) * MSS.Phat, MSS.Phat, "ecp2 group order check passes" | ||
) | ||
|
||
def test_hash(self): | ||
for MSS in [self.MSS1]: # the real hash | ||
self.assertEqual(MSS.HashMessage("foo"), MSS.HashMessage("foo")) | ||
self.assertEqual(MSS.HashMessage("bar"), MSS.HashMessage("bar")) | ||
for MSS in [self.MSS1, self.MSS2]: | ||
self.assertNotEqual(MSS.HashMessage("foo"), MSS.HashMessage("bar")) | ||
self.assertNotEqual(MSS.HashMessage("bar"), MSS.HashMessage("baz")) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |