RFC 3174

US Secure Hash Algorithm 1 (SHA1)

πŸ“… published September 2001, from ietf

RFC 3174: US Secure Hash Algorithm 1 (SHA1) πŸ’–

okay bestie, here's the title block tea β˜•βœ¨

  • Network Working Group: the group that put this one out
  • Number: RFC 3174
  • Authors: D. Eastlake, 3rd (Motorola) and P. Jones (Cisco Systems) 🫢
  • Category: Informational πŸŽ€
  • Date: September 2001
  • Obsoletes / Updates: the title block doesn't list anything this one obsoletes or updates πŸ™ˆ

Status of This Memo πŸ“œ

this memo is just here to share info with the internet community 🌐 it is NOT a standard, not even a little bit. distribution is unlimited, so share away πŸ¦‹

Copyright (C) The Internet Society (2001). All Rights Reserved. πŸ’…

Abstract 🌸

so the whole point of this doc is to make the SHA-1 (Secure Hash Algorithm 1) hash algorithm easy for the internet community to get their hands on 🧚 the US adopted SHA-1, the one described in here, as a Federal Information Processing Standard. most of the words came from FIPS 180-1 (the authors basically borrowed them). the only "original" part is the C code implementation πŸ’»βœ¨

Acknowledgements 🫢

most of the text came from [FIPS 180-1]. the C code is the only "original" bit, but its style is pretty similar to the earlier MD4 and MD5 RFCs [RFCs 1320, 1321] πŸ“

SHA-1 is built on principles that are similar to the ones Professor Ronald L. Rivest of MIT used when he designed the MD4 message digest algorithm [MD4], and it's modeled on that algorithm too [RFC 1320] πŸ‘‘

thank you to these lovelies for their useful comments, which made it into the doc πŸ’œ:

  • Tony Hansen
  • Garrett Wollman

Table of Contents πŸ—‚οΈ

1. Overview of Contents...........................................  2
2. Definitions of Bit Strings and Integers........................  3
3. Operations on Words............................................  3
4. Message Padding................................................  4
5. Functions and Constants Used...................................  6
6. Computing the Message Digest...................................  6
6.1 Method 1......................................................  6
6.2 Method 2......................................................  7
7. C Code.........................................................  8
7.1 .h file.......................................................  8
7.2 .c file....................................................... 10
7.3 Test Driver................................................... 18
8. Security Considerations........................................ 20
References........................................................ 21
Authors' Addresses................................................ 21
Full Copyright Statement.......................................... 22

1. Overview of Contents ✨

NOTE: the text below is mostly lifted from [FIPS 180-1]. the security claims about SHA-1 come from the US Government and the author of [FIPS 180-1], NOT from the authors of this doc πŸ™ˆ

okay so this doc specifies a Secure Hash Algorithm, SHA-1, which takes a message or a data file and makes a condensed representation of it πŸ”₯ when the message is any length < 2^64 bits, SHA-1 spits out a 160-bit output called a message digest πŸ’… the message digest can then be fed into a signature algorithm, which makes or checks the signature for the message. signing the digest instead of the whole message is way more efficient, because the digest is usually much smaller 🧚

the verifier has to use the same hash algorithm the creator used, or it's giving chaos πŸ’€ any change to the message in transit will, with very high probability, produce a different message digest, and then the signature won't verify. no cap 😭

SHA-1 is called secure because it's computationally infeasible to find a message that matches a given message digest, or to find two different messages that produce the same digest πŸ‘‘ any change to a message in transit will, with very high probability, give a different digest, and the signature will fail to verify. iconic behavior 🌈

Section 2 below defines the terminology and functions used as the building blocks of SHA-1 πŸŽ€

2. Definitions of Bit Strings and Integers πŸ“

okay, here's the terminology for bit strings and integers that we'll be using:

a. a hex digit is an element of the set {0, 1, ... , 9, A, ... , F}. a hex digit represents a 4-bit string. examples: 7 = 0111, A = 1010 πŸ¦‹

b. a word equals a 32-bit string, which can be written as 8 hex digits. to turn a word into 8 hex digits, convert each 4-bit string to its hex equivalent as described in (a) above. example:

