Tk Library Source Code

Artifact [1be5e8875c]
Login

Artifact 1be5e8875cea982365e7fc75af2bada459e4aee9:

Attachment "1.diff" to ticket [429695ffff] added by andreas_kupries 2001-06-22 21:31:34.
? frinked
Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcllib/tcllib/ChangeLog,v
retrieving revision 1.53
diff -u -r1.53 ChangeLog
--- ChangeLog	2001/05/01 19:01:23	1.53
+++ ChangeLog	2001/06/22 14:28:45
@@ -1,3 +1,8 @@
+2001-06-21  Andreas Kupries <[email protected]>
+
+	* Makefile.in (MODULES): Added module 'sha1'. This is another
+	  message digest like 'md5'.
+
 2001-05-01  Andreas Kupries <[email protected]>
 
 	* Makefile.in (MODULES):  Added module 'report'.
Index: Makefile.in
===================================================================
RCS file: /cvsroot/tcllib/tcllib/Makefile.in,v
retrieving revision 1.44
diff -u -r1.44 Makefile.in
--- Makefile.in	2001/05/01 19:01:23	1.44
+++ Makefile.in	2001/06/22 14:28:47
@@ -41,6 +41,9 @@
 # MD5 hash digest
 MD5=md5
 
+# SHA1 hash digest
+SHA1=sha1
+
 # Data structures (stack, queue, tree)
 STRUCT=struct
 
@@ -106,7 +109,8 @@
 	$(URI)		\
 	$(LOGGER)	\
 	$(HTMLPARSE)	\
-	$(REPORT)
+	$(REPORT)	\
+	$(SHA1)
 
 #========================================================================
 # Nothing of the variables below this line need to be changed.  Please
Index: modules/base64/ChangeLog
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/base64/ChangeLog,v
retrieving revision 1.7
diff -u -r1.7 ChangeLog
--- modules/base64/ChangeLog	2000/10/11 20:04:11	1.7
+++ modules/base64/ChangeLog	2001/06/22 14:28:47
@@ -1,3 +1,14 @@
+2001-06-02  Miguel Sofer  <[email protected]>
+
+	* base64.tcl: Greatly increased speed, obtained by: using lists
+	  instead of arrays, splitting the input with [binary scan],
+	  taking the bytes to be encoded three at a time, and
+	  reformulating the decoding algorithm to be purely
+	  arithmetic. Improved backwards compatibility, now runs with
+	  Tcl8.0.
+
+	  Nudged version to 2.2
+
 2000-10-11  Brent Welch  <[email protected]>
 
 	* base64.tcl: Fixed bug in base64::decode where trailing
Index: modules/base64/base64.tcl
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/base64/base64.tcl,v
retrieving revision 1.8
diff -u -r1.8 base64.tcl
--- modules/base64/base64.tcl	2000/10/11 20:04:11	1.8
+++ modules/base64/base64.tcl	2001/06/22 14:28:51
@@ -13,162 +13,289 @@
 # Version 1.0 implemented Base64_Encode, Bae64_Decode
 # Version 2.0 uses the base64 namespace
 # Version 2.1 fixes various decode bugs and adds options to encode
+# Version 2.2 is much faster, Tcl8.0 compatible
 
 namespace eval base64 {
-    variable i 0
-    variable char
-    variable base64
-    variable base64_en
-    foreach char {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
-	      a b c d e f g h i j k l m n o p q r s t u v w x y z \
-	      0 1 2 3 4 5 6 7 8 9 + /} {
-	set base64($char) $i
-	set base64_en($i) $char
-	incr i
-    }
-
-    namespace export *
 }
 
