'From Squeak3.7 of ''4 September 2004'' [latest update: #5989] on 4 February 2009 at 3:11:37 pm'!
!classDefinition: #HashFunction category: #Cryptography!
Object subclass: #HashFunction
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Cryptography'!
!classDefinition: #MD5 category: #'Cryptography-MD5'!
HashFunction subclass: #MD5
	instanceVariableNames: 'state'
	classVariableNames: 'ABCDTable IndexTable ShiftTable SinTable'
	poolDictionaries: ''
	category: 'Cryptography-MD5'!

!MD5 commentStamp: '<historical>' prior: 0!
This class implements the MD5 128-bit one-way hash function.  It relies on
the ThirtyTwoBitRegister class supplied as part of the "Digital Signatures"
functionality included in Squeak 2.7.  As of this date (1/20/2000), the
U.S. Government has lifted many of the previous restrictions on the export
of encryption software, but you should check before exporting anything
including this code.  MD5 is commonly used for some secure Internet
protocols, including authentication in HTTP, which is why I wrote it.

Submitted by Duane Maxwell

!


!HashFunction methodsFor: 'accessing' stamp: 'len 8/15/2002 01:43'!
blockSize
	^ self class blockSize! !

!HashFunction methodsFor: 'accessing' stamp: 'len 8/7/2002 16:30'!
hashMessage: aStringOrByteArray
	"MD5 new hashMessage: 'foo'"
	^ self hashStream: aStringOrByteArray asByteArray readStream! !

!HashFunction methodsFor: 'accessing' stamp: 'len 8/9/2002 13:17'!
hashSize
	^ self class hashSize! !

!HashFunction methodsFor: 'accessing' stamp: 'len 8/2/2002 02:21'!
hashStream: aStream
	^ self subclassResponsibility! !

!HashFunction methodsFor: 'converting' stamp: 'jmv 2/4/2009 15:03'!
hmac
	"HMAC class not loaded"
	"
	^ HMAC on: self
	"! !


!HashFunction class methodsFor: 'hashing' stamp: 'len 8/2/2002 02:20'!
hashMessage: aStringOrByteArray
	^ self new hashMessage: aStringOrByteArray! !

!HashFunction class methodsFor: 'hashing' stamp: 'len 8/2/2002 02:20'!
hashStream: aPositionableStream
	^ self new hashStream: aPositionableStream! !

!HashFunction class methodsFor: 'accessing' stamp: 'len 8/15/2002 01:43'!
blockSize
	^ self subclassResponsibility! !

!HashFunction class methodsFor: 'accessing' stamp: 'len 8/9/2002 13:17'!
hashSize
	^ self subclassResponsibility! !


!MD5 methodsFor: 'initialization' stamp: 'DSM 1/20/2000 17:56'!
initialize
	"Some magic numbers to get the process started"
	state _ OrderedCollection newFrom: {
		(ThirtyTwoBitRegister new load: 16r67452301).
		(ThirtyTwoBitRegister new load: 16rEFCDAB89).
		(ThirtyTwoBitRegister new load: 16r98BADCFE).
		(ThirtyTwoBitRegister new load: 16r10325476)}.

! !

!MD5 methodsFor: 'accessing' stamp: 'len 8/2/2002 03:17'!
hashStream: aPositionableStream
	"MD5 new hashStream: (ReadStream on: 'foo')"
	| startPosition buf bitLength |
	self initialize.

	"aPositionableStream atEnd ifTrue: [self error: 'empty stream']."

	startPosition _ aPositionableStream position.
	[aPositionableStream atEnd] whileFalse: [
		buf _ aPositionableStream next: 64.
		(aPositionableStream atEnd not and: [buf size = 64])
			ifTrue: [self processBuffer: buf]
			ifFalse: [
				bitLength _ (aPositionableStream position - startPosition) * 8.
				self processFinalBuffer: buf bitLength: bitLength]].

	^ self finalValue! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 01:47'!
fX: x Y: y Z: z
	" compute 'xy or (not x)z'"
	^ x copy bitAnd: y; bitOr: (x copy bitInvert; bitAnd: z)

	! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 13:38'!
ffA: a B: b C: c D: d M: m S: s T: t
	"compute a = b + ((a + f(b,c,d) + m + t) <<< s)"
	^ a += (self fX: b Y: c Z: d); += m; += t; leftRotateBy: s; += b.
! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 01:48'!
gX: x Y: y Z: z
	" compute 'xz or y(not z)'"
	^ x copy bitAnd: z; bitOr: (z copy bitInvert; bitAnd: y)

	! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 13:38'!
ggA: a B: b C: c D: d M: m S: s T: t
	"compute a = b + ((a + g(b,c,d) + m + t) <<< s)"
	^ a += (self gX: b Y: c Z: d); += m; += t; leftRotateBy: s; += b.
! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 01:48'!
hX: x Y: y Z: z
	" compute 'x xor y xor z'"
	^ x copy bitXor: y; bitXor: z

	! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 13:38'!