1010 0001 0000 0011 1111 1110 0010 0011 = A103FE23.

c. an integer between 0 and 2^32 - 1 inclusive can be represented as a word. the least significant four bits of the integer are represented by the right-most hex digit of the word. example: the integer 291 = 2^8+2^5+2^1+2^0 = 256+32+2+1 is represented by the hex word 00000123. πŸ’–

if z is an integer with 0 <= z < 2^64, then z = (2^32)x + y where 0 <= x < 2^32 and 0 <= y < 2^32. since x and y can each be written as words X and Y, z can be represented as the pair of words (X,Y). literally so clever 🧠✨

d. block = 512-bit string. a block (e.g., B) can be written as a sequence of 16 words 🌸

3. Operations on Words πŸ’…

these logical operators get applied to words:

a. bitwise logical word operations πŸ”₯

X AND Y = bitwise logical "and" of X and Y.

X OR Y = bitwise logical "inclusive-or" of X and Y.

X XOR Y = bitwise logical "exclusive-or" of X and Y.

NOT X = bitwise logical "complement" of X.

example:

         01101100101110011101001001111011
   XOR   01100101110000010110100110110111
         --------------------------------
   =     00001001011110001011101111001100

b. the operation X + Y works like this: words X and Y represent integers x and y, where 0 <= x < 2^32 and 0 <= y < 2^32. for positive integers n and m, let n mod m be the remainder when n is divided by m. compute:

z  =  (x + y) mod 2^32.

then 0 <= z < 2^32. convert z to a word, Z, and define Z = X + Y. the math is giving clean πŸŽ€

c. the circular left shift operation S^n(X), where X is a word and n is an integer with 0 <= n < 32, is defined by:

S^n(X)  =  (X << n) OR (X >> 32-n).

for X << n: throw away the left-most n bits of X, then pad the result with n zeroes on the right (it's still 32 bits). for X >> n: throw away the right-most n bits of X, then pad the result with n zeroes on the left. so S^n(X) is the same as shifting X circularly by n positions to the left πŸ¦‹

4. Message Padding 🧚

SHA-1 computes a message digest for a message or data file that's given as input. the message or data file is treated as a bit string. the length of the message is its number of bits (the empty message has length 0). if the number of bits in a message is a multiple of 8, we can write the message in hex for compactness πŸ“

the whole point of padding is to make the total length of the padded message a multiple of 512. SHA-1 processes blocks of 512 bits, one after another, when it computes the digest. the padding works like this πŸ‘‘ short version: a "1", then m "0"s, then a 64-bit integer get tacked onto the end of the message, so the padded message ends up being 512 * n bits long. that 64-bit integer is the length of the original message. then SHA-1 processes the padded message as n blocks of 512 bits πŸ’–

say a message has length l < 2^64. before it goes into SHA-1, it gets padded on the right like so:

a. a "1" gets appended. example: if the original message is "01010000", it's padded to "010100001" 🌸

b. "0"s get appended. how many depends on the original length. the last 64 bits of the last 512-bit block are reserved for the length l of the original message. example: say the original message is this bit string:

01100001 01100010 01100011 01100100 01100101.

after step (a) this gives:

01100001 01100010 01100011 01100100 01100101 1.

since l = 40, the number of bits in the above is 41, and 407 "0"s get appended, making the total 448. in hex, that's:

61626364 65800000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000

c. get the 2-word representation of l, the number of bits in the original message. if l < 2^32 then the first word is all zeroes. append these two words to the padded message. πŸŽ€

example: same message as in (b). then l = 40 (notice that l is computed BEFORE any padding happens). the two-word representation of 40 is hex 00000000 00000028. so the final padded message in hex is:

61626364 65800000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000028

the padded message will contain 16 * n words for some n > 0. the padded message is regarded as a sequence of n blocks M(1) , M(2), first characters (or bits) of the message. 🫢

5. Functions and Constants Used πŸ”₯

SHA-1 uses a sequence of logical functions, f(0), f(1),..., f(79). each f(t), where 0 <= t <= 79, works on three 32-bit words B, C, D and produces a 32-bit word as output. f(t;B,C,D) is defined like this, for words B, C, D:

f(t;B,C,D) = (B AND C) OR ((NOT B) AND D)         ( 0 <= t <= 19)

f(t;B,C,D) = B XOR C XOR D                        (20 <= t <= 39)

f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D)  (40 <= t <= 59)