-# base64::encode --
-#
-#	Base64 encode a given string.
-#
-# Arguments:
-#	args	?-maxlen maxlen? ?-wrapchar wrapchar? string
-#	
-#		If maxlen is 0, the output is not wrapped.
-#
-# Results:
-#	A Base64 encoded version of $string, wrapped at $maxlen characters
-#	by $wrapchar.
+if {![catch {package require Trf 2.0}]} {
+    # Trf is available, so implement the functionality provided here
+    # in terms of calls to Trf for speed.
+
+    # base64::encode --
+    #
+    #	Base64 encode a given string.
+    #
+    # Arguments:
+    #	args	?-maxlen maxlen? ?-wrapchar wrapchar? string
+    #	
+    #		If maxlen is 0, the output is not wrapped.
+    #
+    # Results:
+    #	A Base64 encoded version of $string, wrapped at $maxlen characters
+    #	by $wrapchar.
+    
+    proc base64::encode {args} {
+	# Set the default wrapchar and maximum line length to match the output
+	# of GNU uuencode 4.2.  Various RFC's allow for different wrapping 
+	# characters and wraplengths, so these may be overridden by command line
+	# options.
+	set wrapchar "\n"
+	set maxlen 60
+
+	if { [llength $args] == 0 } {
+	    error "wrong # args: should be \"[lindex [info level 0] 0]\
+		    ?-maxlen maxlen? ?-wrapchar wrapchar? string\""
+	}
 
-proc base64::encode {args} {
-    variable base64_en
+	set optionStrings [list "-maxlen" "-wrapchar"]
+	for {set i 0} {$i < [llength $args] - 1} {incr i} {
+	    set arg [lindex $args $i]
+	    set index [lsearch -glob $optionStrings "${arg}*"]
+	    if { $index == -1 } {
+		error "unknown option \"$arg\": must be -maxlen or -wrapchar"
+	    }
+	    incr i
+	    if { $i >= [llength $args] - 1 } {
+		error "value for \"$arg\" missing"
+	    }
+	    set val [lindex $args $i]
+	    set [string range [lindex $optionStrings $index] 1 end] $val
+	}
     
-    # Set the default wrapchar and maximum line length to match the output
-    # of GNU uuencode 4.2.  Various RFC's allow for different wrapping 
-    # characters and wraplengths, so these may be overridden by command line
-    # options.
-    set wrapchar "\n"
-    set maxlen 60
-
-    if { [llength $args] == 0 } {
-	error "wrong # args: should be \"[lindex [info level 0] 0]\
-		?-maxlen maxlen? ?-wrapchar wrapchar? string\""
-    }
+	# [string is] requires Tcl8.2; this works with 8.0 too
+	if {[catch {expr {$maxlen % 2}}]} {
+	    error "expected integer but got \"$maxlen\""
+	}
 
-    set optionStrings [list "-maxlen" "-wrapchar"]
-    for {set i 0} {$i < [llength $args] - 1} {incr i} {
-	set arg [lindex $args $i]
-	set index [lsearch -glob $optionStrings "${arg}*"]
-	if { $index == -1 } {
-	    error "unknown option \"$arg\": must be -maxlen or -wrapchar"
-	}
-	incr i
-	if { $i >= [llength $args] - 1 } {
-	    error "value for \"$arg\" missing"
+	set string [lindex $args end]
+	set result [::base64 -mode encode -- $string]
+	regsub -all \n $result {} result
+
+	if {$maxlen > 0} {
+	    set res ""
+	    set edge [expr {$maxlen - 1}]
+	    while {[string length $result] > $maxlen} {
+		append res [string range $result 0 $edge]$wrapchar
+		set result [string range $result $maxlen end]
+	    }
+	    if {[string length $result] > 0} {
+		append res $result
+	    }
+	    set result $res
 	}
-	set val [lindex $args $i]
-	set [string range [lindex $optionStrings $index] 1 end] $val
+
+	return $result
     }
-    
-    if { ![string is integer -strict $maxlen] } {
-	error "expected integer but got \"$maxlen\""
+
+    # base64::decode --
+    #
+    #	Base64 decode a given string.
+    #
+    # Arguments:
+    #	string	The string to decode.  Characters not in the base64
+    #		alphabet are ignored (e.g., newlines)
+    #
+    # Results:
+    #	The decoded value.
+
+    proc base64::decode {string} {
+	::base64 -mode decode -- $string
     }
 
-    set string [lindex $args end]
+} else {
+    # Without Trf use a pure tcl implementation
 
-    set result {}
-    set state 0
-    set length 0
-    foreach {c} [split $string {}] {
-	# Do the line length check before appending so that we don't get an
-	# extra newline if the output is a multiple of $maxlen chars long.
-	if {$maxlen && $length >= $maxlen} {
-	    append result $wrapchar
-	    set length 0
-	}
-	scan $c %c x
-	switch [incr state] {
-	    1 {	append result $base64_en([expr {($x >>2) & 0x3F}]) }
-	    2 { append result \
-		$base64_en([expr {(($old << 4) & 0x30) | (($x >> 4) & 0xF)}]) }
-	    3 { append result \
-		$base64_en([expr {(($old << 2) & 0x3C) | (($x >> 6) & 0x3)}])
-		append result $base64_en([expr {($x & 0x3F)}])
-		incr length
-		set state 0}
+    namespace eval base64 {
+	variable base64 {}
+	variable base64_en {}
+
+	# We create the auxiliary array base64_tmp, it will be unset later.
+
+	set i 0
+	foreach char {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
+		a b c d e f g h i j k l m n o p q r s t u v w x y z \
+		0 1 2 3 4 5 6 7 8 9 + /} {
+	    set base64_tmp($char) $i
+	    lappend base64_en $char
+	    incr i
 	}
-	set old $x
-	incr length
-    }
-    set x 0
-    switch $state {
-	0 { # OK }
-	1 { append result $base64_en([expr {(($old << 4) & 0x30)}])== }
-	2 { append result $base64_en([expr {(($old << 2) & 0x3C)}])=  }
+
+	#
+	# Create base64 as list: to code for instance C<->3, specify
+	# that [lindex $base64 67] be 3 (C is 67 in ascii); non-coded
+	# ascii chars get a {}. we later use the fact that lindex on a
+	# non-existing index returns {}, and that [expr {} < 0] is true
+	#
+
+	# the last ascii char is 'z'
+	scan z %c len
+	for {set i 0} {$i <= $len} {incr i} {
+	    set char [format %c $i]
+	    set val {}
+	    if {[info exists base64_tmp($char)]} {
+		set val $base64_tmp($char)
+	    } else {
+		set val {}
+	    }
+	    lappend base64 $val
+	}
+
+	# code the character "=" as -1; used to signal end of message
+	scan = %c i
+	set base64 [lreplace $base64 $i $i -1]
+
+	# remove unneeded variables
+	unset base64_tmp i char len val
+
+	namespace export *
     }
-    return $result
-}
 
-# base64::decode --
-#
-#	Base64 decode a given string.
-#
-# Arguments:
-#	string	The string to decode.  Characters not in the base64
-#		alphabet are ignored (e.g., newlines)
-#
-# Results:
-#	The decoded value.
-
-proc base64::decode {string} {
-    variable base64
-
-    set output {}
-    set group 0
-    set j 18
-    foreach char [split $string {}] {
-	if {[string compare $char "="]} {
-	    # RFC 2045 says that line breaks and other characters not part
-	    # of the Base64 alphabet must be ignored, and that the decoder
-	    # can optionally emit a warning or reject the message.  We opt
-	    # not to do so, but to just ignore the character.
+    # base64::encode --
+    #
+    #	Base64 encode a given string.
+    #
+    # Arguments:
+    #	args	?-maxlen maxlen? ?-wrapchar wrapchar? string
+    #	
+    #		If maxlen is 0, the output is not wrapped.
+    #
+    # Results:
+    #	A Base64 encoded version of $string, wrapped at $maxlen characters
+    #	by $wrapchar.
+    
+    proc base64::encode {args} {
+	set base64_en $::base64::base64_en
+	
+	# Set the default wrapchar and maximum line length to match the output
+	# of GNU uuencode 4.2.  Various RFC's allow for different wrapping 
+	# characters and wraplengths, so these may be overridden by command line
+	# options.
+	set wrapchar "\n"
+	set maxlen 60
+
+	if { [llength $args] == 0 } {
+	    error "wrong # args: should be \"[lindex [info level 0] 0]\
+		    ?-maxlen maxlen? ?-wrapchar wrapchar? string\""
+	}
 
-	    if { ![info exists base64($char)] } {
-		continue
+	set optionStrings [list "-maxlen" "-wrapchar"]
+	for {set i 0} {$i < [llength $args] - 1} {incr i} {
+	    set arg [lindex $args $i]
+	    set index [lsearch -glob $optionStrings "${arg}*"]
+	    if { $index == -1 } {
+		error "unknown option \"$arg\": must be -maxlen or -wrapchar"
+	    }
+	    incr i
+	    if { $i >= [llength $args] - 1 } {
+		error "value for \"$arg\" missing"
 	    }
-	    set bits $base64($char)
-	    set group [expr {$group | ($bits << $j)}]
-	    if {[incr j -6] < 0} {
-		scan [format %06x $group] %2x%2x%2x a b c
-		append output [format %c%c%c $a $b $c]
-		set group 0
-		set j 18
-	    }
-	} else {
-	    # = indicates end of data.  Output whatever chars are left.
-	    # The encoding algorithm dictates that we can only have 1 or 2
-	    # padding characters.  If j is 6, we have 12 bits of input 
-	    # (enough for 1 8-bit output).  If j is 0, we have 18 bits of
-	    # input (enough for 2 8-bit outputs).
-	    # It is crucial to scan three hex digits even though we
-	    # discard c - older code used %04x and scanned 2 hex digits
-	    # but really ended up generating 5 or 6 (not 4!) digits and
-	    # resulted in alignment errors.
-
-	    scan [format %06x $group] %2x%2x%2x a b c
-	    if {$j == 6} {
-		append output [format %c $a]
-	    } elseif {$j == 0} {
-		append output [format %c%c $a $b]
+	    set val [lindex $args $i]
+	    set [string range [lindex $optionStrings $index] 1 end] $val
+	}
+    
+	# [string is] requires Tcl8.2; this works with 8.0 too
+	if {[catch {expr {$maxlen % 2}}]} {
+	    error "expected integer but got \"$maxlen\""
+	}
+
+	set string [lindex $args end]
+
+	set result {}
+	set state 0
+	set length 0
+
+
+	# Process the input bytes 3-by-3
+
+	binary scan $string c* X
+	foreach {x y z} $X {
+	    # Do the line length check before appending so that we don't get an
+	    # extra newline if the output is a multiple of $maxlen chars long.
+	    if {$maxlen && $length >= $maxlen} {
+		append result $wrapchar
+		set length 0
+	    }
+	
+	    append result [lindex $base64_en [expr {($x >>2) & 0x3F}]] 
+	    if {$y != {}} {
+		append result [lindex $base64_en [expr {(($x << 4) & 0x30) | (($y >> 4) & 0xF)}]] 
+		if {$z != {}} {
+		    append result \
+			    [lindex $base64_en [expr {(($y << 2) & 0x3C) | (($z >> 6) & 0x3)}]]
+		    append result [lindex $base64_en [expr {($z & 0x3F)}]]
+		} else {
+		    set state 2
+		    break
+		}
+	    } else {
+		set state 1
+		break
+	    }
+	    incr length 4
+	}
+	if {$state == 1} {
+	    append result [lindex $base64_en [expr {(($x << 4) & 0x30)}]]== 
+	} elseif {$state == 2} {
+	    append result [lindex $base64_en [expr {(($y << 2) & 0x3C)}]]=  
+	}
+	return $result
+    }
+
+    # base64::decode --
+    #
+    #	Base64 decode a given string.
+    #
+    # Arguments:
+    #	string	The string to decode.  Characters not in the base64
+    #		alphabet are ignored (e.g., newlines)
+    #
+    # Results:
+    #	The decoded value.
+
+    proc base64::decode {string} {
+	set base64 $::base64::base64
+
+	binary scan $string c* X
+	foreach x $X {
+	    set bits [lindex $base64 $x]
+	    if {$bits >= 0} {
+		if {[llength [lappend nums $bits]] == 4} {
+		    foreach {v w x y} $nums break
+		    set a [expr {($v << 2) | ($w >> 4)}]
+		    set b [expr {(($w & 0xF) << 4) | ($x >> 2)}]
+		    set c [expr {(($x & 0x3) << 6) | $y}]
+		    append output [binary format ccc $a $b $c]
+		    set nums {}
+		}		
+	    } elseif {$bits == -1} {
+		# = indicates end of data.  Output whatever chars are left.
+		# The encoding algorithm dictates that we can only have 1 or 2
+		# padding characters.  If x=={}, we have 12 bits of input 
+		# (enough for 1 8-bit output).  If x!={}, we have 18 bits of
+		# input (enough for 2 8-bit outputs).
+		
+		foreach {v w x} $nums break
+		set a [expr {($v << 2) | (($w & 0x30) >> 4)}]
+		
+		if {$x == {}} {
+		    append output [binary format c $a ]
+		} else {
+		    set b [expr {(($w & 0xF) << 4) | (($x & 0x3C) >> 2)}]
+		    append output [binary format cc $a $b]
+		}		
+		break
+	    } else {
+		# RFC 2045 says that line breaks and other characters not part
+		# of the Base64 alphabet must be ignored, and that the decoder
+		# can optionally emit a warning or reject the message.  We opt
+		# not to do so, but to just ignore the character. 
+		continue
 	    }
-	    break
 	}
+	return $output
     }
-    return $output
 }
 
-package provide base64 2.1
+package provide base64 2.2
 
Index: modules/base64/base64.test
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/base64/base64.test,v
retrieving revision 1.6
diff -u -r1.6 base64.test
--- modules/base64/base64.test	2000/10/11 17:21:49	1.6
+++ modules/base64/base64.test	2001/06/22 14:28:51
@@ -18,7 +18,16 @@
     set auto_path [linsert $auto_path 0 [file dirname [info script]]]
 }
 
-source [file join [file dirname [info script]] base64.tcl]
+#source [file join [file dirname [info script]] base64.tcl]
+
+package require base64
+if {[catch {package present Trf}]} {
+    puts "base64 [package present base64] (pure Tcl)"
+} else {
+    puts "base64 [package present base64] (Trf based)"
+}
+
+
 
 test base64-1.1 {base64::encode} {
     base64::encode "this is a test\n"
Index: modules/base64/pkgIndex.tcl
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/base64/pkgIndex.tcl,v
retrieving revision 1.2
diff -u -r1.2 pkgIndex.tcl
--- modules/base64/pkgIndex.tcl	2000/10/11 20:37:14	1.2
+++ modules/base64/pkgIndex.tcl	2001/06/22 14:28:51
@@ -8,4 +8,4 @@
 # script is sourced, the variable $dir must contain the
 # full path name of this file's directory.
 
-package ifneeded base64 2.1 [list source [file join $dir base64.tcl]]
+package ifneeded base64 2.2 [list source [file join $dir base64.tcl]]
Index: modules/md5/ChangeLog
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/md5/ChangeLog,v
retrieving revision 1.3
diff -u -r1.3 ChangeLog
--- modules/md5/ChangeLog	2001/06/19 17:21:57	1.3
+++ modules/md5/ChangeLog	2001/06/22 14:28:51
@@ -2,6 +2,14 @@
 
 	* md5.n: Fixed nroff trouble.
 
+2001-06-02  Miguel Sofer  <[email protected]>
+
+	* md5.tcl: modified the pure Tcl code to run almost 5 times
+	  faster, by inlining (via regsub) function calls and using local
+	  variables instead of arrays.
+
+          Bumped version number to 1.4
+
 2001-04-25  Andreas Kupries <[email protected]>
 
 	* md5.test: Added tests of "md5::hmac". This allows us to test the
Index: modules/md5/md5.tcl
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/md5/md5.tcl,v
retrieving revision 1.2
diff -u -r1.2 md5.tcl
--- modules/md5/md5.tcl	2001/04/25 15:30:03	1.2
+++ modules/md5/md5.tcl	2001/06/22 14:28:54
@@ -18,6 +18,8 @@
 # For more info, see: http://expect.nist.gov/md5pure
 #
 # - Don
+#
+# Modified by Miguel Sofer to use inlines and simple variables
 ##################################################
 
 namespace eval ::md5 {
@@ -77,37 +79,7 @@
 } else {
     # Without Trf use the all-tcl implementation by Don Libes.
 
-    namespace eval ::md5 {
-	variable i
-	variable t
-	variable T
-
-	set i 0
-	foreach t {
-	    0xd76aa478 0xe8c7b756 0x242070db 0xc1bdceee
-	    0xf57c0faf 0x4787c62a 0xa8304613 0xfd469501
-	    0x698098d8 0x8b44f7af 0xffff5bb1 0x895cd7be
-	    0x6b901122 0xfd987193 0xa679438e 0x49b40821
-
-	    0xf61e2562 0xc040b340 0x265e5a51 0xe9b6c7aa
-	    0xd62f105d 0x2441453  0xd8a1e681 0xe7d3fbc8
-	    0x21e1cde6 0xc33707d6 0xf4d50d87 0x455a14ed
-	    0xa9e3e905 0xfcefa3f8 0x676f02d9 0x8d2a4c8a
-
-	    0xfffa3942 0x8771f681 0x6d9d6122 0xfde5380c
-	    0xa4beea44 0x4bdecfa9 0xf6bb4b60 0xbebfbc70
-	    0x289b7ec6 0xeaa127fa 0xd4ef3085 0x4881d05
-	    0xd9d4d039 0xe6db99e5 0x1fa27cf8 0xc4ac5665
-
-	    0xf4292244 0x432aff97 0xab9423a7 0xfc93a039
-	    0x655b59c3 0x8f0ccc92 0xffeff47d 0x85845dd1
-	    0x6fa87e4f 0xfe2ce6e0 0xa3014314 0x4e0811a1
-	    0xf7537e82 0xbd3af235 0x2ad7d2bb 0xeb86d391
-	} {
-	    incr i
-	    set T($i) [expr {$t}]
-	}
-    }
+    # T will be inlined after the definition of md5body
 
     # test md5
     #
@@ -150,15 +122,19 @@
     #
     proc ::md5::time {} {
 	foreach len {10 50 100 500 1000 5000 10000} {
-	    set time [::time {md5 [format %$len.0s ""]} 10]
+	    set time [::time {md5 [format %$len.0s ""]} 100]
 	    regexp "\[0-9]*" $time msec
 	    puts "input length $len: [expr {$msec/1000}] milliseconds per interation"
 	}
     }
 
-    proc ::md5::md5 {msg} {
-	variable T
-	
+    #
+    # We just define the body of md5pure::md5 here; later we
+    # regsub to inline a few function calls for speed
+    #
+
+    set ::md5::md5body {
+
 	#
 	# 3.1 Step 1. Append Padding Bits
 	#
@@ -177,7 +153,7 @@
 
 	# append single 1b followed by 0b's
 	append msg [binary format "a$padLen" \200]
-	
+
 	#
 	# 3.2 Step 2. Append Length
 	#
@@ -186,15 +162,15 @@
 	# code demonstrates little-endian
 	# This step limits our input to size 2^32b or 2^24B
 	append msg [binary format "i1i1" [expr {8*$msgLen}] 0]
-    
+	
 	#
 	# 3.3 Step 3. Initialize MD Buffer
 	#
 
-	set A [expr {0x67452301}]
-	set B [expr {0xefcdab89}]
-	set C [expr {0x98badcfe}]
-	set D [expr {0x10325476}]
+	set A [expr 0x67452301]
+	set B [expr 0xefcdab89]
+	set C [expr 0x98badcfe]
+	set D [expr 0x10325476]
 
 	#
 	# 3.4 Step 4. Process Message in 16-Word Blocks
@@ -204,19 +180,11 @@
 	# RFC doesn't say whether to use little- or big-endian
 	# code says little-endian
 	binary scan $msg i* blocks
-	set i 0
-	foreach b $blocks {
-	    set M($i) $b
-	    incr i
-	}
+	set len [llength $blocks]
 
-	set blockLen [array size M]
-	
-	for {set i 0} {$i < $blockLen/16} {incr i} {
-	    # copy block i into X
-	    for {set j 0} {$j<16} {incr j} {
-		set X($j) $M([expr {$i*16+$j}])
-	    }
+	# loop over the message taking 16 blocks at a time
+
+	foreach {X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15} $blocks {
 
 	    # Save A as AA, B as BB, C as CC, and D as DD.
 	    set AA $A
@@ -228,100 +196,100 @@
 	    # Let [abcd k s i] denote the operation
 	    #      a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s).
 	    # [ABCD  0  7  1]  [DABC  1 12  2]  [CDAB  2 17  3]  [BCDA  3 22  4]
-	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X(0)  + $T(1) }]  7]}]
-	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X(1)  + $T(2) }] 12]}]
-	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X(2)  + $T(3) }] 17]}]
-	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X(3)  + $T(4) }] 22]}]
+	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X0  + $T01}]  7]}]
+	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X1  + $T02}] 12]}]
+	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X2  + $T03}] 17]}]
+	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X3  + $T04}] 22]}]
 	    # [ABCD  4  7  5]  [DABC  5 12  6]  [CDAB  6 17  7]  [BCDA  7 22  8]
