md4.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 package require Tcl 8.2;
00014 catch {package require md4c 1.0};
00015
00016
00017
00018 namespace ::md4 {
00019 variable version 1.0.4
00020 variable rcsid {$Id: md4.tcl,v 1.19 2006/09/19 23:36:17 andreas_kupries Exp $}
00021 variable accel
00022 array accel = {critcl 0 cryptkit 0}
00023
00024 namespace export md4 hmac MD4Init MD4Update MD4Final
00025
00026 variable uid
00027 if {![info exists uid]} {
00028 uid = 0
00029 }
00030 }
00031
00032
00033
00034
00035
00036
00037 ret ::md4::MD4Init () {
00038 variable uid
00039 variable accel
00040 set token [namespace current]::[incr uid]
00041 upvar #0 $token state
00042
00043 # RFC1320:3.3 - Initialize MD4 state structure
00044 array set state \
00045 [list \
00046 A [expr {0x67452301}] \
00047 B [expr {0xefcdab89}] \
00048 C [expr {0x98badcfe}] \
00049 D [expr {0x10325476}] \
00050 n 0 i "" ]
00051 if {$accel(cryptkit)} {
00052 cryptkit::cryptCreateContext state(ckctx) CRYPT_UNUSED CRYPT_ALGO_MD4
00053 }
00054 return $token
00055 }
00056
00057 ret ::md4::MD4Update (type token , type data) {
00058 variable accel
00059 upvar #0 $token state
00060
00061 if {$accel(critcl)} {
00062 if {[info exists state(md4c)]} {
00063 set state(md4c) [md4c $data $state(md4c)]
00064 } else {
00065 set state(md4c) [md4c $data]
00066 }
00067 return
00068 } elseif {[info exists state(ckctx)]} {
00069 if {[string length $data] > 0} {
00070 cryptkit::cryptEncrypt $state(ckctx) $data
00071 }
00072 return
00073 }
00074
00075 # Update the state values
00076 incr state(n) [string length $data]
00077 append state(i) $data
00078
00079 # Calculate the hash for any complete blocks
00080 set len [string length $state(i)]
00081 for {set n 0} {($n + 64) <= $len} {} {
00082 MD4Hash $token [string range $state(i) $n [incr n 64]]
00083 }
00084
00085 # Adjust the state for the blocks completed.
00086 set state(i) [string range $state(i) $n end]
00087 return
00088 }
00089
00090 ret ::md4::MD4Final (type token) {
00091 upvar #0 $token state
00092
00093 if {[info exists state(md4c)]} {
00094 set r $state(md4c)
00095 unset state
00096 return $r
00097 } elseif {[info exists state(ckctx)]} {
00098 cryptkit::cryptEncrypt $state(ckctx) ""
00099 cryptkit::cryptGetAttributeString $state(ckctx) \
00100 CRYPT_CTXINFO_HASHVALUE r 16
00101 cryptkit::cryptDestroyContext $state(ckctx)
00102 # If nothing was hashed, we get no r variable set!
00103 if {[info exists r]} {
00104 unset state
00105 return $r
00106 }
00107 }
00108
00109 # RFC1320:3.1 - Padding
00110 #
00111 set len [string length $state(i)]
00112 set pad [expr {56 - ($len % 64)}]
00113 if {$len % 64 > 56} {
00114 incr pad 64
00115 }
00116 if {$pad == 0} {
00117 incr pad 64
00118 }
00119 append state(i) [binary format a$pad \x80]
00120
00121 # RFC1320:3.2 - Append length in bits as little-endian wide int.
00122 append state(i) [binary format ii [expr {8 * $state(n)}] 0]
00123
00124 # Calculate the hash for the remaining block.
00125 set len [string length $state(i)]
00126 for {set n 0} {($n + 64) <= $len} {} {
00127 MD4Hash $token [string range $state(i) $n [incr n 64]]
00128 }
00129
00130 # RFC1320:3.5 - Output
00131 set r [bytes $state(A)][bytes $state(B)][bytes $state(C)][bytes $state(D)]
00132 unset state
00133 return $r
00134 }
00135
00136
00137
00138
00139
00140
00141 ret ::md4::HMACInit (type K) {
00142
00143 # Key K is adjusted to be 64 bytes long. If K is larger, then use
00144 # the MD4 digest of K and pad this instead.
00145 set len [string length $K]
00146 if {$len > 64} {
00147 set tok [MD4Init]
00148 MD4Update $tok $K
00149 set K [MD4Final $tok]
00150 set len [string length $K]
00151 }
00152 set pad [expr {64 - $len}]
00153 append K [string repeat \0 $pad]
00154
00155 # Cacluate the padding buffers.
00156 set Ki {}
00157 set Ko {}
00158 binary scan $K i16 Ks
00159 foreach k $Ks {
00160 append Ki [binary format i [expr {$k ^ 0x36363636}]]
00161 append Ko [binary format i [expr {$k ^ 0x5c5c5c5c}]]
00162 }
00163
00164 set tok [MD4Init]
00165 MD4Update $tok $Ki; # initialize with the inner pad
00166
00167 # preserve the Ko value for the final stage.
00168 # FRINK: nocheck
00169 set [subst $tok](Ko) $Ko
00170
00171 return $tok
00172 }
00173
00174 ret ::md4::HMACUpdate (type token , type data) {
00175 MD4Update $token $data
00176 return
00177 }
00178
00179 ret ::md4::HMACFinal (type token) {
00180 # FRINK: nocheck
00181 variable $token
00182 upvar 0 $token state
00183
00184 set tok [MD4Init]; # init the outer hashing function
00185 MD4Update $tok $state(Ko); # prepare with the outer pad.
00186 MD4Update $tok [MD4Final $token]; # hash the inner result
00187 return [MD4Final $tok]
00188 }
00189
00190
00191
00192 ::md4 = ::MD4Hash_body {
00193 variable $token
00194 upvar 0 $token state
00195
00196
00197 binary scan $msg i* blocks
00198 foreach {X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15} $blocks {
00199 A = $state(A)
00200 B = $state(B)
00201 C = $state(C)
00202 D = $state(D)
00203
00204
00205
00206
00207
00208
00209 A = [expr {($A + [F $B $C $D] + $X0) <<< 3}]
00210 D = [expr {($D + [F $A $B $C] + $X1) <<< 7}]
00211 C = [expr {($C + [F $D $A $B] + $X2) <<< 11}]
00212 B = [expr {($B + [F $C $D $A] + $X3) <<< 19}]
00213
00214 A = [expr {($A + [F $B $C $D] + $X4) <<< 3}]
00215 D = [expr {($D + [F $A $B $C] + $X5) <<< 7}]
00216 C = [expr {($C + [F $D $A $B] + $X6) <<< 11}]
00217 B = [expr {($B + [F $C $D $A] + $X7) <<< 19}]
00218
00219 A = [expr {($A + [F $B $C $D] + $X8) <<< 3}]
00220 D = [expr {($D + [F $A $B $C] + $X9) <<< 7}]
00221 C = [expr {($C + [F $D $A $B] + $X10) <<< 11}]
00222 B = [expr {($B + [F $C $D $A] + $X11) <<< 19}]
00223
00224 A = [expr {($A + [F $B $C $D] + $X12) <<< 3}]
00225 D = [expr {($D + [F $A $B $C] + $X13) <<< 7}]
00226 C = [expr {($C + [F $D $A $B] + $X14) <<< 11}]
00227 B = [expr {($B + [F $C $D $A] + $X15) <<< 19}]
00228
00229
00230
00231
00232
00233
00234 A = [expr {($A + [G $B $C $D] + $X0 + 0x5a827999) <<< 3}]
00235 D = [expr {($D + [G $A $B $C] + $X4 + 0x5a827999) <<< 5}]
00236 C = [expr {($C + [G $D $A $B] + $X8 + 0x5a827999) <<< 9}]
00237 B = [expr {($B + [G $C $D $A] + $X12 + 0x5a827999) <<< 13}]
00238
00239 A = [expr {($A + [G $B $C $D] + $X1 + 0x5a827999) <<< 3}]
00240 D = [expr {($D + [G $A $B $C] + $X5 + 0x5a827999) <<< 5}]
00241 C = [expr {($C + [G $D $A $B] + $X9 + 0x5a827999) <<< 9}]
00242 B = [expr {($B + [G $C $D $A] + $X13 + 0x5a827999) <<< 13}]
00243
00244 A = [expr {($A + [G $B $C $D] + $X2 + 0x5a827999) <<< 3}]
00245 D = [expr {($D + [G $A $B $C] + $X6 + 0x5a827999) <<< 5}]
00246 C = [expr {($C + [G $D $A $B] + $X10 + 0x5a827999) <<< 9}]
00247 B = [expr {($B + [G $C $D $A] + $X14 + 0x5a827999) <<< 13}]
00248
00249 A = [expr {($A + [G $B $C $D] + $X3 + 0x5a827999) <<< 3}]
00250 D = [expr {($D + [G $A $B $C] + $X7 + 0x5a827999) <<< 5}]
00251 C = [expr {($C + [G $D $A $B] + $X11 + 0x5a827999) <<< 9}]
00252 B = [expr {($B + [G $C $D $A] + $X15 + 0x5a827999) <<< 13}]
00253
00254
00255
00256
00257
00258
00259 A = [expr {($A + [H $B $C $D] + $X0 + 0x6ed9eba1) <<< 3}]
00260 D = [expr {($D + [H $A $B $C] + $X8 + 0x6ed9eba1) <<< 9}]
00261 C = [expr {($C + [H $D $A $B] + $X4 + 0x6ed9eba1) <<< 11}]
00262 B = [expr {($B + [H $C $D $A] + $X12 + 0x6ed9eba1) <<< 15}]
00263
00264 A = [expr {($A + [H $B $C $D] + $X2 + 0x6ed9eba1) <<< 3}]
00265 D = [expr {($D + [H $A $B $C] + $X10 + 0x6ed9eba1) <<< 9}]
00266 C = [expr {($C + [H $D $A $B] + $X6 + 0x6ed9eba1) <<< 11}]
00267 B = [expr {($B + [H $C $D $A] + $X14 + 0x6ed9eba1) <<< 15}]
00268
00269 A = [expr {($A + [H $B $C $D] + $X1 + 0x6ed9eba1) <<< 3}]
00270 D = [expr {($D + [H $A $B $C] + $X9 + 0x6ed9eba1) <<< 9}]
00271 C = [expr {($C + [H $D $A $B] + $X5 + 0x6ed9eba1) <<< 11}]
00272 B = [expr {($B + [H $C $D $A] + $X13 + 0x6ed9eba1) <<< 15}]
00273
00274 A = [expr {($A + [H $B $C $D] + $X3 + 0x6ed9eba1) <<< 3}]
00275 D = [expr {($D + [H $A $B $C] + $X11 + 0x6ed9eba1) <<< 9}]
00276 C = [expr {($C + [H $D $A $B] + $X7 + 0x6ed9eba1) <<< 11}]
00277 B = [expr {($B + [H $C $D $A] + $X15 + 0x6ed9eba1) <<< 15}]
00278
00279
00280
00281
00282 incr state(A) $A
00283 incr state(B) $B
00284 incr state(C) $C
00285 incr state(D) $D
00286 }
00287
00288 return
00289 }
00290
00291 ret ::md4::byte (type n , type v) {expr {((0xFF << (8 * $n)) & $v) >> (8 * $n)}}
00292 ret ::md4::bytes (type v) {
00293 #format %c%c%c%c [byte 0 $v] [byte 1 $v] [byte 2 $v] [byte 3 $v]
00294 format %c%c%c%c \
00295 [expr {0xFF & $v}] \
00296 [expr {(0xFF00 & $v) >> 8}] \
00297 [expr {(0xFF0000 & $v) >> 16}] \
00298 [expr {((0xFF000000 & $v) >> 24) & 0xFF}]
00299 }
00300
00301
00302 ret ::md4::<<< (type v , type n) {
00303 return [expr {((($v << $n) \
00304 | (($v >> (32 - $n)) \
00305 & (0x7FFFFFFF >> (31 - $n))))) \
00306 & 0xFFFFFFFF}]
00307 }
00308
00309
00310 regsub -all -line \
00311 {\[expr {(.*) <<< (\d+)}\]} \
00312 $::md4::MD4Hash_body \
00313 {[<<< [expr {\1}] \2]} \
00314 ::md4::MD4Hash_body
00315
00316
00317 ret ::md4::F (type X , type Y , type Z) {
00318 return [expr {($X & $Y) | ((~$X) & $Z)}]
00319 }
00320
00321
00322 regsub -all -line \
00323 {\[F (\$[ABCD]) (\$[ABCD]) (\$[ABCD])\]} \
00324 $::md4::MD4Hash_body \
00325 {( (\1 \& \2) | ((~\1) \& \3) )} \
00326 ::md4::MD4Hash_body
00327
00328
00329 ret ::md4::G (type X , type Y , type Z) {
00330 return [expr {($X & $Y) | ($X & $Z) | ($Y & $Z)}]
00331 }
00332
00333
00334 regsub -all -line \
00335 {\[G (\$[ABCD]) (\$[ABCD]) (\$[ABCD])\]} \
00336 $::md4::MD4Hash_body \
00337 {((\1 \& \2) | (\1 \& \3) | (\2 \& \3))} \
00338 ::md4::MD4Hash_body
00339
00340
00341 ret ::md4::H (type X , type Y , type Z) {
00342 return [expr {$X ^ $Y ^ $Z}]
00343 }
00344
00345
00346 regsub -all -line \
00347 {\[H (\$[ABCD]) (\$[ABCD]) (\$[ABCD])\]} \
00348 $::md4::MD4Hash_body \
00349 {(\1 ^ \2 ^ \3)} \
00350 ::md4::MD4Hash_body
00351
00352
00353 ret ::md4::MD4Hash (type token , type msg) $::md4::MD4Hash_body
00354
00355 # -------------------------------------------------------------------------
00356
00357 if {[package provide Trf] != {}} {
00358 interp alias {} ::md4::Hex {} ::hex -mode encode --
00359 } else {
00360 ret ::md4::Hex (type data) {
00361 binary scan $data H* result
00362 return [string toupper $result]
00363 }
00364 }
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376 ret ::md4::LoadAccelerator (type name) {
00377 variable accel
00378 set r 0
00379 switch -exact -- $name {
00380 critcl {
00381 if {![catch {package require tcllibc}]
00382 || ![catch {package require md4c}]} {
00383 set r [expr {[info command ::md4::md4c] != {}}]
00384 }
00385 }
00386 cryptkit {
00387 if {![catch {package require cryptkit}]} {
00388 set r [expr {![catch {cryptkit::cryptInit}]}]
00389 }
00390 }
00391 #trf {
00392 # if {![catch {package require Trf}]} {
00393 # set r [expr {![catch {::md4 aa} msg]}]
00394 # }
00395 #}
00396 default {
00397 return -code error "invalid accelerator package:\
00398 must be one of [join [array names accel] {, }]"
00399 }
00400 }
00401 set accel($name) $r
00402 }
00403
00404
00405
00406
00407
00408
00409 ret ::md4::Pop (type varname , optional nth =0) {
00410 upvar $varname args
00411 set r [lindex $args $nth]
00412 set args [lreplace $args $nth $nth]
00413 return $r
00414 }
00415
00416
00417
00418
00419
00420 ret ::md4::Chunk (type token , type channel , optional chunksize =4096) {
00421 # FRINK: nocheck
00422 variable $token
00423 upvar 0 $token state
00424
00425 if {[eof $channel]} {
00426 fileevent $channel readable {}
00427 set state(reading) 0
00428 }
00429
00430 MD4Update $token [read $channel $chunksize]
00431 }
00432
00433
00434
00435 ret ::md4::md4 (type args) {
00436 array set opts {-hex 0 -filename {} -channel {} -chunksize 4096}
00437 while {[string match -* [set option [lindex $args 0]]]} {
00438 switch -glob -- $option {
00439 -hex { set opts(-hex) 1 }
00440 -file* { set opts(-filename) [Pop args 1] }
00441 -channel { set opts(-channel) [Pop args 1] }
00442 -chunksize { set opts(-chunksize) [Pop args 1] }
00443 default {
00444 if {[llength $args] == 1} { break }
00445 if {[string compare $option "--"] == 0 } { Pop args; break }
00446 set err [join [lsort [array names opts]] ", "]
00447 return -code error "bad option $option:\
00448 must be one of $err"
00449 }
00450 }
00451 Pop args
00452 }
00453
00454 if {$opts(-filename) != {}} {
00455 set opts(-channel) [open $opts(-filename) r]
00456 fconfigure $opts(-channel) -translation binary
00457 }
00458
00459 if {$opts(-channel) == {}} {
00460
00461 if {[llength $args] != 1} {
00462 return -code error "wrong # args:\
00463 should be \"md4 ?-hex? -filename file | string\""
00464 }
00465 set tok [MD4Init]
00466 MD4Update $tok [lindex $args 0]
00467 set r [MD4Final $tok]
00468
00469 } else {
00470
00471 set tok [MD4Init]
00472 # FRINK: nocheck
00473 set [subst $tok](reading) 1
00474 fileevent $opts(-channel) readable \
00475 [list [namespace origin Chunk] \
00476 $tok $opts(-channel) $opts(-chunksize)]
00477 vwait [subst $tok](reading)
00478 set r [MD4Final $tok]
00479
00480 # If we opened the channel - we should close it too.
00481 if {$opts(-filename) != {}} {
00482 close $opts(-channel)
00483 }
00484 }
00485
00486 if {$opts(-hex)} {
00487 set r [Hex $r]
00488 }
00489 return $r
00490 }
00491
00492
00493
00494 ret ::md4::hmac (type args) {
00495 array set opts {-hex 0 -filename {} -channel {} -chunksize 4096}
00496 while {[string match -* [set option [lindex $args 0]]]} {
00497 switch -glob -- $option {
00498 -key { set opts(-key) [Pop args 1] }
00499 -hex { set opts(-hex) 1 }
00500 -file* { set opts(-filename) [Pop args 1] }
00501 -channel { set opts(-channel) [Pop args 1] }
00502 -chunksize { set opts(-chunksize) [Pop args 1] }
00503 default {
00504 if {[llength $args] == 1} { break }
00505 if {[string compare $option "--"] == 0 } { Pop args; break }
00506 set err [join [lsort [array names opts]] ", "]
00507 return -code error "bad option $option:\
00508 must be one of $err"
00509 }
00510 }
00511 Pop args
00512 }
00513
00514 if {![info exists opts(-key)]} {
00515 return -code error "wrong # args:\
00516 should be \"hmac ?-hex? -key key -filename file | string\""
00517 }
00518
00519 if {$opts(-filename) != {}} {
00520 set opts(-channel) [open $opts(-filename) r]
00521 fconfigure $opts(-channel) -translation binary
00522 }
00523
00524 if {$opts(-channel) == {}} {
00525
00526 if {[llength $args] != 1} {
00527 return -code error "wrong # args:\
00528 should be \"hmac ?-hex? -key key -filename file | string\""
00529 }
00530 set tok [HMACInit $opts(-key)]
00531 HMACUpdate $tok [lindex $args 0]
00532 set r [HMACFinal $tok]
00533
00534 } else {
00535
00536 set tok [HMACInit $opts(-key)]
00537 # FRINK: nocheck
00538 set [subst $tok](reading) 1
00539 fileevent $opts(-channel) readable \
00540 [list [namespace origin Chunk] \
00541 $tok $opts(-channel) $opts(-chunksize)]
00542 vwait [subst $tok](reading)
00543 set r [HMACFinal $tok]
00544
00545 # If we opened the channel - we should close it too.
00546 if {$opts(-filename) != {}} {
00547 close $opts(-channel)
00548 }
00549 }
00550
00551 if {$opts(-hex)} {
00552 set r [Hex $r]
00553 }
00554 return $r
00555 }
00556
00557
00558
00559
00560 namespace ::md4 {
00561 foreach e {critcl cryptkit} { if {[LoadAccelerator $e]} { break } }
00562 }
00563
00564 package provide md4 $::md4::version
00565
00566
00567
00568
00569
00570
00571
00572
00573