Skip to content

Instantly share code, notes, and snippets.

@eswierk
Created March 11, 2016 16:53
Show Gist options
  • Save eswierk/71435f7d5bc043c55ab9 to your computer and use it in GitHub Desktop.
Save eswierk/71435f7d5bc043c55ab9 to your computer and use it in GitHub Desktop.
golang compiler optimization test
package main
import (
"encoding/binary"
"fmt"
"hash"
"time"
"unsafe"
)
//
// C implementation of SHA-1 from https://tools.ietf.org/html/rfc3174
//
/*
#include <stdint.h>
enum
{
shaSuccess = 0,
shaNull, // Null pointer parameter
shaInputTooLong, // input data too long
shaStateError // called Input after Result
};
#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;
// 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;
}
//
// 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);
}
*/
import "C"
type CSha1 struct {
sha1Ctx C.SHA1Context
}
func NewCSha1() *CSha1 {
s := new(CSha1)
C.SHA1Reset(&s.sha1Ctx)
return s
}
func (s *CSha1) Write(barray []byte) {
dataLen := binary.Size(barray)
if dataLen == 0 {
return
}
barrayPtr := (*C.uint8_t)(unsafe.Pointer(&barray[0]))
C.SHA1Input(&s.sha1Ctx, barrayPtr, C.uint(dataLen))
}
func (s *CSha1) Sum(b []byte) []byte {
if b != nil {
s.Write(b)
}
sha1Out := make([]byte, C.SHA1HashSize)
shaOut1Ptr := (*C.uint8_t)(unsafe.Pointer(&sha1Out[0]))
C.SHA1Result(&s.sha1Ctx, shaOut1Ptr)
return C.GoBytes(unsafe.Pointer(shaOut1Ptr), C.SHA1HashSize)
}
//
// Go implementation of SHA-1 from https://github.com/golang/go/tree/master/src/crypto/sha1
//
// The size of a SHA1 checksum in bytes.
const Size = 20
// The blocksize of SHA1 in bytes.
const BlockSize = 64
const (
chunk = 64
init0 = 0x67452301
init1 = 0xEFCDAB89
init2 = 0x98BADCFE
init3 = 0x10325476
init4 = 0xC3D2E1F0
)
// digest represents the partial evaluation of a checksum.
type digest struct {
h [5]uint32
x [chunk]byte
nx int
len uint64
}
func (d *digest) Reset() {
d.h[0] = init0
d.h[1] = init1
d.h[2] = init2
d.h[3] = init3
d.h[4] = init4
d.nx = 0
d.len = 0
}
// New returns a new hash.Hash computing the SHA1 checksum.
func NewGoSha1() hash.Hash {
d := new(digest)
d.Reset()
return d
}
func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.len += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == chunk {
block(d, d.x[:])
d.nx = 0
}
p = p[n:]
}
if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
hash := d.checkSum()
return append(in, hash[:]...)
}
func (d *digest) checkSum() [Size]byte {
len := d.len
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
var tmp [64]byte
tmp[0] = 0x80
if len%64 < 56 {
d.Write(tmp[0 : 56-len%64])
} else {
d.Write(tmp[0 : 64+56-len%64])
}
// Length in bits.
len <<= 3
for i := uint(0); i < 8; i++ {
tmp[i] = byte(len >> (56 - 8*i))
}
d.Write(tmp[0:8])
if d.nx != 0 {
panic("d.nx != 0")
}
var digest [Size]byte
for i, s := range d.h {
digest[i*4] = byte(s >> 24)
digest[i*4+1] = byte(s >> 16)
digest[i*4+2] = byte(s >> 8)
digest[i*4+3] = byte(s)
}
return digest
}
// Sum returns the SHA1 checksum of the data.
func Sum(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
}
const (
_K0 = 0x5A827999
_K1 = 0x6ED9EBA1
_K2 = 0x8F1BBCDC
_K3 = 0xCA62C1D6
)
// blockGeneric is a portable, pure Go version of the SHA1 block step.
// It's used by sha1block_generic.go and tests.
func block(dig *digest, p []byte) {
var w [16]uint32
h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
for len(p) >= chunk {
// Can interlace the computation of w with the
// rounds below if needed for speed.
for i := 0; i < 16; i++ {
j := i * 4
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
}
a, b, c, d, e := h0, h1, h2, h3, h4
// Each of the four 20-iteration rounds
// differs only in the computation of f and
// the choice of K (_K0, _K1, etc).
i := 0
for ; i < 16; i++ {
f := b&c | (^b)&d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i&0xf] + _K0
a, b, c, d, e = t, a, b30, c, d
}
for ; i < 20; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b&c | (^b)&d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i&0xf] + _K0
a, b, c, d, e = t, a, b30, c, d
}
for ; i < 40; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i&0xf] + _K1
a, b, c, d, e = t, a, b30, c, d
}
for ; i < 60; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := ((b | c) & d) | (b & c)
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i&0xf] + _K2
a, b, c, d, e = t, a, b30, c, d
}
for ; i < 80; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i&0xf] + _K3
a, b, c, d, e = t, a, b30, c, d
}
h0 += a
h1 += b
h2 += c
h3 += d
h4 += e
p = p[chunk:]
}
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4
}
//
//
//
var zeros []byte = make([]byte, 1024*1024)
func main() {
for n := 0; n < 2; n++ {
fmt.Printf("Pass %d: Testing C SHA-1\n", n)
ct0 := time.Now()
csha1 := NewCSha1()
for i := 0; i < 200; i++ {
csha1.Write(zeros)
}
ct1 := time.Now()
fmt.Printf(" Hash is %x\n", csha1.Sum(nil))
fmt.Printf(" Completed in %f sec\n", ct1.Sub(ct0).Seconds())
fmt.Printf("Pass %d: Testing Go SHA-1\n", n)
gt0 := time.Now()
gsha1 := NewGoSha1()
for i := 0; i < 200; i++ {
gsha1.Write(zeros)
}
gt1 := time.Now()
fmt.Printf(" Hash is %x\n", gsha1.Sum(nil))
fmt.Printf(" Completed in %f sec\n", gt1.Sub(gt0).Seconds())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment