diff options
Diffstat (limited to 'tcllib/modules/rc4')
-rw-r--r-- | tcllib/modules/rc4/ChangeLog | 134 | ||||
-rw-r--r-- | tcllib/modules/rc4/pkgIndex.tcl | 13 | ||||
-rw-r--r-- | tcllib/modules/rc4/rc4.bench | 64 | ||||
-rw-r--r-- | tcllib/modules/rc4/rc4.man | 120 | ||||
-rw-r--r-- | tcllib/modules/rc4/rc4.tcl | 422 | ||||
-rw-r--r-- | tcllib/modules/rc4/rc4.test | 273 | ||||
-rw-r--r-- | tcllib/modules/rc4/rc4c.tcl | 168 |
7 files changed, 1194 insertions, 0 deletions
diff --git a/tcllib/modules/rc4/ChangeLog b/tcllib/modules/rc4/ChangeLog new file mode 100644 index 0000000..f140cdf --- /dev/null +++ b/tcllib/modules/rc4/ChangeLog @@ -0,0 +1,134 @@ +2013-02-08 Andreas Kupries <andreask@activestate.com> + + * rc4.man: fixed bogus trailing comma in list of keywords. + +2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.15 ======================== + * + +2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.14 ======================== + * + +2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.13 ======================== + * + +2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.12 ======================== + * + +2009-05-07 Pat Thoyts <patthoyts@users.sourceforge.net> + + * rc4c.tcl: Fixed an object leak in the critcl implementation + +2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11.1 ======================== + * + +2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11 ======================== + * + +2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.10 ======================== + * + +2007-03-21 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * rc4.man: Fixed all warnings due to use of now deprecated + commands. Added a section about how to give feedback. + +2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.9 ======================== + * + +2006-01-29 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * rc4.test: Fixed use and cleanup of temp. files + +2006-01-23 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * rc4.test: More boilerplate simplified via use of test support. + +2006-01-19 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * rc4.test: Hooked into the new common test support code. + +2005-12-20 Pat Thoyts <patthoyts@users.sourceforge.net> + + * rc4.tcl: Dealt with bug #1386101 (rc4 critcl + channels broken) + * rc4.test: Also implemented a -command option. Added tests for + * rc4.man: both and update man page for -command. + * pkgIndex.tcl: Incremented version to 1.1.0 + +2005-10-18 Andreas Kupries <andreask@activestate.com> + + * rc4.bench: Extended with benchmarks for the keyschedule. + +2005-10-06 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.8 ======================== + * + +2005-10-05 Pat Thoyts <patthoyts@users.sourceforge.net> + + * rc4c.tcl: Fixed to permit compilation with msvc and gcc. + +2005-09-04 Pat Thoyts <patthoyts@users.sourceforge.net> + + * rc4.tcl: Frink error suppression. + * rc4.man: Added documentation for the programming api. + +2005-02-20 Pat Thoyts <patthoyts@users.sourceforge.net> + + * rc4.tcl: Ensure all implementations available are tested. Added + * rc4.man: improved hypen handling. '--' is an optional end-of-options + * pkgIndex.tcl: marker. + +2004-10-05 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.7 ======================== + * + +2004-07-29 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * rc4.test: Fixed x-version problems of the testsuite. The tests + wrongly used the tcltest::wrongNumArgs command. In this case the + error message is generated by the proc code and the same across + all versions of Tcl. + +2004-07-04 Pat Thoyts <patthoyts@users.sourceforge.net> + + * rc4.test: Added basic command tests. + * rc4c.tcl: Critcl implementation. + * rc4.tcl: Support critcl implementation. + + * rc4.tcl: Storing the state as a list and using lset is faster + than using an array provided we have a built-in lset + command. Added both versions and switch appropriately to get the + fastest implementation for the tcl version. Tested 8.2 - 8.5. + +2004-07-01 Pat Thoyts <patthoyts@users.sourceforge.net> + + * rc4.tcl: Pure tcl implementation of the Alledged RC4 stream cipher. + * rc4.test: Some published test vectors for RC4. + * rc4.man: Documentation. diff --git a/tcllib/modules/rc4/pkgIndex.tcl b/tcllib/modules/rc4/pkgIndex.tcl new file mode 100644 index 0000000..84a5d47 --- /dev/null +++ b/tcllib/modules/rc4/pkgIndex.tcl @@ -0,0 +1,13 @@ +# pkgIndex.tcl - +# +# RC4 package index file +# +# This package has been tested with tcl 8.2.3 and above. +# +# $Id: pkgIndex.tcl,v 1.4 2005/12/20 16:19:38 patthoyts Exp $ + +if {![package vsatisfies [package provide Tcl] 8.2]} { + # PRAGMA: returnok + return +} +package ifneeded rc4 1.1.0 [list source [file join $dir rc4.tcl]] diff --git a/tcllib/modules/rc4/rc4.bench b/tcllib/modules/rc4/rc4.bench new file mode 100644 index 0000000..ba3e3c8 --- /dev/null +++ b/tcllib/modules/rc4/rc4.bench @@ -0,0 +1,64 @@ +# -*- tcl -*- +# Tcl Benchmark File +# +# This file contains a number of benchmarks for the 'rc4' module. +# This allow developers to monitor/gauge/track package performance. +# +# (c) 2005 Andreas Kupries <andreas_kupries@users.sourceforge.net> + +# We need at least version 8.2 for the package and thus the +# benchmarks. + +if {![package vsatisfies [package provide Tcl] 8.2]} { + return +} + +# ### ### ### ######### ######### ######### ########################### +## Setting up the environment ... + +set moddir [file dirname [file dirname [info script]]] +lappend auto_path $moddir + +package forget rc4 +catch {namespace delete ::rc4} +source [file join [file dirname [info script]] rc4.tcl] + +set i [binary format H* 0000000000000000] +set p [binary format H* 0123456789ABCDEF0123456789ABCDEF]] + +set k [binary format H* FEDCBA9876543210] +set c [binary format H* ED39D950FA74BCC4ED39D950FA74BCC4] + +# ### ### ### ######### ######### ######### ########################### +## Benchmarks. + +bench -desc "RC4 encryption" -body { + rc4::rc4 -key $k $p +} + +bench -desc "RC4 decryption" -body { + rc4::rc4 -key $k $c +} + +bench -desc "RC4 encryption core" -pre { + set key [rc4::RC4Init $k] +} -body { + rc4::RC4 $key $p +} -post { + rc4::RC4Final $key +} + +bench -desc "RC4 decryption core" -pre { + set key [rc4::RC4Init $k] +} -body { + rc4::RC4 $key $c +} -post { + rc4::RC4Final $key +} + +bench -desc "RC4 keyschedule" -body { + rc4::RC4Final [rc4::RC4Init $k] +} + +# ### ### ### ######### ######### ######### ########################### +## Complete diff --git a/tcllib/modules/rc4/rc4.man b/tcllib/modules/rc4/rc4.man new file mode 100644 index 0000000..d257772 --- /dev/null +++ b/tcllib/modules/rc4/rc4.man @@ -0,0 +1,120 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin rc4 n 1.1.0] +[see_also aes(n)] +[see_also blowfish(n)] +[see_also des(n)] +[keywords arcfour] +[keywords {data integrity}] +[keywords encryption] +[keywords rc4] +[keywords security] +[keywords {stream cipher}] +[copyright {2003, Pat Thoyts <patthoyts@users.sourceforge.net>}] +[moddesc {RC4 Stream Cipher}] +[titledesc {Implementation of the RC4 stream cipher}] +[category {Hashes, checksums, and encryption}] +[require Tcl 8.2] +[require rc4 [opt 1.1.0]] +[description] +[para] + +This package is an implementation in Tcl of the RC4 stream cipher +developed by Ron Rivest of RSA Data Security Inc. The cipher was a +trade secret of RSA but was reverse-engineered and published to the +internet in 1994. It is used in a number of network protocols for +securing communications. To evade trademark restrictions this cipher +is sometimes known as ARCFOUR. + +[section {COMMANDS}] + +[list_begin definitions] + +[call [cmd "::rc4::rc4"] \ + [opt "[arg -hex]"] \ + [arg "-key keyvalue" ] \ + [opt [arg "-command lst"]] \ + [opt [arg "-out channel"]] \ + [lb] [arg "-in channel"] | \ + [arg "-infile filename"] | [arg "string"] [rb]] + +Perform the RC4 algorithm on either the data provided by the argument +or on the data read from the [arg "-in"] channel. If an [arg "-out"] +channel is given then the result will be written to this channel. +Giving the [arg "-hex"] option will return a hexadecimal encoded +version of the result if not using an [arg -out] channel. + +[para] + +The data to be processes can be specified either as a string argument to +the rc4 command, or as a filename or a pre-opened channel. If the +[arg "-infile"] argument is given then the file is opened, the data read +and processed and the file is closed. If the [arg "-in"] argument is +given then data is read from the channel until the end of file. The +channel is not closed. If the [arg "-out"] argument is given then the +processing result is written to this channel. + +[para] + +If [arg "-command"] is provided then the rc4 command does not return +anything. Instead the command provided is called with the rc4 result data +appended as the final parameter. This is most useful when reading from Tcl +channels as a fileevent is setup on the channel and the data processed in +chunks + +[para] + +Only one of [arg "-infile"], [arg "-in"] or [arg "string"] should be given. + +[list_end] + +[section "PROGRAMMING INTERFACE"] + +[list_begin definitions] + +[call [cmd "::rc4::RC4Init"] [arg "keydata"]] + +Initialize a new RC4 key. The [arg keydata] is any amount of binary +data and is used to initialize the cipher internal state. + +[call [cmd "::rc4::RC4"] [arg "Key"] [arg "data"]] + +Encrypt or decrypt the input data using the key obtained by calling +[cmd RC4Init]. + +[call [cmd "::rc4::RC4Final"] [arg "Key"]] + +This should be called to clean up resources associated with +[arg Key]. Once this function has been called the key is destroyed. + +[list_end] + +[section "EXAMPLES"] + +[example { +% set keydata [binary format H* 0123456789abcdef] +% rc4::rc4 -hex -key $keydata HelloWorld +3cf1ae8b7f1c670b612f +% rc4::rc4 -hex -key $keydata [binary format H* 3cf1ae8b7f1c670b612f] +HelloWorld +}] + +[example { + set Key [rc4::RC4Init "key data"] + append ciphertext [rc4::RC4 $Key $plaintext] + append ciphertext [rc4::RC4 $Key $additional_plaintext] + rc4::RC4Final $Key +}] + +[example { + proc ::Finish {myState data} { + DoStuffWith $myState $data + } + rc4::rc4 -in $socket -command [list ::Finish $ApplicationState] +}] + +[section "AUTHORS"] +Pat Thoyts + +[vset CATEGORY rc4] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/rc4/rc4.tcl b/tcllib/modules/rc4/rc4.tcl new file mode 100644 index 0000000..37b4a12 --- /dev/null +++ b/tcllib/modules/rc4/rc4.tcl @@ -0,0 +1,422 @@ +# rc4.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net> +# +# RC4 is a symmetric stream cipher developed by Ron Rivest of RSA Data +# Security Inc. The algorithm was a trade secret of RSA but was reverse +# engineered and published to the internet in 1994. This pure Tcl +# implementation is based on the description of the algorithm. +# +# The algorithm is a pseudo-random number generator with the output of +# the PRNG being xored with the plaintext stream. Decryption is done +# by feeding the ciphertext as input with the same key. + +package require Tcl 8.2 + +# @mdgen EXCLUDE: rc4c.tcl + +namespace eval ::rc4 { + namespace export rc4 + + variable uid + if {![info exists uid]} { + set uid 0 + } +} + +# RC4Init - create and initialize the RC4 state as an array +# +proc ::rc4::RC4Init_Array {keystr} { + variable uid + + binary scan $keystr c* key + set keylen [llength $key] + + set Key [namespace current]::key[incr uid] + # FRINK: nocheck + variable $Key + upvar #0 $Key state + catch {unset state} + + set state(x) 0 + set state(y) 0 + for {set cn 0} {$cn < 256} {incr cn} { + set state(s,$cn) $cn + } + set i 0 + set j 0 + for {set cn 0} {$cn < 256} {incr cn} { + set j [expr {([lindex $key $i] + $state(s,$cn) + $j) % 256}] + set t $state(s,$cn) + set state(s,$cn) $state(s,$j) + set state(s,$j) $t + set i [expr {($i + 1) % $keylen}] + } + + return $Key +} + +# RC4 - process the data using the array based state +# +proc ::rc4::RC4_Array {Key datastr} { + upvar #0 $Key state + set res {} + + binary scan $datastr c* data + set datalen [llength $data] + + set x $state(x) + set y $state(y) + + for {set cn 0} {$cn < $datalen} {incr cn} { + set x [expr {($x + 1) % 256}] + set y [expr {($state(s,$x) + $y) % 256}] + set t $state(s,$y) + set state(s,$y) $state(s,$x) + set state(s,$x) $t + set i [expr {($state(s,$x) + $state(s,$y)) % 256}] + lappend res [expr {([lindex $data $cn] ^ $state(s,$i)) & 0xFF}] + } + set state(x) $x + set state(y) $y + return [binary format c* $res] +} + +# RC4Init - create and initialize the RC4 state as a list. +# +proc ::rc4::RC4Init_List {keystr} { + variable uid + + binary scan $keystr c* key + set keylen [llength $key] + + set Key [namespace current]::key[incr uid] + # FRINK: nocheck + variable $Key + upvar #0 $Key State + catch {unset State} + + set i 0 + set j 0 + set s {}; #[::struct::list::Liota 256] + for {set n 0} {$n < 256} {incr n} {lappend s $n} + + for {set cn 0} {$cn < 256} {incr cn} { + set j [expr {([lindex $key $i] + [lindex $s $cn] + $j) % 256}] + set t [lindex $s $cn] + lset s $cn [lindex $s $j] + lset s $j $t + set i [expr {($i + 1) % $keylen}] + } + + set State(x) 0 + set State(y) 0 + set State(s) $s + + return $Key +} + +# RC4 - process the data using the list-based state. +# +proc ::rc4::RC4_List {Key datastr} { + upvar #0 $Key State + set res {} + + binary scan $datastr c* data + set datalen [llength $data] + + set x $State(x) + set y $State(y) + set s $State(s) + + for {set cn 0} {$cn < $datalen} {incr cn} { + set x [expr {($x + 1) % 256}] + set y [expr {([lindex $s $x] + $y) % 256}] + set t [lindex $s $y] + lset s $y [lindex $s $x] + lset s $x $t + set i [expr {([lindex $s $x] + [lindex $s $y]) % 256}] + lappend res [expr {([lindex $data $cn] ^ [lindex $s $i]) & 0xFF}] + } + set State(x) $x + set State(y) $y + set State(s) $s + return [binary format c* $res] +} + +# PRAGMA: nocheck +proc ::rc4::K {x y} {set x} + +# Using this compat function for < 8.4 is 2x slower than using arrays. +if {[package vcompare [package provide Tcl] 8.4] < 0} { + proc ::rc4::lset {var index arg} { + upvar 1 $var list + set list [::lreplace [K $list [set list {}]] $index $index $arg] + } +} + +proc ::rc4::RC4Final {Key} { + upvar #0 $Key state + catch {unset state} + return {} +} + +# ------------------------------------------------------------------------- +# Helper to turn binary data into hex format. +# +proc ::rc4::Hex {data} { + binary scan $data H* result + return $result +} + +# Demo function for use with Trf transform command to add automatic +# RC4 encryption to a channel. Illustrates use of [transform] +# +# For instance, to create a file with all ondisk data encrypted: +# set f [open secretfile r+] +# transform -attach $f -command [list rc4::Transform $f Secret] +# puts -nonewline $f yourdata ;# write to encrypt +# read $f ;# read to decrypt +# close $f +# +proc ::rc4::Transform {channel keystr operation data} { + set readkey [namespace current]::R$channel + # FRINK: nocheck + variable $readkey + upvar #0 $readkey rk + set writekey [namespace current]::W$channel + # FRINK: nocheck + variable $writekey + upvar #0 $writekey wk + set result {} + + #puts stderr "$operation {$data}" + switch -- $operation { + create/write { + if {[info exists wk]} { + RCFinal $wk + } + set wk [RC4Init $keystr] + } + clear/write {} + delete/write { + if {[info exists wk]} { + RC4Final $wk + unset wk + } + } + write - flush/write { + if {![info exists wk]} { + set wk [RC4Init $keystr] + } + set result [RC4 $wk $data] + } + + create/read { + if {[info exists rk]} { + RCFinal $rk + } + set rk [RC4Init $keystr] + } + clear/read {} + delete/read { + if {[info exists rk]} { + RC4Final $rk + unset rk + } + } + read - flush/read { + if {![info exists rk]} { + set rk [RC4Init $keystr] + } + set result [RC4 $rk $data] + } + + query/ratio { + set result {1 1}; # RC4 is a 1:1 stream cipher. + } + query/maxRead { + set result -1; # Permit read of any amount + } + default { + # ignore unknown operations. + } + } + return $result +} + +# ------------------------------------------------------------------------- +# Description: +# Pop the nth element off a list. Used in options processing. +# +proc ::rc4::Pop {varname {nth 0}} { + upvar $varname args + set r [lindex $args $nth] + set args [lreplace $args $nth $nth] + return $r +} + +# ------------------------------------------------------------------------- +# Fileevent handler for chunked file hashing. +# +proc ::rc4::Chunk {State} { + upvar #0 $State state + + if {[eof $state(-in)]} { + fileevent $state(-in) readable {} + set state(reading) 0 + } + set data [read $state(-in) $state(-chunksize)] + if {[llength $state(-out)] == 0} { + append state(output) [RC4 $state(Key) $data] + } else { + puts -nonewline $state(-out) [RC4 $state(Key) $data] + } + if {!$state(reading) && [llength $state(-command)] != 0} { + Cleanup $State; # cleanup and call users command + } +} + + +proc ::rc4::Cleanup {State} { + upvar #0 $State state + set cmd $state(-command) + set res $state(output) + # If we opened the channel then we should close it too. + if {[string length $state(-infile)] > 0} { + close $state(-in) + } + RC4Final $state(Key) + unset state + if {[llength $cmd] != 0} { + eval $cmd [list $res] + } + return $res +} + +# ------------------------------------------------------------------------- + +proc ::rc4::rc4 {args} { + array set opts {-hex 0 -infile {} -in {} -out {} -chunksize 4096 + -key {} -command {}} + while {[string match -* [set option [lindex $args 0]]]} { + switch -exact -- $option { + -key { set opts(-key) [Pop args 1] } + -hex { set opts(-hex) 1} + -infile { set opts(-infile) [Pop args 1] } + -in { set opts(-in) [Pop args 1] } + -out { set opts(-out) [Pop args 1] } + -chunksize { set opts(-chunksize) [Pop args 1] } + -command { set opts(-command) [Pop args 1] } + default { + if {[llength $args] == 1} { break } + if {[string compare $option "--"] == 0} { Pop args; break } + set err [join [lsort [array names opts]] ", "] + return -code error "bad option $option:\ + must be one of $err" + } + } + Pop args + } + + if {[string length $opts(-key)] < 1} { + return -code error "wrong # args:\ + should be \"rc4 ?-hex? -key key -in channel | string\"" + } + + if {$opts(-infile) != {}} { + set opts(-in) [open $opts(-infile) r] + fconfigure $opts(-in) -translation binary + } + + set r {} + if {$opts(-in) == {}} { + if {[llength $args] != 1} { + return -code error "wrong # args:\ + should be \"rc4 ?-hex? -key key -in channel | string\"" + } + + set Key [RC4Init $opts(-key)] + set r [RC4 $Key [lindex $args 0]] + if {[llength $opts(-command)] != 0} { + eval $opts(-command) [list $r] + set r {} + } elseif {$opts(-out) != {}} { + puts -nonewline $opts(-out) $r + set r {} + } + RC4Final $Key + + } else { + + variable uid + set State [namespace current]::state[incr uid] + upvar #0 $State state + array set state [array get opts] + set state(Key) [RC4Init $opts(-key)] + set state(reading) 1 + set state(output) "" + fileevent $opts(-in) readable [list [namespace origin Chunk] $State] + if {[llength $opts(-command)] != 0} { + return {} + } else { + vwait [set State](reading) + set r [Cleanup $State] + } + } + + if {$opts(-hex)} { + set r [Hex $r] + } + return $r +} + +# ------------------------------------------------------------------------- + +proc ::rc4::SelectImplementation {impl} { + switch -exact -- $impl { + critcl { + interp alias {} ::rc4::RC4Init {} ::rc4::rc4c_init + interp alias {} ::rc4::RC4 {} ::rc4::rc4c + } + array { + interp alias {} ::rc4::RC4Init {} ::rc4::RC4Init_Array + interp alias {} ::rc4::RC4 {} ::rc4::RC4_Array + } + list { + interp alias {} ::rc4::RC4Init {} ::rc4::RC4Init_List + interp alias {} ::rc4::RC4 {} ::rc4::RC4_List + } + default { + return -code error "invalid implementation \"$impl\":\ + must be one of \"critcl\", \"array\" or \"list\"" + } + } +} + +# ------------------------------------------------------------------------- + +# Using a list to hold the keystream state is a lot faster than using +# an array. However, for Tcl < 8.4 we don't have the lset command. +# Using a compatability lset is slower than using arrays. +# Obviously, a compiled C version is fastest of all. +# So lets pick the fastest method we can find... +# +namespace eval ::rc4 { + if {[catch {package require tcllibc}]} { + catch {package require rc4c} + } + if {[info commands ::rc4::rc4c] != {}} { + SelectImplementation critcl + } elseif {[package vcompare [package provide Tcl] 8.4] < 0} { + SelectImplementation array + } else { + SelectImplementation list + } +} + +package provide rc4 1.1.0 + +# ------------------------------------------------------------------------- +# Local variables: +# mode: tcl +# indent-tabs-mode: nil +# End: diff --git a/tcllib/modules/rc4/rc4.test b/tcllib/modules/rc4/rc4.test new file mode 100644 index 0000000..9aabd64 --- /dev/null +++ b/tcllib/modules/rc4/rc4.test @@ -0,0 +1,273 @@ +# rc4.test - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net> +# +# $Id: rc4.test,v 1.12 2006/10/09 21:41:41 andreas_kupries Exp $ + +# ------------------------------------------------------------------------- + +source [file join \ + [file dirname [file dirname [file join [pwd] [info script]]]] \ + devtools testutilities.tcl] + +testsNeedTcl 8.2 +testsNeedTcltest 1.0 + +testing { + useLocal rc4.tcl rc4 +} + +# ------------------------------------------------------------------------- + +if {[llength [info commands ::rc4::rc4c]]} { + puts "> critcl" +} + +# ------------------------------------------------------------------------- +# Now the package specific tests.... +# ------------------------------------------------------------------------- + +test rc4-1.0 {rc4 basic command options} { + list [catch {::rc4::rc4} msg] $msg +} {1 {wrong # args: should be "rc4 ?-hex? -key key -in channel | string"}} + +test rc4-1.1 {rc4 basic command options} { + list [catch {::rc4::rc4 -key secret} msg] $msg +} {1 {wrong # args: should be "rc4 ?-hex? -key key -in channel | string"}} + +# ------------------------------------------------------------------------- + +# Test vectors +set tests { + "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\x75\xb7\x87\x80\x99\xe0\xc5\x96" + + "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x74\x94\xc2\xe7\x10\x4b\x08\x79" + + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\xde\x18\x89\x41\xa3\x37\x5d\x3a" + + "\xef\x01\x23\x45" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf\xbd\x61" +} + +lappend tests \ + "\x01\x23\x45\x67\x89\xab\xcd\xef" \ + [string repeat \x01 512] \ + [binary format H* \ + [join \ + "75 95 c3 e6 11 4a 09 78 0c 4a d4 52 33 8e 1f fd 9a 1b e9 49\ + 8f 81 3d 76 53 34 49 b6 77 8d ca d8 c7 8a 8d 2b a9 ac 66 08\ + 5d 0e 53 d5 9c 26 c2 d1 c4 90 c1 eb be 0c e6 6d 1b 6b 1b 13\ + b6 b9 19 b8 47 c2 5a 91 44 7a 95 e7 5e 4e f1 67 79 cd e8 bf\ + 0a 95 85 0e 32 af 96 89 44 4f d3 77 10 8f 98 fd cb d4 e7 26\ + 56 75 00 99 0b cc 7e 0c a3 c4 aa a3 04 a3 87 d2 0f 3b 8f bb\ + cd 42 a1 bd 31 1d 7a 43 03 dd a5 ab 07 88 96 ae 80 c1 8b 0a\ + f6 6d ff 31 96 16 eb 78 4e 49 5a d2 ce 90 d7 f7 72 a8 17 47\ + b6 5f 62 09 3b 1e 0d b9 e5 ba 53 2f af ec 47 50 83 23 e6 71\ + 32 7d f9 44 44 32 cb 73 67 ce c8 2f 5d 44 c0 d0 0b 67 d6 50\ + a0 75 cd 4b 70 de dd 77 eb 9b 10 23 1b 6b 5b 74 13 47 39 6d\ + 62 89 74 21 d4 3d f9 b4 2e 44 6e 35 8e 9c 11 a9 b2 18 4e cb\ + ef 0c d8 e7 a8 77 ef 96 8f 13 90 ec 9b 3d 35 a5 58 5c b0 09\ + 29 0e 2f cd e7 b5 ec 66 d9 08 4b e4 40 55 a6 19 d9 dd 7f c3\ + 16 6f 94 87 f7 cb 27 29 12 42 64 45 99 85 14 c1 5d 53 a1 8c\ + 86 4c e3 a2 b7 55 57 93 98 81 26 52 0e ac f2 e3 06 6e 23 0c\ + 91 be e4 dd 53 04 f5 fd 04 05 b3 5b d9 9c 73 13 5d 3d 9b c3\ + 35 ee 04 9e f6 9b 38 67 bf 2d 7b d1 ea a5 95 d8 bf c0 06 6f\ + f8 d3 15 09 eb 0c 6c aa 00 6c 80 7a 62 3e f8 4c 3d 33 c1 95\ + d2 3e e3 20 c4 0d e0 55 81 57 c8 22 d4 b8 c5 69 d8 49 ae d5\ + 9d 4e 0f d7 f3 79 58 6b 4b 7f f6 84 ed 6a 18 9f 74 86 d4 9b\ + 9c 4b ad 9b a2 4b 96 ab f9 24 37 2c 8a 8f ff b1 0d 55 35 49\ + 00 a7 7a 3d b5 f2 05 e1 b9 9f cd 86 60 86 3a 15 9a d4 ab e4\ + 0f a4 89 34 16 3d dd e5 42 a6 58 55 40 fd 68 3c bf d8 c0 0f\ + 12 12 9a 28 4d ea cc 4c de fe 58 be 71 37 54 1c 04 71 26 c8\ + d4 9e 27 55 ab 18 1a b7 e9 40 b0 c0" {}]] + +if {[llength [info commands ::rc4::rc4c]] != 0} { + set n 0 + foreach {key input output} $tests { + test rc4-critcl-2.$n {rc4 test vectors (critcl based)} { + list [catch {::rc4::rc4 -key $key $input} msg] $msg + } [list 0 $output] + incr n + } +} + +puts "> pure Tcl - array based" +::rc4::SelectImplementation array +set n 0 +foreach {key input output} $tests { + test rc4-array-2.$n {rc4 test vectors (pure tcl array based)} { + list [catch {::rc4::rc4 -key $key $input} msg] $msg + } [list 0 $output] + incr n +} + +puts "> pure Tcl - list based" +::rc4::SelectImplementation list +set n 0 +foreach {key input output} $tests { + test rc4-list-2.$n {rc4 test vectors (pure tcl list based)} { + list [catch {::rc4::rc4 -key $key $input} msg] $msg + } [list 0 $output] + incr n +} + +test rc4-3.0 {rc4 check hyphen handling} { + list [catch {rc4::rc4 -hex -key - \0} msg] $msg +} {0 9d} + +test rc4-3.1 {rc4 check hyphen handling} { + list [catch {rc4::rc4 -hex -key -- \0} msg] $msg +} {0 9d} + +test rc4-3.2 {rc4 check hyphen handling} { + list [catch {rc4::rc4 -hex -key - -} msg] $msg +} {0 b0} + +test rc4-3.3 {rc4 check hyphen handling} { + list [catch {rc4::rc4 -hex -key - --} msg] $msg +} {0 b046} + +test rc4-3.4 {rc4 check hyphen handling} { + list [catch {rc4::rc4 -hex -key - -- -} msg] $msg +} {0 b0} + +test rc4-3.5 {rc4 check hyphen handling} { + list [catch {rc4::rc4 -hex -key - -- --} msg] $msg +} {0 b046} + +test rc4-4.0 {check file reading} { + list [catch { + set f [open [set path [makeFile {} rc4test.data]] w] + fconfigure $f -translation lf -eofchar {} -encoding binary + puts -nonewline $f "\0\1\2" + close $f + set res [rc4::rc4 -hex -key 01234567 -infile $path] + removeFile rc4test.data + set res + } msg] $msg +} {0 91d4f1} + +test rc4-4.1 {check channel reading} { + list [catch { + set f [open [set path [makeFile {} rc4test.data]] w] + fconfigure $f -translation lf -eofchar {} -encoding binary + puts -nonewline $f "\0\1\2" + close $f + + set f [open $path r] + fconfigure $f -translation lf -eofchar {} -encoding binary + set r [rc4::rc4 -hex -key 01234567 -in $f] + close $f + removeFile rc4test.data + set r + } msg] $msg +} {0 91d4f1} + +test rc4-4.2 {check channel output} { + list [catch { + set f [open [makeFile {} rc4test.data] w+] + fconfigure $f -translation lf -eofchar {} -encoding binary + set r [rc4::rc4 -hex -key 01234567 -out $f "abcdef"] + seek $f 0 + set s [rc4::Hex [read $f]] + close $f + removeFile rc4test.data + list $r $s + } msg] $msg +} {0 {{} f0b7907b2341}} + +test rc4-4.3 {check channel input and output} { + list [catch { + set f [open [makeFile {} rc4test.data] w+] + fconfigure $f -translation lf -eofchar {} -encoding binary + puts -nonewline $f "abcdef" + seek $f 0 + set g [open [makeFile {} rc4test2.data] w+] + fconfigure $g -translation lf -eofchar {} -encoding binary + set r [rc4::rc4 -hex -key 01234567 -in $f -out $g] + close $f + seek $g 0 + set s [rc4::Hex [read $g]] + close $g + removeFile rc4test.data + removeFile rc4test2.data + list $r $s + } msg] $msg +} {0 {{} f0b7907b2341}} + +proc ::rc4::TestCommandProc {junk data} { + if {[string compare $junk "JuNk"] != 0} { + set ::rc4::_test magicfailure + } else { + set ::rc4::_test [Hex $data] + } +} + +test rc4-5.0 {check -command option} { + set ::rc4::_test unset + list [catch { + set r [rc4::rc4 -key 01234567 \ + -command [list ::rc4::TestCommandProc JuNk] "abcdef"] + list $r $::rc4::_test + } msg] $msg +} {0 {{} f0b7907b2341}} + +test rc4-5.1 {check -command option (-hex should be ignored)} { + set ::rc4::_test unset + list [catch { + set r [rc4::rc4 -hex -key 01234567 \ + -command [list ::rc4::TestCommandProc JuNk] "abcdef"] + list $r $::rc4::_test + } msg] $msg +} {0 {{} f0b7907b2341}} + +test rc4-5.2 {check -command option with channel input} { + set ::rc4::_test unset + list [catch { + set f [open [makeFile {} rc4test.data] w+] + fconfigure $f -translation lf -eofchar {} -encoding binary + puts -nonewline $f "abcdef" + seek $f 0 + set id [after 1000 {set ::rc4::_test timeout}] + set r [rc4::rc4 -key 01234567 -in $f \ + -command [list ::rc4::TestCommandProc JuNk]] + vwait ::rc4::_test + after cancel $id + close $f + removeFile rc4test.data + list $r $::rc4::_test + } msg] $msg +} {0 {{} f0b7907b2341}} + +test rc4-5.3 {check -command option with file input} { + set ::rc4::_test unset + list [catch { + set f [open [set path [makeFile {} rc4test.data]] w] + fconfigure $f -translation lf -eofchar {} -encoding binary + puts -nonewline $f "abcdef" + close $f + set id [after 1000 {set ::rc4::_test timeout}] + set r [rc4::rc4 -key 01234567 -infile $path \ + -command [list ::rc4::TestCommandProc JuNk]] + vwait ::rc4::_test + after cancel $id + removeFile rc4test.data + list $r $::rc4::_test + } msg] $msg +} {0 {{} f0b7907b2341}} + +# ------------------------------------------------------------------------- + +catch {unset ::rc4::_test} +rename ::rc4::TestCommandProc {} +testsuiteCleanup + +# Local variables: +# mode: tcl +# indent-tabs-mode: nil +# End: diff --git a/tcllib/modules/rc4/rc4c.tcl b/tcllib/modules/rc4/rc4c.tcl new file mode 100644 index 0000000..fd717df --- /dev/null +++ b/tcllib/modules/rc4/rc4c.tcl @@ -0,0 +1,168 @@ +# rc4c.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net> +# +# This provides a critcl C implementation of RC4 +# +# INSTALLATION +# ------------ +# This package uses critcl (http://wiki.tcl.tk/critcl). To build do: +# critcl -libdir <your-tcl-lib-dir> -pkg rc4c rc4c +# +# To build this for tcllib use sak.tcl: +# tclsh sak.tcl critcl +# generates a tcllibc module. +# +# $Id: rc4c.tcl,v 1.4 2009/05/07 00:14:02 patthoyts Exp $ + +package require critcl +# @sak notprovided rc4c +package provide rc4c 1.1.0 + +namespace eval ::rc4 { + + critcl::ccode { + #include <string.h> + + typedef struct RC4_CTX { + unsigned char x; + unsigned char y; + unsigned char s[256]; + } RC4_CTX; + + /* #define TRACE trace */ + #define TRACE 1 ? ((void)0) : trace + + static void trace(const char *format, ...) + { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + } + static Tcl_ObjType rc4_type; + + static void rc4_free_rep(Tcl_Obj *obj) + { + RC4_CTX *ctx = (RC4_CTX *)obj->internalRep.otherValuePtr; + TRACE("rc4_free_rep(%08x)\n", (long)obj); + Tcl_Free((char *)ctx); + } + + static void rc4_dup_rep(Tcl_Obj *obj, Tcl_Obj *dup) + { + RC4_CTX *ctx = (RC4_CTX *)obj->internalRep.otherValuePtr; + TRACE("rc4_dup_rep(%08x,%08x)\n", (long)obj, (long)dup); + dup->internalRep.otherValuePtr = (RC4_CTX *)Tcl_Alloc(sizeof(RC4_CTX)); + memcpy(dup->internalRep.otherValuePtr, ctx, sizeof(RC4_CTX)); + dup->typePtr = &rc4_type; + } + + static void rc4_string_rep(Tcl_Obj* obj) + { + RC4_CTX *ctx = (RC4_CTX *)obj->internalRep.otherValuePtr; + Tcl_Obj* tmpObj; + char* str; + TRACE("rc4_string_rep(%08x)\n", (long)obj); + /* convert via a byte array to properly handle null bytes */ + tmpObj = Tcl_NewByteArrayObj((unsigned char *)ctx, sizeof(RC4_CTX)); + Tcl_IncrRefCount(tmpObj); + + str = Tcl_GetStringFromObj(tmpObj, &obj->length); + obj->bytes = Tcl_Alloc(obj->length + 1); + memcpy(obj->bytes, str, obj->length + 1); + + Tcl_DecrRefCount(tmpObj); + } + + static int rc4_from_any(Tcl_Interp* interp, Tcl_Obj* obj) + { + TRACE("rc4_from_any %08x\n", (long)obj); + return TCL_ERROR; + } + + static Tcl_ObjType rc4_type = { + "rc4c", rc4_free_rep, rc4_dup_rep, rc4_string_rep, rc4_from_any + }; +#ifdef __GNUC__ + inline +#elif defined(_MSC_VER) + __inline +#endif + void swap (unsigned char *lhs, unsigned char *rhs) { + unsigned char t = *lhs; + *lhs = *rhs; + *rhs = t; + } + } + + critcl::ccommand rc4c_init {dummy interp objc objv} { + RC4_CTX *ctx; + Tcl_Obj *obj; + const unsigned char *k; + int n = 0, i = 0, j = 0, keylen; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "keystring"); + return TCL_ERROR; + } + + k = Tcl_GetByteArrayFromObj(objv[1], &keylen); + + obj = Tcl_NewObj(); + ctx = (RC4_CTX *)Tcl_Alloc(sizeof(RC4_CTX)); + ctx->x = 0; + ctx->y = 0; + for (n = 0; n < 256; n++) + ctx->s[n] = n; + for (n = 0; n < 256; n++) { + j = (k[i] + ctx->s[n] + j) % 256; + swap(&ctx->s[n], &ctx->s[j]); + i = (i + 1) % keylen; + } + + if (obj->typePtr != NULL && obj->typePtr->freeIntRepProc != NULL) + obj->typePtr->freeIntRepProc(obj); + obj->internalRep.otherValuePtr = ctx; + obj->typePtr = &rc4_type; + Tcl_InvalidateStringRep(obj); + Tcl_SetObjResult(interp, obj); + return TCL_OK; + } + + critcl::ccommand rc4c {dummy interp objc objv} { + Tcl_Obj *resObj = NULL; + RC4_CTX *ctx = NULL; + unsigned char *data, *res, x, y; + int size, n, i; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "key data"); + return TCL_ERROR; + } + + if (objv[1]->typePtr != &rc4_type + && rc4_from_any(interp, objv[1]) != TCL_OK) { + return TCL_ERROR; + } + + ctx = objv[1]->internalRep.otherValuePtr; + data = Tcl_GetByteArrayFromObj(objv[2], &size); + res = (unsigned char *)Tcl_Alloc(size); + + x = ctx->x; + y = ctx->y; + for (n = 0; n < size; n++) { + x = (x + 1) % 256; + y = (ctx->s[x] + y) % 256; + swap(&ctx->s[x], &ctx->s[y]); + i = (ctx->s[x] + ctx->s[y]) % 256; + res[n] = data[n] ^ ctx->s[i]; + } + ctx->x = x; + ctx->y = y; + + resObj = Tcl_NewByteArrayObj(res, size); + Tcl_SetObjResult(interp, resObj); + Tcl_Free((char*)res); + return TCL_OK; + } +} |