f(t;B,C,D) = B XOR C XOR D                        (60 <= t <= 79).

SHA-1 also uses a sequence of constant words, K(0), K(1), ... , K(79). in hex, they're:

K(t) = 5A827999         ( 0 <= t <= 19)

K(t) = 6ED9EBA1         (20 <= t <= 39)

K(t) = 8F1BBCDC         (40 <= t <= 59)

K(t) = CA62C1D6         (60 <= t <= 79).

6. Computing the Message Digest πŸ’œ

the two methods in 6.1 and 6.2 below give the exact same message digest πŸ¦‹ method 2 saves sixty-four 32-bit words of storage, BUT it's likely to make execution take longer, because the address computations for the { W[t] } in step (c) get more complicated 😭 there are other computation methods that give identical results too ✨

6.1. Method 1 πŸŽ€

okay so the message digest gets computed using the message padded exactly like we talked about in section 4 πŸ’…. the whole thing runs on two buffers, each one five 32-bit words, plus an 80-word sequence of 32-bit words. the first 5-word buffer is labeled A,B,C,D,E, the second is H0, H1, H2, H3, H4, and the 80-word sequence is W(0), W(1),..., W(79). there's also one single word buffer called TEMP 🧚

now the 16-word blocks M(1), M(2),..., M(n) from section 4 get processed in order, and each M(i) takes 80 steps to get through πŸ”₯

before any blocks get processed, the H's get initialized. in hex:

H0 = 67452301

H1 = EFCDAB89

H2 = 98BADCFE

H3 = 10325476

H4 = C3D2E1F0

okay now we process M(1), M(2), ... , M(n) 🌸 to process each M(i), we do the following:

  • a. Divide M(i) into 16 words W(0), W(1), ... , W(15), where W(0) is the left-most word.
  • b. For t = 16 to 79 let
W(t) = S^1(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16)).
  • c. Let A = H0, B = H1, C = H2, D = H3, E = H4.
  • d. For t = 0 to 79 do
TEMP = S^5(A) + f(t;B,C,D) + E + W(t) + K(t);

E = D;  D = C;  C = S^30(B);  B = A; A = TEMP;
  • e. Let
H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4
+ E.

and once M(n) has been processed, the message digest is the 160-bit string represented by the 5 words

H0 H1 H2 H3 H4.

periodt, that's the whole digest πŸ‘‘

6.2. Method 2 πŸ¦‹

ngl this is the one where you go "okay but what if memory is tight" πŸ₯Ί Method 1 assumes W(0), ... , W(79) is an array of eighty 32-bit words. that's super efficient for execution time, since the addresses of W(t-3), ... ,W(t-16) in step (b) are easy to compute. but if space is at a premium, you can instead treat { W(t) } as a circular queue. that can be built with an array of sixteen 32-bit words W[0], ... W[15].

in this version, in hex, we let

MASK = 0000000F

and then processing of M(i) goes like this ✨

  • a. Divide M(i) into 16 words W[0], ... , W[15], where W[0] is the left-most word.
  • b. Let A = H0, B = H1, C = H2, D = H3, E = H4.
  • c. For t = 0 to 79 do
s = t AND MASK;

if (t >= 16) W[s] = S^1(W[(s + 13) AND MASK] XOR W[(s + 8) AND
MASK] XOR W[(s + 2) AND MASK] XOR W[s]);

TEMP = S^5(A) + f(t;B,C,D) + E + W[s] + K(t);

E = D; D = C; C = S^30(B); B = A; A = TEMP;
  • d. Let
H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4
+ E.

the circular queue trick is giving main character efficiency πŸ’œ


7. C Code πŸ’»βœ¨

bestie, here's a demo implementation of SHA-1 in C 🫢 section 7.1 has the header file, 7.2 has the C code, and 7.3 is the test driver.

7.1. .h file 🌈

