This commit is contained in:
kayos@tcp.direct 2023-11-11 14:00:52 -08:00
commit f8b82a6eed
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
3 changed files with 151 additions and 0 deletions

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.tcp.direct/kayos/shishua-go
go 1.21.2

0
go.sum Normal file
View File

148
shishua.go Normal file
View File

@ -0,0 +1,148 @@
package main
import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"fmt"
"os"
)
type SHISHUA struct {
state [16]uint64
output [16]uint64
counter [4]uint64
}
var phi = [16]uint64{
0x9E3779B97F4A7C15, 0xF39CC0605CEDC834, 0x1082276BF3A27251, 0xF86C6A11D0C18E95,
0x2767F0B153D27B7F, 0x0347045B5BF1827F, 0x01886F0928403002, 0xC1D64BA40F335E36,
0xF06AD7AE9717877E, 0x85839D6EFFBD7DC6, 0x64D325D1C5371682, 0xCADD0CCCFDFFBBE1,
0x626E33B8D04B4331, 0xBBF73C790D94F79D, 0x471C4AB3ED3D82A5, 0xFEC507705E4AE6E5,
}
func (srng *SHISHUA) shuffle() {
for j := 0; j < 2; j++ {
s := srng.state[j*8 : (j+1)*8]
o := srng.output[j*4 : (j+1)*4]
var t [8]uint64
for k := 0; k < 4; k++ {
s[k+4] += srng.counter[k]
}
shufOffsets := []uint8{2, 3, 0, 1, 5, 6, 7, 4, 3, 0, 1, 2, 6, 7, 4, 5}
for k := 0; k < 8; k++ {
t[k] = (s[shufOffsets[k]] >> 32) | (s[shufOffsets[k+8]] << 32)
}
for k := 0; k < 4; k++ {
uLo := s[k] >> 1
uHi := s[k+4] >> 3
s[k] = uLo + t[k]
s[k+4] = uHi + t[k+4]
o[k] = uLo ^ t[k+4]
}
}
for j := 0; j < 4; j++ {
srng.output[j+8] = srng.state[j] ^ srng.state[j+12]
srng.output[j+12] = srng.state[j+8] ^ srng.state[j+4]
srng.counter[j] += 7 - uint64(j*2)
}
}
func (srng *SHISHUA) Uint64() uint64 {
out := srng.output[0]
srng.shuffle()
return out
}
/*
func (srng *SHISHUA) Seed(seed int64) {
srng.Seed64(uint64(seed))
}
*/
func (srng *SHISHUA) prngGen(buf []byte, size int) []byte {
if buf == nil {
panic("buf is nil")
}
for i := 0; i < size; i += 128 {
for j := 0; j < 16; j++ {
binary.LittleEndian.PutUint64(buf[i+j*8:], srng.Uint64())
}
}
return buf
}
func (srng *SHISHUA) Read(buf []byte) (n int, err error) {
size := cap(buf)
if size%128 != 0 {
panic("buf's size must be a multiple of 128 bytes.")
}
return copy(buf, srng.prngGen(buf, size)), nil
}
func NewSHISHUA() *SHISHUA {
srng := &SHISHUA{}
buf := make([]byte, 64)
if n, e := rand.Read(buf); n != 64 || e != nil {
panic("rand.Read failed")
}
var seed [4]uint64
for i := 0; i < 4; i++ {
seed[i] = uint64(buf[i*8+0]) | uint64(buf[i*8+1])<<8 | uint64(buf[i*8+2])<<16 | uint64(buf[i*8+3])<<24
}
copy(srng.state[:], phi[:])
for i := 0; i < 4; i++ {
srng.state[i*2+0] ^= seed[i]
srng.state[i*2+8] ^= seed[(i+2)%4]
}
const rounds = 13
for i := 0; i < rounds; i++ {
srng.shuffle()
for j := 0; j < 4; j++ {
srng.state[j] = srng.output[j+12]
srng.state[j+4] = srng.output[j+8]
srng.state[j+8] = srng.output[j+4]
srng.state[j+12] = srng.output[j]
}
}
return srng
}
func main() {
s := NewSHISHUA()
buf := make([]byte, 128)
var occurrences = make(map[string]struct{})
for i := 0; i < 100; i++ {
n, _ := s.Read(buf)
if n != cap(buf) {
panic("n != cap(buf)")
}
// spew.Dump(buf[:n])
str := hex.EncodeToString(buf)
if _, ok := occurrences[str]; ok {
panic("duplicate hex")
}
occurrences[str] = struct{}{}
_, _ = os.Stdout.WriteString(str)
_, _ = os.Stdout.WriteString("\n")
}
for i := 0; i < 5000; i++ {
str := fmt.Sprintf("%x", s.Uint64())
if _, ok := occurrences[str]; ok {
panic("duplicate uint64")
}
}
}