You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
2.6 KiB

8 years ago
  1. package crypto
  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. crand "crypto/rand"
  6. "crypto/sha256"
  7. "encoding/hex"
  8. "io"
  9. "sync"
  10. . "github.com/tendermint/tmlibs/common"
  11. )
  12. var gRandInfo *randInfo
  13. func init() {
  14. gRandInfo = &randInfo{}
  15. gRandInfo.MixEntropy(randBytes(32)) // Init
  16. }
  17. // Mix additional bytes of randomness, e.g. from hardware, user-input, etc.
  18. // It is OK to call it multiple times. It does not diminish security.
  19. func MixEntropy(seedBytes []byte) {
  20. gRandInfo.MixEntropy(seedBytes)
  21. }
  22. // This only uses the OS's randomness
  23. func randBytes(numBytes int) []byte {
  24. b := make([]byte, numBytes)
  25. _, err := crand.Read(b)
  26. if err != nil {
  27. PanicCrisis(err)
  28. }
  29. return b
  30. }
  31. // This uses the OS and the Seed(s).
  32. func CRandBytes(numBytes int) []byte {
  33. b := make([]byte, numBytes)
  34. _, err := gRandInfo.Read(b)
  35. if err != nil {
  36. PanicCrisis(err)
  37. }
  38. return b
  39. }
  40. // CRandHex returns a hex encoded string that's floor(numDigits/2) * 2 long.
  41. //
  42. // Note: CRandHex(24) gives 96 bits of randomness that
  43. // are usually strong enough for most purposes.
  44. func CRandHex(numDigits int) string {
  45. return hex.EncodeToString(CRandBytes(numDigits / 2))
  46. }
  47. // Returns a crand.Reader mixed with user-supplied entropy
  48. func CReader() io.Reader {
  49. return gRandInfo
  50. }
  51. //--------------------------------------------------------------------------------
  52. type randInfo struct {
  53. mtx sync.Mutex
  54. seedBytes [32]byte
  55. cipherAES256 cipher.Block
  56. streamAES256 cipher.Stream
  57. reader io.Reader
  58. }
  59. // You can call this as many times as you'd like.
  60. // XXX TODO review
  61. func (ri *randInfo) MixEntropy(seedBytes []byte) {
  62. ri.mtx.Lock()
  63. defer ri.mtx.Unlock()
  64. // Make new ri.seedBytes using passed seedBytes and current ri.seedBytes:
  65. // ri.seedBytes = sha256( seedBytes || ri.seedBytes )
  66. h := sha256.New()
  67. h.Write(seedBytes)
  68. h.Write(ri.seedBytes[:])
  69. hashBytes := h.Sum(nil)
  70. hashBytes32 := [32]byte{}
  71. copy(hashBytes32[:], hashBytes)
  72. ri.seedBytes = xorBytes32(ri.seedBytes, hashBytes32)
  73. // Create new cipher.Block
  74. var err error
  75. ri.cipherAES256, err = aes.NewCipher(ri.seedBytes[:])
  76. if err != nil {
  77. PanicSanity("Error creating AES256 cipher: " + err.Error())
  78. }
  79. // Create new stream
  80. ri.streamAES256 = cipher.NewCTR(ri.cipherAES256, randBytes(aes.BlockSize))
  81. // Create new reader
  82. ri.reader = &cipher.StreamReader{S: ri.streamAES256, R: crand.Reader}
  83. }
  84. func (ri *randInfo) Read(b []byte) (n int, err error) {
  85. ri.mtx.Lock()
  86. defer ri.mtx.Unlock()
  87. return ri.reader.Read(b)
  88. }
  89. func xorBytes32(bytesA [32]byte, bytesB [32]byte) (res [32]byte) {
  90. for i, b := range bytesA {
  91. res[i] = b ^ bytesB[i]
  92. }
  93. return res
  94. }