/*
 *  sha1.h
 *
 *  Description:
 *      This is the header file for code which implements the Secure
 *      Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
 *      April 17, 1995.
 *
 *      Many of the variable names in this code, especially the
 *      single character names, were used because those were the names
 *      used in the publication.
 *
 *      Please read the file sha1.c for more information.
 *
 */

#ifndef _SHA1_H_
#define _SHA1_H_

#include <stdint.h>
/*
 * If you do not have the ISO standard stdint.h header file, then you
 * must typdef the following:
 *    name              meaning
 *  uint32_t         unsigned 32 bit integer
 *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
 *  int_least16_t    integer of >= 16 bits
 *
 */

#ifndef _SHA_enum_
#define _SHA_enum_
enum
{
    shaSuccess = 0,
    shaNull,            /* Null pointer parameter */
    shaInputTooLong,    /* input data too long */
    shaStateError       /* called Input after Result */
};
#endif
#define SHA1HashSize 20

/*
 *  This structure will hold context information for the SHA-1
 *  hashing operation
 */
typedef struct SHA1Context
{
    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */

    uint32_t Length_Low;            /* Message length in bits      */
    uint32_t Length_High;           /* Message length in bits      */

                               /* Index into message block array   */
    int_least16_t Message_Block_Index;
    uint8_t Message_Block[64];      /* 512-bit message blocks      */

    int Computed;               /* Is the digest computed?         */
    int Corrupted;             /* Is the message digest corrupted? */
} SHA1Context;

/*
 *  Function Prototypes
 */

int SHA1Reset(  SHA1Context *);
int SHA1Input(  SHA1Context *,
                const uint8_t *,
                unsigned int);
int SHA1Result( SHA1Context *,
                uint8_t Message_Digest[SHA1HashSize]);

#endif

7.2. .c file πŸ¦‹

/*
 *  sha1.c
 *
 *  Description:
 *      This file implements the Secure Hashing Algorithm 1 as
 *      defined in FIPS PUB 180-1 published April 17, 1995.
 *
 *      The SHA-1, produces a 160-bit message digest for a given
 *      data stream.  It should take about 2**n steps to find a
 *      message with the same digest as a given message and
 *      2**(n/2) to find any two messages with the same digest,
 *      when n is the digest size in bits.  Therefore, this
 *      algorithm can serve as a means of providing a
 *      "fingerprint" for a message.
 *
 *  Portability Issues:
 *      SHA-1 is defined in terms of 32-bit "words".  This code
 *      uses <stdint.h> (included via "sha1.h" to define 32 and 8
 *      bit unsigned integer types.  If your C compiler does not
 *      support 32 bit unsigned integers, this code is not
 *      appropriate.
 *
 *  Caveats:
 *      SHA-1 is designed to work with messages less than 2^64 bits
 *      long.  Although SHA-1 allows a message digest to be generated
 *      for messages of any number of bits less than 2^64, this
 *      implementation only works with messages with a length that is
 *      a multiple of the size of an 8-bit character.
 *
 */

#include "sha1.h"

/*
 *  Define the SHA1 circular left shift macro
 */
#define SHA1CircularShift(bits,word) \
                (((word) << (bits)) | ((word) >> (32-(bits))))

/* Local Function Prototyptes */
void SHA1PadMessage(SHA1Context *);
void SHA1ProcessMessageBlock(SHA1Context *);

/*
 *  SHA1Reset
 *
 *  Description:
 *      This function will initialize the SHA1Context in preparation
 *      for computing a new SHA1 message digest.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to reset.
 *
 *  Returns:
 *      sha Error Code.
 *
 */
int SHA1Reset(SHA1Context *context)
{
    if (!context)
    {
        return shaNull;
    }

    context->Length_Low             = 0;
    context->Length_High            = 0;
    context->Message_Block_Index    = 0;

    context->Intermediate_Hash[0]   = 0x67452301;
    context->Intermediate_Hash[1]   = 0xEFCDAB89;
    context->Intermediate_Hash[2]   = 0x98BADCFE;
    context->Intermediate_Hash[3]   = 0x10325476;
    context->Intermediate_Hash[4]   = 0xC3D2E1F0;

    context->Computed   = 0;
    context->Corrupted  = 0;
    return shaSuccess;
}