-	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X(4)  + $T(5) }]  7]}]
-	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X(5)  + $T(6) }] 12]}]
-	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X(6)  + $T(7) }] 17]}]
-	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X(7)  + $T(8) }] 22]}]
+	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X4  + $T05}]  7]}]
+	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X5  + $T06}] 12]}]
+	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X6  + $T07}] 17]}]
+	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X7  + $T08}] 22]}]
 	    # [ABCD  8  7  9]  [DABC  9 12 10]  [CDAB 10 17 11]  [BCDA 11 22 12]
-	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X(8)  + $T(9) }]  7]}]
-	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X(9)  + $T(10)}] 12]}]
-	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X(10) + $T(11)}] 17]}]
-	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X(11) + $T(12)}] 22]}]
+	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X8  + $T09}]  7]}]
+	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X9  + $T10}] 12]}]
+	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X10 + $T11}] 17]}]
+	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X11 + $T12}] 22]}]
 	    # [ABCD 12  7 13]  [DABC 13 12 14]  [CDAB 14 17 15]  [BCDA 15 22 16]
-	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X(12) + $T(13)}]  7]}]
-	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X(13) + $T(14)}] 12]}]
-	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X(14) + $T(15)}] 17]}]
-	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X(15) + $T(16)}] 22]}]
+	    set A [expr {$B + [<<< [expr {$A + [F $B $C $D] + $X12 + $T13}]  7]}]
+	    set D [expr {$A + [<<< [expr {$D + [F $A $B $C] + $X13 + $T14}] 12]}]
+	    set C [expr {$D + [<<< [expr {$C + [F $D $A $B] + $X14 + $T15}] 17]}]
+	    set B [expr {$C + [<<< [expr {$B + [F $C $D $A] + $X15 + $T16}] 22]}]
 
 	    # Round 2.
 	    # Let [abcd k s i] denote the operation
 	    #      a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s).
 	    # Do the following 16 operations.
 	    # [ABCD  1  5 17]  [DABC  6  9 18]  [CDAB 11 14 19]  [BCDA  0 20 20]