hhA: a B: b C: c D: d M: m S: s T: t
	"compute a = b + ((a + h(b,c,d) + m + t) <<< s)"
	^ a += (self hX: b Y: c Z: d); += m; += t; leftRotateBy: s; += b.
! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 01:48'!
iX: x Y: y Z: z
	" compute 'y xor (x or (not z))'"
	^ y copy bitXor: (z copy bitInvert; bitOr: x)
! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 13:39'!
iiA: a B: b C: c D: d M: m S: s T: t
	"compute a = b + ((a + i(b,c,d) + m + t) <<< s)"
	^ a += (self iX: b Y: c Z: d); += m; += t; leftRotateBy: s; += b.
! !

!MD5 methodsFor: 'private-functions' stamp: 'DSM 1/20/2000 17:58'!
step: data template: item selector: selector
	"Perform one step in the round"

	| args |
	args _  {
		state at: (item at: 1).
		state at: (item at: 2).
		state at: (item at: 3).
		state at: (item at: 4).
		data at: (item at: 5).
		item at: 6.
		item at: 7
		}.
	(self perform: selector withArguments: args).
! !

!MD5 methodsFor: 'private-buffers' stamp: 'len 10/15/2002 19:58'!
finalValue
	"Concatenate the state values to produce the 128-bite result"
	^ (state at: 1) asByteArray, (state at: 2) asByteArray, (state at: 3) asByteArray, (state at: 4) asByteArray! !

!MD5 methodsFor: 'private-buffers' stamp: 'mdr 2/23/2001 22:50'!
processBuffer: aByteArray
	"Process a 64-byte buffer"

	| saveState data |
	saveState _ state collect: [ :item | item copy ].
	data _ Array new: 16.
	1 to: 16 do: [ :index |
		data at: index put:
			(ThirtyTwoBitRegister new reverseLoadFrom: aByteArray at: (index * 4) - 3)].
	self rounds: data.
	1 to: 4 do: [ :index | (state at: index) += (saveState at: index) ].
! !

!MD5 methodsFor: 'private-buffers' stamp: 'DSM 1/20/2000 17:55'!
processFinalBuffer: aByteArray bitLength: bitLength
	"Pad the buffer until we have an even 64 bytes, then transform"

	| out |
	out _ ByteArray new: 64.
	out replaceFrom: 1 to: aByteArray size with: aByteArray startingAt: 1.
	aByteArray size < 56 ifTrue: [
		out at: aByteArray size + 1 put: 128. "trailing bit"
		self storeLength: bitLength in: out.
		self processBuffer: out.
		^ self].

	"not enough room for the length, so just pad this one, then..."
	aByteArray size < 64 ifTrue: [ out at: aByteArray size + 1 put: 128 ].
	self processBuffer: out.

	"process one additional block of padding ending with the length"
	out _ ByteArray new: 64.  "filled with zeros"
	aByteArray size = 64 ifTrue: [ out at: 1 put: 128].
	self storeLength: bitLength in: out.
	self processBuffer: out.
! !

!MD5 methodsFor: 'private-buffers' stamp: 'DSM 1/20/2000 17:19'!
storeLength: bitLength in: aByteArray
	"Fill in the final 8 bytes of the given ByteArray with a 64-bit
	little-endian representation of the original message length in bits."

	| n i |
	n _ bitLength.
	i _ aByteArray size - 8 + 1.
	[n > 0] whileTrue: [
		aByteArray at: i put: (n bitAnd: 16rFF).
		n _ n bitShift: -8.
		i _ i + 1].
! !

!MD5 methodsFor: 'private-rounds' stamp: 'DSM 1/20/2000 17:57'!
round: data selector: selector round: round
	"Do one round with the given function"

	| shiftIndex template abcd |
	1 to: 16 do: [ :i |
		shiftIndex _ (i - 1) \\ 4 + 1.
		abcd _ ABCDTable at: shiftIndex.
		template _ {
			abcd at: 1. abcd at: 2. abcd at: 3. abcd at: 4.
			(IndexTable at: round) at: i.
			(ShiftTable at: round) at: shiftIndex.
			SinTable at: round - 1 * 16 + i }.
		self step: data template: template selector: selector ].
! !

!MD5 methodsFor: 'private-rounds' stamp: 'DSM 1/20/2000 17:58'!
rounds: data
	"Perform the four rounds with different functions"
	#(
	ffA:B:C:D:M:S:T:
	ggA:B:C:D:M:S:T:
	hhA:B:C:D:M:S:T:
	iiA:B:C:D:M:S:T:
	) doWithIndex: [ :selector :index |
		self round: data selector: selector round: index.]
! !


