sha1v1.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 package require Tcl 8.2;
00029
00030 namespace ::sha1 {
00031 variable version 1.1.0
00032 variable rcsid {$Id: sha1v1.tcl,v 1.1 2006/03/12 22:46:13 andreas_kupries Exp $}
00033 variable accel
00034 array accel = {critcl 0 cryptkit 0 trf 0}
00035
00036 namespace export sha1 hmac SHA1Init SHA1Update SHA1Final
00037
00038 variable uid
00039 if {![info exists uid]} {
00040 uid = 0
00041 }
00042 }
00043
00044
00045
00046
00047
00048
00049
00050
00051 ret ::sha1::SHA1Init () {
00052 variable accel
00053 variable uid
00054 set token [namespace current]::[incr uid]
00055 upvar #0 $token state
00056
00057 # FIPS 180-1: 7 - Initialize the hash state
00058 array set state \
00059 [list \
00060 A [expr {int(0x67452301)}] \
00061 B [expr {int(0xEFCDAB89)}] \
00062 C [expr {int(0x98BADCFE)}] \
00063 D [expr {int(0x10325476)}] \
00064 E [expr {int(0xC3D2E1F0)}] \
00065 n 0 i "" ]
00066 if {$accel(cryptkit)} {
00067 cryptkit::cryptCreateContext state(ckctx) CRYPT_UNUSED CRYPT_ALGO_SHA
00068 } elseif {$accel(trf)} {
00069 set s {}
00070 switch -exact -- $::tcl_platform(platform) {
00071 windows { set s [open NUL w] }
00072 unix { set s [open /dev/null w] }
00073 }
00074 if {$s != {}} {
00075 fconfigure $s -translation binary -buffering none
00076 ::sha1 -attach $s -mode write \
00077 -read-type variable \
00078 -read-destination [subst $token](trfread) \
00079 -write-type variable \
00080 -write-destination [subst $token](trfwrite)
00081 array set state [list trfread 0 trfwrite 0 trf $s]
00082 }
00083 }
00084 return $token
00085 }
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097 ret ::sha1::SHA1Update (type token , type data) {
00098 variable accel
00099 upvar #0 $token state
00100
00101 if {$accel(critcl)} {
00102 if {[info exists state(sha1c)]} {
00103 set state(sha1c) [sha1c $data $state(sha1c)]
00104 } else {
00105 set state(sha1c) [sha1c $data]
00106 }
00107 return
00108 } elseif {[info exists state(ckctx)]} {
00109 if {[string length $data] > 0} {
00110 cryptkit::cryptEncrypt $state(ckctx) $data
00111 }
00112 return
00113 } elseif {[info exists state(trf)]} {
00114 puts -nonewline $state(trf) $data
00115 return
00116 }
00117
00118 # Update the state values
00119 incr state(n) [string length $data]
00120 append state(i) $data
00121
00122 # Calculate the hash for any complete blocks
00123 set len [string length $state(i)]
00124 for {set n 0} {($n + 64) <= $len} {} {
00125 SHA1Transform $token [string range $state(i) $n [incr n 64]]
00126 }
00127
00128 # Adjust the state for the blocks completed.
00129 set state(i) [string range $state(i) $n end]
00130 return
00131 }
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141 ret ::sha1::SHA1Final (type token) {
00142 upvar #0 $token state
00143
00144 # Check for either of the C-compiled versions.
00145 if {[info exists state(sha1c)]} {
00146 set r $state(sha1c)
00147 unset state
00148 return $r
00149 } elseif {[info exists state(ckctx)]} {
00150 cryptkit::cryptEncrypt $state(ckctx) ""
00151 cryptkit::cryptGetAttributeString $state(ckctx) \
00152 CRYPT_CTXINFO_HASHVALUE r 20
00153 cryptkit::cryptDestroyContext $state(ckctx)
00154 # If nothing was hashed, we get no r variable set!
00155 if {[info exists r]} {
00156 unset state
00157 return $r
00158 }
00159 } elseif {[info exists state(trf)]} {
00160 close $state(trf)
00161 set r $state(trfwrite)
00162 unset state
00163 return $r
00164 }
00165
00166 # Padding
00167 #
00168 set len [string length $state(i)]
00169 set pad [expr {56 - ($len % 64)}]
00170 if {$len % 64 > 56} {
00171 incr pad 64
00172 }
00173 if {$pad == 0} {
00174 incr pad 64
00175 }
00176 append state(i) [binary format a$pad \x80]
00177
00178 # Append length in bits as big-endian wide int.
00179 set dlen [expr {8 * $state(n)}]
00180 append state(i) [binary format II 0 $dlen]
00181
00182 # Calculate the hash for the remaining block.
00183 set len [string length $state(i)]
00184 for {set n 0} {($n + 64) <= $len} {} {
00185 SHA1Transform $token [string range $state(i) $n [incr n 64]]
00186 }
00187
00188 # Output
00189 set r [bytes $state(A)][bytes $state(B)][bytes $state(C)][bytes $state(D)][bytes $state(E)]
00190 unset state
00191 return $r
00192 }
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205 ret ::sha1::HMACInit (type K) {
00206
00207 # Key K is adjusted to be 64 bytes long. If K is larger, then use
00208 # the SHA1 digest of K and pad this instead.
00209 set len [string length $K]
00210 if {$len > 64} {
00211 set tok [SHA1Init]
00212 SHA1Update $tok $K
00213 set K [SHA1Final $tok]
00214 set len [string length $K]
00215 }
00216 set pad [expr {64 - $len}]
00217 append K [string repeat \0 $pad]
00218
00219 # Cacluate the padding buffers.
00220 set Ki {}
00221 set Ko {}
00222 binary scan $K i16 Ks
00223 foreach k $Ks {
00224 append Ki [binary format i [expr {$k ^ 0x36363636}]]
00225 append Ko [binary format i [expr {$k ^ 0x5c5c5c5c}]]
00226 }
00227
00228 set tok [SHA1Init]
00229 SHA1Update $tok $Ki; # initialize with the inner pad
00230
00231 # preserve the Ko value for the final stage.
00232 # FRINK: nocheck
00233 set [subst $tok](Ko) $Ko
00234
00235 return $tok
00236 }
00237
00238
00239
00240
00241
00242 ret ::sha1::HMACUpdate (type token , type data) {
00243 SHA1Update $token $data
00244 return
00245 }
00246
00247
00248
00249
00250
00251
00252 ret ::sha1::HMACFinal (type token) {
00253 upvar #0 $token state
00254
00255 set tok [SHA1Init]; # init the outer hashing function
00256 SHA1Update $tok $state(Ko); # prepare with the outer pad.
00257 SHA1Update $tok [SHA1Final $token]; # hash the inner result
00258 return [SHA1Final $tok]
00259 }
00260
00261
00262
00263
00264
00265
00266 ::sha1 = ::SHA1Transform_body {
00267 upvar
00268
00269
00270 binary scan $msg I* blocks
00271 blockLen = [llength $blocks]
00272 for { i = 0} {$i < $blockLen} {incr i 16} {
00273 W = [lrange $blocks $i [expr {$i+15}]]
00274
00275
00276
00277
00278 t3 = 12
00279 t8 = 7
00280 t14 = 1
00281 t16 = -1
00282 for { t = 16} {$t < 80} {incr t} {
00283 x = [expr {[lindex $W [incr t3]] ^ [lindex $W [incr t8]] ^ \
00284 [lindex $W [incr t14]] ^ [lindex $W [incr t16]]}]
00285 lappend W [expr {int(($x << 1) | (($x >> 31) & 1))}]
00286 }
00287
00288
00289 A = $state(A)
00290 B = $state(B)
00291 C = $state(C)
00292 D = $state(D)
00293 E = $state(E)
00294
00295
00296
00297
00298
00299
00300
00301 for { t = 0} {$t < 20} {incr t} {
00302 TEMP = [F1 $A $B $C $D $E [lindex $W $t]]
00303 E = $D
00304 D = $C
00305 C = [rotl32 $B 30]
00306 B = $A
00307 A = $TEMP
00308 }
00309
00310
00311 for {} {$t < 40} {incr t} {
00312 TEMP = [F2 $A $B $C $D $E [lindex $W $t]]
00313 E = $D
00314 D = $C
00315 C = [rotl32 $B 30]
00316 B = $A
00317 A = $TEMP
00318 }
00319
00320
00321 for {} {$t < 60} {incr t} {
00322 TEMP = [F3 $A $B $C $D $E [lindex $W $t]]
00323 E = $D
00324 D = $C
00325 C = [rotl32 $B 30]
00326 B = $A
00327 A = $TEMP
00328 }
00329
00330
00331 for {} {$t < 80} {incr t} {
00332 TEMP = [F4 $A $B $C $D $E [lindex $W $t]]
00333 E = $D
00334 D = $C
00335 C = [rotl32 $B 30]
00336 B = $A
00337 A = $TEMP
00338 }
00339
00340
00341
00342
00343 incr state(A) $A
00344 incr state(B) $B
00345 incr state(C) $C
00346 incr state(D) $D
00347 incr state(E) $E
00348 }
00349
00350 return
00351 }
00352
00353 ret ::sha1::F1 (type A , type B , type C , type D , type E , type W) {
00354 expr {(((($A << 5) & 0xffffffff) | (($A >> 27) & 0x1f)) \
00355 + ($D ^ ($B & ($C ^ $D))) + $E + $W + 0x5a827999) & 0xffffffff}
00356 }
00357
00358 ret ::sha1::F2 (type A , type B , type C , type D , type E , type W) {
00359 expr {(((($A << 5) & 0xffffffff) | (($A >> 27) & 0x1f)) \
00360 + ($B ^ $C ^ $D) + $E + $W + 0x6ed9eba1) & 0xffffffff}
00361 }
00362
00363 ret ::sha1::F3 (type A , type B , type C , type D , type E , type W) {
00364 expr {(((($A << 5) & 0xffffffff)| (($A >> 27) & 0x1f)) \
00365 + (($B & $C) | ($D & ($B | $C))) + $E + $W + 0x8f1bbcdc) & 0xffffffff}
00366 }
00367
00368 ret ::sha1::F4 (type A , type B , type C , type D , type E , type W) {
00369 expr {(((($A << 5) & 0xffffffff)| (($A >> 27) & 0x1f)) \
00370 + ($B ^ $C ^ $D) + $E + $W + 0xca62c1d6) & 0xffffffff}
00371 }
00372
00373 ret ::sha1::rotl32 (type v , type n) {
00374 return [expr {((($v << $n) \
00375 | (($v >> (32 - $n)) \
00376 & (0x7FFFFFFF >> (31 - $n))))) \
00377 & 0xFFFFFFFF}]
00378 }
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393 regsub -all -line \
00394 {\[F1 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00395 $::sha1::SHA1Transform_body \
00396 {[expr {(rotl32($A,5) + ($D ^ ($B \& ($C ^ $D))) + $E + \1 + 0x5a827999) \& 0xffffffff}]} \
00397 ::sha1::SHA1Transform_body_tmp
00398
00399 regsub -all -line \
00400 {\[F2 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00401 $::sha1::SHA1Transform_body_tmp \
00402 {[expr {(rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0x6ed9eba1) \& 0xffffffff}]} \
00403 ::sha1::SHA1Transform_body_tmp
00404
00405 regsub -all -line \
00406 {\[F3 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00407 $::sha1::SHA1Transform_body_tmp \
00408 {[expr {(rotl32($A,5) + (($B \& $C) | ($D \& ($B | $C))) + $E + \1 + 0x8f1bbcdc) \& 0xffffffff}]} \
00409 ::sha1::SHA1Transform_body_tmp
00410
00411 regsub -all -line \
00412 {\[F4 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00413 $::sha1::SHA1Transform_body_tmp \
00414 {[expr {(rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0xca62c1d6) \& 0xffffffff}]} \
00415 ::sha1::SHA1Transform_body_tmp
00416
00417 regsub -all -line \
00418 {rotl32\(\$A,5\)} \
00419 $::sha1::SHA1Transform_body_tmp \
00420 {((($A << 5) \& 0xffffffff) | (($A >> 27) \& 0x1f))} \
00421 ::sha1::SHA1Transform_body_tmp
00422
00423 regsub -all -line \
00424 {\[rotl32 \$B 30\]} \
00425 $::sha1::SHA1Transform_body_tmp \
00426 {[expr {int(($B << 30) | (($B >> 2) \& 0x3fffffff))}]} \
00427 ::sha1::SHA1Transform_body_tmp
00428
00429
00430
00431 regsub -all -line \
00432 {\[F1 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00433 $::sha1::SHA1Transform_body \
00434 {[expr {rotl32($A,5) + ($D ^ ($B \& ($C ^ $D))) + $E + \1 + 0x5a827999}]} \
00435 ::sha1::SHA1Transform_body_tmp2
00436
00437 regsub -all -line \
00438 {\[F2 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00439 $::sha1::SHA1Transform_body_tmp2 \
00440 {[expr {rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0x6ed9eba1}]} \
00441 ::sha1::SHA1Transform_body_tmp2
00442
00443 regsub -all -line \
00444 {\[F3 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00445 $::sha1::SHA1Transform_body_tmp2 \
00446 {[expr {rotl32($A,5) + (($B \& $C) | ($D \& ($B | $C))) + $E + \1 + 0x8f1bbcdc}]} \
00447 ::sha1::SHA1Transform_body_tmp2
00448
00449 regsub -all -line \
00450 {\[F4 \$A \$B \$C \$D \$E (\[.*?\])\]} \
00451 $::sha1::SHA1Transform_body_tmp2 \
00452 {[expr {rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0xca62c1d6}]} \
00453 ::sha1::SHA1Transform_body_tmp2
00454
00455 regsub -all -line \
00456 {rotl32\(\$A,5\)} \
00457 $::sha1::SHA1Transform_body_tmp2 \
00458 {(($A << 5) | (($A >> 27) \& 0x1f))} \
00459 ::sha1::SHA1Transform_body_tmp2
00460
00461 regsub -all -line \
00462 {\[rotl32 \$B 30\]} \
00463 $::sha1::SHA1Transform_body_tmp2 \
00464 {[expr {($B << 30) | (($B >> 2) \& 0x3fffffff)}]} \
00465 ::sha1::SHA1Transform_body_tmp2
00466
00467 if {[package vsatisfies [package provide Tcl] 8.5]} {
00468 ret ::sha1::SHA1Transform (type token , type msg) $::sha1::SHA1Transform_body_tmp
00469 } else {
00470 proc ::sha1::SHA1Transform {token msg} $::sha1::SHA1Transform_body_tmp2
00471 }
00472
00473 un ::sha1 = ::SHA1Transform_body_tmp
00474 un ::sha1 = ::SHA1Transform_body_tmp2
00475
00476
00477
00478 ret ::sha1::byte (type n , type v) {expr {((0xFF << (8 * $n)) & $v) >> (8 * $n)}}
00479 ret ::sha1::bytes (type v) {
00480 #format %c%c%c%c [byte 0 $v] [byte 1 $v] [byte 2 $v] [byte 3 $v]
00481 format %c%c%c%c \
00482 [expr {((0xFF000000 & $v) >> 24) & 0xFF}] \
00483 [expr {(0xFF0000 & $v) >> 16}] \
00484 [expr {(0xFF00 & $v) >> 8}] \
00485 [expr {0xFF & $v}]
00486 }
00487
00488
00489
00490 ret ::sha1::Hex (type data) {
00491 binary scan $data H* result
00492 return $result
00493 }
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505 ret ::sha1::LoadAccelerator (type name) {
00506 variable accel
00507 set r 0
00508 switch -exact -- $name {
00509 critcl {
00510 if {![catch {package require tcllibc}]
00511 || ![catch {package require sha1c}]} {
00512 set r [expr {[info command ::sha1::sha1c] != {}}]
00513 }
00514 }
00515 cryptkit {
00516 if {![catch {package require cryptkit}]} {
00517 set r [expr {![catch {cryptkit::cryptInit}]}]
00518 }
00519 }
00520 trf {
00521 if {![catch {package require Trf}]} {
00522 set r [expr {![catch {::sha1 aa} msg]}]
00523 }
00524 }
00525 default {
00526 return -code error "invalid accelerator package:\
00527 must be one of [join [array names accel] {, }]"
00528 }
00529 }
00530 set accel($name) $r
00531 }
00532
00533
00534
00535
00536
00537
00538 ret ::sha1::Pop (type varname , optional nth =0) {
00539 upvar $varname args
00540 set r [lindex $args $nth]
00541 set args [lreplace $args $nth $nth]
00542 return $r
00543 }
00544
00545
00546
00547
00548
00549 ret ::sha1::Chunk (type token , type channel , optional chunksize =4096) {
00550 upvar #0 $token state
00551
00552 if {[eof $channel]} {
00553 fileevent $channel readable {}
00554 set state(reading) 0
00555 }
00556
00557 SHA1Update $token [read $channel $chunksize]
00558 }
00559
00560
00561
00562 ret ::sha1::sha1 (type args) {
00563 array set opts {-hex 0 -filename {} -channel {} -chunksize 4096}
00564 if {[llength $args] == 1} {
00565 set opts(-hex) 1
00566 } else {
00567 while {[string match -* [set option [lindex $args 0]]]} {
00568 switch -glob -- $option {
00569 -hex { set opts(-hex) 1 }
00570 -bin { set opts(-hex) 0 }
00571 -file* { set opts(-filename) [Pop args 1] }
00572 -channel { set opts(-channel) [Pop args 1] }
00573 -chunksize { set opts(-chunksize) [Pop args 1] }
00574 default {
00575 if {[llength $args] == 1} { break }
00576 if {[string compare $option "--"] == 0} { Pop args; break }
00577 set err [join [lsort [concat -bin [array names opts]]] ", "]
00578 return -code error "bad option $option:\
00579 must be one of $err"
00580 }
00581 }
00582 Pop args
00583 }
00584 }
00585
00586 if {$opts(-filename) != {}} {
00587 set opts(-channel) [open $opts(-filename) r]
00588 fconfigure $opts(-channel) -translation binary
00589 }
00590
00591 if {$opts(-channel) == {}} {
00592
00593 if {[llength $args] != 1} {
00594 return -code error "wrong # args:\
00595 should be \"sha1 ?-hex? -filename file | string\""
00596 }
00597 set tok [SHA1Init]
00598 SHA1Update $tok [lindex $args 0]
00599 set r [SHA1Final $tok]
00600
00601 } else {
00602
00603 set tok [SHA1Init]
00604 # FRINK: nocheck
00605 set [subst $tok](reading) 1
00606 fileevent $opts(-channel) readable \
00607 [list [namespace origin Chunk] \
00608 $tok $opts(-channel) $opts(-chunksize)]
00609 # FRINK: nocheck
00610 vwait [subst $tok](reading)
00611 set r [SHA1Final $tok]
00612
00613 # If we opened the channel - we should close it too.
00614 if {$opts(-filename) != {}} {
00615 close $opts(-channel)
00616 }
00617 }
00618
00619 if {$opts(-hex)} {
00620 set r [Hex $r]
00621 }
00622 return $r
00623 }
00624
00625
00626
00627 ret ::sha1::hmac (type args) {
00628 array set opts {-hex 1 -filename {} -channel {} -chunksize 4096}
00629 if {[llength $args] != 2} {
00630 while {[string match -* [set option [lindex $args 0]]]} {
00631 switch -glob -- $option {
00632 -key { set opts(-key) [Pop args 1] }
00633 -hex { set opts(-hex) 1 }
00634 -bin { set opts(-hex) 0 }
00635 -file* { set opts(-filename) [Pop args 1] }
00636 -channel { set opts(-channel) [Pop args 1] }
00637 -chunksize { set opts(-chunksize) [Pop args 1] }
00638 default {
00639 if {[llength $args] == 1} { break }
00640 if {[string compare $option "--"] == 0} { Pop args; break }
00641 set err [join [lsort [array names opts]] ", "]
00642 return -code error "bad option $option:\
00643 must be one of $err"
00644 }
00645 }
00646 Pop args
00647 }
00648 }
00649
00650 if {[llength $args] == 2} {
00651 set opts(-key) [Pop args]
00652 }
00653
00654 if {![info exists opts(-key)]} {
00655 return -code error "wrong # args:\
00656 should be \"hmac ?-hex? -key key -filename file | string\""
00657 }
00658
00659 if {$opts(-filename) != {}} {
00660 set opts(-channel) [open $opts(-filename) r]
00661 fconfigure $opts(-channel) -translation binary
00662 }
00663
00664 if {$opts(-channel) == {}} {
00665
00666 if {[llength $args] != 1} {
00667 return -code error "wrong # args:\
00668 should be \"hmac ?-hex? -key key -filename file | string\""
00669 }
00670 set tok [HMACInit $opts(-key)]
00671 HMACUpdate $tok [lindex $args 0]
00672 set r [HMACFinal $tok]
00673
00674 } else {
00675
00676 set tok [HMACInit $opts(-key)]
00677 # FRINK: nocheck
00678 set [subst $tok](reading) 1
00679 fileevent $opts(-channel) readable \
00680 [list [namespace origin Chunk] \
00681 $tok $opts(-channel) $opts(-chunksize)]
00682 # FRINK: nocheck
00683 vwait [subst $tok](reading)
00684 set r [HMACFinal $tok]
00685
00686 # If we opened the channel - we should close it too.
00687 if {$opts(-filename) != {}} {
00688 close $opts(-channel)
00689 }
00690 }
00691
00692 if {$opts(-hex)} {
00693 set r [Hex $r]
00694 }
00695 return $r
00696 }
00697
00698
00699
00700
00701 namespace ::sha1 {
00702 foreach e {critcl cryptkit trf} { if {[LoadAccelerator $e]} { break } }
00703 }
00704
00705 package provide sha1 $::sha1::version
00706
00707
00708
00709
00710
00711
00712
00713
00714