Hacking around with Twitter (Part 3 !) …

Well that took a little while, but it is now all done 🙂

Et voila :

#!/usr/bin/perl -w
#use strict;

use Getopt::Long;
use Storable;
use Net::Twitter::Lite;
use Crypt::OpenSSL::Random;
use Crypt::OpenSSL::RSA;
use MIME::Base64;

#----------------------------------------------#
#                  This is :                   #
 $clientname = "twitter crypt";       #
#                  Version :                   #
 $clientver = "0.1";            #
#----------------------------------------------#
#            By : Simon Biles                  #
$clienturl="http://computersecurityonline.com";#
#----------------------------------------------#

# Saved Public Keys File
$Public_Keys_File = "twc_public_keys";

GetOptions("genkey=s" => \$genkey,
 "publishkey=s" => \$publishkey,
 "getkeys" => \$getkeys,
 "showkeys" => \$showkeys,
 "encrypt=s" => \$encrypt,
 "getmessages=s" => \$getmessages,
 "user=s" => \$user,
 "pass=s" => \$password );

if (defined $genkey){
 $good_entropy = "10010101001010101";

 Crypt::OpenSSL::Random::random_seed($good_entropy);
 Crypt::OpenSSL::RSA->import_random_seed();

 $rsa = Crypt::OpenSSL::RSA->generate_key(640);

 $private = $rsa->get_private_key_string();
 $public = $rsa->get_public_key_string();

 print "\nprivate key is:\n $private\n";
 print "\npublic key (in PKCS1 format) is:\n $public\n"; 

 open(KEYFILE, "> $genkey") or die $!;
 print KEYFILE "$private\n$public";
 close KEYFILE;

 print "\nKeys written to file $genkey ...\n\n";

 exit 0;
}

if (defined $publishkey && defined $user && defined $password){

 my $nt = Net::Twitter::Lite->new(
 username => $user,
 password => $password,
 clientname => $clientname,
 clientver => $clientver,
 clienturl => $clienturl
 );

 open (KEYFILE, "< $publishkey");
 $flag = 0;
 $public_key = "";
 while (<KEYFILE>){
 if ($_ =~ /-----END RSA PUBLIC KEY-----/) { last; };
 if ($flag == 1) { $public_key = $public_key.$_; };
 if ($_ =~ /-----BEGIN RSA PUBLIC KEY-----/) { $flag = 1; };
 }

 $public_key = "twc-public -".$public_key;

 $key_length = length $public_key;

 print "Length: $key_length \n$public_key";

 if($key_length > 140){
 print "\nFor some reason we've over shot our 140 chars for Twitter, complain ...\n";
 exit 0;
 }

 my $result = eval { $nt->update($public_key) };

 exit 0;
}

if($getkeys && defined $user && defined $password){

if(-e $Public_Keys_File){
 %keys_hash = %{retrieve($Public_Keys_File)};
} else {
 %keys_hash = ();
}

 my $nt = Net::Twitter::Lite->new(
 username => $user,
 password => $password,
 clientname => $clientname,
 clientver => $clientver,
 clienturl => $clienturl
 );

 eval {
 my $statuses = $nt->friends_timeline();
 for my $status ( @$statuses ) {
 if ($status->{text} =~ /twc-public -/){
 ($public_key_string = $status->{text}) =~ s/twc-public -//;
 $keys_hash{ $status->{user}{screen_name} } = $public_key_string;
 }
 }
 };
 warn "$@\n" if $@;

store(\%keys_hash, $Public_Keys_File) or die "\nCan't save public keys to $Public_Keys_File.\n\n";

 exit 0;
}

if($showkeys){

if(-e $Public_Keys_File){
 %keys_hash = %{retrieve($Public_Keys_File)};
 while (($key, $value) = each(%keys_hash)){
 print "$key - $value\n";
 }

} else {

 print "\n\n Public Keys file $Public_Keys_File does not exist, create it using the --getkeys option.";

}

 exit 0;
}

