sha1.tcl

Go to the documentation of this file.
00001 /*  sha1.tcl - */
00002 /* */
00003 /*  Copyright (C) 2001 Don Libes <libes@nist.gov>*/
00004 /*  Copyright (C) 2003 Pat Thoyts <patthoyts@users.sourceforge.net>*/
00005 /* */
00006 /*  SHA1 defined by FIPS 180-1, "The SHA1 Message-Digest Algorithm"*/
00007 /*  HMAC defined by RFC 2104, "Keyed-Hashing for Message Authentication"*/
00008 /* */
00009 /*  This is an implementation of SHA1 based upon the example code given in*/
00010 /*  FIPS 180-1 and upon the tcllib MD4 implementation and taking some ideas*/
00011 /*  and methods from the earlier tcllib sha1 version by Don Libes.*/
00012 /* */
00013 /*  This implementation permits incremental updating of the hash and */
00014 /*  provides support for external compiled implementations either using*/
00015 /*  critcl (sha1c) or Trf.*/
00016 /* */
00017 /*  ref: http://www.itl.nist.gov/fipspubs/fip180-1.htm*/
00018 /* */
00019 /*  -------------------------------------------------------------------------*/
00020 /*  See the file "license.terms" for information on usage and redistribution*/
00021 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00022 /*  -------------------------------------------------------------------------*/
00023 /* */
00024 /*  $Id: sha1.tcl,v 1.21 2007/05/03 21:41:10 andreas_kupries Exp $*/
00025 
00026 /*  @mdgen EXCLUDE: sha1c.tcl*/
00027 
00028 package require Tcl 8.2;                /*  tcl minimum version*/
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 /*  Management of sha1 implementations.*/
00050 
00051 /*  LoadAccelerator --*/
00052 /* */
00053 /*  This package can make use of a number of compiled extensions to*/
00054 /*  accelerate the digest computation. This procedure manages the*/
00055 /*  use of these extensions within the package. During normal usage*/
00056 /*  this should not be called, but the test package manipulates the*/
00057 /*  list of enabled accelerators.*/
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 /*  ::sha1::Implementations --*/
00093 /* */
00094 /*  Determines which implementations are*/
00095 /*  present, i.e. loaded.*/
00096 /* */
00097 /*  Arguments:*/
00098 /*  None.*/
00099 /* */
00100 /*  Results:*/
00101 /*  A list of implementation keys.*/
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 /*  ::sha1::KnownImplementations --*/
00114 /* */
00115 /*  Determines which implementations are known*/
00116 /*  as possible implementations.*/
00117 /* */
00118 /*  Arguments:*/
00119 /*  None.*/
00120 /* */
00121 /*  Results:*/
00122 /*  A list of implementation keys. In the order*/
00123 /*  of preference, most prefered first.*/
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 /*  ::sha1::SwitchTo --*/
00139 /* */
00140 /*  Activates a loaded named implementation.*/
00141 /* */
00142 /*  Arguments:*/
00143 /*  key Name of the implementation to activate.*/
00144 /* */
00145 /*  Results:*/
00146 /*  None.*/
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 /*  SHA1Init --*/
00183 /* */
00184 /*    Create and initialize an SHA1 state variable. This will be*/
00185 /*    cleaned up when we call SHA1Final*/
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 /*  SHA1Update --*/
00225 /* */
00226 /*    This is called to add more data into the hash. You may call this*/
00227 /*    as many times as you require. Note that passing in "ABC" is equivalent*/
00228 /*    to passing these letters in as separate calls -- hence this proc */
00229 /*    permits hashing of chunked data*/
00230 /* */
00231 /*    If we have a C-based implementation available, then we will use*/
00232 /*    it here in preference to the pure-Tcl implementation.*/
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 /*  SHA1Final --*/
00271 /* */
00272 /*     This procedure is used to close the current hash and returns the*/
00273 /*     hash data. Once this procedure has been called the hash context*/
00274 /*     is freed and cannot be used again.*/
00275 /* */
00276 /*     Note that the output is 160 bits represented as binary data.*/
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 /*  HMAC Hashed Message Authentication (RFC 2104)*/
00333 /* */
00334 /*  hmac = H(K xor opad, H(K xor ipad, text))*/
00335 /* */
00336 
00337 /*  HMACInit --*/
00338 /* */
00339 /*     This is equivalent to the SHA1Init procedure except that a key is*/
00340 /*     added into the algorithm*/
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 /*  HMACUpdate --*/
00376 /* */
00377 /*     Identical to calling SHA1Update*/
00378 /* */
00379 ret  ::sha1::HMACUpdate (type token , type data) {
00380     SHA1Update $token $data
00381     return
00382 }
00383 
00384 /*  HMACFinal --*/
00385 /* */
00386 /*     This is equivalent to the SHA1Final procedure. The hash context is*/
00387 /*     closed and the binary representation of the hash result is returned.*/
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 /*  Description:*/
00400 /*   This is the core SHA1 algorithm. It is a lot like the MD4 algorithm but*/
00401 /*   includes an extra round and a set of constant modifiers throughout.*/
00402 /* */
00403  ::sha1 = ::SHA1Transform_body {
00404     upvar /* 0 $token state*/
00405 
00406     /*  FIPS 180-1: 7a: Process Message in 16-Word Blocks*/
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         /*  FIPS 180-1: 7b: Expand the input into 80 words*/
00413         /*  For t = 16 to 79 */
00414         /*    let Wt = (Wt-3 ^ Wt-8 ^ Wt-14 ^ Wt-16) <<< 1*/
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         /*  FIPS 180-1: 7c: Copy hash state.*/
00426          A =  $state(A)
00427          B =  $state(B)
00428          C =  $state(C)
00429          D =  $state(D)
00430          E =  $state(E)
00431 
00432         /*  FIPS 180-1: 7d: Do permutation rounds*/
00433         /*  For t = 0 to 79 do*/
00434         /*    TEMP = (A<<<5) + ft(B,C,D) + E + Wt + Kt;*/
00435         /*    E = D; D = C; C = S30(B); B = A; A = TEMP;*/
00436 
00437         /*  Round 1: ft(B,C,D) = (B & C) | (~B & D) ( 0 <= t <= 19)*/
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         /*  Round 2: ft(B,C,D) = (B ^ C ^ D) ( 20 <= t <= 39)*/
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         /*  Round 3: ft(B,C,D) = ((B & C) | (B & D) | (C & D)) ( 40 <= t <= 59)*/
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         /*  Round 4: ft(B,C,D) = (B ^ C ^ D) ( 60 <= t <= 79)*/
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         /*  Then perform the following additions. (That is, increment each*/
00478         /*  of the four registers by the value it had before this block*/
00479         /*  was started.)*/
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 /*  In order to get this code to go as fast as possible while leaving*/
00521 /*  the main code readable we can substitute the above function bodies*/
00522 /*  into the transform procedure. This inlines the code for us an avoids*/
00523 /*  a procedure call overhead within the loops.*/
00524 /* */
00525 /*  We can do some minor tweaking to improve speed on Tcl < 8.5 where we*/
00526 /*  know our arithmetic is limited to 64 bits. On > 8.5 we may have */
00527 /*  unconstrained integer arithmetic and must avoid letting it run away.*/
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 /*  Version 2 avoids a few truncations to 32 bits in non-essential places.*/
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 /*  Description:*/
00635 /*   Pop the nth element off a list. Used in options processing.*/
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 /*  fileevent handler for chunked file hashing.*/
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 /*  Try and load a compiled extension to help.*/
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 /*  Local Variables:*/
00815 /*    mode: tcl*/
00816 /*    indent-tabs-mode: nil*/
00817 /*  End:*/
00818 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1