├── README.md ├── gimli_test.go ├── gimli.go ├── LICENSE ├── hash_test.go └── hash.go /README.md: -------------------------------------------------------------------------------- 1 | # gimli 2 | 3 | Gimli, a 384-bit permutation designed to achieve high 4 | security with high performance across a broad range of platforms. 5 | 6 | This is a golang port of the public domain reference implementation in C from: 7 | 8 | https://gimli.cr.yp.to 9 | 10 | ## Benchmarks 11 | ``` 12 | Intel(R) Core(TM) i3-4010U CPU @ 1.70GHz 13 | go version go1.9.2 linux/amd64 14 | 15 | name time/op 16 | Gimli-4 488ns ± 1% 17 | ``` 18 | -------------------------------------------------------------------------------- /gimli_test.go: -------------------------------------------------------------------------------- 1 | package gimli 2 | 3 | import "testing" 4 | 5 | func TestGimli(t *testing.T) { 6 | var got Gimli 7 | want := Gimli{ 8 | 0xba11c85a, 0x91bad119, 0x380ce880, 0xd24c2c68, 9 | 0x3eceffea, 0x277a921c, 0x4f73a0bd, 0xda5a9cd8, 10 | 0x84b673f0, 0x34e52ff7, 0x9e2bef49, 0xf41bb8d6} 11 | 12 | for i := uint32(0); i < 12; i++ { 13 | got[i] = i*i*i + i*0x9e3779b9 14 | } 15 | 16 | got.Update() 17 | 18 | for i := uint32(0); i < 12; i++ { 19 | if got[i] != want[i] { 20 | t.Fatalf("got %v, want %v", got, want) 21 | } 22 | } 23 | } 24 | 25 | func BenchmarkGimli(b *testing.B) { 26 | var gimli Gimli 27 | for i := 0; i < b.N; i++ { 28 | gimli.Update() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /gimli.go: -------------------------------------------------------------------------------- 1 | package gimli 2 | 3 | import ( 4 | "math/bits" 5 | ) 6 | 7 | // Gimli stores the state of the gimli permutation 8 | type Gimli [12]uint32 9 | 10 | // Update advances the permutation to the next state 11 | func (state *Gimli) Update() { 12 | for round := 24; round > 0; round-- { 13 | for column := 0; column < 4; column++ { 14 | // sp-box 15 | x := bits.RotateLeft32(state[column], 24) 16 | y := bits.RotateLeft32(state[4+column], 9) 17 | z := state[8+column] 18 | 19 | state[8+column] = x ^ (z << 1) ^ ((y & z) << 2) 20 | state[4+column] = y ^ x ^ ((x | z) << 1) 21 | state[column] = z ^ y ^ ((x & y) << 3) 22 | } 23 | switch round & 3 { 24 | case 0: 25 | // small swap 26 | state[0], state[1], state[2], state[3] = state[1], state[0], state[3], state[2] 27 | // add constant 28 | state[0] ^= (0x9e377900 | uint32(round)) 29 | case 2: 30 | // big swap 31 | state[0], state[1], state[2], state[3] = state[2], state[3], state[0], state[1] 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 bmkessler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hash_test.go: -------------------------------------------------------------------------------- 1 | package gimli 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | var testVectors = []struct { 10 | input string 11 | output string 12 | }{ 13 | 14 | { 15 | "There's plenty for the both of us, may the best Dwarf win.", 16 | "4afb3ff784c7ad6943d49cf5da79facfa7c4434e1ce44f5dd4b28f91a84d22c8", 17 | }, 18 | { 19 | "If anyone was to ask for my opinion, which I note they're not, I'd say we were taking the long way around.", 20 | "ba82a16a7b224c15bed8e8bdc88903a4006bc7beda78297d96029203ef08e07c", 21 | }, 22 | { 23 | "Speak words we can all understand!", 24 | "8dd4d132059b72f8e8493f9afb86c6d86263e7439fc64cbb361fcbccf8b01267", 25 | }, 26 | { 27 | "It's true you don't see many Dwarf-women. And in fact, they are so alike in voice and appearance, that they are often mistaken for Dwarf-men. And this in turn has given rise to the belief that there are no Dwarf-women, and that Dwarves just spring out of holes in the ground! Which is, of course, ridiculous.", 28 | "ebe9bfc05ce15c73336fc3c5b52b01f75cf619bb37f13bfc7f567f9d5603191a", 29 | }, 30 | { 31 | "", // empty string 32 | "b0634b2c0b082aedc5c0a2fe4ee3adcfc989ec05de6f00addb04b3aaac271f67", 33 | }, 34 | } 35 | 36 | func TestHash(t *testing.T) { 37 | for i, tt := range testVectors { 38 | want := tt.output 39 | outputLen := len(want) / 2 40 | output := make([]byte, outputLen) 41 | Hash(output, []byte(tt.input)) 42 | got := hex.EncodeToString(output) 43 | if got != want { 44 | t.Errorf("testVector #%d\ninput: %q\ngot: %s\nwant: %s\n", i+1, tt.input, got, want) 45 | } 46 | // test variable output length 47 | for j := 0; j < outputLen; j++ { 48 | want := hex.EncodeToString(output[:j]) 49 | varOutput := make([]byte, j) 50 | Hash(varOutput, []byte(tt.input)) 51 | got := hex.EncodeToString(varOutput) 52 | if got != want { 53 | t.Errorf("testVector #%d\ninput: %q\noutput bytes: %d\ngot: %s\nwant: %s\n", i+1, tt.input, j, got, want) 54 | } 55 | } 56 | 57 | } 58 | } 59 | 60 | func BenchmarkHash(b *testing.B) { 61 | for _, tt := range testVectors { 62 | input := []byte(tt.input) 63 | for _, outputLen := range []int{16, 32, 64} { 64 | output := make([]byte, outputLen) 65 | b.Run(fmt.Sprintf("%d/%d", len(input), len(output)), func(b *testing.B) { 66 | for i := 0; i < b.N; i++ { 67 | Hash(output, input) 68 | } 69 | }) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /hash.go: -------------------------------------------------------------------------------- 1 | package gimli 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | const rateInBytes = 16 8 | 9 | // Hash computes the hash using the sponge construction 10 | func Hash(output, input []byte) { 11 | var state Gimli 12 | // absorb full input blocks 13 | for len(input) >= rateInBytes { 14 | state[0] ^= binary.LittleEndian.Uint32(input[0:4]) 15 | state[1] ^= binary.LittleEndian.Uint32(input[4:8]) 16 | state[2] ^= binary.LittleEndian.Uint32(input[8:12]) 17 | state[3] ^= binary.LittleEndian.Uint32(input[12:16]) 18 | state.Update() 19 | input = input[rateInBytes:] 20 | } 21 | 22 | n := len(input) 23 | // handle partial input block at the end 24 | t := make([]byte, 4) 25 | switch { 26 | case n > 12: 27 | state[0] ^= binary.LittleEndian.Uint32(input[0:4]) 28 | state[1] ^= binary.LittleEndian.Uint32(input[4:8]) 29 | state[2] ^= binary.LittleEndian.Uint32(input[8:12]) 30 | copy(t, input[12:]) 31 | state[3] ^= binary.LittleEndian.Uint32(t) 32 | case n > 8: 33 | state[0] ^= binary.LittleEndian.Uint32(input[0:4]) 34 | state[1] ^= binary.LittleEndian.Uint32(input[4:8]) 35 | copy(t, input[8:]) 36 | state[2] ^= binary.LittleEndian.Uint32(t) 37 | case n > 4: 38 | state[0] ^= binary.LittleEndian.Uint32(input[0:4]) 39 | copy(t, input[4:]) 40 | state[1] ^= binary.LittleEndian.Uint32(t) 41 | case n > 0: 42 | copy(t, input) 43 | state[0] ^= binary.LittleEndian.Uint32(t) 44 | } 45 | 46 | // do the padding bytes 47 | paddingIndex := n / 4 48 | paddingShift := uint((n % 4) * 8) 49 | state[paddingIndex] ^= 0x1F << (paddingShift) 50 | // second bit of padding 51 | state[rateInBytes/4-1] ^= 0x80 << (3 * 8) 52 | 53 | // squeeze full output blocks 54 | for len(output) >= rateInBytes { 55 | state.Update() 56 | binary.LittleEndian.PutUint32(output[0:4], state[0]) 57 | binary.LittleEndian.PutUint32(output[4:8], state[1]) 58 | binary.LittleEndian.PutUint32(output[8:12], state[2]) 59 | binary.LittleEndian.PutUint32(output[12:16], state[3]) 60 | output = output[rateInBytes:] 61 | } 62 | n = len(output) 63 | if n == 0 { 64 | return 65 | } 66 | // handle partial output block 67 | state.Update() 68 | switch { 69 | case n > 12: 70 | binary.LittleEndian.PutUint32(output[0:4], state[0]) 71 | binary.LittleEndian.PutUint32(output[4:8], state[1]) 72 | binary.LittleEndian.PutUint32(output[8:12], state[2]) 73 | binary.LittleEndian.PutUint32(t, state[3]) 74 | copy(output[12:], t) 75 | case n > 8: 76 | binary.LittleEndian.PutUint32(output[0:4], state[0]) 77 | binary.LittleEndian.PutUint32(output[4:8], state[1]) 78 | binary.LittleEndian.PutUint32(t, state[2]) 79 | copy(output[8:], t) 80 | case n > 4: 81 | binary.LittleEndian.PutUint32(output[0:4], state[0]) 82 | binary.LittleEndian.PutUint32(t, state[1]) 83 | copy(output[4:], t) 84 | case n > 0: 85 | binary.LittleEndian.PutUint32(t, state[0]) 86 | copy(output, t) 87 | } 88 | } 89 | --------------------------------------------------------------------------------