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.

crypto.go 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright (c) 2018 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package utils
  4. import (
  5. "crypto/rand"
  6. "crypto/sha256"
  7. "crypto/subtle"
  8. "crypto/tls"
  9. "crypto/x509"
  10. "encoding/base32"
  11. "encoding/base64"
  12. "encoding/hex"
  13. "errors"
  14. "net"
  15. "strings"
  16. "time"
  17. )
  18. var (
  19. // slingamn's own private b32 alphabet, removing 1, l, o, and 0
  20. B32Encoder = base32.NewEncoding("abcdefghijkmnpqrstuvwxyz23456789").WithPadding(base32.NoPadding)
  21. ErrInvalidCertfp = errors.New("Invalid certfp")
  22. ErrNoPeerCerts = errors.New("No certfp available")
  23. ErrNotTLS = errors.New("Connection is not TLS")
  24. )
  25. const (
  26. SecretTokenLength = 26
  27. )
  28. // generate a secret token that cannot be brute-forced via online attacks
  29. func GenerateSecretToken() string {
  30. // 128 bits of entropy are enough to resist any online attack:
  31. var buf [16]byte
  32. rand.Read(buf[:])
  33. // 26 ASCII characters, should be fine for most purposes
  34. return B32Encoder.EncodeToString(buf[:])
  35. }
  36. // "munge" a secret token to a new value. requirements:
  37. // 1. MUST be roughly as unlikely to collide with `GenerateSecretToken` outputs
  38. // as those outputs are with each other
  39. // 2. SHOULD be deterministic (motivation: if a JOIN line has msgid x,
  40. // create a deterministic msgid y for the fake HistServ PRIVMSG that "replays" it)
  41. // 3. SHOULD be in the same "namespace" as `GenerateSecretToken` outputs
  42. // (same length and character set)
  43. func MungeSecretToken(token string) (result string) {
  44. bytes, err := B32Encoder.DecodeString(token)
  45. if err != nil {
  46. // this should never happen
  47. return GenerateSecretToken()
  48. }
  49. // add 1 with carrying
  50. for i := len(bytes) - 1; 0 <= i; i -= 1 {
  51. bytes[i] += 1
  52. if bytes[i] != 0 {
  53. break
  54. } // else: overflow, carry to the next place
  55. }
  56. return B32Encoder.EncodeToString(bytes)
  57. }
  58. // securely check if a supplied token matches a stored token
  59. func SecretTokensMatch(storedToken string, suppliedToken string) bool {
  60. // XXX fix a potential gotcha: if the stored token is uninitialized,
  61. // then nothing should match it, not even supplying an empty token.
  62. if len(storedToken) == 0 {
  63. return false
  64. }
  65. return subtle.ConstantTimeCompare([]byte(storedToken), []byte(suppliedToken)) == 1
  66. }
  67. // generate a 256-bit secret key that can be written into a config file
  68. func GenerateSecretKey() string {
  69. var buf [32]byte
  70. rand.Read(buf[:])
  71. return base64.RawURLEncoding.EncodeToString(buf[:])
  72. }
  73. // Normalize openssl-formatted certfp's to oragono's format
  74. func NormalizeCertfp(certfp string) (result string, err error) {
  75. result = strings.ToLower(strings.Replace(certfp, ":", "", -1))
  76. decoded, err := hex.DecodeString(result)
  77. if err != nil || len(decoded) != 32 {
  78. return "", ErrInvalidCertfp
  79. }
  80. return
  81. }
  82. func GetCertFP(conn net.Conn, handshakeTimeout time.Duration) (fingerprint string, peerCerts []*x509.Certificate, err error) {
  83. tlsConn, isTLS := conn.(*tls.Conn)
  84. if !isTLS {
  85. return "", nil, ErrNotTLS
  86. }
  87. // ensure handshake is performed
  88. tlsConn.SetDeadline(time.Now().Add(handshakeTimeout))
  89. err = tlsConn.Handshake()
  90. tlsConn.SetDeadline(time.Time{})
  91. if err != nil {
  92. return "", nil, err
  93. }
  94. peerCerts = tlsConn.ConnectionState().PeerCertificates
  95. if len(peerCerts) < 1 {
  96. return "", nil, ErrNoPeerCerts
  97. }
  98. rawCert := sha256.Sum256(peerCerts[0].Raw)
  99. fingerprint = hex.EncodeToString(rawCert[:])
  100. return fingerprint, peerCerts, nil
  101. }