Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing SHA1 Hash Algorithm in JavaScript

Tech May 10 5

When builidng web applications, securing data transmission betwean client and server becomes essential. SHA1 (Secure Hash Algorithm 1) provides a one-way cryptographic hash function that converts input data into a fixed-size hexadecimal digest, making it useful for integrity verification and digital signature scenarios.

SHA1 Implemantation

The following is a standalone SHA1 implementation using vanilla JavaScript:

function stringToUtf8Bytes(text) {
    const result = [];
    for (let i = 0; i < text.length; i++) {
        const charCode = text.charCodeAt(i);
        if (charCode < 0x80) {
            result.push(charCode);
        } else if (charCode < 0x800) {
            result.push(0xC0 | (charCode >> 6), 0x80 | (charCode & 0x3F));
        } else if (charCode < 0x10000) {
            result.push(0xE0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3F), 0x80 | (charCode & 0x3F));
        } else if (charCode < 0x110000) {
            const surrogate = (charCode - 0x10000) >> 10;
            const lowSurrogate = charCode & 0x3FF;
            result.push(0xF0 | surrogate, 0x80 | ((lowSurrogate >> 6) & 0x3F), 0x80 | (lowSurrogate & 0x3F));
        }
    }
    return result;
}

function rotateLeft(value, shiftAmount) {
    return (value << shiftAmount) | (value >>> (32 - shiftAmount));
}

function computeSha1(input) {
    const utf8Bytes = stringToUtf8Bytes(input);
    const originalLength = utf8Bytes.length;
    
    const paddedLength = ((originalLength + 8) >>> 6 << 4) + 16;
    const wordArray = new Uint32Array(paddedLength);
    
    for (let i = 0; i < originalLength; i++) {
        const offset = (i >>> 2);
        wordArray[offset] |= utf8Bytes[i] << (24 - (i & 3) * 8);
    }
    
    const lengthBitOffset = (originalLength >>> 2);
    wordArray[lengthBitOffset] |= 0x80 << (24 - (originalLength & 3) * 8);
    wordArray[paddedLength - 1] = originalLength << 3;
    
    const h0 = 0x67452301;
    const h1 = 0xEFCDAB89;
    const h2 = 0x98BADCFE;
    const h3 = 0x10325476;
    const h4 = 0xC3D2E1F0;
    
    const constants = [
        0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
    ];
    
    function roundFunction(k) {
        return function(m) {
            return (m[1] & m[2]) | (~m[1] & m[3]);
        };
    }
    
    function roundFunction2(k) {
        return function(m) {
            return m[1] ^ m[2] ^ m[3];
        };
    }
    
    function roundFunction3(k) {
        return function(m) {
            return (m[1] & m[2]) | (m[1] & m[3]) | (m[2] & m[3]);
        };
    }
    
    const functions = [
        roundFunction(0), roundFunction2(0), roundFunction3(0), roundFunction2(0)
    ];
    
    for (let chunk = 0; chunk < wordArray.length; chunk += 16) {
        const words = wordArray.subarray(chunk, chunk + 16);
        let current = [h0, h1, h2, h3, h4];
        const initial = [...current];
        
        const extended = new Uint32Array(80);
        for (let i = 0; i < 16; i++) {
            extended[i] = words[i];
        }
        
        for (let i = 16; i < 80; i++) {
            const a = extended[i - 3];
            const b = extended[i - 8];
            const c = extended[i - 14];
            const d = extended[i - 16];
            extended[i] = rotateLeft(a ^ b ^ c ^ d, 1);
        }
        
        for (let i = 0; i < 80; i++) {
            const fn = functions[Math.floor(i / 20)];
            const temp = rotateLeft(current[0], 5) + fn(current) + current[4] + extended[i] + constants[Math.floor(i / 20)];
            current[4] = current[3];
            current[3] = current[2];
            current[2] = rotateLeft(current[1], 30);
            current[1] = current[0];
            current[0] = temp;
        }
        
        for (let i = 0; i < 5; i++) {
            current[i] = (current[i] + initial[i]) >>> 0;
        }
    }
    
    const hashBuffer = new ArrayBuffer(20);
    const hashView = new DataView(hashBuffer);
    for (let i = 0; i < 5; i++) {
        hashView.setUint32(i * 4, current[i], false);
    }
    
    const hashArray = new Uint8Array(hashBuffer);
    return Array.from(hashArray)
        .map byte => byte.toString(16).padStart(2, '0')
        .join('');
}

Usage Example

const plaintext = 'Hello World';
const hashResult = computeSha1(plaintext);
console.log('Input:', plaintext);
console.log('SHA1 Hash:', hashResult);

const password = 'userPassword123';
const hashedPassword = computeSha1(password);
console.log('Password Hash:', hashedPassword);

This implementation processes the input string through UTF-8 encoding, applies SHA1 padding according to the FIPS 180-1 specification, and produces a 40-character hexadecimal hash value. The algorithm processes data in 512-bit chunks using 80 rounds of operations with modular addition and bitwise manipulations.

Note that SHA1 is considered cryptographically weak by modern standards due to collision vulnerabilities. For new security-critical applications, SHA-256 or SHA-3 from the SHA-2 family are recommended instead.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.