-	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X(1)  + $T(17)}]  5]}]
-	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X(6)  + $T(18)}]  9]}]
-	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X(11) + $T(19)}] 14]}]
-	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X(0)  + $T(20)}] 20]}]
+	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X1  + $T17}]  5]}]
+	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X6  + $T18}]  9]}]
+	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X11 + $T19}] 14]}]
+	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X0  + $T20}] 20]}]
 	    # [ABCD  5  5 21]  [DABC 10  9 22]  [CDAB 15 14 23]  [BCDA  4 20 24]
-	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X(5)  + $T(21)}]  5]}]
-	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X(10) + $T(22)}]  9]}]
-	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X(15) + $T(23)}] 14]}]
-	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X(4)  + $T(24)}] 20]}]
+	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X5  + $T21}]  5]}]
+	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X10 + $T22}]  9]}]
+	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X15 + $T23}] 14]}]
+	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X4  + $T24}] 20]}]
 	    # [ABCD  9  5 25]  [DABC 14  9 26]  [CDAB  3 14 27]  [BCDA  8 20 28]
-	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X(9)  + $T(25)}]  5]}]
-	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X(14) + $T(26)}]  9]}]
-	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X(3)  + $T(27)}] 14]}]
-	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X(8)  + $T(28)}] 20]}]
+	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X9  + $T25}]  5]}]
+	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X14 + $T26}]  9]}]
+	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X3  + $T27}] 14]}]
+	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X8  + $T28}] 20]}]
 	    # [ABCD 13  5 29]  [DABC  2  9 30]  [CDAB  7 14 31]  [BCDA 12 20 32]
-	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X(13) + $T(29)}]  5]}]
-	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X(2)  + $T(30)}]  9]}]
-	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X(7)  + $T(31)}] 14]}]
-	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X(12) + $T(32)}] 20]}]
+	    set A [expr {$B + [<<< [expr {$A + [G $B $C $D] + $X13 + $T29}]  5]}]
+	    set D [expr {$A + [<<< [expr {$D + [G $A $B $C] + $X2  + $T30}]  9]}]
+	    set C [expr {$D + [<<< [expr {$C + [G $D $A $B] + $X7  + $T31}] 14]}]
+	    set B [expr {$C + [<<< [expr {$B + [G $C $D $A] + $X12 + $T32}] 20]}]
 
 	    # Round 3.
 	    # Let [abcd k s t] [sic] denote the operation
 	    #     a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s).
 	    # Do the following 16 operations.
 	    # [ABCD  5  4 33]  [DABC  8 11 34]  [CDAB 11 16 35]  [BCDA 14 23 36]