/*
 *  SHA1Result
 *
 *  Description:
 *      This function will return the 160-bit message digest into the
 *      Message_Digest array  provided by the caller.
 *      NOTE: The first octet of hash is stored in the 0th element,
 *            the last octet of hash in the 19th element.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to use to calculate the SHA-1 hash.
 *      Message_Digest: [out]
 *          Where the digest is returned.
 *
 *  Returns:
 *      sha Error Code.
 *
 */
int SHA1Result( SHA1Context *context,
                uint8_t Message_Digest[SHA1HashSize])
{
    int i;

    if (!context || !Message_Digest)
    {
        return shaNull;
    }

    if (context->Corrupted)
    {
        return context->Corrupted;
    }

    if (!context->Computed)
    {
        SHA1PadMessage(context);
        for(i=0; i<64; ++i)
        {
            /* message may be sensitive, clear it out */
            context->Message_Block[i] = 0;
        }
        context->Length_Low = 0;    /* and clear length */
        context->Length_High = 0;
        context->Computed = 1;

    }

    for(i = 0; i < SHA1HashSize; ++i)
    {
        Message_Digest[i] = context->Intermediate_Hash[i>>2]
                            >> 8 * ( 3 - ( i & 0x03 ) );
    }

    return shaSuccess;
}

/*
 *  SHA1Input
 *
 *  Description:
 *      This function accepts an array of octets as the next portion
 *      of the message.
 *
 *  Parameters:
 *      context: [in/out]
 *          The SHA context to update
 *      message_array: [in]
 *          An array of characters representing the next portion of
 *          the message.
 *      length: [in]
 *          The length of the message in message_array
 *
 *  Returns:
 *      sha Error Code.
 *
 */
int SHA1Input(    SHA1Context    *context,
                  const uint8_t  *message_array,
                  unsigned       length)
{
    if (!length)
    {
        return shaSuccess;
    }

    if (!context || !message_array)
    {
        return shaNull;
    }

    if (context->Computed)
    {
        context->Corrupted = shaStateError;
        return shaStateError;
    }

    if (context->Corrupted)
    {
         return context->Corrupted;
    }
    while(length-- && !context->Corrupted)
    {
    context->Message_Block[context->Message_Block_Index++] =
                    (*message_array & 0xFF);

    context->Length_Low += 8;
    if (context->Length_Low == 0)
    {
        context->Length_High++;
        if (context->Length_High == 0)
        {
            /* Message is too long */
            context->Corrupted = 1;
        }
    }

    if (context->Message_Block_Index == 64)
    {
        SHA1ProcessMessageBlock(context);
    }

    message_array++;
    }

    return shaSuccess;
}

/*
 *  SHA1ProcessMessageBlock
 *
 *  Description:
 *      This function will process the next 512 bits of the message
 *      stored in the Message_Block array.
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:

 *      Many of the variable names in this code, especially the
 *      single character names, were used because those were the
 *      names used in the publication.
 *
 *
 */
void SHA1ProcessMessageBlock(SHA1Context *context)

{ const uint32_t K[] = { /* Constants defined in SHA-1 / 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; int t; / Loop counter / uint32_t temp; / Temporary word value / uint32_t W[80]; / Word sequence / uint32_t A, B, C, D, E; / Word buffers */

/*

  • Initialize the first 16 words in the array W */ for(t = 0; t < 16; t++) { W[t] = context->Message_Block[t * 4] << 24; W[t] |= context->Message_Block[t * 4 + 1] << 16; W[t] |= context->Message_Block[t * 4 + 2] << 8; W[t] |= context->Message_Block[t * 4 + 3]; }

for(t = 16; t < 80; t++) { W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); }

A = context->Intermediate_Hash[0]; B = context->Intermediate_Hash[1]; C = context->Intermediate_Hash[2]; D = context->Intermediate_Hash[3]; E = context->Intermediate_Hash[4];

for(t = 0; t < 20; t++) { temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; E = D; D = C; C = SHA1CircularShift(30,B); B = A; A = temp; }

for(t = 20; t < 40; t++) { temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; E = D; D = C; C = SHA1CircularShift(30,B); B = A; A = temp; }

for(t = 40; t < 60; t++) { temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; E = D; D = C; C = SHA1CircularShift(30,B); B = A; A = temp; }

for(t = 60; t < 80; t++) { temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; E = D; D = C; C = SHA1CircularShift(30,B); B = A; A = temp; }

context->Intermediate_Hash[0] += A; context->Intermediate_Hash[1] += B; context->Intermediate_Hash[2] += C; context->Intermediate_Hash[3] += D; context->Intermediate_Hash[4] += E;

context->Message_Block_Index = 0; }


