./c0de/perl/vanitykey.txt

0.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
#!/usr/bin/perl -w
 
# openpgp vanity key generator
# version 1.0, 6-29-2004
# written by seth hardy - [email protected]
#
# this program will generate an openpgp v4 dsa key pair with a vanity key id.
# this is accomplished by brute force, randomly selecting key pairs and
# computing the fingerprint until a matching pair is found.
#
# this program was written with a specific someone in mind. you know who you
# are, and what vanity key you want. use it well. :)
#
# copyright (c) 2004 seth hardy <[email protected]>. all rights reserved.
# this program is free software; you can redistribute it and/or modify it
# under the same terms as perl itself.
 
 
use strict;
 
use Getopt::Long;
use Pod::Usage;
use IO::Socket;
use Math::Pari;
use Crypt::OpenPGP::Certificate;
use Crypt::OpenPGP::UserID;
use Crypt::OpenPGP::Signature;
use Crypt::OpenPGP::Armour;
use Crypt::DSA::KeyChain;
use Crypt::DSA::Key;
 
# at least let's print out a message before dying to make things look ok
$SIG{INT}  = sub {
  die "control c caught, exiting.\n\n"; 
}; 
 
# stuff all the server code here, whee
sub start_server {
  my $port = shift;
  my $keyid = shift;
  my $key = shift;
  my $str;
  my $ssock = new IO::Socket::INET (
    LocalPort => $port,
    Proto => 'tcp',
    Listen => 5, # hardcoded in but seems reasonable
    Reuse => 1, # i wish this existed in c, gah
  ) or die " ! could not create socket: $!\n";
  print " - starting server.\n";
  my $insock;
  while( $insock = $ssock->accept() ) {
    my $mysockaddr = getsockname($insock);
    my ($p, $addr) = sockaddr_in($mysockaddr);
    printf "client connection from %s\n", inet_ntoa($addr);
    # figure out what the client wants to do in an overly simple way
    chomp ( $str = <$insock> );
    # the client wants parameters to start number crunching
    if ( $str eq "param" ) {
      print $insock $keyid . "\n";
      print $insock $key->{g} . "\n";
      print $insock $key->{p} . "\n";
      print $insock $key->{q} . "\n";
      printf "client at %s getting parameters.\n", inet_ntoa($addr);
    }
    # the client wants to report that it is done
    elsif ( $str eq "done" ) {
      printf "client at %s reports success.\n", inet_ntoa($addr);
    }
    # otherwise it's crap
    else {
      printf "someone at %s is screwing around.\n", inet_ntoa($addr);
    }
    close $insock;
  }
  close($ssock);
}
 
# command line arguments
my %cl;
GetOptions(\%cl,
  "keyid=s",
  "client",
  "server",
  "help|?",
  "verbose",
  "keyfile=s",
  "port=s",
  "host=s",
  "keypass=s",
  "uid=s"
);
 
# yar, this be the verbosity level
my $verbose = 1 if $cl{verbose};
 
# omg like let's do this over the network kthx
my $client = 1 if $cl{client};
my $server = 1 if $cl{server};
 
# berate the user if she can't figure out how to use the program correctly
pod2usage(-verbose => 2) if $cl{help};
pod2usage(
  -verbose => 0,
  -message => "\nyou need to specify the vanity keyid to generate.\n"
) if (!$cl{keyid} and !$client);
pod2usage(
  -verbose => 0,
  -message => "\nyou can't be both the client and the server.\n"
) if ($cl{client} and $cl{server});
pod2usage(
  -verbose => 0,
  -message => "\nthe keyid must be in hex, without the leading 0x.\n"
) if ( !$client and $cl{keyid} =~ /[^0-9A-Fa-f]/ );
 
# need to strip off everything over the keyid 
my $modulus = PARI 1;
$modulus = $modulus << 32;
 
# header
print "\nopenpgp vanity key generator.\n";
print "acting as server!\n\n" if $server;
print "acting as client!\n\n" if $client;
print "working in standalone mode.\n\n" if !($server or $client);
 
# key generation variables
my $kc = Crypt::DSA::KeyChain->new; 
my $key;
 
# the desired keyid
my $target;
 
# socket to use if we're in client mode
my $sock;
 
# if we're the client, get the target and the values of p,q,g from the server
if ( $client ) {
  $sock = new IO::Socket::INET (
    PeerAddr => $cl{host} ? $cl{host} : 'localhost',
    PeerPort => $cl{port} ? $cl{port} : '9887',
    Proto => 'tcp',
  ) or die " ! could not create socket: $!\n" ;
  $key = Crypt::DSA::Key->new;
  $key->{size} = 1024;
  print $sock "param\n";
  chomp ( $target = <$sock> );
  chomp ( $key->{g} = <$sock> );
  chomp ( $key->{p} = <$sock> );
  chomp ( $key->{q} = <$sock> );
  print " - parameters received from server.\n";
  close($sock);
}
 
# if we're in standalone mode or the server, generate parameters
if ( !$client ) {
  $key = $kc->generate_params (
    Size => 1024,
    Verbosity => 0,
  );
  print " - parameters generated.\n";
  $target = $cl{keyid};
}
 
# if we're the server, let's get this shit rolling
if ( $server ) {
  my $port = $cl{port} ? $cl{port} : '9887';
  start_server($port,$cl{keyid},$key);
}
 
print " - looking for key with fingerprint ending in 0x$target.\n";
 
# print out the public parameters
if ( $verbose ) {
  print " * g = $key->{g}\n";
  print " * p = $key->{p}\n";
  print " * q = $key->{q}\n";
}
 
# certificate building variables
my $cert;
my $sec = Crypt::OpenPGP::Key::Secret->new('DSA');
my $certpass = $cl{keypass} ? "$cl{keypass}" : "yar!";
 
