util.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package key
  4. import (
  5. crand "crypto/rand"
  6. "encoding/base64"
  7. "encoding/hex"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "go4.org/mem"
  12. )
  13. // rand fills b with cryptographically strong random bytes. Panics if
  14. // no random bytes are available.
  15. func rand(b []byte) {
  16. if _, err := io.ReadFull(crand.Reader, b[:]); err != nil {
  17. panic(fmt.Sprintf("unable to read random bytes from OS: %v", err))
  18. }
  19. }
  20. // clamp25519 clamps b, which must be a 32-byte Curve25519 private
  21. // key, to a safe value.
  22. //
  23. // The clamping effectively constrains the key to a number between
  24. // 2^251 and 2^252-1, which is then multiplied by 8 (the cofactor of
  25. // Curve25519). This produces a value that doesn't have any unsafe
  26. // properties when doing operations like ScalarMult.
  27. //
  28. // See
  29. // https://web.archive.org/web/20210228105330/https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/
  30. // for a more in-depth explanation of the constraints that led to this
  31. // clamping requirement.
  32. //
  33. // PLEASE NOTE that not all Curve25519 values require clamping. When
  34. // implementing a new key type that uses Curve25519, you must evaluate
  35. // whether that particular key's use requires clamping. Here are some
  36. // existing uses and whether you should clamp private keys at
  37. // creation.
  38. //
  39. // - NaCl box: yes, clamp at creation.
  40. // - WireGuard (userspace uapi or kernel): no, do not clamp.
  41. // - Noise protocols: no, do not clamp.
  42. func clamp25519Private(b []byte) {
  43. b[0] &= 248
  44. b[31] = (b[31] & 127) | 64
  45. }
  46. func toHex(k []byte, prefix string) []byte {
  47. ret := make([]byte, len(prefix)+len(k)*2)
  48. copy(ret, prefix)
  49. hex.Encode(ret[len(prefix):], k)
  50. return ret
  51. }
  52. // parseHex decodes a key string of the form "<prefix><hex string>"
  53. // into out. The prefix must match, and the decoded base64 must fit
  54. // exactly into out.
  55. //
  56. // Note the errors in this function deliberately do not echo the
  57. // contents of in, because it might be a private key or part of a
  58. // private key.
  59. func parseHex(out []byte, in, prefix mem.RO) error {
  60. if !mem.HasPrefix(in, prefix) {
  61. return fmt.Errorf("key hex string doesn't have expected type prefix %s", prefix.StringCopy())
  62. }
  63. in = in.SliceFrom(prefix.Len())
  64. if want := len(out) * 2; in.Len() != want {
  65. return fmt.Errorf("key hex has the wrong size, got %d want %d", in.Len(), want)
  66. }
  67. for i := range out {
  68. a, ok1 := fromHexChar(in.At(i*2 + 0))
  69. b, ok2 := fromHexChar(in.At(i*2 + 1))
  70. if !ok1 || !ok2 {
  71. return errors.New("invalid hex character in key")
  72. }
  73. out[i] = (a << 4) | b
  74. }
  75. return nil
  76. }
  77. // fromHexChar converts a hex character into its value and a success flag.
  78. func fromHexChar(c byte) (byte, bool) {
  79. switch {
  80. case '0' <= c && c <= '9':
  81. return c - '0', true
  82. case 'a' <= c && c <= 'f':
  83. return c - 'a' + 10, true
  84. case 'A' <= c && c <= 'F':
  85. return c - 'A' + 10, true
  86. }
  87. return 0, false
  88. }
  89. // debug32 returns the Tailscale conventional debug representation of
  90. // a key: the first five base64 digits of the key, in square brackets.
  91. func debug32(k [32]byte) string {
  92. if k == [32]byte{} {
  93. return ""
  94. }
  95. // The goal here is to generate "[" + base64.StdEncoding.EncodeToString(k[:])[:5] + "]".
  96. // Since we only care about the first 5 characters, it suffices to encode the first 4 bytes of k.
  97. // Encoding those 4 bytes requires 8 bytes.
  98. // Make dst have size 9, to fit the leading '[' plus those 8 bytes.
  99. // We slice the unused ones away at the end.
  100. dst := make([]byte, 9)
  101. dst[0] = '['
  102. base64.StdEncoding.Encode(dst[1:], k[:4])
  103. dst[6] = ']'
  104. return string(dst[:7])
  105. }