-	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X(5)  + $T(33)}]  4]}]
-	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X(8)  + $T(34)}] 11]}]
-	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X(11) + $T(35)}] 16]}]
-	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X(14) + $T(36)}] 23]}]
-	# [ABCD  1  4 37]  [DABC  4 11 38]  [CDAB  7 16 39]  [BCDA 10 23 40]
-	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X(1)  + $T(37)}]  4]}]
-	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X(4)  + $T(38)}] 11]}]
-	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X(7)  + $T(39)}] 16]}]
-	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X(10) + $T(40)}] 23]}]
+	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X5  + $T33}]  4]}]
+	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X8  + $T34}] 11]}]
+	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X11 + $T35}] 16]}]
+	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X14 + $T36}] 23]}]
+	    # [ABCD  1  4 37]  [DABC  4 11 38]  [CDAB  7 16 39]  [BCDA 10 23 40]
+	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X1  + $T37}]  4]}]
+	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X4  + $T38}] 11]}]
+	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X7  + $T39}] 16]}]
+	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X10 + $T40}] 23]}]
 	    # [ABCD 13  4 41]  [DABC  0 11 42]  [CDAB  3 16 43]  [BCDA  6 23 44]
-	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X(13) + $T(41)}]  4]}]
-	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X(0)  + $T(42)}] 11]}]
-	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X(3)  + $T(43)}] 16]}]
-	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X(6)  + $T(44)}] 23]}]
+	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X13 + $T41}]  4]}]
+	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X0  + $T42}] 11]}]
+	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X3  + $T43}] 16]}]
+	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X6  + $T44}] 23]}]
 	    # [ABCD  9  4 45]  [DABC 12 11 46]  [CDAB 15 16 47]  [BCDA  2 23 48]
-	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X(9)  + $T(45)}]  4]}]
-	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X(12) + $T(46)}] 11]}]
-	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X(15) + $T(47)}] 16]}]
-	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X(2)  + $T(48)}] 23]}]
-	    
+	    set A [expr {$B + [<<< [expr {$A + [H $B $C $D] + $X9  + $T45}]  4]}]
+	    set D [expr {$A + [<<< [expr {$D + [H $A $B $C] + $X12 + $T46}] 11]}]
+	    set C [expr {$D + [<<< [expr {$C + [H $D $A $B] + $X15 + $T47}] 16]}]
+	    set B [expr {$C + [<<< [expr {$B + [H $C $D $A] + $X2  + $T48}] 23]}]
+
 	    # Round 4.
 	    # Let [abcd k s t] [sic] denote the operation
 	    #     a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s).
 	    # Do the following 16 operations.
 	    # [ABCD  0  6 49]  [DABC  7 10 50]  [CDAB 14 15 51]  [BCDA  5 21 52]
-	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X(0)  + $T(49)}]  6]}]
-	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X(7)  + $T(50)}] 10]}]
-	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X(14) + $T(51)}] 15]}]
-	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X(5)  + $T(52)}] 21]}]
+	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X0  + $T49}]  6]}]
+	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X7  + $T50}] 10]}]
+	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X14 + $T51}] 15]}]
+	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X5  + $T52}] 21]}]
 	    # [ABCD 12  6 53]  [DABC  3 10 54]  [CDAB 10 15 55]  [BCDA  1 21 56]
-	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X(12) + $T(53)}]  6]}]
-	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X(3)  + $T(54)}] 10]}]
-	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X(10) + $T(55)}] 15]}]
-	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X(1)  + $T(56)}] 21]}]
+	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X12 + $T53}]  6]}]
+	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X3  + $T54}] 10]}]
+	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X10 + $T55}] 15]}]
+	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X1  + $T56}] 21]}]
 	    # [ABCD  8  6 57]  [DABC 15 10 58]  [CDAB  6 15 59]  [BCDA 13 21 60]
-	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X(8)  + $T(57)}]  6]}]
-	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X(15) + $T(58)}] 10]}]
-	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X(6)  + $T(59)}] 15]}]
-	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X(13) + $T(60)}] 21]}]
+	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X8  + $T57}]  6]}]
+	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X15 + $T58}] 10]}]
+	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X6  + $T59}] 15]}]
+	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X13 + $T60}] 21]}]
 	    # [ABCD  4  6 61]  [DABC 11 10 62]  [CDAB  2 15 63]  [BCDA  9 21 64]
-	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X(4)  + $T(61)}]  6]}]
-	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X(11) + $T(62)}] 10]}]
-	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X(2)  + $T(63)}] 15]}]
-	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X(9)  + $T(64)}] 21]}]
+	    set A [expr {$B + [<<< [expr {$A + [I $B $C $D] + $X4  + $T61}]  6]}]
+	    set D [expr {$A + [<<< [expr {$D + [I $A $B $C] + $X11 + $T62}] 10]}]
+	    set C [expr {$D + [<<< [expr {$C + [I $D $A $B] + $X2  + $T63}] 15]}]
+	    set B [expr {$C + [<<< [expr {$B + [I $C $D $A] + $X9  + $T64}] 21]}]
 
 	    # Then perform the following additions. (That is increment each
 	    #   of the four registers by the value it had before this block
@@ -332,34 +300,93 @@
 	    incr D $DD
 	}
 	# 3.5 Step 5. Output
-	
+
 	# ... begin with the low-order byte of A, and end with the high-order byte
 	# of D.
-	
+
 	return [bytes $A][bytes $B][bytes $C][bytes $D]
     }
 