if(defined $encrypt && defined $user && defined $password){

if(-e $Public_Keys_File){
 %keys_hash = %{retrieve($Public_Keys_File)};

 $encryption_key = "-----BEGIN RSA PUBLIC KEY-----\n"."$keys_hash{$encrypt}\n"."-----END RSA PUBLIC KEY-----\n";

 print "$encryption_key";

 $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($encryption_key);
 print "                                           |------------------------------------|\n";
 print "Enter your message : (between the lines !) ";
 $plaintext = <STDIN>;    

 if ((length $plaintext) > 38){
 print "Message too long I'm afraid, I did say to stay between the lines ...\n";
 exit 0;
 }

 $ciphertext = $rsa_pub->encrypt($plaintext);

 $encoded_ciphertext = "twc-msg-$encrypt-".encode_base64($ciphertext);
 $encoded_length = length $encoded_ciphertext;

 if ($encoded_length > 140){
 print "Uh-oh, we've had an over Twitter length moment there !\n";
 exit 0;
 }

 my $nt = Net::Twitter::Lite->new(
 username => $user,
 password => $password,
 clientname => $clientname,
 clientver => $clientver,
 clienturl => $clienturl
 );

 my $result = eval { $nt->update($encoded_ciphertext) };

} else {
 print "\n\n Public Keys file $Public_Keys_File does not exist, so there is no key for $encrypt create it using the --getkeys option.";
 exit 0;
}

 exit 0;
}

if(defined $getmessages && defined $user && defined $password){

$private_key = "";

 if (-e $getmessages){
 open (FILEHANDLE, "< $getmessages");
 while(<FILEHANDLE>){
 if ($_ =~ /-----BEGIN RSA PRIVATE KEY-----/){
 $flag = 1;
 }
 if ($flag == 1){
 $private_key = "$private_key"."$_";
 }
 if($_ =~ /-----END RSA PRIVATE KEY-----/){ last; }
 }

 $rsa_priv = Crypt::OpenSSL::RSA->new_private_key($private_key);

 my $nt = Net::Twitter::Lite->new(
 username => $user,
 password => $password,
 clientname => $clientname,
 clientver => $clientver,
 clienturl => $clienturl
 );

 eval {
 my $statuses = $nt->friends_timeline();
 for my $status ( @$statuses ) {
 if ($status->{text} =~ /twc-msg-$user-/){
 $from = $status->{user}{screen_name};
 ($encoded_ciphertext = $status->{text}) =~ s/twc-msg-$user-//;
 $ciphertext = decode_base64($encoded_ciphertext);
 $decrypted = $rsa_priv->decrypt($ciphertext);
 print "Message from $from : $decrypted\n";
 }
 }
 };
 warn "$@\n" if $@;

 exit 0;
 } else {
 print "That key file doesn't appear to exist ...\n";
 exit 0;
 }

 exit 0;
}

#Useage information ... should get here if all else fails !

print "\nSorry the correct useage of twcrypt.pl is :\n\n";
print "twcrypt.pl --genkey keyfile\n\t- generate a key file of name keyfile.\n\n";
print "twcrypt.pl --publishkey keyfile --user username --pass password\n\t- publish the keyfile to twitter for specified account.\n\n";
print "twcrypt.pl --getkeys --user username --pass password\n\t- collect keys published by people you are following.\n\n";
print "twcrypt.pl --showkeys\n\t- show cached public keys.\n\n";
print "twcrypt.pl --encrypt to --user username --pass password\n\t- encrypt a message to a given user.\n\n";
print "twcrypt.pl --getmessages --user username --pass password\n\t- get messages sent to you.\n\n\n";
print "I do realise this isn't the best interface, but hell, live with it ...\n\n";

exit 0;

It works too ! Creating, uploading, encrypting and decrypting through Twitter – Twitter PKI – you saw it here first 😛

I only made one small modification to the planed implementation, and that was to include the name of the recipient – I found that the attempt to decrypt a message with the wrong private key for the encryption caused a crash out, so I though that it would be best to keep it to only decrypting what it is supposed to.

All comments and critique of my code welcome … Please remember though that it’s only a proof of concept – I would be interested in any major flaws though … ( A low key length isn’t a flaw, it’s a practicality by the way – 640 bits should keep most people at bay for a fairly reasonable amount of time 🙂 and is all I could fit into Twitter. ) I hope that the code is self explanatory, if you do have any questions, please drop me a line at si at thinking-security.com

I’m going to go and have a break from the computer now … I might fiddle with this some more later to make it a little more useable, maybe a GUI or iPhone version 😉

Tagged , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: