One-time pad: Difference between revisions
No edit summary |
Thundergnat (talk | contribs) (→{{header|Perl 6}}: Add a perl 6 example) |
||
Line 39: | Line 39: | ||
* [http://search.cpan.org/~sifukurt/Crypt-OTP-2.00/OTP.pm Crypt-OTP-2.00] on CPAN (Perl) |
* [http://search.cpan.org/~sifukurt/Crypt-OTP-2.00/OTP.pm Crypt-OTP-2.00] on CPAN (Perl) |
||
<br><br> |
<br><br> |
||
=={{header|Perl 6}}== |
|||
{{works with|Rakudo|2017.10}} |
|||
The task is somewhat under-specified, especially the third (optional) section so I'm skipping that for now. Each sub-task has it's own code. |
|||
'''Sub-task one:''' generate one-time-pad files. |
|||
This is a fairly basic otp file generator. Uses Crypt::Random for decently high quality random numbers. (Random bytes are drawn from /dev/urandom on Unix-like systems, and CryptGenRandom() on Windows.) It will ask for a file name to save to, and the number of lines you want. Each line can be used to encode up to 48 characters of data. Default is 1000 lines, Only generating 4 lines here for demonstration purposes. Saving the file to 'rosettacode.1tp'. |
|||
<lang perl6>sub MAIN { |
|||
put "Generate data for one time pad encryption.\n" ~ |
|||
"File will have .1tp extension."; |
|||
my $fn; |
|||
loop { |
|||
$fn = prompt 'Filename for one time pad data: '; |
|||
if $fn !~~ /'.1tp' $/ { $fn ~= '.1tp' } |
|||
if $fn.IO.e { |
|||
my $ow = prompt "$fn aready exists, over-write? y/[n] "; |
|||
last if $ow ~~ m:i/'y'/; |
|||
redo; |
|||
} |
|||
last; |
|||
} |
|||
put 'Each line will contain 48 characters of encyption data.'; |
|||
my $lines = prompt 'How many lines of data to generate? [1000] '; |
|||
$lines ||= 1000; |
|||
generate($fn, $lines); |
|||
say "One-time-pad data saved to: ", $fn.IO.absolute; |
|||
sub generate ( $fn, $lines) { |
|||
use Crypt::Random; |
|||
$fn.IO.spurt: "# one-time-pad encryption data\n" ~ |
|||
((sprintf(" %s %s %s %s %s %s %s %s\n", |
|||
((('A'..'Z')[crypt_random_uniform(26)] xx 6).join) xx 8)) |
|||
xx $lines).join; |
|||
} |
|||
}</lang> |
|||
{{out}} |
|||
<pre>Generate data for one time pad encryption. |
|||
File wile have .1tp extension. |
|||
Filename for one time pad data: rosettacode |
|||
Each line will contain 48 characters of encyption data. |
|||
How many lines of data to generate? [1000] 4 |
|||
One-time-pad data saved to: /home/myhome/mydirectory/rosettacode.1tp |
|||
</pre> |
|||
Sample file generated by above code: |
|||
<pre># one-time-pad encryption data |
|||
DSUJOU UUWDZD VHFWRR AJDMDC ERZDGD WWKLHJ YITCML FORXCV |
|||
BXGFCL ANGCGY VTAEUG UYAIPK FXWMNI INDLOR JIDZQL BOFFQD |
|||
JISNOS CMLRPW TFGELQ HPTMRN SHBBDP AIVDAC CEWIFH TRLQVK |
|||
FRBUUC GDCQHQ CEEURS RGVWVT JZIQLP NQCABF BWUPUI UDTZAF</pre> |
|||
'''Sub-task two:''' encrypt/decrypt text using the otp files generated by part one. |
|||
One-time-pad encryption gets it's security from the fact that the pads are used '''one time'''. As a line is used in the otp file, it needs to be marked as used, or removed so it doesn't get reused. Theoretically, you would make a copy of rosettacode.1tp and send it by secure means to the receiver of your encrypted text so that they can use it to decrypt. Since we are encrypting and decrypting on the same computer, we'll make a copy of the otp file named rosettacopy.1tp and use that for decryption so the encrypt and decrypt functions don't conflict. |
|||
<lang perl6>sub s2v ($s) { $s.uc.comb(/ <[ A..Z ]> /)».ord »-» 65 } |
|||
sub v2s (@v) { (@v »%» 26 »+» 65)».chr.join } |
|||
sub hide ($secret, $otp) { v2s(s2v($secret) »+» s2v($otp)) } |
|||
sub reveal ($hidden, $otp) { v2s(s2v($hidden) »-» s2v($otp)) } |
|||
sub otp-data ($fn, $lines) { |
|||
my $fh = $fn.IO.open :rw; |
|||
my $data; |
|||
my $count = 0; |
|||
repeat { |
|||
my $pos = $fh.tell; |
|||
my $line = $fh.get; |
|||
if $line.substr(0,1) ne '-'|'#' { |
|||
$data ~= $line; |
|||
$fh.seek($pos); |
|||
$fh.put: '-' ~ $line.substr(1); |
|||
$count++; |
|||
} |
|||
} until $count == $lines or $fh.eof; |
|||
note "Insufficient lines of data remaining in $fn" if $count != $lines; |
|||
$data; |
|||
} |
|||
sub otp-size (Str $string) { ceiling $string.uc.comb(/ <[ A..Z ]> /) / 48 } |
|||
sub otp-encrypt ( $secret, $fn ) { |
|||
my $otp-size = otp-size $secret; |
|||
my $otp-data = otp-data($fn, $otp-size); |
|||
my $encrypted = hide $secret, $otp-data; |
|||
# pad encryted text out to a full line with random text |
|||
$encrypted ~= ('A'..'Z').roll while $encrypted.chars % 48; |
|||
join "\n", $encrypted.comb(6).rotor(8, :partial).map: |
|||
{ sprintf "{ join ' ', "%s" xx $_ }", $_ }; |
|||
} |
|||
sub otp-decrypt ( $secret, $fn ) { |
|||
my $otp-size = otp-size $secret; |
|||
my $otp-data = otp-data($fn, $otp-size); |
|||
my $plain-text = reveal $secret, $otp-data; |
|||
join "\n", $plain-text.comb(6).rotor(8, :partial).map: |
|||
{ sprintf "{ join ' ', "%s" xx $_ }", $_ }; |
|||
} |
|||
my $otp-encrypt-fn = 'rosettacode.1tp'; |
|||
my $otp-decrypt-fn = 'rosettacopy.1tp'; |
|||
my $secret = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
say "Secret:\n$secret\n\nEncrypted:"; |
|||
say my $encrypted = otp-encrypt $secret, $otp-encrypt-fn; |
|||
say "\nDecrypted:\n", otp-decrypt $encrypted, $otp-decrypt-fn;</lang> |
|||
{{out}} |
|||
<pre>Secret: |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Encrypted: |
|||
EWQJFY NBAMZE WLWSFT KVBERP XYDMGZ OPRLAK GBXVTP HZRTUO |
|||
IXZHCE CUUORL THOFDI DCXRLG JQICPI ZEREHP RLOEAE PRMVJH |
|||
Decrypted: |
|||
BEWARE THEJAB BERWOC KMYSON THEJAW STHATB ITETHE CLAWST |
|||
HATCAT CHOMLN YOOBJC JEXJWW ETMQCA RROTTY IDLFKT ODHQTE</pre> |
|||
Contents of rosettacode.1tp after encryption / rosettacopy.1tp after decryption: |
|||
<pre># one-time-pad encryption data |
|||
-DSUJOU UUWDZD VHFWRR AJDMDC ERZDGD WWKLHJ YITCML FORXCV |
|||
-BXGFCL ANGCGY VTAEUG UYAIPK FXWMNI INDLOR JIDZQL BOFFQD |
|||
JISNOS CMLRPW TFGELQ HPTMRN SHBBDP AIVDAC CEWIFH TRLQVK |
|||
FRBUUC GDCQHQ CEEURS RGVWVT JZIQLP NQCABF BWUPUI UDTZAF</pre> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
Revision as of 18:55, 18 November 2017
Implement a One-time pad, for encrypting and decrypting messages.
To keep it simple, we will be using letters only.
- Sub-Tasks
- Generate the data for a One-time pad (user needs to specify a filename and length)
- The important part is to get "true random" numbers, e.g. from /dev/random
- encryption / decryption ( basically the same operation, much like Rot-13 )
- For this step, much of Vigenère cipher could be reused,
with the key to be read from the file containing the One-time pad.
- optional: management of One-time pads: list, mark as used, delete, etc.
- Somehow, the users needs to keep track which pad to use for which partner.
To support the management of pad-files:
- Such files have a file-extension ".1tp"
- Lines starting with "#" may contain arbitary meta-data (i.e. comments)
- Lines starting with "-" count as "used"
- Whitespace within the otp-data is ignored
For example, here is the data from Wikipedia:
# Example data - Wikipedia - 2014-11-13 -ZDXWWW EJKAWO FECIFE WSNZIP PXPKIY URMZHI JZTLBC YLGDYJ -HTSVTV RRYYEG EXNCGA GGQVRF FHZCIB EWLGGR BZXQDQ DGGIAK YHJYEQ TDLCQT HZBSIZ IRZDYS RBYJFZ AIRCWI UCVXTW YKPQMK CKHVEX VXYVCS WOGAAZ OUVVON GCNEVR LMBLYB SBDCDC PCGVJX QXAUIP PXZQIJ JIUWYH COVWMJ UZOJHL DWHPER UBSRUJ HGAAPR CRWVHI FRNTQW AJVWRT ACAKRD OZKIIB VIQGBK IJCWHF GTTSSE EXFIPJ KICASQ IOUQTP ZSGXGH YTYCTI BAZSTN JKMFXI RERYWE
- See also
- one time pad encryption in Python
- snapfractalpop - One-Time-Pad Command-Line-Utility (C).
- Crypt-OTP-2.00 on CPAN (Perl)
Perl 6
The task is somewhat under-specified, especially the third (optional) section so I'm skipping that for now. Each sub-task has it's own code.
Sub-task one: generate one-time-pad files.
This is a fairly basic otp file generator. Uses Crypt::Random for decently high quality random numbers. (Random bytes are drawn from /dev/urandom on Unix-like systems, and CryptGenRandom() on Windows.) It will ask for a file name to save to, and the number of lines you want. Each line can be used to encode up to 48 characters of data. Default is 1000 lines, Only generating 4 lines here for demonstration purposes. Saving the file to 'rosettacode.1tp'.
<lang perl6>sub MAIN {
put "Generate data for one time pad encryption.\n" ~ "File will have .1tp extension."; my $fn; loop { $fn = prompt 'Filename for one time pad data: '; if $fn !~~ /'.1tp' $/ { $fn ~= '.1tp' } if $fn.IO.e { my $ow = prompt "$fn aready exists, over-write? y/[n] "; last if $ow ~~ m:i/'y'/; redo; } last; }
put 'Each line will contain 48 characters of encyption data.'; my $lines = prompt 'How many lines of data to generate? [1000] '; $lines ||= 1000; generate($fn, $lines); say "One-time-pad data saved to: ", $fn.IO.absolute;
sub generate ( $fn, $lines) { use Crypt::Random; $fn.IO.spurt: "# one-time-pad encryption data\n" ~ ((sprintf(" %s %s %s %s %s %s %s %s\n", ((('A'..'Z')[crypt_random_uniform(26)] xx 6).join) xx 8)) xx $lines).join; }
}</lang>
- Output:
Generate data for one time pad encryption. File wile have .1tp extension. Filename for one time pad data: rosettacode Each line will contain 48 characters of encyption data. How many lines of data to generate? [1000] 4 One-time-pad data saved to: /home/myhome/mydirectory/rosettacode.1tp
Sample file generated by above code:
# one-time-pad encryption data DSUJOU UUWDZD VHFWRR AJDMDC ERZDGD WWKLHJ YITCML FORXCV BXGFCL ANGCGY VTAEUG UYAIPK FXWMNI INDLOR JIDZQL BOFFQD JISNOS CMLRPW TFGELQ HPTMRN SHBBDP AIVDAC CEWIFH TRLQVK FRBUUC GDCQHQ CEEURS RGVWVT JZIQLP NQCABF BWUPUI UDTZAF
Sub-task two: encrypt/decrypt text using the otp files generated by part one.
One-time-pad encryption gets it's security from the fact that the pads are used one time. As a line is used in the otp file, it needs to be marked as used, or removed so it doesn't get reused. Theoretically, you would make a copy of rosettacode.1tp and send it by secure means to the receiver of your encrypted text so that they can use it to decrypt. Since we are encrypting and decrypting on the same computer, we'll make a copy of the otp file named rosettacopy.1tp and use that for decryption so the encrypt and decrypt functions don't conflict.
<lang perl6>sub s2v ($s) { $s.uc.comb(/ <[ A..Z ]> /)».ord »-» 65 } sub v2s (@v) { (@v »%» 26 »+» 65)».chr.join }
sub hide ($secret, $otp) { v2s(s2v($secret) »+» s2v($otp)) } sub reveal ($hidden, $otp) { v2s(s2v($hidden) »-» s2v($otp)) }
sub otp-data ($fn, $lines) {
my $fh = $fn.IO.open :rw; my $data; my $count = 0; repeat { my $pos = $fh.tell; my $line = $fh.get; if $line.substr(0,1) ne '-'|'#' { $data ~= $line; $fh.seek($pos); $fh.put: '-' ~ $line.substr(1); $count++; } } until $count == $lines or $fh.eof; note "Insufficient lines of data remaining in $fn" if $count != $lines; $data;
}
sub otp-size (Str $string) { ceiling $string.uc.comb(/ <[ A..Z ]> /) / 48 }
sub otp-encrypt ( $secret, $fn ) {
my $otp-size = otp-size $secret; my $otp-data = otp-data($fn, $otp-size); my $encrypted = hide $secret, $otp-data; # pad encryted text out to a full line with random text $encrypted ~= ('A'..'Z').roll while $encrypted.chars % 48; join "\n", $encrypted.comb(6).rotor(8, :partial).map: { sprintf "{ join ' ', "%s" xx $_ }", $_ };
}
sub otp-decrypt ( $secret, $fn ) {
my $otp-size = otp-size $secret; my $otp-data = otp-data($fn, $otp-size); my $plain-text = reveal $secret, $otp-data; join "\n", $plain-text.comb(6).rotor(8, :partial).map: { sprintf "{ join ' ', "%s" xx $_ }", $_ };
}
my $otp-encrypt-fn = 'rosettacode.1tp'; my $otp-decrypt-fn = 'rosettacopy.1tp';
my $secret = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!";
say "Secret:\n$secret\n\nEncrypted:"; say my $encrypted = otp-encrypt $secret, $otp-encrypt-fn; say "\nDecrypted:\n", otp-decrypt $encrypted, $otp-decrypt-fn;</lang>
- Output:
Secret: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Encrypted: EWQJFY NBAMZE WLWSFT KVBERP XYDMGZ OPRLAK GBXVTP HZRTUO IXZHCE CUUORL THOFDI DCXRLG JQICPI ZEREHP RLOEAE PRMVJH Decrypted: BEWARE THEJAB BERWOC KMYSON THEJAW STHATB ITETHE CLAWST HATCAT CHOMLN YOOBJC JEXJWW ETMQCA RROTTY IDLFKT ODHQTE
Contents of rosettacode.1tp after encryption / rosettacopy.1tp after decryption:
# one-time-pad encryption data -DSUJOU UUWDZD VHFWRR AJDMDC ERZDGD WWKLHJ YITCML FORXCV -BXGFCL ANGCGY VTAEUG UYAIPK FXWMNI INDLOR JIDZQL BOFFQD JISNOS CMLRPW TFGELQ HPTMRN SHBBDP AIVDAC CEWIFH TRLQVK FRBUUC GDCQHQ CEEURS RGVWVT JZIQLP NQCABF BWUPUI UDTZAF
Racket
Tcl
Part 1: random strings
Get true random numbers, and turn them into strings.
With "randInt" from Tcl'ers wiki Cryptographically secure random numbers using /dev/urandom
<lang Tcl>puts "# True random chars for one-time pad"
proc randInt { min max } {
set randDev [open /dev/urandom rb] set random [read $randDev 8] binary scan $random H16 random set random [expr {([scan $random %x] % (($max-$min) + 1) + $min)}] close $randDev return $random
}
proc randStr { sLen grp alfa } {
set aLen [string length $alfa]; incr aLen -1 set rs "" for {set i 0} {$i < $sLen} {incr i} { if { [expr {$i % $grp} ] == 0} { append rs " " } set r [randInt 0 $aLen] set char [string index $alfa $r] append rs $char ##puts "$i: $r $char" } return $rs
}
set alfa "ABCDEFGHIJKLMNOPQRSTUVWXYZ" set len 48 set lines 4 set fn "test.1tp"
- Write file:
set fh [open $fn w] puts $fh "# OTP" for {set ln 0} {$ln < $lines} {incr ln} {
set line [randStr $len 6 $alfa] ##puts "$ln :$line." puts $fh $line
} close $fh
- Read file:
puts "# File $fn:" set fh [open $fn] puts [read $fh [file size $fn]] close $fh
puts "# Done."</lang>
- Output:
# True random chars for one-time pad # File test.1tp: # OTP OWCTEL SGDQEA UKEWCU PUTDEA XICBOL VVMJHD OHAXSE ZFAGDE QHDHKQ CCJBYF CMRCMC IXXPVM IOHQDA XIDTPX FGRIJC NPDOAT MYYQUV ZVKGDF ZLYKSX MBPLON RMQKQT QDYJVO LNKUFV DNKIQP NQOZKU MQOWHS VOQFWL EQWBFA HZQAMG JWNHGZ QERNNV GBKQTM # Done.
Part 2: Encrypt/Decrypt
See Tcl'ers wiki: vignere Vigenere ...
Part 3: Management
- list padfiles in directory
- list lines / blocks between "comment"-lines in padfile (i.e. remaining usable data)
...