!MD5 class methodsFor: 'class initialization' stamp: 'mdr 2/23/2001 22:50'!
initialize
	"MD5 initialize"

	"Obscure fact: those magic hex numbers that are hard to type in correctly are
	actually the result of a simple trigonometric function and are therefore
	easier to compute than proofread.  Laziness is sometimes a virtue."

	| c |
	c _ 2 raisedTo: 32.
	SinTable _ Array new: 64.
	1 to: 64 do: [ :i |
		SinTable
			at: i
			put: (ThirtyTwoBitRegister new load: (c * i sin abs) truncated)].
	ShiftTable _ {
		#(7 12 17 22).
		#(5 9 14 20).
		#(4 11 16 23).
		#(6 10 15 21)
		}.
	IndexTable _ {
		#(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16).
		#(2 7 12 1 6 11 16 5 10 15 4 9 14 3 8 13).
		#(6 9 12 15 2 5 8 11 14 1 4 7 10 13 16 3).
		#(1 8 15 6 13 4 11 2 9 16 7 14 5 12 3 10)
		}.
	ABCDTable _ {
		#(1 2 3 4).
		#(4 1 2 3).
		#(3 4 1 2).
		#(2 3 4 1)
		}
! !

!MD5 class methodsFor: 'accessing' stamp: 'len 8/15/2002 01:44'!
blockSize
	^ 64! !

!MD5 class methodsFor: 'accessing' stamp: 'len 8/15/2002 01:44'!
hashSize
	^ 16! !

!MD5 class methodsFor: 'hashing' stamp: 'jmv 2/3/2009 16:20'!
hashOf: aString
	^self new hashStream: aString readStream! !

!MD5 class methodsFor: 'hashing' stamp: 'jmv 2/3/2009 16:20'!
stringHashOf: aString
	^ (self hashOf: aString) asString! !


!ThirtyTwoBitRegister methodsFor: 'accessing' stamp: 'len 8/7/2002 17:37'!
asByteArray
	^ ByteArray with: (low bitAnd: 16rFF) with: (low bitShift: -8) with: (hi bitAnd: 16rFF) with: (hi bitShift: -8)! !

!ThirtyTwoBitRegister methodsFor: 'accessing' stamp: 'DSM 1/20/2000 17:17'!
asReverseInteger
	"Answer the byte-swapped integer value of my current contents."

	^ ((low bitAnd: 16rFF) bitShift: 24) +
       ((low bitAnd: 16rFF00) bitShift: 8) +
	  ((hi bitAnd: 16rFF) bitShift: 8) +
       (hi bitShift: -8)
! !

!ThirtyTwoBitRegister methodsFor: 'accessing' stamp: 'cs 8/7/2002 16:12'!
byte1: hi1 byte2: hi2 byte3: low1 byte4: low2
	hi _ (hi1 bitShift: 8) + hi2.
	low _ (low1 bitShift: 8) + low2.! !

!ThirtyTwoBitRegister methodsFor: 'accessing' stamp: 'len 8/15/2002 01:34'!
byteAt: anInteger
	anInteger = 1 ifTrue: [^ hi bitShift: -8].
	anInteger = 2 ifTrue: [^ hi bitAnd: 16rFF].
	anInteger = 3 ifTrue: [^ low bitShift: -8].
	anInteger = 4 ifTrue: [^ low bitAnd: 16rFF]! !

!ThirtyTwoBitRegister methodsFor: 'accessing' stamp: 'mdr 2/23/2001 22:51'!
reverseLoadFrom: aByteArray at: index
	"Load my 32-bit value from the four bytes of the given ByteArray
starting at the given index. Consider the first byte to contain the most
significant bits of the word (i.e., use big-endian byte ordering)."

	hi _ ((aByteArray at: index + 3) bitShift: 8) + ( aByteArray at: index + 2).
	low _ ((aByteArray at: index + 1) bitShift: 8) + ( aByteArray at: index).
! !

!ThirtyTwoBitRegister methodsFor: 'accessing' stamp: 'len 8/15/2002 01:29'!
storeInto: aByteArray at: index
	"Store my 32-bit value into the four bytes of the given ByteArray starting at the given index. Consider the first byte to contain the most significant bits of the word (i.e., use big-endian byte ordering)."

	aByteArray at: index put: (hi bitShift: -8).
	aByteArray at: index + 1 put: (hi bitAnd: 16rFF).
	aByteArray at: index + 2 put: (low bitShift: -8).
	aByteArray at: index + 3 put: (low bitAnd: 16rFF)! !


!ThirtyTwoBitRegister class methodsFor: 'agregado !!!!!!' stamp: 'cs 8/7/2002 16:19'!
byte1: hi1 byte2: hi2 byte3: low1 byte4: low2
	^ super new byte1: hi1 byte2: hi2 byte3: low1 byte4: low2! !

!ThirtyTwoBitRegister class methodsFor: 'agregado !!!!!!' stamp: 'cs 8/7/2002 16:19'!
loadFrom: aByteArray at: index
	^ super new loadFrom: aByteArray at: index! !

MD5 initialize!
