ca.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package cert
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "time"
  7. )
  8. type NebulaCAPool struct {
  9. CAs map[string]*NebulaCertificate
  10. certBlocklist map[string]struct{}
  11. }
  12. // NewCAPool creates a CAPool
  13. func NewCAPool() *NebulaCAPool {
  14. ca := NebulaCAPool{
  15. CAs: make(map[string]*NebulaCertificate),
  16. certBlocklist: make(map[string]struct{}),
  17. }
  18. return &ca
  19. }
  20. // NewCAPoolFromBytes will create a new CA pool from the provided
  21. // input bytes, which must be a PEM-encoded set of nebula certificates.
  22. // If the pool contains any expired certificates, an ErrExpired will be
  23. // returned along with the pool. The caller must handle any such errors.
  24. func NewCAPoolFromBytes(caPEMs []byte) (*NebulaCAPool, error) {
  25. pool := NewCAPool()
  26. var err error
  27. var expired bool
  28. for {
  29. caPEMs, err = pool.AddCACertificate(caPEMs)
  30. if errors.Is(err, ErrExpired) {
  31. expired = true
  32. err = nil
  33. }
  34. if err != nil {
  35. return nil, err
  36. }
  37. if len(caPEMs) == 0 || strings.TrimSpace(string(caPEMs)) == "" {
  38. break
  39. }
  40. }
  41. if expired {
  42. return pool, ErrExpired
  43. }
  44. return pool, nil
  45. }
  46. // AddCACertificate verifies a Nebula CA certificate and adds it to the pool
  47. // Only the first pem encoded object will be consumed, any remaining bytes are returned.
  48. // Parsed certificates will be verified and must be a CA
  49. func (ncp *NebulaCAPool) AddCACertificate(pemBytes []byte) ([]byte, error) {
  50. c, pemBytes, err := UnmarshalNebulaCertificateFromPEM(pemBytes)
  51. if err != nil {
  52. return pemBytes, err
  53. }
  54. if !c.Details.IsCA {
  55. return pemBytes, fmt.Errorf("%s: %w", c.Details.Name, ErrNotCA)
  56. }
  57. if !c.CheckSignature(c.Details.PublicKey) {
  58. return pemBytes, fmt.Errorf("%s: %w", c.Details.Name, ErrNotSelfSigned)
  59. }
  60. sum, err := c.Sha256Sum()
  61. if err != nil {
  62. return pemBytes, fmt.Errorf("could not calculate shasum for provided CA; error: %s; %s", err, c.Details.Name)
  63. }
  64. ncp.CAs[sum] = c
  65. if c.Expired(time.Now()) {
  66. return pemBytes, fmt.Errorf("%s: %w", c.Details.Name, ErrExpired)
  67. }
  68. return pemBytes, nil
  69. }
  70. // BlocklistFingerprint adds a cert fingerprint to the blocklist
  71. func (ncp *NebulaCAPool) BlocklistFingerprint(f string) {
  72. ncp.certBlocklist[f] = struct{}{}
  73. }
  74. // ResetCertBlocklist removes all previously blocklisted cert fingerprints
  75. func (ncp *NebulaCAPool) ResetCertBlocklist() {
  76. ncp.certBlocklist = make(map[string]struct{})
  77. }
  78. // NOTE: This uses an internal cache for Sha256Sum() that will not be invalidated
  79. // automatically if you manually change any fields in the NebulaCertificate.
  80. func (ncp *NebulaCAPool) IsBlocklisted(c *NebulaCertificate) bool {
  81. return ncp.isBlocklistedWithCache(c, false)
  82. }
  83. // IsBlocklisted returns true if the fingerprint fails to generate or has been explicitly blocklisted
  84. func (ncp *NebulaCAPool) isBlocklistedWithCache(c *NebulaCertificate, useCache bool) bool {
  85. h, err := c.sha256SumWithCache(useCache)
  86. if err != nil {
  87. return true
  88. }
  89. if _, ok := ncp.certBlocklist[h]; ok {
  90. return true
  91. }
  92. return false
  93. }
  94. // GetCAForCert attempts to return the signing certificate for the provided certificate.
  95. // No signature validation is performed
  96. func (ncp *NebulaCAPool) GetCAForCert(c *NebulaCertificate) (*NebulaCertificate, error) {
  97. if c.Details.Issuer == "" {
  98. return nil, fmt.Errorf("no issuer in certificate")
  99. }
  100. signer, ok := ncp.CAs[c.Details.Issuer]
  101. if ok {
  102. return signer, nil
  103. }
  104. return nil, fmt.Errorf("could not find ca for the certificate")
  105. }
  106. // GetFingerprints returns an array of trusted CA fingerprints
  107. func (ncp *NebulaCAPool) GetFingerprints() []string {
  108. fp := make([]string, len(ncp.CAs))
  109. i := 0
  110. for k := range ncp.CAs {
  111. fp[i] = k
  112. i++
  113. }
  114. return fp
  115. }