okay so after the 80 rounds we just add everything back into the running hash πŸ’– this is the part where the block gets absorbed, and the Message_Block_Index gets reset to 0 so we're ready for the next chunk ✨

Now the padding step, which is lowkey my favourite bit 🧁. The message has to be padded out, and the code below walks through exactly how that works πŸŽ€

/*
 *  SHA1PadMessage
 *
 *  Description:
 *      According to the standard, the message must be padded to an even
 *      512 bits.  The first padding bit must be a '1'.  The last 64
 *      bits represent the length of the original message.  All bits in
 *      between should be 0.  This function will pad the message
 *      according to those rules by filling the Message_Block array
 *      accordingly.  It will also call the ProcessMessageBlock function
 *      provided appropriately.  When it returns, it can be assumed that
 *      the message digest has been computed.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to pad
 *      ProcessMessageBlock: [in]
 *          The appropriate SHA*ProcessMessageBlock function
 *  Returns:
 *      Nothing.
 *
 */

void SHA1PadMessage(SHA1Context *context)
{
    /*
     *  Check to see if the current message block is too small to hold
     *  the initial padding bits and length.  If so, we will pad the
     *  block, process it, and then continue padding into a second
     *  block.
     */
    if (context->Message_Block_Index > 55)
    {
        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while(context->Message_Block_Index < 64)
        {
            context->Message_Block[context->Message_Block_Index++] = 0;
        }

        SHA1ProcessMessageBlock(context);

        while(context->Message_Block_Index < 56)
        {
            context->Message_Block[context->Message_Block_Index++] = 0;
        }
    }
    else
    {
        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while(context->Message_Block_Index < 56)
        {

            context->Message_Block[context->Message_Block_Index++] = 0;
        }
    }

    /*
     *  Store the message length as the last 8 octets
     */
    context->Message_Block[56] = context->Length_High >> 24;
    context->Message_Block[57] = context->Length_High >> 16;
    context->Message_Block[58] = context->Length_High >> 8;
    context->Message_Block[59] = context->Length_High;
    context->Message_Block[60] = context->Length_Low >> 24;
    context->Message_Block[61] = context->Length_Low >> 16;
    context->Message_Block[62] = context->Length_Low >> 8;
    context->Message_Block[63] = context->Length_Low;

    SHA1ProcessMessageBlock(context);
}

7.3 Test Driver πŸ§ͺ

okay bestie, this part is the test driver 🫢 it's a main program that exercises all the sha1.c code, so you can see the hashes actually come out right πŸ”₯ the file runs the three tests from FIPS PUB 180-1, plus one more that calls SHA1Input with an exact multiple of 512 bits, plus a few error checks πŸ’…

/*
 *  sha1test.c
 *
 *  Description:
 *      This file will exercise the SHA-1 code performing the three
 *      tests documented in FIPS PUB 180-1 plus one which calls
 *      SHA1Input with an exact multiple of 512 bits, plus a few
 *      error test checks.
 *
 *  Portability Issues:
 *      None.
 *
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "sha1.h"

/*
 *  Define patterns for testing
 */
#define TEST1   "abc"
#define TEST2a  "abcdbcdecdefdefgefghfghighijhi"
#define TEST2b  "jkijkljklmklmnlmnomnopnopq"
#define TEST2   TEST2a TEST2b
#define TEST3   "a"
#define TEST4a  "01234567012345670123456701234567"
#define TEST4b  "01234567012345670123456701234567"
    /* an exact multiple of 512 bits */
