Skip to content

Instantly share code, notes, and snippets.

@pgchamberlin
Created July 25, 2019 13:46
Show Gist options
  • Save pgchamberlin/e2933b4765f4bfc10aea12b8e76e1377 to your computer and use it in GitHub Desktop.
Save pgchamberlin/e2933b4765f4bfc10aea12b8e76e1377 to your computer and use it in GitHub Desktop.
Playing with blind signatures
#!/usr/local/bin/python3
#
# THIS IS JUST TOY CODE DON'T DO ANYTHING RECKLESS LIKE USE IT IN PRODUCTION
#
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from random import SystemRandom
import sys
# mod mult inverse function copied from pycrypto
# https://github.com/dlitz/pycrypto/blob/7acba5f3a6ff10f1424c309d0d34d2b713233019/lib/Crypto/Util/number.py
def inverse(u, v):
"""
Return the inverse of u mod v.
"""
u3, v3 = u, v
u1, v1 = 1, 0
while v3 > 0:
q=divmod(u3, v3)[0]
u1, v1 = v1, u1 - v1*q
u3, v3 = v3, u3 - v3*q
while u1<0:
u1 = u1 + v
return u1
# blind an unblind functions adapted from pycrypto
# https://github.com/dlitz/pycrypto/blob/7acba5f3a6ff10f1424c309d0d34d2b713233019/lib/Crypto/PublicKey/_slowmath.py
def blind(m, r, e, n):
# compute r**e * m (mod n)
return (m * pow(r, e)) % n
def unblind(m, r, n):
# compute m / r (mod n)
return inverse(r, n) * m % n
# insecure RSA sign function is required for blind sigs
def blind_sign(m, d, n):
# compute m ** d (mod n)
return pow(m, d, n)
def validate(m, e, n):
# compute m ** e (mod n)
return pow(m, e, n)
#######
pub_exponent = 65537
blinding_factor = SystemRandom().randrange(pub_exponent >> 10, pub_exponent)
private_key = rsa.generate_private_key(
public_exponent=pub_exponent,
key_size=2048,
backend=default_backend()
)
private_numbers = private_key.private_numbers()
public_numbers = private_key.public_key().public_numbers()
message_bytes = b"Sign this for me please" # in reality the message always needs to be bigger than the exponent
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(message_bytes)
message_hash = digest.finalize()
message_int = int.from_bytes(message_hash, sys.byteorder)
print("""
This script demonstrates blind signing.
Blind signing enables a signed message to be sent from a Sender to a Recipient with the signature being from a third party Signer who never sees the message.
This is what the message "Sign this for me please" looks like as a SHA256 digest in numeric form:
{:d}""".format(message_int))
blinded = blind(message_int, blinding_factor, pub_exponent, public_numbers.n)
print("""
This is what that number looks like when blinded using the Signer's public key and a random signing factor generated by the Sender:
{:d}
This blinded message digest gets sent to the Signer to be signed with their private key.""".format(blinded))
blind_sig = blind_sign(blinded, private_numbers.d, public_numbers.n)
print("""
A blind signature is returned to the Sender:
{:d}
The Sender now needs to unblind the signature so it can be sent to the Recipient along with the message. This is again done using the Signer's public key and the random factor generated earlier""".format(blind_sig))
unblinded_sig = unblind(blind_sig, blinding_factor, public_numbers.n)
print("""
This is the unblinded signature:
{:d}
The Recipient takes that unblinded signature and validates it using the Signer's public key.""".format(unblinded_sig))
validation = validate(unblinded_sig, pub_exponent, public_numbers.n)
#validation_bytes = validation.to_bytes(-(-validation.bit_length()//8), byteorder=sys.byteorder)
print("""
This should match the digest of the Sender's original message
{:d}
""".format(validation))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment