Blind Signature
A Blind Signature is a signature over a blinded or masked message. Blind signatures are typically used in privacy-related protocols where the signer of the message is not necessarily the author of the message. For example, blind signatures can be used in cryptocurrency for sender anonymity. Also see A New Blind ECDSA Scheme for Bitcoin Transaction Anonymity.
The Crypto++ library does not offer blind signature classes. As far as we know there is no standard covering the signature scheme. The lack of standardization will surely cause interop problems. For example, the code below uses SHA256 to hash the message to be signed, while RSA Blind Signature Scheme for golang uses full domain hashing. Also see Is there a standard padding/format for RSA Blind Signatures? on Crypto.SE.
Blind signature can achieved with the following code. Thanks to Cory Geesaman for raising the topic on the mailing list. Also see Is there a standard padding/format for RSA Blind Signatures?, Usability of padding scheme in blinded RSA signature? and RSA blind signatures in practice on the Crypto Stack Exchange.
The code below is based on information for Raw RSA. The article explains the use of functions like ApplyFunction and CalculateInverse. Also see RSA Signature Schemes for other ways to sign a message using RSA.
#include "cryptlib.h" #include "integer.h" #include "nbtheory.h" #include "osrng.h" #include "rsa.h" #include "sha.h" #include <iostream> #include <stdexcept> int main(int argc, char* argv[]) { using namespace CryptoPP; // Bob artificially small key pair AutoSeededRandomPool prng; RSA::PrivateKey privKey; privKey.GenerateRandomWithKeySize(prng, 64); RSA::PublicKey pubKey(privKey); // Convenience const Integer& n = pubKey.GetModulus(); const Integer& e = pubKey.GetPublicExponent(); const Integer& d = privKey.GetPrivateExponent(); // Print params std::cout << "Pub mod: " << std::hex << pubKey.GetModulus() << std::endl; std::cout << "Pub exp: " << std::hex << e << std::endl; std::cout << "Priv mod: " << std::hex << privKey.GetModulus() << std::endl; std::cout << "Priv exp: " << std::hex << d << std::endl; // For sizing the hashed message buffer. This should be SHA256 size. const size_t SIG_SIZE = UnsignedMin(SHA256::BLOCKSIZE, n.ByteCount()); // Scratch SecByteBlock buff1, buff2, buff3; // Alice original message to be signed by Bob SecByteBlock orig((const byte*)"secret", 6); Integer m(orig.data(), orig.size()); std::cout << "Message: " << std::hex << m << std::endl; // Hash message per Rabin (1979) buff1.resize(SIG_SIZE); SHA256 hash1; hash1.CalculateTruncatedDigest(buff1, buff1.size(), orig, orig.size()); // H(m) as Integer Integer hm(buff1.data(), buff1.size()); std::cout << "H(m): " << std::hex << hm << std::endl; // Alice blinding Integer r; do { r.Randomize(prng, Integer::One(), n - Integer::One()); } while (!RelativelyPrime(r, n)); // Blinding factor Integer b = a_exp_b_mod_c(r, e, n); std::cout << "Random: " << std::hex << b << std::endl; // Alice blinded message Integer mm = a_times_b_mod_c(hm, b, n); std::cout << "Blind msg: " << std::hex << mm << std::endl; // Bob sign Integer ss = privKey.CalculateInverse(prng, mm); std::cout << "Blind sign: " << ss << std::endl; // Alice checks s(s'(x)) = x. This is from Chaum's paper Integer ck = pubKey.ApplyFunction(ss); std::cout << "Check sign: " << ck << std::endl; if (ck != mm) throw std::runtime_error("Alice cross-check failed"); // Alice remove blinding Integer s = a_times_b_mod_c(ss, r.InverseMod(n), n); std::cout << "Unblind sign: " << std::hex << s << std::endl; // Eve verifies Integer v = pubKey.ApplyFunction(s); std::cout << "Verify: " << std::hex << v << std::endl; // Convert to a string size_t req = v.MinEncodedSize(); buff2.resize(req); v.Encode(&buff2[0], buff2.size()); // Hash message per Rabin (1979) buff3.resize(SIG_SIZE); SHA256 hash2; hash2.CalculateTruncatedDigest(buff3, buff3.size(), orig, orig.size()); // Constant time compare bool equal = buff2.size() == buff3.size() && VerifyBufsEqual( buff2.data(), buff3.data(), buff3.size()); if (!equal) throw std::runtime_error("Eve verified failed"); std::cout << "Verified signature" << std::endl; return 0; }
Running the program results in:
$ g++ -g2 -O3 -I. blind.cxx ./libcryptopp.a -o blind.exe $ ./blind.exe Pub mod: ef0a8e12b020cb43h Pub exp: 11h Priv mod: ef0a8e12b020cb43h Priv exp: 626dc206e636cc49h Message: 736563726574h H(m): 2bb80d537b1da3e3h Random: b5ebee0076e53fcbh Blind msg: 6b327b608027df1fh Blind sign: c2e92d5c262a524ch Check sign: 6b327b608027df1fh Unblind sign: 58c28011bd85b1f9h Verify: 2bb80d537b1da3e3h Verified signature