In .NET and other programming platforms all hash functions (such as MD5, SHA256, etc) can be used with streams of bytes, potentially forever - so it isn't necessary to read an entire multigigabyte file into an in-memory buffer to compute the hash of the file, the hash algorithm simply considers new data as it arrives and updates its internal state accordingly.
...it makes me think of the arithmetic Mean function which can work in the same way below, using C# IEnumerable<Byte>
to represent a potentially infinite stream of data:
public static int Mean(IEnumerable<Byte> bytes)
{
UInt64 sum = 0;
UInt64 count = 0;
foreach( Byte b in bytes )
{
count++;
sum += b;
}
return (Double)sum / (Double)count;
}
I've long assumed that hash functions might work in the same way as accumulators (warning: considerable over-simplification, I know there's more at work):
public static Byte[] SomeHashFunction(IEnumerable<Byte> bytes)
{
Byte[] hashState = new Byte[32];
foreach( Byte b in bytes )
{
ConsiderByte( hashState, b );
}
return hashState;
}
With arithmetic-mean, provided you know the count
and the mean so-far, then you can compute an updated average as new data arrives without needing to see all previous data. E.g. the high-school exam question "If the average height of 20 students is 157cm, and the 21st student is 181cm, what is the new average?" ( ( 157 * 20 ) + 181 ) / 21 == 158cm
.
I'm assuming we can do the same for hash functions like SHA256, then SHA256( concat( messageA, messageB ) )
should be computable given SHA256( messageA )
and messageB
separately.
But instantly I imagine there's some "final processing step" that non-trivial hash algorithms will do to prevent that (such as not processing individual bytes, but processing blocks of bytes and padding them to some amount if the input data isn't long enough (indeed, in .NET there's the TransformBlock
function but also the TransformFinalBlock
function so there definitely is special processing when the end of data is reached). This means that provided we can get the internal state of the hash function after processing messageA
and persist it then we can compute hashes of concatenated messages. Are there any weaknesses in any algorithms like SHA256 where the internal state can be derived from the output? Is this a weakness at all? What techniques can be used to prevent the internal state being derivable from the output?