Number Representations & States

"how numbers are stored and used in computers"

SHA-512 Hash Function

SHA-512 is a cryptographic hash function that produces a 512-bit (64-byte) hash value, typically expressed as a 128-character hexadecimal number. It is part of the SHA-2 family and is designed to provide a higher level of security than SHA-256.

Mathematical Definition

The SHA-512 algorithm processes input data in 1024-bit blocks and produces a 512-bit hash value. The algorithm can be mathematically defined as:

In this definition, the input space represents any binary string of arbitrary length, allowing for a wide range of input data. The output space represents a fixed-length 512-bit binary string, ensuring a consistent output size regardless of the input length.

Algorithm Steps

  1. Padding: The input message is padded to ensure its length is congruent to 896 modulo 1024 bits. This padding process involves appending a single '1' bit to the message, followed by enough '0' bits to make the length congruent to 896 modulo 1024. Finally, a 128-bit representation of the original message length is appended to the end.

  2. Initialization: The algorithm initializes eight 64-bit variables (a, b, c, d, e, f, g, h) with specific values. These variables are set to the following hexadecimal values: , , , , , , , and . These initial values are derived from the square roots of prime numbers and are used to set up the initial state of the hash computation.

code.js
1/** Initial SHA512 state. Bits 0..64 of frac part of sqrt of primes 2..19 */ 2const initialState = Uint32Array.from([ 3 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1, 4 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179, 5]);
  1. Main Loop: The algorithm processes the message in 1024-bit blocks through 80 rounds of operations. These operations utilize bitwise logical functions (Ch, Maj, Σ0, Σ1, σ0, σ1), modular addition, right rotations, and predefined constants (Kt) to transform the input data into the final hash value.
