from time import time; # timing
from random import SystemRandom; # cryptographic random byte generator
rand=SystemRandom(); # create strong random number generator
# This is the prime modulus used by SECG secp256k1, the BitCoin elliptic curve.
# Note for real security, you should use at least a 2048-bit prime,
# to resist number sieve field attacks. (Or use an elliptic curve.)
P=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
G=3; # generator for the field P
nbits=256; # approximate size of the prime P
start=time();
# Alice side:
a=rand.getrandbits(nbits);
A=pow(G,a,P); # = (G^a) mod P
# Bob side:
b=rand.getrandbits(nbits);
B=pow(G,b,P); # = (G^b) mod P
# Alice and Bob exchange A and B here (in public)
print("Alice public key: ",hex(A));
print(" Bob public key: ",hex(B));
# Now we can compute shared secrets in private:
Ba=pow(B,a,P);
print("Alice gets shared secret: ",hex(Ba));
Ab=pow(A,b,P);
print(" Bob gets shared secret: ",hex(Ab));
elapsed=time()-start;
print("Elapsed time in seconds: ",elapsed);Here's a version in C++ using my own personal bignum library:#include "osl/bignum.h" /* multi-precision arithmetic */
// We operate on 256-bit integers
typedef bignum<256> integer;
// This function returns a cryptographically random integer.
// Or at least, it's supposed to!
integer randInteger(void) {
return rand(); // fail!
}
/* This is a big prime number. It's the same one
as used by SECG secp256k1 (the BitCoin elliptic curve);
but for Diffie-Hellman any prime will do. */
integer P("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
/* This is the generator, ideally with high order in the field. */
integer G(3);
void foo(void) {
// Alice picks an integer, and raises G to that power (mod P)
integer a=randInteger(); // secret
integer A=G.powmod(a,P); // public
std::cout<<"Sending Bob A="<<A;
// Bob picks an integer, and does the same thing
integer b=randInteger(); // secret
integer B=G.powmod(b,P); // public
std::cout<<"Sending Alice B="<<B;
// Alice and Bob multiply their integers
integer Ba=B.powmod(a,P); // (G^b)^a
integer Ab=A.powmod(b,P); // (G^a)^b
std::cout<<" Alice's B^a="<<Ba;
std::cout<<" Bob's A^b="<<Ab;
// Eve has both sent values A and B, but not either secret a or b
std::cout<<" Eve's A*B="<<(A*B).mod(P);
}
Example run:
Sending Bob A=0x13218987dc0254d9ec3b71751e15371ef31fd0d283c553fe55590b0e76781bb3 Sending Alice B=0xac255f9fcbe9811bc033c1da2e72e1c21090d9062d175dbb3d64c191c87b1639
Alice's B^a=0xe735195909796602a676bf77f8f06962ef42d2e415e921e68675964c04ce50f8 Bob's A^b=0xe735195909796602a676bf77f8f06962ef42d2e415e921e68675964c04ce50f8
Eve's A*B=0x5193e245862d9f2f610dfac680d7dba0650e16513c30b2ee61a9bf76bbf0f453
Note that Eve knows A=Ga mod P,
and she knows the integer A, the generator G (probably just 2),
and the fixed prime P. It seems like it would be trivial to
figure out the exponent a from this, but because of the
mod P, the generator wraps around again and again during the
exponent computation.
Essentially, we're using modular exponentiation like a hash
algorithm, to conceal the secret exponent a. We don't care
much about collision resistance; and indeed every time you add P
to the exponent, you get another collision (because P is the
modulus). We care a lot about preimage resistance, since
exponentiation is the only thing keeping our secret exponent
secret.
How hard is this discrete
logarithm problem, the basis of Diffie-Hellman and several
other cryptographic algorithms?
We'll talk a lot more about multiplication on Monday, but here's
a table of exponents for arithmetic modulo the tiny prime
P=509. Each row acts as a generator, each column as the
exponent, and the brightness of the pixel shows the product.
Raising 0 or 1 to any power doesn't do anything, so the top two
rows are black. Raising any number to the power 0 or 1 isn't
very interesting, so the left two columns are uninteresting.
Note how much of the table looks like random chaos!