+    #
+    # Here we inline/regsub the functions F, G, H, I and <<< 
+    #
+
+    namespace eval ::md5 {
+	#proc md5pure::F {x y z} {expr {(($x & $y) | ((~$x) & $z))}}
+	regsub -all {\[ *F +(\$.) +(\$.) +(\$.) *\]} $md5body {((\1 \& \2) | ((~\1) \& \3))} md5body
 
-    # bitwise left-rotate
-    proc ::md5::<<< {x i} {
-	# This works by bitwise-ORing together right piece and left
-	# piece so that the (original) right piece becomes the left
-	# piece and vice versa.
-	#
-	# The (original) right piece is a simple left shift.
-	# The (original) left piece should be a simple right shift
-	# but Tcl does sign extension on right shifts so we
-	# shift it 1 bit, mask off the sign, and finally shift
-	# it the rest of the way.
+	#proc md5pure::G {x y z} {expr {(($x & $z) | ($y & (~$z)))}}
+	regsub -all {\[ *G +(\$.) +(\$.) +(\$.) *\]} $md5body {((\1 \& \3) | (\2 \& (~\3)))} md5body
+
+	#proc md5pure::H {x y z} {expr {$x ^ $y ^ $z}}
+	regsub -all {\[ *H +(\$.) +(\$.) +(\$.) *\]} $md5body {(\1 ^ \2 ^ \3)} md5body
+
+	#proc md5pure::I {x y z} {expr {$y ^ ($x | (~$z))}}
+	regsub -all {\[ *I +(\$.) +(\$.) +(\$.) *\]} $md5body {(\2 ^ (\1 | (~\3)))} md5body
+
+	# bitwise left-rotate
+	if 0 {
+	    proc md5pure::<<< {x i} {
+		# This works by bitwise-ORing together right piece and left
+		# piece so that the (original) right piece becomes the left
+		# piece and vice versa.
+		#
+		# The (original) right piece is a simple left shift.
+		# The (original) left piece should be a simple right shift
+		# but Tcl does sign extension on right shifts so we
+		# shift it 1 bit, mask off the sign, and finally shift
+		# it the rest of the way.
+		
+		expr {($x << $i) | ((($x >> 1) & 0x7fffffff) >> (31-$i))}
+	    }
+	}
+	# inline <<<
+	regsub -all {\[ *<<< +\[ *expr +({[^\}]*})\] +([0-9]+) *\]} $md5body {(([set x [expr \1]] << \2) |  ((($x >> 1) \& 0x7fffffff) >> (31-\2)))} md5body
+
+	# inline the values of T
+	set map {}
+	foreach \
+		tName {
+	    T01 T02 T03 T04 T05 T06 T07 T08 T09 T10 
+	    T11 T12 T13 T14 T15 T16 T17 T18 T19 T20 
+	    T21 T22 T23 T24 T25 T26 T27 T28 T29 T30 
+	    T31 T32 T33 T34 T35 T36 T37 T38 T39 T40 
+	    T41 T42 T43 T44 T45 T46 T47 T48 T49 T50 
+	    T51 T52 T53 T54 T55 T56 T57 T58 T59 T60 
+	    T61 T62 T63 T64 } \
+		tVal {
+	    0xd76aa478 0xe8c7b756 0x242070db 0xc1bdceee
+	    0xf57c0faf 0x4787c62a 0xa8304613 0xfd469501
+	    0x698098d8 0x8b44f7af 0xffff5bb1 0x895cd7be
+	    0x6b901122 0xfd987193 0xa679438e 0x49b40821
+
+	    0xf61e2562 0xc040b340 0x265e5a51 0xe9b6c7aa
+	    0xd62f105d 0x2441453  0xd8a1e681 0xe7d3fbc8
+	    0x21e1cde6 0xc33707d6 0xf4d50d87 0x455a14ed
+	    0xa9e3e905 0xfcefa3f8 0x676f02d9 0x8d2a4c8a
+
+	    0xfffa3942 0x8771f681 0x6d9d6122 0xfde5380c
+	    0xa4beea44 0x4bdecfa9 0xf6bb4b60 0xbebfbc70
+	    0x289b7ec6 0xeaa127fa 0xd4ef3085 0x4881d05
+	    0xd9d4d039 0xe6db99e5 0x1fa27cf8 0xc4ac5665
+
+	    0xf4292244 0x432aff97 0xab9423a7 0xfc93a039
+	    0x655b59c3 0x8f0ccc92 0xffeff47d 0x85845dd1
+	    0x6fa87e4f 0xfe2ce6e0 0xa3014314 0x4e0811a1
+	    0xf7537e82 0xbd3af235 0x2ad7d2bb 0xeb86d391
+	} {
+	    lappend map \$$tName $tVal
+	}
+	set md5body [string map $map $md5body]
 	
-	expr {($x << $i) | ((($x >> 1) & 0x7fffffff) >> (31-$i))}
+
+	# Finally, define the proc
+	proc md5 {msg} $md5body
+
+	# unset auxiliary variables
+	unset md5body tName tVal map
     }
-    
-    proc ::md5::F {x y z} {expr {(($x & $y) | ((~$x) & $z))}}
-    proc ::md5::G {x y z} {expr {(($x & $z) | ($y & (~$z)))}}
-    proc ::md5::H {x y z} {expr {$x ^ $y ^ $z}}
-    proc ::md5::I {x y z} {expr {$y ^ ($x | (~$z))}}
-    
+
     proc ::md5::byte0 {i} {expr {0xff & $i}}
     proc ::md5::byte1 {i} {expr {(0xff00 & $i) >> 8}}
     proc ::md5::byte2 {i} {expr {(0xff0000 & $i) >> 16}}
@@ -402,5 +429,6 @@
 	md5 $k_opad
     }
 }
+
+package provide md5 1.4
 
-package provide md5 1.3
Index: modules/md5/pkgIndex.tcl
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/md5/pkgIndex.tcl,v
retrieving revision 1.2
diff -u -r1.2 pkgIndex.tcl
--- modules/md5/pkgIndex.tcl	2001/04/25 15:30:03	1.2
+++ modules/md5/pkgIndex.tcl	2001/06/22 14:28:54
@@ -8,4 +8,4 @@
 # script is sourced, the variable $dir must contain the
 # full path name of this file's directory.
 
