nl.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package key
  4. import (
  5. "crypto/ed25519"
  6. "crypto/subtle"
  7. "go4.org/mem"
  8. "tailscale.com/types/structs"
  9. "tailscale.com/types/tkatype"
  10. )
  11. const (
  12. // nlPrivateHexPrefix is the prefix used to identify a
  13. // hex-encoded tailnet-lock key.
  14. nlPrivateHexPrefix = "nlpriv:"
  15. // nlPublicHexPrefix is the prefix used to identify the public
  16. // side of a hex-encoded tailnet-lock key.
  17. nlPublicHexPrefix = "nlpub:"
  18. // nlPublicHexPrefixCLI is the prefix used for tailnet-lock keys
  19. // when shown on the CLI.
  20. // It's not practical for us to change the prefix everywhere due to
  21. // compatibility with existing clients, but we can support both prefixes
  22. // as well as use the CLI form when presenting to the user.
  23. nlPublicHexPrefixCLI = "tlpub:"
  24. )
  25. // NLPrivate is a node-managed network-lock key, used for signing
  26. // node-key signatures and authority update messages.
  27. type NLPrivate struct {
  28. _ structs.Incomparable // because == isn't constant-time
  29. k [ed25519.PrivateKeySize]byte
  30. }
  31. // IsZero reports whether k is the zero value.
  32. func (k NLPrivate) IsZero() bool {
  33. empty := NLPrivate{}
  34. return subtle.ConstantTimeCompare(k.k[:], empty.k[:]) == 1
  35. }
  36. // NewNLPrivate creates and returns a new network-lock key.
  37. func NewNLPrivate() NLPrivate {
  38. // ed25519.GenerateKey 'clamps' the key, not that it
  39. // matters given we don't do Diffie-Hellman.
  40. _, priv, err := ed25519.GenerateKey(nil) // nil == crypto/rand
  41. if err != nil {
  42. panic(err)
  43. }
  44. var out NLPrivate
  45. copy(out.k[:], priv)
  46. return out
  47. }
  48. // MarshalText implements encoding.TextUnmarshaler.
  49. func (k *NLPrivate) UnmarshalText(b []byte) error {
  50. return parseHex(k.k[:], mem.B(b), mem.S(nlPrivateHexPrefix))
  51. }
  52. // AppendText implements encoding.TextAppender.
  53. func (k NLPrivate) AppendText(b []byte) ([]byte, error) {
  54. return appendHexKey(b, nlPrivateHexPrefix, k.k[:]), nil
  55. }
  56. // MarshalText implements encoding.TextMarshaler.
  57. func (k NLPrivate) MarshalText() ([]byte, error) {
  58. return k.AppendText(nil)
  59. }
  60. // Equal reports whether k and other are the same key.
  61. func (k NLPrivate) Equal(other NLPrivate) bool {
  62. return subtle.ConstantTimeCompare(k.k[:], other.k[:]) == 1
  63. }
  64. // Public returns the public component of this key.
  65. func (k NLPrivate) Public() NLPublic {
  66. var out NLPublic
  67. copy(out.k[:], ed25519.PrivateKey(k.k[:]).Public().(ed25519.PublicKey))
  68. return out
  69. }
  70. // KeyID returns an identifier for this key.
  71. func (k NLPrivate) KeyID() tkatype.KeyID {
  72. // The correct way to compute this is:
  73. // return tka.Key{
  74. // Kind: tka.Key25519,
  75. // Public: pub.k[:],
  76. // }.ID()
  77. //
  78. // However, under the hood the key id for a 25519
  79. // key is just the public key, so we avoid the
  80. // dependency on tka by just doing this ourselves.
  81. pub := k.Public().k
  82. return pub[:]
  83. }
  84. // SignAUM implements tka.Signer.
  85. func (k NLPrivate) SignAUM(sigHash tkatype.AUMSigHash) ([]tkatype.Signature, error) {
  86. return []tkatype.Signature{{
  87. KeyID: k.KeyID(),
  88. Signature: ed25519.Sign(ed25519.PrivateKey(k.k[:]), sigHash[:]),
  89. }}, nil
  90. }
  91. // SignNKS signs the tka.NodeKeySignature identified by sigHash.
  92. func (k NLPrivate) SignNKS(sigHash tkatype.NKSSigHash) ([]byte, error) {
  93. return ed25519.Sign(ed25519.PrivateKey(k.k[:]), sigHash[:]), nil
  94. }
  95. // NLPublic is the public portion of a a NLPrivate.
  96. type NLPublic struct {
  97. k [ed25519.PublicKeySize]byte
  98. }
  99. // NLPublicFromEd25519Unsafe converts an ed25519 public key into
  100. // a type of NLPublic.
  101. //
  102. // New uses of this function should be avoided, as its possible to
  103. // accidentally construct an NLPublic from a non network-lock key.
  104. func NLPublicFromEd25519Unsafe(public ed25519.PublicKey) NLPublic {
  105. var out NLPublic
  106. copy(out.k[:], public)
  107. return out
  108. }
  109. // UnmarshalText implements encoding.TextUnmarshaler. This function
  110. // is able to decode both the CLI form (tlpub:<hex>) & the
  111. // regular form (nlpub:<hex>).
  112. func (k *NLPublic) UnmarshalText(b []byte) error {
  113. if mem.HasPrefix(mem.B(b), mem.S(nlPublicHexPrefix)) {
  114. return parseHex(k.k[:], mem.B(b), mem.S(nlPublicHexPrefix))
  115. }
  116. return parseHex(k.k[:], mem.B(b), mem.S(nlPublicHexPrefixCLI))
  117. }
  118. // AppendText implements encoding.TextAppender.
  119. func (k NLPublic) AppendText(b []byte) ([]byte, error) {
  120. return appendHexKey(b, nlPublicHexPrefix, k.k[:]), nil
  121. }
  122. // MarshalText implements encoding.TextMarshaler, emitting a
  123. // representation of the form nlpub:<hex>.
  124. func (k NLPublic) MarshalText() ([]byte, error) {
  125. return k.AppendText(nil)
  126. }
  127. // CLIString returns a marshalled representation suitable for use
  128. // with tailnet lock commands, of the form tlpub:<hex> instead of
  129. // the nlpub:<hex> form emitted by MarshalText. Both forms can
  130. // be decoded by UnmarshalText.
  131. func (k NLPublic) CLIString() string {
  132. return string(appendHexKey(nil, nlPublicHexPrefixCLI, k.k[:]))
  133. }
  134. // Verifier returns a ed25519.PublicKey that can be used to
  135. // verify signatures.
  136. func (k NLPublic) Verifier() ed25519.PublicKey {
  137. return ed25519.PublicKey(k.k[:])
  138. }
  139. // IsZero reports whether k is the zero value.
  140. func (k NLPublic) IsZero() bool {
  141. return k.Equal(NLPublic{})
  142. }
  143. // Equal reports whether k and other are the same key.
  144. func (k NLPublic) Equal(other NLPublic) bool {
  145. return subtle.ConstantTimeCompare(k.k[:], other.k[:]) == 1
  146. }
  147. // KeyID returns a tkatype.KeyID that can be used with a tka.Authority.
  148. func (k NLPublic) KeyID() tkatype.KeyID {
  149. return k.k[:]
  150. }