Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Sat, 31 Dec 2011 0:17:10 -0500
From:  <jfoug@....net>
To: john-dev@...ts.openwall.com
Cc: magnum <john.magnum@...hmail.com>
Subject: Re: SSE/intrinsics for sapB/sapG [was: john-users]

> Cool, I'll try it out. But I still don't get how the functions can tell 
> the difference between an ending 0x80 and one that is *part* of data 
> (perhaps coincidentally followed by lots of nulls plus something that 
> could be a length word, but all being just parts of a 64-byte binary 
> data block)

They do not 'know'.  There are several 'parts' to the crypt.  Initialization of the key accumulators with a 'known' seed, and then 64 byte block encryption.  The buffer itself is the bytes to be encrypted, that have a bit appended, and then null bytes added as needed, to get to an even 56===mod(64) length.  Then those last 8 bytes store the actual number of bits that were encrypted (not the bit that was appended).  If there are not enough bytes left over in the last block, to allow this byte to be added (the 0x80), and the 8 bytes of bit count data, then another block is required.  Again, null data is 'padded' to the right of the data, and then at the tail end of this new block, is where the bit count is stored.

The function(s) that we have, are very low level.  They do NOT do manipulations with the buffer.  Well, they 'do' do some manipulations, in the md5-mmx.S code.  however, that is only for the '1 block' specific code.

So, to perform the encryption, you have to build a 'proper', buffer.  This is all of the data you want, where a 0x80 is appened, and then 8 bytes of 'bit count' length is appended, at the end of the next even 64 byte boundary, and all bytes between this 0x80, and the length have been set to 0's.      Then the sha (md4/5) init's the key accumulators, and loops through this buffer, 64 bytes at a time.  The results of the accumulator is the 'hash'.

What we have is functions which perform the crypt of 'any' of these 64 byte buffers.  To do this, we will load the key buffer with init values for the very first block.  We then return this internal key value at the end of running the crypt on this 64 bit block.  If we want to run on the next 64 byte block of the full 'proper' SHA buffer, we need to get the internal key setup to exactly what it was at the end of the prior block.  We have to make it 'appear' that the SHA crypt simply continued forward to the next block.  We can do this for as many blocks as we want.  OUR code is in charge of properly preparing the buffers, so that they are in the proper format required by spec.

This is exactly what happens 'behind' the scene in the CTX model.  In that model, each time 64 bytes of data is accumlated, it is sent to the crypt, with the building internal key.  When a final() is called, the code will finish out the current block, and if there is room, it sets the 0x80, nulls any additional data, sets the length, calls the final crypt.  If there is NOT enough room, then it adds the 0x80, nulls out the rest of the blocks bytes, does the crypt, nulls the full next buffer, sets the length, and crypts that one.

With the code in john's MMX code (in md4/5-mmx and sha-mmx.S and SSE2-i code), there is NO CTX coding.  All of that buffer maintenance is done within the format.

Jim.

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.