From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:31:24 pm (Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned. Choose terminate or close.'] <class> <name>MessageDigest</name> <environment>Security</environment> <super>Core.Object</super> <private>false</private> none <imports></imports> <category>Security-Hashes</category> <attributes> <package>HashesBase</package> </attributes> </class> <comment> Security.MessageDigest
This is the abstract superclass of message digests. It defines common API with two level structure. The higher level (protocol 'services') is simpler, providing methods to perform a digest computation on a byte object using single message. These messages provide the digest as a return value. The lower level API (protocol 'services-basic') requires several messages to complete the digest computation, but provides more flexibility. It allows to compute a "running digest" on data that is not all available at the same time. It's derived from the usual Update/Final API scheme, except in our case the "Final" phase is embedded into the call for obtaining the digest value, #digest. That makes for a simpler API, and even allows to support intermediate calls for digest value before all the data was processed. Subclasses must implement: instance protocol: copy updateWithNext:from: updateWith:from:to: blockSize updateWith: messageLength digestInto:startingAt: updateFrom: digestSize
</comment> <class> <name>HMAC</name> <environment>Security</environment> <super>Security.MessageDigest</super> <private>false</private> none ihash ohash ipad opad <imports></imports> <category>Security-Hashes</category> <attributes> <package>HashesBase</package> </attributes> </class> <comment> Security.HMAC
This is an implementation of HMAC as described in RFC 2104, http://www.ietf.org/rfc/rfc2104.txt. MACs (Message authentication codes) are used to provide integrity of messages. A MAC digest prevents an attacker from altering contents of protected messages without being detected. MACs are often characterized as a 'hash function with a secret key', as such they are usually built on top of secure hash functions like MD5 and SHA. HMAC is one such MAC function. The API sticks to the 'hash with a key' definition, it includes all the public messages of MessageDigests, plus the additional (required) #setKey: message. There are predefined instance creation messages that preinitialize the instance with the undelying hash function (MD5 or SHA) and optionally with the key as well. Note that it is required to set the key before any data can be processed by the HMAC. Here's an example: (HMAC MD5: 'secret key' asByteArray) hash: 'Try changing this without knowing the secret key!' Instance Variables: ihash <Hash> inner hash function ohash <Hash> outer hash function ipad <ByteArray> inner pad cache, (ByteArray new: 64 withAll: 16r36) bitXor: key opad <ByteArray> outer pad cache, (ByteArray new: 64 withAll: 16r5C) bitXor: key
</comment> <class> <name>Hash</name> <environment>Security</environment> <super>Security.MessageDigest</super> <private>false</private> none block position total <imports></imports> <category>Security-Hashes</category> <attributes> <package>HashesBase</package> </attributes> </class> <comment> Security.Hash
This is the abstract superclass of hash algorithms. It implements common aspects of the two supported hash functions MD5 and SHA. It fleshes out the generic "compression function" aspect (buffer management and block processing) and the common aspects of the MD4 class of hash functions (message length encoding and digest value assembly). Subclasses must implement: instance protocol: storeLength backupRegisters restoreRegisters: processBlock writeDigestInto:startingAt: readBlock class protocol: blockSize digestSize Instance Variables: block <ByteArray> a message block buffer of size self blockSize position <SmallInteger> number of bytes currently in the buffer total <Integer> number of already processed blocks
</comment> <methods> Security.MessageDigest <category>services-basic</category>
copy "Allows to clone the algorithm instance to proceed with the hash computation in multiple directions" ^self subclassResponsibility
digest "Return the digest value for the data provided so far" | result | result := ByteArray new: self digestSize. self digestInto: result startingAt: 1. ^result
digestInto: aByteArray startingAt: start "Write the current digest value into aByteArray at index start" ^self subclassResponsibility
messageLength "Returns current length of the message in bits" ^self subclassResponsibility
reset "Reset the hash function to its initial state"
updateFrom: aReadStream "Update the hash function with all the available data from aReadStream" ^self subclassResponsibility
updateWith: aByteArray "Update the hash function with the data from aByteArray" ^self subclassResponsibility
updateWith: aByteArray from: start to: end "Update the hash function with the block of data from position start to position end in aByteArray" ^self subclassResponsibility
updateWithNext: count from: aReadStream "Update the hash function with count bytes from aReadStream" ^self subclassResponsibility
</methods> <methods> Security.MessageDigest <category>services</category>
hash: aByteArrayOrString "Return the digest value for aByteArrayOrString" ^self reset; updateWith: aByteArrayOrString asByteArray; digest
hash: aByteArrayOrString from: start to: end "Returns the digest value for the block of data from position start to position end in aByteArray" ^self reset; updateWith: aByteArrayOrString asByteArray from: start to: end; digest
hashFrom: aReadStream "Return the digest value for the available data in aReadStream" ^self reset; updateFrom: aReadStream; digest
hashNext: anInteger from: aReadStream "Returns digest value for count bytes from aReadStream" ^self reset; updateWithNext: anInteger from: aReadStream; digest
</methods> <methods> Security.MessageDigest <category>accessing</category>
blockSize "The size of data chunks being processed at once (bytes)" ^self subclassResponsibility
digestSize "The size of resulting hash value (bytes)" ^self subclassResponsibility
hashSize "The size of resulting hash value (bytes)" ^self digestSize
</methods> <methods> Security.HMAC <category>accessing</category>
blockSize ^ohash blockSize
digestSize ^ihash digestSize
</methods> <methods> Security.HMAC <category>initialize-release</category>
setHash: aHash ihash := aHash. ohash := aHash copy
setKey: aByteArray | key | aByteArray size < self digestSize ifTrue: [ | resumeValue | resumeValue := Warning raiseRequestWith: aByteArray errorString: 'Key is very short. Continue ?'. (resumeValue isNil or: [ resumeValue ]) ifFalse: [^self] ]. key := aByteArray size > self blockSize ifTrue: [ ihash hash: aByteArray ] ifFalse: [ aByteArray ]. ipad := ByteArray new: ihash blockSize withAll: 16r36. opad := ByteArray new: ohash blockSize withAll: 16r5C. key keysAndValuesDo: [ :i :b | ipad at: i put: ((ipad at: i) bitXor: b). opad at: i put: ((opad at: i) bitXor: b) ]. self reset
</methods> <methods> Security.HMAC <category>printing</category>
printOn: aStream aStream nextPutAll: 'HMAC-'; nextPutAll: ihash class name
</methods> <methods> Security.HMAC <category>services-basic</category>
copy "Allows to clone the algorithm instance to proceed with the hash computation in multiple directions" ^self class new setIHash: ihash copy oHash: ohash copy iPad: ipad copy oPad: opad copy; yourself
digestInto: aByteArray startingAt: start self checkKey. ihash digestInto: aByteArray startingAt: start. ohash reset; updateWith: opad; updateWith: aByteArray from: start to: start + ihash digestSize - 1; digestInto: aByteArray startingAt: start
messageLength "Returns current length of the message in bits" self checkKey. ^ihash messageLength - (ihash blockSize * 8)
reset self checkKey. ihash reset; updateWith: ipad
updateFrom: aReadStream self checkKey. ^ihash updateFrom: aReadStream
updateWith: aByteArray self checkKey. ^ihash updateWith: aByteArray
updateWith: aByteArray from: start to: end self checkKey. ^ihash updateWith: aByteArray from: start to: end
updateWithNext: count from: aReadStream self checkKey. ^ihash updateWithNext: count from: aReadStream
</methods> <methods> Security.HMAC <category>private</category>
checkKey ipad isNil ifTrue: [ self error: 'Cannot use HMAC without setting the key!' ]
setIHash: iHash oHash: oHash iPad: iPad oPad: oPad "Used to initialize a clone" ihash := iHash. ohash := oHash. ipad := iPad. opad := oPad
</methods> <methods> Security.HMAC class <category>instance creation</category>
hash: aHash ^self new setHash: aHash; yourself
</methods> <methods> Security.Hash <category>deprecated</category>
byteHash: aStringOrByteArray ^self hash: aStringOrByteArray
byteHash: aByteArrayOrString from: start to: end ^self hash: aByteArrayOrString from: start to: end
byteHashNext: anInteger inStream: aStream ^self hashNext: anInteger from: aStream
byteHashStream: aStream ^self hashFrom: aStream
integerHash: aByteArrayOrString ^(self hash: aByteArrayOrString) asLargePositiveInteger
integerHash: aByteArrayOrString from: start to: end ^(self hash: aByteArrayOrString from: start to: end) asLargePositiveInteger
integerHashNext: anInteger inStream: aStream ^(self hashNext: anInteger from: aStream) asLargePositiveInteger
integerHashStream: aStream ^(self hashFrom: aStream) asLargePositiveInteger
</methods> <methods> Security.Hash <category>accessing</category>
blockSize "The size of data chunks being processed at once (bytes)" ^self class blockSize
digestSize "The size of resulting hash value (bytes)" ^self class digestSize
</methods> <methods> Security.Hash <category>private-algorithm</category>
processBlock self subclassResponsibility
readBlock "read in the message block" self subclassResponsibility
resetBuffer position := 0. total := 0.
updateWithBlock self processBlock. total := total + 1.
</methods> <methods> Security.Hash <category>private-digest</category>
backupRegisters "This has to match with #restoreRegisters:" ^self subclassResponsibility
doublePaddedDigestInto: aByteArray startingAt: start | backup bufferBackup wroteOne | backup := self backupRegisters. wroteOne := false. position < 64 ifTrue: [ block at: position + 1 put: 16r80. wroteOne := true. position + 2 to: self blockSize do: [ :i | block at: i put: 0 ] ]. self processBlock. bufferBackup := block. block := ByteArray new: self blockSize. (wroteOne ifTrue: [1] ifFalse: [block at: 1 put: 16r80. 2] ) to: self blockSize - 8 do: [ :i | block at: i put: 0 ]. self storeLength. self processBlock. self writeDigestInto: aByteArray startingAt: start. block := bufferBackup. self restoreRegisters: backup
enoughRoomToPad ^self blockSize - 9 >= position
restoreBuffer: aByteArray total: blockCount position: blockPosition block replaceFrom: 1 to: block size with: aByteArray. total := blockCount. position := blockPosition
restoreRegisters: anArrayOfRegisters "This has to match with #backupRegisters" self subclassResponsibility
singlePaddedDigestInto: aByteArray startingAt: start | backup | backup := self backupRegisters. block at: position + 1 put: 16r80. position + 2 to: (self blockSize - 8) do: [ :i | block at: i put: 0 ]. self storeLength. self processBlock. self writeDigestInto: aByteArray startingAt: start. self restoreRegisters: backup.
storeLength self subclassResponsibility
writeDigestInto: aByteArray startingAt: start self subclassResponsibility
</methods> <methods> Security.Hash <category>services-basic</category>
copy "Allows to clone the algorithm instance to proceed with the hash computation in multiple directions" | new | new := self class new. new restoreRegisters: self backupRegisters. new restoreBuffer: block total: total position: position. ^new
digestInto: aByteArray startingAt: start "Write the current digest value into aByteArray at index start" self enoughRoomToPad ifTrue: [ self singlePaddedDigestInto: aByteArray startingAt: start ] ifFalse: [ self doublePaddedDigestInto: aByteArray startingAt: start ]
messageLength "Returns current length of the message in bits" ^(total * self blockSize + position) bitShift: 3
reset "Reset the hash function to its initial state" self resetBuffer
updateFrom: aReadStream "Update the hash function with all the available data from aReadStream" | blockSize left count | blockSize := self blockSize. left := blockSize - position. "Try to finish current buffer" count := aReadStream nextAvailable: left into: block startingAt: position + 1. count < left ifTrue: [ position := position + count. ^self ]. self updateWithBlock. "Process as many full buffers as you can" [ count := aReadStream nextAvailable: blockSize into: block startingAt: 1. count < blockSize ] whileFalse: [ self updateWithBlock ]. "Buffer the leftover" position := count
updateWith: aByteArray "Update the hash function with the data from aByteArray" ^self updateWith: aByteArray from: 1 to: aByteArray size
updateWith: aByteArray from: start to: end "Update the hash function with the block of data from position start to position end in aByteArray" | blockSize left count pastEnd stop from | blockSize := self blockSize. "Try to finish current block" left := blockSize - position. pastEnd := end + 1. count := pastEnd - start. count < left ifTrue: [ block replaceFrom: position + 1 to: position + count with: aByteArray startingAt: start. position := position + count. ^self ]. block replaceFrom: position + 1 to: blockSize with: aByteArray startingAt: start. self updateWithBlock. from := start + left. "Do as many full buffers as possible" stop := pastEnd - blockSize. [ from > stop ] whileFalse: [ block replaceFrom: 1 to: blockSize with: aByteArray startingAt: from. self updateWithBlock. from := from + blockSize ]. "Buffer the leftover" position := pastEnd - from. block replaceFrom: 1 to: position with: aByteArray startingAt: from.
updateWithNext: count from: aReadStream "Update the hash function with count bytes from aReadStream" | blockSize left | blockSize := self blockSize. "Try to finish current block" left := blockSize - position. count < left ifTrue: [ aReadStream next: count into: block startingAt: position + 1. position := position + count. ^self ]. aReadStream next: left into: block startingAt: position + 1. self updateWithBlock. "Note that we're changing the meaning of 'left' here. Hope that's OK." left := count - left. "Do as many full buffers as possible" [ left > blockSize ] whileTrue: [ aReadStream next: blockSize into: block startingAt: 1. self updateWithBlock. left := left - blockSize ]. "Buffer the leftover" aReadStream next: left into: block startingAt: 1. position := left
</methods> <methods> Security.Hash <category>initialize-release</category>
initialize block := ByteArray new: self blockSize
</methods> <methods> Security.Hash class <category>services</category>
hash: aByteArrayOrString ^self new hash: aByteArrayOrString
hash: aByteArrayOrString from: start to: end ^self new hash: aByteArrayOrString from: start to: end
hashFrom: aReadStream ^self new hashFrom: aReadStream
hashNext: anInteger from: aReadStream ^self new hashNext: anInteger from: aReadStream
</methods> <methods> Security.Hash class <category>deprecated</category>
byteHash: aByteArrayOrString ^self new byteHash: aByteArrayOrString
byteHash: aByteArrayOrString from: start to: end ^self new byteHash: aByteArrayOrString from: start to: end
byteHashNext: anInteger inStream: aStream ^self new byteHashNext: anInteger inStream: aStream
byteHashStream: aPositionableStream ^self new byteHashStream: aPositionableStream
integerHash: aByteArrayOrString ^self new integerHash: aByteArrayOrString
integerHash: aByteArrayOrString from: start to: end ^self new integerHash: aByteArrayOrString from: start to: end
integerHashNext: anInteger inStream: aStream ^self new integerHashNext: anInteger inStream: aStream
integerHashStream: aPositionableStream ^self new integerHashStream: aPositionableStream
</methods> <methods> Security.Hash class <category>accessing</category>
blockSize ^self subclassResponsibility
digestSize ^self subclassResponsibility
</methods> <methods> Security.Hash class <category>instance creation</category>
new ^super new initialize
</methods> "Imported Classes:" self error: 'Attempting to file-in parcel imports. Choose terminate or close' <class> <name>Object</name> <environment>Core</environment> <super></super> <private>false</private> none <imports></imports> <category>Kernel-Objects</category> <attributes> <package>Kernel-Objects</package> </attributes> </class>