#define TEST4   TEST4a TEST4b
char *testarray[4] =
{
    TEST1,
    TEST2,
    TEST3,
    TEST4
};
long int repeatcount[4] = { 1, 1, 1000000, 10 };
char *resultarray[4] =
{
    "A9 99 3E 36 47 06 81 6A BA 3E 25 71 78 50 C2 6C 9C D0 D8 9D",
    "84 98 3E 44 1C 3B D2 6E BA AE 4A A1 F9 51 29 E5 E5 46 70 F1",
    "34 AA 97 3C D4 C4 DA A4 F6 1E EB 2B DB AD 27 31 65 34 01 6F",
    "DE A3 56 A2 CD DD 90 C7 A7 EC ED C5 EB B5 63 93 4F 46 04 52"
};

int main()
{
    SHA1Context sha;
    int i, j, err;
    uint8_t Message_Digest[20];

    /*
     *  Perform SHA-1 tests
     */
    for(j = 0; j < 4; ++j)
    {
        printf( "\nTest %d: %d, '%s'\n",
                j+1,
                repeatcount[j],
                testarray[j]);

        err = SHA1Reset(&sha);
        if (err)
        {
            fprintf(stderr, "SHA1Reset Error %d.\n", err );
            break;    /* out of for j loop */
        }

        for(i = 0; i < repeatcount[j]; ++i)
        {

            err = SHA1Input(&sha,
                  (const unsigned char *) testarray[j],
                  strlen(testarray[j]));
            if (err)
            {
                fprintf(stderr, "SHA1Input Error %d.\n", err );
                break;    /* out of for i loop */
            }
        }

        err = SHA1Result(&sha, Message_Digest);
        if (err)
        {
            fprintf(stderr,
            "SHA1Result Error %d, could not compute message digest.\n",
            err );
        }
        else
        {
            printf("\t");
            for(i = 0; i < 20 ; ++i)
            {
                printf("%02X ", Message_Digest[i]);
            }
            printf("\n");
        }
        printf("Should match:\n");
        printf("\t%s\n", resultarray[j]);
    }

    /* Test some error returns */
    err = SHA1Input(&sha,(const unsigned char *) testarray[1], 1);
    printf ("\nError %d. Should be %d.\n", err, shaStateError );
    err = SHA1Reset(0);
    printf ("\nError %d. Should be %d.\n", err, shaNull );
    return 0;
}

8. Security Considerations πŸ”

okay real talk, this bit is serious 😭 the document is meant to give the Internet community open source access to the United States of America Federal Information Processing Standard Secure Hash Function SHA-1 [FIPS 180-1]. nobody, including the authors, is making any independent assertion that this hash function is secure for any particular use πŸ™ˆ

References πŸ“š

[FIPS 180-1] "Secure Hash Standard", United States of American,
             National Institute of Science and Technology, Federal
             Information Processing Standard (FIPS) 180-1, April
             1993.

[MD4]        "The MD4 Message Digest Algorithm," Advances in
             Cryptology - CRYPTO '90 Proceedings, Springer-Verlag,
             1991, pp. 303-311.

[RFC 1320]   Rivest, R., "The MD4 Message-Digest Algorithm", RFC
             1320, April 1992.

[RFC 1321]   Rivest, R., "The MD5 Message-Digest Algorithm", RFC
             1321, April 1992.

[RFC 1750]   Eastlake, D., Crocker, S. and J. Schiller, "Randomness
             Requirements for Security", RFC 1750, December 1994.

Authors' Addresses πŸ¦‹

Donald E. Eastlake, 3rd
Motorola
155 Beaver Street
Milford, MA 01757 USA

Phone:   +1 508-634-2066 (h)
         +1 508-261-5434 (w)
Fax:     +1 508-261-4777
EMail:   Donald.Eastlake@motorola.com


Paul E. Jones
Cisco Systems, Inc.
7025 Kit Creek Road
Research Triangle Park, NC 27709 USA

Phone:   +1 919 392 6948
EMail:   paulej@packetizer.com

Copyright (C) The Internet Society (2001). All Rights Reserved.

This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of developing Internet standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English.

The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns.

This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Acknowledgement 🫢

Funding for the RFC Editor function is currently provided by the Internet Society. πŸ’œ

πŸ“¦ where this page comes from