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