This is the general calling process.

Initialization Part
SHA1Reset(&context)
Pass in a structure and then initialize it. The structure looks like this:

As you can see, many things are defined, including constants, data block size, and Length_High and Length_Low representing the plaintext length, etc.
Enter the SHA1Reset function.

Here, it is actually initializing the structure. The important part is the setting of the initialization constants. When modifying the algorithm, this is usually where the changes are made.
Native SHA1 initialization constants:
context->Intermediate_Hash[0] = 0x67452301;
context->Intermediate_Hash[1] = 0xEFCDAB89;
context->Intermediate_Hash[2] = 0x98BADCFE;
context->Intermediate_Hash[3] = 0x5E4A1F7C;
context->Intermediate_Hash[4] = 0x10325476;
It is important to note that both the values of the constants and their corresponding order will affect the final encryption result.
Plaintext Input
SHA1Input(&context, plainText, strlen(plainText));
Pass in the context structure, plaintext, and plaintext length.
This function does not perform any major actions; its main purpose is to place the incoming plaintext data into the structure.
If the plaintext length exceeds 512 bits, it will be divided into several groups.

As you can see, after the function ends, the Message_Block array in the structure already contains the incoming plaintext.
Before Encryption
If the plaintext does not exceed 512 bits, a 0x80 is added to the end of the plaintext, followed by padding with 0s until it reaches 448 bits. The last 64 bits are used to store the plaintext length.
If the data exceeds 512 bits, it will be grouped into 512-bit blocks until the last block does not meet 512 bits, and then the aforementioned padding operation will be performed.
The 0x80 can be seen as the end of the plaintext; only at the end of the plaintext should 0x80 be added.
Encryption
Direct conversion to generate 16 32-bit integers

What is happening here? In fact, it is converting every 4 bytes in the block into a 32-bit integer. Let’s see how this is done.
We can first ignore this loop and look at what one round does. Suppose the incoming plaintext is 123456.
context->Message_Block[t * 4]; The obtained value is 1, then shift 1 left by 24 bits.
context->Message_Block[t * 4 + 1] The obtained value is 2, then shift 2 left by 16 bits.
context->Message_Block[t * 4 + 2] The obtained value is 3, then shift 3 left by 8 bits.
context->Message_Block[t * 4 + 3] The obtained value is 4, then shift 4 left by 0 bits.
What pattern do we find? It is simply extracting the four bytes from the block and shifting the earlier bytes to the higher bits.
So why shift 24, 16, and 8 bits? Let’s look at the results after shifting each byte in the block.
1 << 24 = 0000 0001 0000 0000 0000 0000 0000 00002 << 16 = 0000 0000 0000 0010 0000 0000 0000 00003 << 8 = 0000 0000 0000 0000 0000 0011 0000 00004 <<0 = 0000 0000 0000 0000 0000 0000 0000 0100
Each byte occupies 8 bits, so the bytes will not interfere with each other and will not be overwritten by other bytes.
Finally, through the ‘ | ‘ operation, each byte is combined to form a 32-bit integer representing 4 bytes.
Why loop 16 times? The reason is that SHA1 uses 512 bits as a block size, and looping 16 times can cover every byte.
Message Expansion to Generate 64 32-bit Integers

Why expand? The reason is that SHA1 requires 80 rounds of encryption, which means that W must contain 80 32-bit integers. However, in the previous steps, we only generated 16 32-bit integers, so we need to perform message expansion to fill W. How do we get the remaining 64 32-bit integers?
W[t – 3] ^ W[t – 8] ^ W[t – 14] ^ W[t – 16]. Assuming the index t is 16, this statement becomes W[13] ^ W[8] ^ W[2] ^ W[0]. What do we find? It takes values from the previously filled W, performs XOR and circular left shift, and then fills the result into W. However, it is not entirely based on the previous 16 W; as the index increases, it will also use the expanded W for further expansion.
W[16]=f(W[13],W[8],W[2],W[0]) W[17]=f(W[14],W[9],W[3],W[1]) W[18]=f(W[15],W[10],W[4],W[2]) W[19]=f(W[16],W[11],W[5],W[3])
So this step relies on the previously generated 16 32-bit integers and the expanded 32-bit integers to expand to 80 32-bit integers.
80 Rounds of Encryption

This is the operation of the 80 rounds of SHA1 encryption. All 80 rounds operate on the initialization constants. Here, A, B, C, D, and E are the initialization constants.

Finally, the original constants and the constants after 80 rounds are added together.
I find it interesting that the values between rounds are interconnected.

Strictly speaking, only temp is refreshed in each round.
You can see that D, C, B, and A are updated only after assigning values to others. However, B is a bit of an exception because it is assigned a value after a circular left shift.
A in round 70 and B in round 71 are equal.

The result of B in round 70 after a circular left shift of 30 rounds is the same as C in round 71.

C, because C, D, and E are directly assigned values, unlike B, which is assigned after a left shift, and A, which is calculated after temp, is more persistent.

This is the connection between rounds.

So in the end, it can also be perceived that the updates of A, B, C, D, and E depend on the generation of temp, along with the chain assignment, allowing the values to remain updated.
Modified Algorithm
1. For example, we know that the final result of SHA1 is obtained by adding the initialization constants to the encrypted initialization constants. If the initialization constants are modified, the result will definitely be different.

2. Alternatively, during encryption, a fixed K can be added to the calculation of temp every 20 rounds. If K changes, the result will definitely change.


3. Alternatively, the logical operations (AND, OR, NOT) used to generate temp can be modified (though this is more difficult). The temp generation logic for every four 20 rounds is different. If one is changed, or if all are changed, it will also affect the result.

4. It may also be possible to modify the logic of how the plaintext is pushed in and the logic of message expansion, but I have not encountered this yet.

Summary: When learning encryption algorithms, it is recommended to use C language implementation code, debugging while asking AI questions. Watching videos can easily induce sleep. The weekend is also for looking at modified algorithm cases. I learned about the SHA1 encryption process (previously only knew that SHA1 is 40 bits). It was quite interesting.
by: xiaodu