aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/randentropy/rand_entropy.go
blob: b87fa564e0c498b25b2df02f56056d79b8f8a479 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package randentropy

import (
    crand "crypto/rand"
    "encoding/binary"
    "github.com/ethereum/go-ethereum/crypto/sha3"
    "io"
    "os"
    "strings"
    "time"
)

var Reader io.Reader = &randEntropy{}

type randEntropy struct {
}

func (*randEntropy) Read(bytes []byte) (n int, err error) {
    readBytes := GetEntropyMixed(len(bytes))
    copy(bytes, readBytes)
    return len(bytes), nil
}

// TODO: copied from crypto.go , move to sha3 package?
func Sha3(data []byte) []byte {
    d := sha3.NewKeccak256()
    d.Write(data)

    return d.Sum(nil)
}

// TODO: verify. this needs to be audited
// we start with crypt/rand, then XOR in additional entropy from OS
func GetEntropyMixed(n int) []byte {
    startTime := time.Now().UnixNano()
    // for each source, we take SHA3 of the source and use it as seed to math/rand
    // then read bytes from it and XOR them onto the bytes read from crypto/rand
    mainBuff := GetEntropyCSPRNG(n)
    // 1. OS entropy sources
    startTimeBytes := make([]byte, 32)
    binary.PutVarint(startTimeBytes, startTime)
    startTimeHash := Sha3(startTimeBytes)
    mixBytes(mainBuff, startTimeHash)

    pid := os.Getpid()
    pidBytes := make([]byte, 32)
    binary.PutUvarint(pidBytes, uint64(pid))
    pidHash := Sha3(pidBytes)
    mixBytes(mainBuff, pidHash)

    osEnv := os.Environ()
    osEnvBytes := []byte(strings.Join(osEnv, ""))
    osEnvHash := Sha3(osEnvBytes)
    mixBytes(mainBuff, osEnvHash)

    // not all OS have hostname in env variables
    osHostName, err := os.Hostname()
    if err != nil {
        osHostNameBytes := []byte(osHostName)
        osHostNameHash := Sha3(osHostNameBytes)
        mixBytes(mainBuff, osHostNameHash)
    }
    return mainBuff
}

func GetEntropyCSPRNG(n int) []byte {
    mainBuff := make([]byte, n)
    _, err := io.ReadFull(crand.Reader, mainBuff)
    if err != nil {
        panic("reading from crypto/rand failed: " + err.Error())
    }
    return mainBuff
}

func mixBytes(buff []byte, mixBuff []byte) []byte {
    bytesToMix := len(buff)
    if bytesToMix > 32 {
        bytesToMix = 32
    }
    for i := 0; i < bytesToMix; i++ {
        buff[i] ^= mixBuff[i]
    }
    return buff
}