選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

crypto.go 2.5KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  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/subtle"
  7. "encoding/base32"
  8. "encoding/base64"
  9. "strings"
  10. )
  11. var (
  12. // slingamn's own private b32 alphabet, removing 1, l, o, and 0
  13. B32Encoder = base32.NewEncoding("abcdefghijkmnpqrstuvwxyz23456789").WithPadding(base32.NoPadding)
  14. )
  15. const (
  16. SecretTokenLength = 26
  17. )
  18. // generate a secret token that cannot be brute-forced via online attacks
  19. func GenerateSecretToken() string {
  20. // 128 bits of entropy are enough to resist any online attack:
  21. var buf [16]byte
  22. rand.Read(buf[:])
  23. // 26 ASCII characters, should be fine for most purposes
  24. return B32Encoder.EncodeToString(buf[:])
  25. }
  26. // "munge" a secret token to a new value. requirements:
  27. // 1. MUST be roughly as unlikely to collide with `GenerateSecretToken` outputs
  28. // as those outputs are with each other
  29. // 2. SHOULD be deterministic (motivation: if a JOIN line has msgid x,
  30. // create a deterministic msgid y for the fake HistServ PRIVMSG that "replays" it)
  31. // 3. SHOULD be in the same "namespace" as `GenerateSecretToken` outputs
  32. // (same length and character set)
  33. func MungeSecretToken(token string) (result string) {
  34. bytes, err := B32Encoder.DecodeString(token)
  35. if err != nil {
  36. // this should never happen
  37. return GenerateSecretToken()
  38. }
  39. // add 1 with carrying
  40. for i := len(bytes) - 1; 0 <= i; i -= 1 {
  41. bytes[i] += 1
  42. if bytes[i] != 0 {
  43. break
  44. } // else: overflow, carry to the next place
  45. }
  46. return B32Encoder.EncodeToString(bytes)
  47. }
  48. // securely check if a supplied token matches a stored token
  49. func SecretTokensMatch(storedToken string, suppliedToken string) bool {
  50. // XXX fix a potential gotcha: if the stored token is uninitialized,
  51. // then nothing should match it, not even supplying an empty token.
  52. if len(storedToken) == 0 {
  53. return false
  54. }
  55. return subtle.ConstantTimeCompare([]byte(storedToken), []byte(suppliedToken)) == 1
  56. }
  57. // generate a 256-bit secret key that can be written into a config file
  58. func GenerateSecretKey() string {
  59. var buf [32]byte
  60. rand.Read(buf[:])
  61. return base64.RawURLEncoding.EncodeToString(buf[:])
  62. }
  63. func normalizeCertfp(certfp string) string {
  64. return strings.ToLower(strings.Replace(certfp, ":", "", -1))
  65. }
  66. // Convenience to compare certfps as returned by different tools, e.g., openssl vs. oragono
  67. func CertfpsMatch(storedCertfp, suppliedCertfp string) bool {
  68. if storedCertfp == "" {
  69. return false
  70. }
  71. return normalizeCertfp(storedCertfp) == normalizeCertfp(suppliedCertfp)
  72. }