#!/usr/bin/perl
use strict;
use warnings;
use Math::BigInt lib => 'GMP';
my $g = new Math::BigInt('5'); # typically 2 or 5
my $p = new Math::BigInt(
"537139360602477525125655043677356597740" .
"672426915294213641576278281056255413159" .
"9074907426010737503501" # any 100+ digit prime will do
);
my $a = new Math::BigInt(join("",
map(chr(48+int rand 10),
1 .. 50))); # $a is the lhs secret
my $b = new Math::BigInt(join("",
map(chr(48+int rand 10),
1 .. 50))); # $b is the rhs secret
my ($A, $B, $Kab, $Kba);
print "g=$g\n"; # $p and $g generate a group
print "p=$p\n"; # cyclic group G
print "a=$a\n"; # $p and $g are chosen in the clear
print "b=$b\n";
print "\n";
# it's safe to send A=g^a mod p in the clear
$A = $g->copy->bmodpow( $a, $p );
# it's safe to send B=g^b mod p in the clear
$B = $g->copy->bmodpow( $b, $p );
# g^b^a mod p should be equal to g^a^b mod p
$Kba = $B->copy->bmodpow( $a, $p );
$Kab = $A->copy->bmodpow( $b, $p );
# so we both know the secret without transmitting it!
# magic!
print "These better be equal:\n\t$Kab\n\t$Kba\n\n";
####
use Crypt::PBC;
my $pairing = new Crypt::PBC("params_d.txt");
my $P = $pairing->init_G2->random; # generator in G2
my $s = $pairing->init_Zr->random; # master secret
my $P_pub = $pairing->init_G2->pow_zn($P, $s); # master public key
##
##
use Digest::SHA1 qw(sha1);
my $ID_i = q(node_id=16186);
my $Q_i = $pairing->init_G1->set_to_hash( sha1($ID_i) );
print "ID_i=$ID_i\nQ_i=", $Q_i->as_hex, "\n";
##
##
my $d_i = $pairing->init_G1->pow_zn( $Q_i, $s ); # the private key corresponding to $Q_i
##
##
use Crypt::CBC;
use Crypt::Blowfish;
my $g_i = $pairing->init_GT->e_hat( $Q_i, $P_pub );
my $r = $pairing->init_Zr->random;
my $rP = $pairing->init_G2->pow_zn( $P, $r );
my $W1 = $g_i->clone->pow_zn( $r );
my $C1 = new Crypt::CBC({header=>'randomiv', key=>$W1->as_bytes, cipher=>"Blowfish"});
my @m = ($rP, $C1->encrypt("LOL! Funny URL: blither blather message"));
# Note that the message is a two tuple, not just the ciphertext.
# We transmit @m in the clear. No worries.
my $W2 = $pairing->init_GT->e_hat( $d_i, $m[0] );
my $C2 = new Crypt::CBC({header=>"randomiv", key=>$W2->as_bytes, cipher=>"Blowfish"});
my $txt = $C2->decrypt( $m[1] );
print "W1 =? W2\nW1=", substr($W1->as_base64, 0, 80), "\nW2=", substr($W2->as_base64, 0, 80), "\n";
print "And here's the original message: $txt\n\n";
##
##
$pairing = new Crypt::PBC("params_a.txt");
$P = $pairing->init_G2->random; # generator in G2
$s = $pairing->init_Zr->random; # master secret
$P_pub = $pairing->init_G2->pow_zn($P, $s); # master public key
my $Q_0 = $pairing->init_G1->set_to_hash( sha1("node_id=22609") ); # tye
my $Q_1 = $pairing->init_G1->set_to_hash( sha1("node_id=9073") ); # merlyn
my $d_0 = $pairing->init_G1->pow_zn( $Q_0, $s );
my $d_1 = $pairing->init_G1->pow_zn( $Q_1, $s );
my $K_01_a = $pairing->init_GT->e_hat( $Q_0, $d_1 ); # merlyn's version of K_01
my $K_01_b = $pairing->init_GT->e_hat( $d_0, $Q_1 ); # tye's version
print "K_01 (these better be the same):\n\t", substr($K_01_a->as_base64, 0, 80),
"\n\t", substr($K_01_b->as_base64, 0, 80), "\n";
my $K_10_a = $pairing->init_GT->e_hat( $Q_1, $d_0 ); # tye's version of K_10
my $K_10_b = $pairing->init_GT->e_hat( $d_1, $Q_0 ); # merlyn's version
print "K_10 (these also better be the same):\n\t", substr($K_10_a->as_base64, 0, 80),
"\n\t", substr($K_10_b->as_base64, 0, 80), "\n\n";
##
##
# We can construct authenticated ciphers:
my $authcipha = new Crypt::CBC({header=>"randomiv", key=>$K_01_a->as_bytes, cipher=>"Blowfish"});
my $authciphb = new Crypt::CBC({header=>"randomiv", key=>$K_01_b->as_bytes, cipher=>"Blowfish"});
# or we can use the secrets to construct authentic clear-text messages:
my @verifiable_message;
SCOPE1: {
my $msg = "This is verifiable.";
my $sha = new Digest::SHA1;
$sha->add( $msg );
$sha->add( $K_01_a->as_bytes );
my $MAC = $sha->digest;
@verifiable_message = ( $msg, $MAC );
}
SCOPE2: {
my $sha = new Digest::SHA1;
$sha->add( $verifiable_message[0] );
$sha->add( $K_01_b->as_bytes );
my $MAC = $sha->digest;
print "Verified message from ID_0: $verifiable_message[0]\n\n"
if $MAC eq $verifiable_message[1];
}
##
##
my $ID_c = 'node_id=1382'; # chromatic is looking to join our IBE
my $Q_c = $pairing->init_G1->set_to_hash( sha1($ID_c) );
my $x_c = $pairing->init_Zr->random;
my $X_c = $pairing->init_G2->pow_zn( $P, $x_c );
my $req = $X_c->as_bytes;
# we transmit $X_c in the open along with our request for $d_c
# the TA then builds our private key and constructs a secret $W_0.
# IRL, the TA would calculate $Q_c on its own ...
my $dtmp = $pairing->init_G1->pow_zn( $Q_c, $s );
my $W_0 = $pairing->init_GT->e_hat(
$pairing->init_G2->set_to_bytes($req)->pow_zn( $s ), # only the TA can make this
$P_pub );
my $cipher0 = new Crypt::CBC({header=>"randomiv", cipher=>"Blowfish", key=>$W_0->as_bytes});
my $reqfil = $cipher0->encrypt( $dtmp->as_bytes );
# Back at the $ID_c node, we can recover our private key
# from $reqfil like so:
my $W_1 = $pairing->init_GT->e_hat( $P_pub, $P_pub )->pow_zn($x_c);
my $cipher1 = new Crypt::CBC({header=>"randomiv", cipher=>"Blowfish", key=>$W_1->as_bytes});
my $d_c = $pairing->init_G1->set_to_bytes( $cipher1->decrypt( $reqfil ) );
print "I have aquired my key from the TA over a secure authentic channel:\n\t",
substr($dtmp->as_base64, 0, 80), "\n\t",
substr($d_c->as_base64, 0, 80), "\n\n";
##
##
# $P is chosen the same way as the examples above, so we'll just use that.
my $N = 10; # the number of PAs, plus the TA
my @s = map( $pairing->init_Zr->random, 1 .. $N );
my @P = map( $P->clone->pow_zn( $_ ), @s );
my $Y = $P->clone; $Y->pow_zn( $_ ) for @s;
# The TA calculates P0 := $s[0]$P and transmits to the first PA who
# calculates P1 := $s[1]$P[0] and so on. Notice the in-efficiency?
# Hint: $P[0] == ($P->clone->pow_zn($s[0])). In fact, @P isn't needed
# at all, unless you're combining shared-secrets with secure key
# distribution.
# $Q_0 (tye) and $Q_1 (merlyn) are defined above ...
$d_0 = $Q_0->clone; $d_0->pow_zn( $_ ) for @s;
$d_1 = $Q_1->clone; $d_1->pow_zn( $_ ) for @s;
$K_01_a = $pairing->init_GT->e_hat( $Q_0, $d_1 );
$K_01_b = $pairing->init_GT->e_hat( $d_0, $Q_1 );
print "[Y] authenticated secret is the same:\n\t", substr($K_01_a->as_base64, 0, 80),
"\n\t", substr($K_01_b->as_base64, 0, 80), "\n\n";
$r = $pairing->init_Zr->random;
$rP = $P->clone->pow_zn( $r );
$W1 = $pairing->init_GT->e_hat( $Q_0, $Y )->pow_zn( $r );
$W2 = $pairing->init_GT->e_hat( $d_0, $rP );
print "[Y] un-authenticated secret is the same:\n\t", substr($W1->as_base64, 0, 80),
"\n\t", substr($W2->as_base64, 0, 80), "\n\n";