Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Web Cryptomatic

by oakbox (Chaplain)
on Sep 21, 2001 at 18:40 UTC ( [id://113882]=sourcecode: print w/replies, xml ) Need Help??
Category: Cryptography
Author/Contact Info Richard Still oakbox
Description: Update: Read the reply by no slogan about why this is NOT secure. Hopefully, you'll find this as educational as I am. ;)

A simple encrypt-decrypt web program that uses MD5 and One Time Pad together. It's as secure as: The 'seed' key used by the sender and the SSL encryption in your web browser.

What happens:

  • Read incoming text to encrypt and a unique 'seed'.
  • Get a MD5 hexhash of the seed, this produces a string of letters and numbers that will be used as the pad.
  • Pad the incoming text against the pad.
  • Hex encode the text so that it will fit over a 6-bit connection.
  • Checksum the hex code with another MD5 Digest
  • Display the encoded text and checksum to the sender.
  • Sender emails the endcoded text and checksum
  • Recipient enters encoded text, checksum, and 'seed'
  • Program reverses the above steps and prints out the original text

    As long as the 'seed' is sufficiently unique (RANDOM), is only used ONCE, and is SECRET, I think this scheme is pretty secure. This is my first shot at a crypto program and I would very much appreciate your input, suggestions, and corrections.

    You can see a working version of this script here. DEMO ONLY, it's not a SSL connection and is NOT SECURE.

  • #!/usr/bin/perl -w
    
    ############
    # Cryptomatic
    # by Richard Still (oakbox.com)
    ############
    # (C) 2001 oakbox.com  This program is freeware and may 
    # be used at no cost to you (just leave this notice intact). 
    # Feel free to modify, hack, and play  with this script.
    # No guarantees about the utility of this script for any particular
    # purpose! 
    ############
    # This should be placed on a web site with SSL enabled.
    # see bottom for more comments :)
    
    use CGI::Carp qw(fatalsToBrowser);
    use MD5;
    use strict;
    
    my ($message, $temp, $key, $content, $item, @pairs);
    my %fields;
    
    # accept input from user and decode variables
    
    read(STDIN,$temp,$ENV{'CONTENT_LENGTH'});
    @pairs=split(/&/,$temp);
    foreach $item(@pairs)
     {
      ($key,$content)=split(/=/,$item,2);
      $content=~tr/+/ /;
      $content =~ s/<!--(.|\n)*-->//g;
      $content=~s/%(..)/pack("c",hex($1))/ge;
      $fields{$key}=$content;
     }
    
    
    
    if($fields{'action'} eq ""){&firstscreen; &shellout; exit;}
    if($fields{'action'} eq "encoder"){&hexhex; &firstscreen; &shellout; e
    +xit;}
    if($fields{'action'} eq "decoder"){&ghex; &firstscreen; &shellout; exi
    +t;}
    
    
    sub hexhex {
    
    my $pad_text = MD5->hexhash($fields{'seeder'});
    
    # pad this key against the incoming text
    my $ciphered = &pad_it($fields{'textinput'},$pad_text);
    
    # hex the content so that it can travel through a 6-bit connection
    $ciphered = unpack("h*",$ciphered);
    
    # grab a checksum based on this hexed string
    my $checksum = MD5->hexhash($ciphered);
    
    # modify it a little so that it looks good in the browser
    $ciphered =~ s/(\S{50})/$1<br> /mg; 
    
    $message.="<table width=\"200\"><tr><td>Cipher:<p>$ciphered
    <p><P>checksum:<p>$checksum<p></td></tr><tr><td>Email both 
    of the above codes to your intended recipient.  They can 
    DECODE this by coming back to this form and entering these 
    codes in the 'decode' area below.  Your recipient has to 
    know your secret 'seed' to unlock this message.  DO NOT 
    communicate this seed in your email or in any clear-channel 
    way.</table>";
    
    }
    
    sub ghex {
    
    # remove spaces from input (there shouldn't be any spaces in the hexed
    + code)
    $fields{'textinput'}=~s/\s//g;
    
    my $pad_text = MD5->hexhash($fields{'seed'});
    
    my $check = MD5->hexhash($fields{'textinput'});
    
    # look at the checksum
    if($check ne $fields{'checksum'}){$message.="<font size+2>Invalid 
    checksum!</font> I cannot guarantee that this message was not 
    altered en-route.  Even if the text decodes clearly, there may 
    have been some tampering. . . . sorry <p>\n";}
    
    # remove the hex encoding
    my $ciphered=pack("h*",$fields{'textinput'});
    
    # now we pad our key against our text
    my $content = &pad_it($ciphered,$pad_text);
    
    $message.=" Your decoded text:<P><table border=3><tr><td><pre>$content
    </pre></td></tr></table> ";
    
    }
    
    
    sub firstscreen {
    
    $message.=qq( <hr>
    <font size=+1>Oakbox Super-Duper One-Shot Encryptomatic!</font><p>
    
    Send your message securely over the internet!  This particular 
    implementation is meant FOR DEMONSTATION PORPOISES ONLY.  To be 
    genuinely secure, this form must be placed behind an SSL browser 
    connection (https://).  Your recipient must know the secret 
    'seed' you use to encrypt your message.  Without it, your message 
    remains a meaningless jumble.<p>
    During Encryption, I take your 'seed', which should be a random 
    jumble of letters and numbers (think 'password'), and encrypt 
    that using <a href="http://www.faqs.org/rfcs/rfc1321.html">MD5 
    encryption</a>.  That produces a string of letters and number 
    that I use as a  <a href="http://pubweb.nfr.net/~mjr/pubs/otpfaq/">
    one time pad</a> against the text of your message.  As a last step, 
    I put everything into hexcode so that you can copy and paste it into 
    an email message.  A checksum is produced from this hexcode so that 
    your recipient knows that they received an unaltered message.<p>
    To decode a message, you need three pieces of info.  The encoded 
    text, the checksum (to verify the encoded text is unaltered) and 
    the 'seed' code.
      
    
    <hr>
    Encrypt! <form method="post" action="commlink.cgi">
    Text: <textarea name="textinput" cols="45" rows="10"></textarea>
    Seed: <input type="text" name="seeder">
    <input type="hidden" name="action" value="encoder">
    <input type="submit"></form>
    <hr>
    <hr>DECRYPT
    <form method="post" action="commlink.cgi">
    Text: <textarea name="textinput" cols="45" rows="10"></textarea>
    Checksum: <input type="text" name="checksum">
    Seed: <input type="text" name="seed">
    <input type="hidden" name="action" value="decoder">
    <input type="submit"></form> <p> Written by Richard Still at Oakbox.co
    +m 
    &copy; 2001. There are NO guarantees about the utility of this script 
    for any particular purpose!<br>  Thanks to Kurt Kincaid, author of 
    Crypt-OTP module (available on CPAN), for his OTP code!);
    
    }
    
    sub shellout {
    print "Content-type: text/html\n\n";
    print<<_TTT_;
    <html>
    <head>
    <title>Cryptomatic by Oakbox</title>
    </head>
    <body>
    $message
    </body>
    </html>
    
    _TTT_
    
    }
    
    sub pad_it {
    
    # Credit to Kurt Kincaid, author of Crypt-OTP module, 
    # available on CPAN, for this chunk of code!
    
    my ($raw_text,$pad_text)=@_;
    
        while ( length($pad_text) < length($raw_text) ) {
            $pad_text .= $pad_text;
        }
        my @bart = split ( //, $raw_text );
        my @pad     = split ( //, $pad_text );
        my $cipher  = ();
        my $i;
    
        for ( $i = 0 ; $i <= $#bart ; $i++ ) {
            $cipher .= pack( 'C', unpack( 'C', $bart[$i] ) ^ unpack( 'C', 
    +$pad[$i] ) );
        }
    
    return($cipher);
    }
    
    
    # Modifications that I'm too lazy to make:
    #
    # - To make this more secure, you should block the number of 'decrypt'
    # attempts any single IP can make in an hour.  
    # - You can have encryptions 'expire' by tacking the Julian date
    # onto the end of the entered seed.
    # - If you want to do this yourself, and use it for personal purposes
    # only, I would install and use Kurt Kincaid's full Crypt-OTP module
    # which allows you to use uploaded files as pads.
    # - What happens if you don't have MD5?  Modifications to accomodate
    # DES, Blowfish, or Triple-DES should be relatively easy :)
    
    Replies are listed 'Best First'.
    Re: Web Cryptomatic
    by no_slogan (Deacon) on Sep 22, 2001 at 01:13 UTC
      This is just simple xor encryption using a 16-byte key generated with MD5. Completely insecure. The entire point of a one-time pad is that you NEVER REUSE A KEY BYTE. Your scheme reuses the same key byte once for each 16 bytes of plaintext.

      I just had a look at Crypt::OTP. Gack. Let's just say that the author was cryptographically unsophisticated.

        I just had a look at Crypt::OTP. Gack. Let's just say that the author was cryptographically unsophisticated.

        Ouch. That's a little uncalled for, I think. As I said in the POD for Crypt::OTP, the safest method (that is, the method that should be used) is to use a large pad file. I take for granted that you're referring to the second, substantially less secure method that I worked into Crypt::OTP. Again, as I said in the POD, it is substantially less secure. That method, quite obviously, isn't intended to be used for anything that requires any serious degree of security. I included it in the module because 1.) it was already there because I used it for testing purposes; 2.) some people are going to use the module that way anyway; and 3.) it is handy for things that really only require the most modest level of security. Your point on the proper use of one-time pad encryption is absolutely correct. You aren't supposed to reuse anything. Period. End of discussion. But as with any tool, it is only as good as the way that you use it. If you use it in an insecure fashion, it will be insecure. The shortcoming is not a result of being "cryptographically unsophisticated." I'm not about to sit here and claim to be an international authority on the subject of cryptography. However, I have more than my fair share of experience in the field. I've read the books, attended the seminars, taken the classes, had memberships at one time or another in probably six or eight security related organizations, etc., etc., etc. At no time have I ever claimed that my implementation of OTP is the cryptographic magic bullet, so to speak. If you use it correctly, it will serve you in good stead. If you use it incorrectly, well, you're doing it at your own risk. Taking all of that into account, that you would make assumptions about my level of cryptographic sophistication without knowing me or anything about me, I find rather disturbing.
        ___________________
        Kurt
          Maybe I was a bit too harsh. Sorry about that. But I'm going to stick to my guns and maintain that Crypt::OTP is deeply flawed. It solves the easy problem (xoring the pad into the message), but provides no help at all on the difficult problem (key management). If a one-time pad is going to be secure, it is absolutely critical that no part of the pad ever be reused to encrypt a second message. Recovering plaintexts encrypted with the same key is easy for a cryptanalyst (try it sometime, it's kind of fun). It would be useful if Crypt::OTP would help remember which parts of the pad have already been used, but it doesn't. Even if I remember which parts I've used, there's no way to tell Crypt::OTP to seek to the unused parts. I have to extract the unused portion into a temp file, then pass that to Crypt::OTP, and wipe it afterwards. I also need to make sure there are enough bytes left in the pad to encrypt the message, or Crypt::OTP will happily recycle key bytes. It's a lot of work to use this module securely.

          To make matters worse, none of this is explained in the module documentation. And there's some rather bad advice in there to boot. You suggest that the pad file be a "semi-random text file." Wrong -- in order to be secure, a one-time pad must be completely random. Any patterns in the pad provide a handhold for the cryptanalyst. Also, a lot of people are going to interpret "text file" to mean "a file of English text." You reinforce that idea by showing an English phrase as the key in your less secure example. However, recovering an English text message encrypted with an English text running key is an easy problem for someone who knows what they're doing. So maybe you really do know your stuff, but you sure didn't show it in Crypt::OTP.

        I appreciate the feedback. My original problem was "How to send short strings of text (less that 256 characters) in a hidden form field between two different web sites?" I needed to make the string encrypted enough to not be worth breaking (I'm not passing CC numbers or anything like that).

        After looking at some different schemes, my problem became 'How do I whip up a good pad?' and this was my answer. My question is, HOW secure is the above scheme? Are we talking about a couple of hours or a couple of minutes to crack? What if the pad was 32 bytes long? 64?

        I whipped up the above code so that I could get some good crypto advice and maybe make something useful. I guess my 'big' question is, can someone break into a short section of text (~256 bytes), encrypted by this engine, without the password?


        -oakbox

          It doesn't matter how long your "pad" is, if you cycle through it in a predictable fashion. If the message is longer than the key, you will reuse key bytes. If you reuse key bytes, you're a sitting duck. 256 bytes of encrypted English text would be easy to break in a matter of minutes. Half that or less would probably be enough.

          When you're doing crypto, don't try to get clever. Use a real cipher module like Crypt::Rijndael or Crypt::IDEA or whatever. There was an article on crypto modules on perl.com recently.

          If for some reason you can't use one of those, there are secure ways to use hash functions like MD5 for encryption. You have to be very careful, though, because that's not what they were designed to do. For details, see <cite>Applied Cryptography</cite>.

    (ichimunki) Re: Web Cryptomatic
    by ichimunki (Priest) on Sep 22, 2001 at 04:30 UTC
      no slogan's criticism is correct. You should probably error out if the pad is too short to effectively cover the entire raw text. Otherwise you lose the whole benefit of having a one time pad.

      Almost, and maybe even more important, is that you have some Perl 4 idioms scattered throughout your script-- especially in the CGI-specific areas. Maybe you've been reading some older editions for examples of good ways to do stuff. You should definitely use the CGI.pm module to accept and parse the form data. It also makes a nice touch to use the CGI.pm to produce your HTML-- especially for making forms, since it excels at that sort of thing.

    Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Domain Nodelet?
    Node Status?
    node history
    Node Type: sourcecode [id://113882]
    help
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this?Last hourOther CB clients
    Other Users?
    Others musing on the Monastery: (7)
    As of 2024-04-23 16:54 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found