-package ifneeded md5 1.3 [list source [file join $dir md5.tcl]]
+package ifneeded md5 1.4 [list source [file join $dir md5.tcl]]
Index: modules/sha1/ChangeLog
===================================================================
RCS file: ChangeLog
diff -N ChangeLog
--- /dev/null	Thu May 24 22:33:05 2001
+++ ChangeLog	Fri Jun 22 07:28:54 2001
@@ -0,0 +1,6 @@
+2001-06-21  Andreas Kupries <[email protected]>
+
+	* New module, 'sha1'. The code is Don Libes's <[email protected]>
+	  sha1pure, with Donal K. Fellows's patches to speed it up, and
+	  extended with a soft dependency on Trf to allow higher speed if
+	  the environment is right.
Index: modules/sha1/pkgIndex.tcl
===================================================================
RCS file: pkgIndex.tcl
diff -N pkgIndex.tcl
--- /dev/null	Thu May 24 22:33:05 2001
+++ pkgIndex.tcl	Fri Jun 22 07:28:54 2001
@@ -0,0 +1,11 @@
+# Tcl package index file, version 1.1
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script.  It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands.  When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+package ifneeded sha1 1.0 [list source [file join $dir sha1.tcl]]
Index: modules/sha1/sha1.n
===================================================================
RCS file: sha1.n
diff -N sha1.n
--- /dev/null	Thu May 24 22:33:05 2001
+++ sha1.n	Fri Jun 22 07:28:54 2001
@@ -0,0 +1,45 @@
+'\" Copyright (c) 2001 ActiveState Tool Corp.
+'\" All rights reserved.
+'\" 
+'\" RCS: @(#) $Id: sha1.n,v 1.2 2001/06/19 17:21:57 andreas_kupries Exp $
+'\" 
+.so man.macros
+.TH sha1 n 1.0 Sha1 "sha1 hash"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+::sha1::sha1 \- Perform sha1 hashing
+.SH SYNOPSIS
+\fBpackage require sha1 ?1.0?\fR
+.sp
+\fB::sha1::sha1\fR \fImsg\fR?
+.sp
+\fB::sha1::hmac\fR \fIkey text\fR
+.sp
+.BE
+.SH DESCRIPTION
+.PP
+This package provides commands to compute a SHA1 digests of arbitrary
+messages.
+.SH COMMANDSS
+.TP
+\fB::sha1::sha1\fR \fImsg\fR
+The command takes a message and returns the SHA1 digest of this message
+as a hexadecimal string.
+.TP
+\fB::sha1::hmac\fR \fIkey text\fR
+The command takes a key string and a text and returns the hmac of the
+text nder the chosen key as a hexadecimal string.
+.SH EXAMPLES
+.PP
+.CS
+% sha1::sha1 "hello world"
+2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
+.CE
+.PP
+.CS
+% sha1::hmac "our little secret" "hello world"
+a7ed9d62819b9788e22171d9108a00c370104526
+.CE
+.SH KEYWORDS
+sha1, hashing, security
Index: modules/sha1/sha1.tcl
===================================================================
RCS file: sha1.tcl
diff -N sha1.tcl
--- /dev/null	Thu May 24 22:33:05 2001
+++ sha1.tcl	Fri Jun 22 07:28:54 2001
@@ -0,0 +1,330 @@
+##################################################
+#
+# sha1.tcl - SHA1 in Tcl
+# Author: Don Libes <[email protected]>, May 2001
+# Version 1.0.0
+#
+# SHA1 defined by FIPS 180-1, "The SHA1 Message-Digest Algorithm",
+#          http://www.itl.nist.gov/fipspubs/fip180-1.htm
+# HMAC defined by RFC 2104, "Keyed-Hashing for Message Authentication"
+#
+# Some of the comments below come right out of FIPS 180-1; That's why
+# they have such peculiar numbers.  In addition, I have retained
+# original syntax, etc. from the FIPS.  All remaining bugs are mine.
+#
+# HMAC implementation by D. J. Hagberg <[email protected]> and
+# is based on C code in FIPS 2104.
+#
+# For more info, see: http://expect.nist.gov/sha1pure
+#
+# - Don
+##################################################
+
+### Code speedups by Donal Fellows <[email protected]> who may well
+### have added some extra bugs of his own...  :^)
+
+### Changed the code to use Trf if this package is present on the
+### system requiring the sha1 package. Analogous to md5.
+
+namespace eval ::sha1 {
+}
+
+if {![catch {package require Trf 2.0}]} {
+    # Trf is available, so implement the functionality provided here
+    # in terms of calls to Trf for speed.
+
+    proc ::sha1::sha1 {msg} {
+	string tolower [::hex -mode encode [::sha1 $msg]]
+    }
+
+    # hmac: hash for message authentication
+
+    # SHA1 of Trf and SHA1 as defined by this package have slightly
+    # different results. Trf returns the digest in binary, here we get
+    # it as hex-string. In the computation of the HMAC the latter
+    # requires back conversion into binary in some places. With Trf we
+    # can use omit these. (Not all, the first place must not the changed,
+    # see [x]
+
+    proc ::sha1::hmac {key text} {
+	# if key is longer than 64 bytes, reset it to SHA1(key).  If shorter, 
+	# pad it out with null (\x00) chars.
+	set keyLen [string length $key]
+	if {$keyLen > 64} {
+	    set key [binary format H32 [sha1 $key]]
+	    # [x] set key [::sha1 $key]
+	    set keyLen [string length $key]
+	}
+    
+	# ensure the key is padded out to 64 chars with nulls.
+	set padLen [expr {64 - $keyLen}]
+	append key [binary format "a$padLen" {}]
+
+	# Split apart the key into a list of 16 little-endian words
+	binary scan $key i16 blocks
+
+	# XOR key with ipad and opad values
+	set k_ipad {}
+	set k_opad {}
+	foreach i $blocks {
+	    append k_ipad [binary format i [expr {$i ^ 0x36363636}]]
+	    append k_opad [binary format i [expr {$i ^ 0x5c5c5c5c}]]
+	}
+    
+	# Perform inner sha1, appending its results to the outer key
+	append k_ipad $text
+	#append k_opad [binary format H* [sha1 $k_ipad]]
+	append k_opad [::sha1 $k_ipad]
+
+	# Perform outer sha1
+	#sha1 $k_opad
+	string tolower [::hex -mode encode [::sha1 $k_opad]]
+    }
+
+} else {
+    # Without Trf use the all-tcl implementation by Don Libes.
+
+    namespace eval sha1 {
+	variable K
+
+	proc initK {} {
+	    variable K {}
+	    foreach t {
+		0x5A827999
+		0x6ED9EBA1
+		0x8F1BBCDC
+		0xCA62C1D6
+	    } {
+		for {set i 0} {$i < 20} {incr i} {
+		    lappend K $t
+		}
+	    }
+	}
+	initK
+    }
+
+    # test sha1
+    #
+    # This proc is not necessary during runtime and may be omitted if you
+    # are simply inserting this file into a production program.
+    #
+    proc sha1::test {} {
+	foreach {msg expected} {
+	    "abc"
+	    "a9993e364706816aba3e25717850c26c9cd0d89d"
+	    "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+	    "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
+	    "[string repeat a 1000000]"
+	    "34aa973cd4c4daa4f61eeb2bdbad27316534016f"
+	} {
+	    puts "testing: sha1 \"$msg\""
+	    set msg [subst $msg]
+	    set msgLen [string length $msg]
+	    if {$msgLen > 10000} {
+		puts "warning: msg length = $msgLen; this may take a while . . ."
+	    }
+	    set computed [sha1 $msg]
+	    puts "expected: $expected"
+	    puts "computed: $computed"
+	    if {0 != [string compare $computed $expected]} {
+		puts "FAILED"
+	    } else {
+		puts "SUCCEEDED"
+	    }
+	}
+    }
+
+    # time sha1
+    #
+    # This proc is not necessary during runtime and may be omitted if you
+    # are simply inserting this file into a production program.
+    #
+    proc sha1::time {} {
+	foreach len {10 50 100 500 1000 5000 10000} {
+	    set time [::time {sha1 [format %$len.0s ""]} 10]
+	    regexp "\[0-9]*" $time msec
+	    puts "input length $len: [expr {$msec/1000}] milliseconds per interation"
+	}
+    }
+
+    proc sha1::sha1 {msg} {
+	variable K
+
+	#
+	# 4. MESSAGE PADDING
+	#
+
+	# pad to 512 bits (512/8 = 64 bytes)
+
+	set msgLen [string length $msg]
+
+	# last 8 bytes are reserved for msgLen
+	# plus 1 for "1"
+
+	set padLen [expr {56 - $msgLen%64}]
+	if {$msgLen % 64 >= 56} {
+	    incr padLen 64
+	}
+
+	# 4a. and b. append single 1b followed by 0b's
+	append msg [binary format "a$padLen" \200]
+
+	# 4c. append 64-bit length
+	# Our implementation obviously limits string length to 32bits.
+	append msg \0\0\0\0[binary format "I" [expr {8*$msgLen}]]
+    
+	#
+	# 7. COMPUTING THE MESSAGE DIGEST
+	#
+
+	# initial H buffer
+
+	set H0 0x67452301
+	set H1 0xEFCDAB89
+	set H2 0x98BADCFE
+	set H3 0x10325476
+	set H4 0xC3D2E1F0
+
+	#
+	# process message in 16-word blocks (64-byte blocks)
+	#
+
+	# convert message to array of 32-bit integers
+	# each block of 16-words is stored in M($i,0-16)
+
+	binary scan $msg I* words
+	set blockLen [llength $words]
+
+	for {set i 0} {$i < $blockLen} {incr i 16} {
+	    # 7a. Divide M[i] into 16 words W[0], W[1], ...
+	    set W [lrange $words $i [expr {$i+15}]]
+
+	    # 7b. For t = 16 to 79 let W[t] = ....
+	    set t   16
+	    set t3  12
+	    set t8   7
+	    set t14  1
+	    set t16 -1
+	    for {} {$t < 80} {incr t} {
+		set x [expr {[lindex $W [incr t3]] ^ [lindex $W [incr t8]] ^ \
+			[lindex $W [incr t14]] ^ [lindex $W [incr t16]]}]
+		lappend W [expr {($x << 1) | (($x >> 31) & 1)}]
+	    }
+
+	    # 7c. Let A = H[0] ....
+	    set A $H0
+	    set B $H1
+	    set C $H2
+	    set D $H3
+	    set E $H4
+
+	    # 7d. For t = 0 to 79 do
+	    for {set t 0} {$t < 20} {incr t} {
+		set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
+			(($B & $C) | ((~$B) & $D)) \
+			+ $E + [lindex $W $t] + [lindex $K $t]}]
+		set E $D
+		set D $C
+		set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
+		set B $A
+		set A $TEMP
+	    }
+	    for {} {$t<40} {incr t} {
+		set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
+			($B ^ $C ^ $D) \
+			+ $E + [lindex $W $t] + [lindex $K $t]}]
+		set E $D
+		set D $C
+		set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
+		set B $A
+		set A $TEMP
+	    }
+	    for {} {$t<60} {incr t} {
+		set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
+			(($B & $C) | ($B & $D) | ($C & $D)) \
+			+ $E + [lindex $W $t] + [lindex $K $t]}]
+		set E $D
+		set D $C
+		set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
+		set B $A
+		set A $TEMP
+	    }
+	    for {} {$t<80} {incr t} {
+		set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
+			($B ^ $C ^ $D) \
+			+ $E + [lindex $W $t] + [lindex $K $t]}]
+		set E $D
+		set D $C
+		set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
+		set B $A
+		set A $TEMP
+	    }
+
+	    incr H0 $A
+	    incr H1 $B
+	    incr H2 $C
+	    incr H3 $D
+	    incr H4 $E
+	}
+
+	return [format %0.8x%0.8x%0.8x%0.8x%0.8x $H0 $H1 $H2 $H3 $H4]
+    }
+
+    ### These procedures are either inlined or replaced with a normal [format]!
+    #
+    #proc sha1::f {t B C D} {
+    #    switch [expr {$t/20}] {
+    #	 0 {
+    #	     expr {($B & $C) | ((~$B) & $D)}
+    #	 } 1 - 3 {
+    #	     expr {$B ^ $C ^ $D}
+    #	 } 2 {
+    #	     expr {($B & $C) | ($B & $D) | ($C & $D)}
+    #	 }
+    #    }
+    #}
+    #
+    #proc sha1::byte0 {i} {expr {0xff & $i}}
+    #proc sha1::byte1 {i} {expr {(0xff00 & $i) >> 8}}
+    #proc sha1::byte2 {i} {expr {(0xff0000 & $i) >> 16}}
+    #proc sha1::byte3 {i} {expr {((0xff000000 & $i) >> 24) & 0xff}}
+    #
+    #proc sha1::bytes {i} {
+    #    format %0.2x%0.2x%0.2x%0.2x [byte3 $i] [byte2 $i] [byte1 $i] [byte0 $i]
+    #}
+
+    # hmac: hash for message authentication
+    proc sha1::hmac {key text} {
+	# if key is longer than 64 bytes, reset it to SHA1(key).  If shorter, 
+	# pad it out with null (\x00) chars.
+	set keyLen [string length $key]
+	if {$keyLen > 64} {
+	    set key [binary format H32 [sha1 $key]]
+	    set keyLen [string length $key]
+	}
+
+	# ensure the key is padded out to 64 chars with nulls.
+	set padLen [expr {64 - $keyLen}]
+	append key [binary format "a$padLen" {}]
+
+	# Split apart the key into a list of 16 little-endian words
+	binary scan $key i16 blocks
+
+	# XOR key with ipad and opad values
+	set k_ipad {}
+	set k_opad {}
+	foreach i $blocks {
+	    append k_ipad [binary format i [expr {$i ^ 0x36363636}]]
+	    append k_opad [binary format i [expr {$i ^ 0x5c5c5c5c}]]
+	}
+    
+	# Perform inner sha1, appending its results to the outer key
+	append k_ipad $text
+	append k_opad [binary format H* [sha1 $k_ipad]]
+
+	# Perform outer sha1
+	sha1 $k_opad
+    }
+}
+
+package provide sha1 1.0
Index: modules/sha1/sha1.test
===================================================================
RCS file: sha1.test
diff -N sha1.test
--- /dev/null	Thu May 24 22:33:05 2001
+++ sha1.test	Fri Jun 22 07:28:54 2001
@@ -0,0 +1,70 @@
+# -*- tcl -*-
+# sha1.test:  tests for the sha1 commands
+#
+# This file contains a collection of tests for one or more of the Tcl
+# built-in commands.  Sourcing this file into Tcl runs the tests and
+# generates output for errors.  No output means no errors were found.
+#
+# Copyright (c) 2001 by ActiveState Tool Corp.
+# All rights reserved.
+#
+# RCS: @(#) $Id: sha1.test,v 1.2 2001/04/25 15:30:03 andreas_kupries Exp $
+
+if {[lsearch [namespace children] ::tcltest] == -1} {
+    package require tcltest
+    namespace import ::tcltest::*
+}
+
+package require sha1
+if {[catch {package present Trf}]} {
+    puts "sha1 [package present sha1] (pure Tcl)"
+} else {
+    puts "sha1 [package present sha1] (Trf based)"
+}
+
+
+test sha1-1.0 {sha1} {
+    catch {::sha1::sha1} result
+    set result
+} {no value given for parameter "msg" to "::sha1::sha1"}
+
+test sha1-1.1 {sha1} {
+    catch {::sha1::hmac} result
+    set result
+} {no value given for parameter "key" to "::sha1::hmac"}
+
+test sha1-1.2 {sha1} {
+    catch {::sha1::hmac key} result
+    set result
+} {no value given for parameter "text" to "::sha1::hmac"}
+
+
+foreach {n msg expected} {
+    1 "abc"
+    "a9993e364706816aba3e25717850c26c9cd0d89d"
+    2 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+    "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
+} {
+    test sha1-2.$n {sha1} {
+	::sha1::sha1 $msg
+    } $expected ; # {}
+}
+
+foreach {n key text expected} {
+    1 ""     ""      "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d"
+    2 "foo"  "hello" "4c883e9bc42763641bba04185d492de00de7ce2c"
+    3 "bar"  "world" "a905e79f51faa446cb5a3888b577e34577ef7fce"
+    4 "key"  "text"  "369e2959eb49450338b212748f77d8ded74847bb"
+    5 "sha1" "hmac"  "2660aeeccf432596e56f8f8260de971322e8935b"
+    6 "hmac" "sha1"  "170523fd610da92dd4b4fb948a01a8365d66511a"
+    7 "sha1" "sha1"  "5154473317173f66212fc59365233ffd9cbaab94"
+    8 "hmac" "hmac"  "9e08393f6ac829c4385930ea38567dad582d958f"
+    9 "01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh==" "hello world"
+    "6541c34492618a052c12cb9f88fb795d97595b34"
+} {
+    test sha1-3.$n {hmac} {
+	::sha1::hmac $key $text
+    } $expected ; # {}
+}
+
+::tcltest::cleanupTests