# copy over the public parameters to the secret key
for ( qw( p q g size ) ) {
  $sec->$_( $key->$_ );
}
 
# here's the main loop, and an angsty haiku about it
#   massive compute job
#   snow falling on black roses
#   perl is fucking slow
do {
 
  # generate a public and private key using the above parameters
  $kc->generate_keys($key);
 
  # copy the public and private key values to the private key
  $sec->x( $key->priv_key );
  $sec->y( $key->pub_key );
 
  # wrap the secret key in a certificate
  $cert = Crypt::OpenPGP::Certificate->new(
    Key => $sec,
    Version => 4,
    Passphrase => $certpass,
  ) or die Crypt::OpenPGP::Certificate->errstr;
 
# and keep going until the fingerprint matches the target
} while ( substr($cert->public_cert->fingerprint_hex,-1 * length $target)
  ne $target ); 
 
# we're done.
print "\n - found it!\n";
print " * x = $key->{priv_key}\n" if $verbose;
print " * y = $key->{pub_key}\n" if $verbose;
print " - id = 0x" . substr($cert->public_cert->fingerprint_hex,-8) . "\n";
 
# make a keyblock and add the secret key certificate to the keyblock
my $kb_sec = Crypt::OpenPGP::KeyBlock->new;
$kb_sec->add($cert);
print " - certificate 0x" . $cert->key_id_hex . " added to keyblock.\n";
 
# add uid
my $id = Crypt::OpenPGP::UserID->new( 
  Identity => $cl{uid} ? $cl{uid} : "vanity key"
);
$kb_sec->add($id);
print " - identity added to keyblock.\n";
 
# create a self-sig on the public key certificate and add to the keyblock
my $siggy = Crypt::OpenPGP::Signature->new(
  Data    => [ $cert->public_cert, $id ],
  Key     => $cert,
  Version => 4,
  Type    => 0x13,
) or die " ! " . Crypt::OpenPGP::Signature->errstr;
$kb_sec->add($siggy);
print " - key self signature created and added to keyblock.\n";
 
# write the keyblock to disk in armored format
my $kf = $cl{keyfile} ? $cl{keyfile} : "seckey.asc";
open KEYFILE, ">$kf";
print KEYFILE Crypt::OpenPGP::Armour->armour(
  Data => $kb_sec->save,
  Object => 'PRIVATE KEY BLOCK',
  Headers => {
    Comment => 'created by vanity key generator',
  },
);
close KEYFILE;
print " - key armored and written to disk as $kf.\n\n";
 
# report in and let the server know we're done, if we're a client
if ( $client ) {
  $sock = new IO::Socket::INET (
    PeerAddr => $cl{host} ? $cl{host} : 'localhost',
    PeerPort => $cl{port} ? $cl{port} : '9887',
    Proto => 'tcp',
  ) or die " ! could not create socket: $!\n" ;
  print $sock "done\n";
  print " - informed server of success.\n\n";
  close($sock);
}
 
 
__END__
 
=head1 NAME
 
vanitykey.pl - openpgp vanity key generator
 
=head1 SYNOPSIS
 
vanitykey.pl [options] --keyid=value
 
=head1 DESCRIPTION
 
B<vanitykey.pl> will generate an openpgp v4 dsa key pair with a vanity key id.
this is accomplished by brute force, randomly selecting key pairs and computing
the fingerprint until a matching pair is found.
 
in order to speed the process up, B<vanitykey.pl> can operate in a simple
distributed mode. the program can perform both the tasks of server and client,
distributing parameters to other instances of the program or retrieving the
parameters and performing the brute force number crunching.
 
when a match is found, B<vanitykey.pl> will write an ascii armored openpgp
private key block containing a v4 dsa signing key. because the dsa key is the
primary id, an elgamal encryption key can be generated and attached to the
dsa key using any openpgp compliant program.
 
=head1 OPTIONS
 
=over 8
 
=item B<--help>
 
prints out this documentation. but you probably already knew that.
 
=item B<--verbose>
 
prints verbose output, for people who like that kind of thing.
 
=item B<--client>
 
tells the program to operate as a client. should be used in conjunction
with B<--host> and B<--port>. this is the only option that overrides the
need for using the B<--keyid> parameter, as it is sent by the server along
with the other parameters.
 
=item B<--server>
 
tells the program to operate as the server. when using the distributed
mode, the server process generates the parameters and passes them along
to any client wishing to do computation. clients that finish will then
report back to the server, so the user can know where to look for her
newly generated key.
 
=item B<--host=hostname>
 
when running in client mode, this tells the program what host the server is
running on. this defaults to B<localhost>.
 
=item B<--port=portnum>
 
when running in client mode, this tells the program what port the server is
running on. when running in server mode, this tells the program which port
to run on. in both cases, this parameter defaults to B<9887>.
 
=item B<--keyfile=file>
 
the name of the file which will contain the secret key. this defaults to
B<seckey.asc>.
 
=item B<--keypass=password>
 
the password used to lock the secret key. you'll need this to actually use
the secret key once you've imported it to your keyring. if no password is 
specified, this defaults to B<"yar!">.
 
=item B<--uid=id>
 
the user id to use in the key. this should take the form of "User Name 
<[email protected]> (comment)". defaults to B<"vanity key">.
 
=back
 
=head1 COPYRIGHT
 
copyright (c) 2004 seth hardy <[email protected]>. all rights reserved. this
program be free software, matey; you can redistribute it and/or modify it under
the same terms as perl itself.
 
=head1 WHY AM I WRITING ALL THIS GODDAMN DOCUMENTATION?
 
i really have no idea. someone please put me out of my misery.
 
=head1 AUTHOR
 
written by seth hardy ([email protected]).
 
=cut
 

Back