code.js
1export class SHA512 extends HashMD<SHA512> { 2 // Using an array is avoided here because it permits variable indexing, 3 // which prevents the optimizer/compiler from utilizing registers efficiently. 4 // 'h' represents the high 32 bits, and 'l' represents the low 32 bits. 5 protected Ah: number = SHA512_IV[0] | 0; 6 protected Al: number = SHA512_IV[1] | 0; 7 protected Bh: number = SHA512_IV[2] | 0; 8 protected Bl: number = SHA512_IV[3] | 0; 9 protected Ch: number = SHA512_IV[4] | 0; 10 protected Cl: number = SHA512_IV[5] | 0; 11 protected Dh: number = SHA512_IV[6] | 0; 12 protected Dl: number = SHA512_IV[7] | 0; 13 protected Eh: number = SHA512_IV[8] | 0; 14 protected El: number = SHA512_IV[9] | 0; 15 protected Fh: number = SHA512_IV[10] | 0; 16 protected Fl: number = SHA512_IV[11] | 0; 17 protected Gh: number = SHA512_IV[12] | 0; 18 protected Gl: number = SHA512_IV[13] | 0; 19 protected Hh: number = SHA512_IV[14] | 0; 20 protected Hl: number = SHA512_IV[15] | 0; 21 22 constructor(outputLen: number = 64) { 23 super(128, outputLen, 16, false); 24 } 25 // prettier-ignore 26 protected get(): [ 27 number, number, number, number, number, number, number, number, 28 number, number, number, number, number, number, number, number 29 ] { 30 const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; 31 return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl]; 32 } 33 // prettier-ignore 34 protected set( 35 Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number, 36 Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number 37 ): void { 38 this.Ah = Ah | 0; 39 this.Al = Al | 0; 40 this.Bh = Bh | 0; 41 this.Bl = Bl | 0; 42 this.Ch = Ch | 0; 43 this.Cl = Cl | 0; 44 this.Dh = Dh | 0; 45 this.Dl = Dl | 0; 46 this.Eh = Eh | 0; 47 this.El = El | 0; 48 this.Fh = Fh | 0; 49 this.Fl = Fl | 0; 50 this.Gh = Gh | 0; 51 this.Gl = Gl | 0; 52 this.Hh = Hh | 0; 53 this.Hl = Hl | 0; 54 } 55 protected process(view: DataView, offset: number): void { 56 // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array 57 for (let i = 0; i < 16; i++, offset += 4) { 58 SHA512_W_H[i] = view.getUint32(offset); 59 SHA512_W_L[i] = view.getUint32((offset += 4)); 60 } 61 for (let i = 16; i < 80; i++) { 62 // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7) 63 const W15h = SHA512_W_H[i - 15] | 0; 64 const W15l = SHA512_W_L[i - 15] | 0; 65 const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7); 66 const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7); 67 // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6) 68 const W2h = SHA512_W_H[i - 2] | 0; 69 const W2l = SHA512_W_L[i - 2] | 0; 70 const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6); 71 const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6); 72 // SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16]; 73 const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]); 74 const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]); 75 SHA512_W_H[i] = SUMh | 0; 76 SHA512_W_L[i] = SUMl | 0; 77 } 78 let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; 79 // Compression function main loop, 80 rounds 80 for (let i = 0; i < 80; i++) { 81 // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41) 82 const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41); 83 const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41); 84 //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0; 85 const CHIh = (Eh & Fh) ^ (~Eh & Gh); 86 const CHIl = (El & Fl) ^ (~El & Gl); 87 // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i] 88 // prettier-ignore 89 const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]); 90 const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]); 91 const T1l = T1ll | 0; 92 // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39) 93 const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39); 94 const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39); 95 const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch); 96 const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl); 97 Hh = Gh | 0; 98 Hl = Gl | 0; 99 Gh = Fh | 0; 100 Gl = Fl | 0; 101 Fh = Eh | 0; 102 Fl = El | 0; 103 ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0)); 104 Dh = Ch | 0; 105 Dl = Cl | 0; 106 Ch = Bh | 0; 107 Cl = Bl | 0; 108 Bh = Ah | 0; 109 Bl = Al | 0; 110 const All = u64.add3L(T1l, sigma0l, MAJl); 111 Ah = u64.add3H(All, T1h, sigma0h, MAJh); 112 Al = All | 0; 113 } 114 // Add the compressed chunk to the current hash value 115 ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0)); 116 ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0)); 117 ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0)); 118 ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0)); 119 ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0)); 120 ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0)); 121 ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0)); 122 ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0)); 123 this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl); 124 } 125 protected roundClean(): void { 126 clean(SHA512_W_H, SHA512_W_L); 127 } 128 destroy(): void { 129 clean(this.buffer); 130 this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 131 } 132}
  1. Output: The final hash value is the concatenation of the eight 64-bit variables (a, b, c, d, e, f, g, h) after all blocks have been processed. This concatenated value represents the SHA-512 hash of the input message.

Security Considerations

SHA-512 offers strong security guarantees due to its design. It provides collision resistance with a complexity of approximately operations, making it extremely difficult for an attacker to find two different inputs that produce the same hash value. Additionally, SHA-512 offers pre-image resistance and second pre-image resistance with a complexity of approximately operations. These properties make SHA-512 suitable for security-critical applications.

Time and Space Complexity

The time complexity of the SHA-512 algorithm is , where n is the length of the input message. This linear time complexity ensures that the hash computation is efficient, even for large input sizes. The space complexity is , as the algorithm uses a fixed amount of memory regardless of the input size, due to the fixed-length output.

Common Applications

SHA-512 is used in various security-critical applications. It is commonly employed in digital signatures, where the goal is to ensure the authenticity and integrity of a document. SHA-512 is also used for file integrity verification, SSL/TLS security, password hashing (with proper salting), blockchain technology, and high-security applications requiring 512-bit hash outputs.

Example Hash Values

For an empty string, the SHA-512 hash value is cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e. For the string "Hello, World!", the hash value is 2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b. These examples illustrate the fixed-length output of the SHA-512 algorithm, regardless of the input size.

Implementation Considerations

When implementing SHA-512, it is important to consider the algorithm's use of 64-bit words. All operations are performed on these 64-bit words, and the algorithm uses big-endian byte ordering. The output is typically represented as a 128-character hexadecimal string, which is a common format for displaying hash values. The algorithm is designed to be resistant to length extension attacks, providing an additional layer of security. However, SHA-512 requires more computational resources than SHA-256, which should be considered in resource-constrained environments.

Best Practices

Given the strong security guarantees of SHA-512, it is recommended for use in applications requiring the highest level of security. Always use proper salting when hashing passwords to enhance security. Consider using SHA-512 in combination with HMAC for message authentication to provide additional security. Be aware of the algorithm's performance characteristics, and consider the specific security requirements of your application. Use SHA-512 when the longer output length is acceptable and provides additional security benefits