DEV Community

Masui Masanori
Masui Masanori

Posted on

3 2

[Go] Try SHA-512 implementation

#go

Intro

I want to try authentications in Go.

Because I have tried in ASP.NET Core Identity before, I read its source code.

I thought I should know about password hashing first.
According to the "PasswordHasher.cs", it use HMAC-SHA512 by default.

So this time I decided to learn about SHA-512.

Although Go has sha512 package, but I will try implementing it by myself.

Examples

main.go

package main

import (
    "crypto/sha512"
    "log"
)

func main() {
    result := Hash("hello")
    result2 := sha512.Sum512([]byte("hello"))

    log.Println(result)
    log.Printf("%x", result2)
}
Enter fullscreen mode Exit fullscreen mode

sha512Hasher.go

package main

import (
    "fmt"
)

const (
    messageBlockSize = 128
)
/* eighty constant 64-bit words, K0, K1, ... K79 */
var K = []uint64{
    0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
    0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
    0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
    0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
    0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
    0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
    0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
    0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
    0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
    0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
    0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
    0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
    0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
    0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
    0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
    0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
    0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
    0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
    0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
    0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
}

func Hash(original string) string {
    inputValues := []byte(original)
    formattedMessage := formatInput(inputValues)
    computed := compute(formattedMessage)
    return computed
}

func formatInput(values []byte) []byte {
    // Calculate as binary 8-digit numbers
    L := len(values) * 8
    mod := len(values) % messageBlockSize
    padcount := messageBlockSize - mod
    /* 896 < L < 1024 */
    if mod > messageBlockSize-8 {
        padcount += messageBlockSize
    }
    results := make([]byte, len(values)+padcount)
    copy(results, values)
    // add 10000000
    results[len(values)] = 128
    // set L into the end of the message
    for i := 1; i <= 8; i++ {
        results[len(results)-i] = byte(L)
        L = L >> 8
    }
    return results
}
/*
* For t = 0 to 15
*     Wt = M(i)t
*/
func initFirst16Words(words []byte) []uint64 {
    W := make([]uint64, 16)
    for t := 0; t < 16; t++ {
        t8 := t * 8
        W[t] = uint64(words[t8])<<56 |
            uint64(words[t8+1])<<48 |
            uint64(words[t8+2])<<40 |
            uint64(words[t8+3])<<32 |
            uint64(words[t8+4])<<24 |
            uint64(words[t8+5])<<16 |
            uint64(words[t8+6])<<8 |
            uint64(words[t8+7])
    }
    return W
}

func generateMessageBlock(words []byte) [][]byte {
    var M [][]byte
    for i := 0; i < len(words); i += messageBlockSize {
        M = append(M, words[i:i+messageBlockSize])
    }
    return M
}

/* SHR^n(x) = x>>n */
func SHR(bits, word uint64) uint64 {
    return word >> bits
}

/* ROTR^n(x) = (x>>n) OR (x<<(w-n)) */
func ROTR(bits, word uint64) uint64 {
    return (word >> bits) | (word << (64 - bits))
}

/* CH( x, y, z) = (x AND y) XOR ( (NOT x) AND z) */
func CH(x, y, z uint64) uint64 {
    return (x & y) ^ ((^x) & z)
}

/* MAJ( x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) */
func MAJ(x, y, z uint64) uint64 {
    return (x & y) ^ (x & z) ^ (y & z)
}

/* BSIG0(x) = ROTR^28(x) XOR ROTR^34(x) XOR ROTR^39(x) */
func BSIG0(x uint64) uint64 {
    return ROTR(28, x) ^ ROTR(34, x) ^ ROTR(39, x)
}

/* BSIG1(x) = ROTR^14(x) XOR ROTR^18(x) XOR ROTR^41(x) */
func BSIG1(x uint64) uint64 {
    return ROTR(14, x) ^ ROTR(18, x) ^ ROTR(41, x)
}

/* SSIG0(x) = ROTR^1(x) XOR ROTR^8(x) XOR SHR^7(x) */
func SSIG0(x uint64) uint64 {
    return ROTR(1, x) ^ ROTR(8, x) ^ SHR(7, x)
}

/* SSIG1(x) = ROTR^19(x) XOR ROTR^61(x) XOR SHR^6(x) */
func SSIG1(x uint64) uint64 {
    return ROTR(19, x) ^ ROTR(61, x) ^ SHR(6, x)
}
func compute(messages []byte) string {
    /* initial H(0) hash values */
    H := [8]uint64{
        0x6a09e667f3bcc908,
        0xbb67ae8584caa73b,
        0x3c6ef372fe94f82b,
        0xa54ff53a5f1d36f1,
        0x510e527fade682d1,
        0x9b05688c2b3e6c1f,
        0x1f83d9abfb41bd6b,
        0x5be0cd19137e2179,
    }

    for _, block := range generateMessageBlock(messages) {
        // init W[0]~W[15]
        words := initFirst16Words(block)
        for t := 16; t <= 79; t++ {
            words = append(words, SSIG1(words[t-2])+words[t-7]+SSIG0(words[t-15])+words[t-16])
        }
        a := H[0]
        b := H[1]
        c := H[2]
        d := H[3]
        e := H[4]
        f := H[5]
        g := H[6]
        h := H[7]

        for t, w := range words {
            T1 := h + BSIG1(e) + CH(e, f, g) + K[t] + w
            T2 := BSIG0(a) + MAJ(a, b, c)
            h = g
            g = f
            f = e
            e = d + T1
            d = c
            c = b
            b = a
            a = T1 + T2
        }

        H[0] = a + H[0]
        H[1] = b + H[1]
        H[2] = c + H[2]
        H[3] = d + H[3]
        H[4] = e + H[4]
        H[5] = f + H[5]
        H[6] = g + H[6]
        H[7] = h + H[7]
    }
    // get H[N]
    result := ""
    for _, h := range H {
        // Fill with 0s and align to 16 digits
        result += fmt.Sprintf("%016x", h)
    }
    return result
}
Enter fullscreen mode Exit fullscreen mode

0 padding

To get final result, I wrote like below.

...
    // get H[N]
    result := ""
    for _, h := range H {
        result += fmt.Sprintf("%x", h)
    }
    return result
}
Enter fullscreen mode Exit fullscreen mode

But it wasn't as same as the result of crypto/sha512.

// the result of my code
9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5dac4663475c2e5c3adef46f73bcdec043

// the result of crypto/sha512
9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043
Enter fullscreen mode Exit fullscreen mode

Because one of the H[N] started from "0" and it was omitted.

9b71d224bd62f378
5d96d46ad3ea3d73
319bfbc2890caada
e2dff72519673ca7
2323c3d99ba5c11d
7c7acc6e14b8c5da
c4663475c2e5c3a
def46f73bcdec043
Enter fullscreen mode Exit fullscreen mode

So I fill with 0s like below.

...
    // get H[N]
    result := ""
    for _, h := range H {
        log.Printf("%x", h)
        // Fill with 0s and align to 16 digits
        result += fmt.Sprintf("%016x", h)
    }
    return result
}
Enter fullscreen mode Exit fullscreen mode

Resources

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (2)

Collapse
 
jonasbn profile image
Jonas Brømsø

Absolutely marvellous post. I will spend some time reading though your code - thank you for making the effort.

Collapse
 
masanori_msl profile image
Masui Masanori

Thank you for reading my post.
I wish it will help you :)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay