rc4.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 package require Tcl 8.2
00015
00016
00017
00018 namespace ::rc4 {
00019 variable version 1.1.0
00020 variable rcsid {$Id: rc4.tcl,v 1.7 2005/12/20 16:19:38 patthoyts Exp $}
00021
00022 namespace export rc4
00023
00024 variable uid
00025 if {![info exists uid]} {
00026 uid = 0
00027 }
00028 }
00029
00030
00031
00032 ret ::rc4::RC4Init_Array (type keystr) {
00033 variable uid
00034
00035 binary scan $keystr c* key
00036 set keylen [llength $key]
00037
00038 set Key [namespace current]::key[incr uid]
00039 # FRINK: nocheck
00040 variable $Key
00041 upvar #0 $Key state
00042 catch {unset state}
00043
00044 set state(x) 0
00045 set state(y) 0
00046 for {set cn 0} {$cn < 256} {incr cn} {
00047 set state(s,$cn) $cn
00048 }
00049 set i 0
00050 set j 0
00051 for {set cn 0} {$cn < 256} {incr cn} {
00052 set j [expr {([lindex $key $i] + $state(s,$cn) + $j) % 256}]
00053 set t $state(s,$cn)
00054 set state(s,$cn) $state(s,$j)
00055 set state(s,$j) $t
00056 set i [expr {($i + 1) % $keylen}]
00057 }
00058
00059 return $Key
00060 }
00061
00062
00063
00064 ret ::rc4::RC4_Array (type Key , type datastr) {
00065 upvar #0 $Key state
00066 set res {}
00067
00068 binary scan $datastr c* data
00069 set datalen [llength $data]
00070
00071 set x $state(x)
00072 set y $state(y)
00073
00074 for {set cn 0} {$cn < $datalen} {incr cn} {
00075 set x [expr {($x + 1) % 256}]
00076 set y [expr {($state(s,$x) + $y) % 256}]
00077 set t $state(s,$y)
00078 set state(s,$y) $state(s,$x)
00079 set state(s,$x) $t
00080 set i [expr {($state(s,$x) + $state(s,$y)) % 256}]
00081 lappend res [expr {([lindex $data $cn] ^ $state(s,$i)) & 0xFF}]
00082 }
00083 set state(x) $x
00084 set state(y) $y
00085 return [binary format c* $res]
00086 }
00087
00088
00089
00090 ret ::rc4::RC4Init_List (type keystr) {
00091 variable uid
00092
00093 binary scan $keystr c* key
00094 set keylen [llength $key]
00095
00096 set Key [namespace current]::key[incr uid]
00097 # FRINK: nocheck
00098 variable $Key
00099 upvar #0 $Key State
00100 catch {unset State}
00101
00102 set i 0
00103 set j 0
00104 set s {}; #[::struct::list::Liota 256]
00105 for {set n 0} {$n < 256} {incr n} {lappend s $n}
00106
00107 for {set cn 0} {$cn < 256} {incr cn} {
00108 set j [expr {([lindex $key $i] + [lindex $s $cn] + $j) % 256}]
00109 set t [lindex $s $cn]
00110 lset s $cn [lindex $s $j]
00111 lset s $j $t
00112 set i [expr {($i + 1) % $keylen}]
00113 }
00114
00115 set State(x) 0
00116 set State(y) 0
00117 set State(s) $s
00118
00119 return $Key
00120 }
00121
00122
00123
00124 ret ::rc4::RC4_List (type Key , type datastr) {
00125 upvar #0 $Key State
00126 set res {}
00127
00128 binary scan $datastr c* data
00129 set datalen [llength $data]
00130
00131 set x $State(x)
00132 set y $State(y)
00133 set s $State(s)
00134
00135 for {set cn 0} {$cn < $datalen} {incr cn} {
00136 set x [expr {($x + 1) % 256}]
00137 set y [expr {([lindex $s $x] + $y) % 256}]
00138 set t [lindex $s $y]
00139 lset s $y [lindex $s $x]
00140 lset s $x $t
00141 set i [expr {([lindex $s $x] + [lindex $s $y]) % 256}]
00142 lappend res [expr {([lindex $data $cn] ^ [lindex $s $i]) & 0xFF}]
00143 }
00144 set State(x) $x
00145 set State(y) $y
00146 set State(s) $s
00147 return [binary format c* $res]
00148 }
00149
00150
00151 ret ::rc4::K (type x , type y) {set x}
00152
00153
00154 if {[package vcompare [package provide Tcl] 8.4] < 0} {
00155 ret ::rc4::lset (type var , type index , type arg) {
00156 upvar 1 $var list
00157 set list [::lreplace [K $list [set list {}]] $index $index $arg]
00158 }
00159 }
00160
00161 ret ::rc4::RC4Final (type Key) {
00162 upvar #0 $Key state
00163 catch {unset state}
00164 return {}
00165 }
00166
00167
00168
00169
00170 ret ::rc4::Hex (type data) {
00171 binary scan $data H* result
00172 return $result
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185 ret ::rc4::Transform (type channel , type keystr , type operation , type data) {
00186 set readkey [namespace current]::R$channel
00187 # FRINK: nocheck
00188 variable $readkey
00189 upvar #0 $readkey rk
00190 set writekey [namespace current]::W$channel
00191 # FRINK: nocheck
00192 variable $writekey
00193 upvar #0 $writekey wk
00194 set result {}
00195
00196 #puts stderr "$operation {$data}"
00197 switch -- $operation {
00198 create/write {
00199 if {[info exists wk]} {
00200 RCFinal $wk
00201 }
00202 set wk [RC4Init $keystr]
00203 }
00204 clear/write {}
00205 delete/write {
00206 if {[info exists wk]} {
00207 RC4Final $wk
00208 unset wk
00209 }
00210 }
00211 write - flush/write {
00212 if {![info exists wk]} {
00213 set wk [RC4Init $keystr]
00214 }
00215 set result [RC4 $wk $data]
00216 }
00217
00218 create/read {
00219 if {[info exists rk]} {
00220 RCFinal $rk
00221 }
00222 set rk [RC4Init $keystr]
00223 }
00224 clear/read {}
00225 delete/read {
00226 if {[info exists rk]} {
00227 RC4Final $rk
00228 unset rk
00229 }
00230 }
00231 read - flush/read {
00232 if {![info exists rk]} {
00233 set rk [RC4Init $keystr]
00234 }
00235 set result [RC4 $rk $data]
00236 }
00237
00238 query/ratio {
00239 set result {1 1}; # RC4 is a 1:1 stream cipher.
00240 }
00241 query/maxRead {
00242 set result -1; # Permit read of any amount
00243 }
00244 default {
00245 # ignore unknown operations.
00246 }
00247 }
00248 return $result
00249 }
00250
00251
00252
00253
00254
00255 ret ::rc4::Pop (type varname , optional nth =0) {
00256 upvar $varname args
00257 set r [lindex $args $nth]
00258 set args [lreplace $args $nth $nth]
00259 return $r
00260 }
00261
00262
00263
00264
00265 ret ::rc4::Chunk (type State) {
00266 upvar #0 $State state
00267
00268 if {[eof $state(-in)]} {
00269 fileevent $state(-in) readable {}
00270 set state(reading) 0
00271 }
00272 set data [read $state(-in) $state(-chunksize)]
00273 if {[llength $state(-out)] == 0} {
00274 append state(output) [RC4 $state(Key) $data]
00275 } else {
00276 puts -nonewline $state(-out) [RC4 $state(Key) $data]
00277 }
00278 if {!$state(reading) && [llength $state(-command)] != 0} {
00279 Cleanup $State; # cleanup and call users command
00280 }
00281 }
00282
00283
00284 ret ::rc4::Cleanup (type State) {
00285 upvar #0 $State state
00286 set cmd $state(-command)
00287 set res $state(output)
00288 # If we opened the channel then we should close it too.
00289 if {[string length $state(-infile)] > 0} {
00290 close $state(-in)
00291 }
00292 RC4Final $state(Key)
00293 unset state
00294 if {[llength $cmd] != 0} {
00295 eval $cmd [list $res]
00296 }
00297 return $res
00298 }
00299
00300
00301
00302 ret ::rc4::rc4 (type args) {
00303 array set opts {-hex 0 -infile {} -in {} -out {} -chunksize 4096
00304 -key {} -command {}}
00305 while {[string match -* [set option [lindex $args 0]]]} {
00306 switch -exact -- $option {
00307 -key { set opts(-key) [Pop args 1] }
00308 -hex { set opts(-hex) 1}
00309 -infile { set opts(-infile) [Pop args 1] }
00310 -in { set opts(-in) [Pop args 1] }
00311 -out { set opts(-out) [Pop args 1] }
00312 -chunksize { set opts(-chunksize) [Pop args 1] }
00313 -command { set opts(-command) [Pop args 1] }
00314 default {
00315 if {[llength $args] == 1} { break }
00316 if {[string compare $option "--"] == 0} { Pop args; break }
00317 set err [join [lsort [array names opts]] ", "]
00318 return -code error "bad option $option:\
00319 must be one of $err"
00320 }
00321 }
00322 Pop args
00323 }
00324
00325 if {[string length $opts(-key)] < 1} {
00326 return -code error "wrong # args:\
00327 should be \"rc4 ?-hex? -key key -in channel | string\""
00328 }
00329
00330 if {$opts(-infile) != {}} {
00331 set opts(-in) [open $opts(-infile) r]
00332 fconfigure $opts(-in) -translation binary
00333 }
00334
00335 set r {}
00336 if {$opts(-in) == {}} {
00337 if {[llength $args] != 1} {
00338 return -code error "wrong # args:\
00339 should be \"rc4 ?-hex? -key key -in channel | string\""
00340 }
00341
00342 set Key [RC4Init $opts(-key)]
00343 set r [RC4 $Key [lindex $args 0]]
00344 if {[llength $opts(-command)] != 0} {
00345 eval $opts(-command) [list $r]
00346 set r {}
00347 } elseif {$opts(-out) != {}} {
00348 puts -nonewline $opts(-out) $r
00349 set r {}
00350 }
00351 RC4Final $Key
00352
00353 } else {
00354
00355 variable uid
00356 set State [namespace current]::state[incr uid]
00357 upvar #0 $State state
00358 array set state [array get opts]
00359 set state(Key) [RC4Init $opts(-key)]
00360 set state(reading) 1
00361 set state(output) ""
00362 fileevent $opts(-in) readable [list [namespace origin Chunk] $State]
00363 if {[llength $opts(-command)] != 0} {
00364 return {}
00365 } else {
00366 vwait [set State](reading)
00367 set r [Cleanup $State]
00368 }
00369 }
00370
00371 if {$opts(-hex)} {
00372 set r [Hex $r]
00373 }
00374 return $r
00375 }
00376
00377
00378
00379 ret ::rc4::SelectImplementation (type impl) {
00380 switch -exact -- $impl {
00381 critcl {
00382 interp alias {} ::rc4::RC4Init {} ::rc4::rc4c_init
00383 interp alias {} ::rc4::RC4 {} ::rc4::rc4c
00384 }
00385 array {
00386 interp alias {} ::rc4::RC4Init {} ::rc4::RC4Init_Array
00387 interp alias {} ::rc4::RC4 {} ::rc4::RC4_Array
00388 }
00389 list {
00390 interp alias {} ::rc4::RC4Init {} ::rc4::RC4Init_List
00391 interp alias {} ::rc4::RC4 {} ::rc4::RC4_List
00392 }
00393 default {
00394 return -code error "invalid implementation \"$impl\":\
00395 must be one of \"critcl\", \"array\" or \"list\""
00396 }
00397 }
00398 }
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408 namespace ::rc4 {
00409 if {[catch {package require tcllibc}]} {
00410 catch {package require rc4c}
00411 }
00412 if {[info commands ::rc4::rc4c] != {}} {
00413 SelectImplementation critcl
00414 } elseif {[package vcompare [package provide Tcl] 8.4] < 0} {
00415 SelectImplementation array
00416 } else {
00417 SelectImplementation list
00418 }
00419 }
00420
00421 package provide rc4 $::rc4::version
00422
00423
00424
00425
00426
00427
00428