ca_pool.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. package cert
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/pem"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net/netip"
  10. "slices"
  11. "time"
  12. )
  13. type CAPool struct {
  14. CAs map[string]*CachedCertificate
  15. certBlocklist map[string]struct{}
  16. // v3CAs indexes the version 3 CAs in CAs by the fingerprint of their v2 form, which is what the certificates
  17. // they issue name as issuer. A version 3 CA only verifies version 3 certificates: version 2 certificates find
  18. // their issuer in CAs, where only the v2 form of the CA, if listed itself, answers to that fingerprint.
  19. v3CAs map[string]*CachedCertificate
  20. }
  21. // NewCAPool creates an empty CAPool
  22. func NewCAPool() *CAPool {
  23. ca := CAPool{
  24. CAs: make(map[string]*CachedCertificate),
  25. certBlocklist: make(map[string]struct{}),
  26. v3CAs: make(map[string]*CachedCertificate),
  27. }
  28. return &ca
  29. }
  30. // NewCAPoolFromPEM will create a new CA pool from the provided
  31. // input bytes, which must be a PEM-encoded set of nebula certificates.
  32. // If the pool contains any expired certificates, an ErrExpired will be
  33. // returned along with the pool. The caller must handle any such errors.
  34. func NewCAPoolFromPEM(caPEMs []byte) (*CAPool, error) {
  35. return NewCAPoolFromPEMReader(bytes.NewReader(caPEMs))
  36. }
  37. // NewCAPoolFromPEMReader will create a new CA pool from the provided reader.
  38. // The reader must contain a PEM-encoded set of nebula certificates.
  39. func NewCAPoolFromPEMReader(r io.Reader) (*CAPool, error) {
  40. pool := NewCAPool()
  41. var expired bool
  42. scanner := bufio.NewScanner(r)
  43. scanner.Split(SplitPEM)
  44. for scanner.Scan() {
  45. pemBytes := scanner.Bytes()
  46. block, rest := pem.Decode(pemBytes)
  47. if len(bytes.TrimSpace(rest)) > 0 {
  48. return nil, ErrInvalidPEMBlock
  49. }
  50. if block == nil {
  51. return nil, ErrInvalidPEMBlock
  52. }
  53. c, err := unmarshalCertificateBlock(block)
  54. if err != nil {
  55. return nil, err
  56. }
  57. err = pool.AddCA(c)
  58. if errors.Is(err, ErrExpired) {
  59. expired = true
  60. continue
  61. } else if err != nil {
  62. return nil, err
  63. }
  64. }
  65. if err := scanner.Err(); err != nil {
  66. return nil, ErrInvalidPEMBlock
  67. }
  68. if expired {
  69. return pool, ErrExpired
  70. }
  71. return pool, nil
  72. }
  73. // AddCAFromPEM verifies a Nebula CA certificate and adds it to the pool.
  74. // Only the first pem encoded object will be consumed, any remaining bytes are returned.
  75. // Parsed certificates will be verified and must be a CA
  76. func (ncp *CAPool) AddCAFromPEM(pemBytes []byte) ([]byte, error) {
  77. c, pemBytes, err := UnmarshalCertificateFromPEM(pemBytes)
  78. if err != nil {
  79. return pemBytes, err
  80. }
  81. err = ncp.AddCA(c)
  82. if err != nil {
  83. return pemBytes, err
  84. }
  85. return pemBytes, nil
  86. }
  87. // AddCA verifies a Nebula CA certificate and adds it to the pool.
  88. func (ncp *CAPool) AddCA(c Certificate) error {
  89. if !c.IsCA() {
  90. return fmt.Errorf("%s: %w", c.Name(), ErrNotCA)
  91. }
  92. if !c.CheckSignature(c.PublicKey()) {
  93. return fmt.Errorf("%s: %w", c.Name(), ErrNotSelfSigned)
  94. }
  95. sum, err := c.Fingerprint()
  96. if err != nil {
  97. return fmt.Errorf("could not calculate fingerprint for provided CA; error: %w; %s", err, c.Name())
  98. }
  99. cc := &CachedCertificate{
  100. Certificate: c,
  101. Fingerprint: sum,
  102. InvertedGroups: make(map[string]struct{}),
  103. }
  104. for _, g := range c.Groups() {
  105. cc.InvertedGroups[g] = struct{}{}
  106. }
  107. if v2 := V2Form(c); v2 != nil {
  108. v2Sum, err := v2.Fingerprint()
  109. if err != nil {
  110. return fmt.Errorf("could not calculate fingerprint for provided CA; error: %w; %s", err, c.Name())
  111. }
  112. if other, ok := ncp.v3CAs[v2Sum]; ok && other.Fingerprint != sum {
  113. return fmt.Errorf("%s: two version 3 CAs extend the same version 2 CA %s", c.Name(), v2Sum)
  114. }
  115. ncp.v3CAs[v2Sum] = cc
  116. }
  117. ncp.CAs[sum] = cc
  118. if c.Expired(time.Now()) {
  119. return fmt.Errorf("%s: %w", c.Name(), ErrExpired)
  120. }
  121. return nil
  122. }
  123. // BlocklistFingerprint adds a cert fingerprint to the blocklist
  124. func (ncp *CAPool) BlocklistFingerprint(f string) {
  125. ncp.certBlocklist[f] = struct{}{}
  126. }
  127. // ResetCertBlocklist removes all previously blocklisted cert fingerprints
  128. func (ncp *CAPool) ResetCertBlocklist() {
  129. ncp.certBlocklist = make(map[string]struct{})
  130. }
  131. // IsBlocklisted tests the provided fingerprint against the pools blocklist.
  132. // Returns true if the fingerprint is blocked.
  133. func (ncp *CAPool) IsBlocklisted(fingerprint string) bool {
  134. if _, ok := ncp.certBlocklist[fingerprint]; ok {
  135. return true
  136. }
  137. return false
  138. }
  139. // VerifyCertificate verifies the certificate is valid and is signed by a trusted CA in the pool.
  140. // If the certificate is valid then the returned CachedCertificate can be used in subsequent verification attempts
  141. // to increase performance.
  142. func (ncp *CAPool) VerifyCertificate(now time.Time, c Certificate) (*CachedCertificate, error) {
  143. if c == nil {
  144. return nil, fmt.Errorf("no certificate")
  145. }
  146. fp, err := c.Fingerprint()
  147. if err != nil {
  148. return nil, fmt.Errorf("could not calculate fingerprint to verify: %w", err)
  149. }
  150. signer, err := ncp.verify(c, now, fp, "")
  151. if err != nil {
  152. return nil, err
  153. }
  154. // Pre nebula v1.10.3 could generate signatures in either high or low s form and validation
  155. // of signatures allowed for either. Nebula v1.10.3 and beyond clamps signature generation to low-s form
  156. // but validation still allows for either. Since a change in the signature bytes affects the fingerprint, we
  157. // need to test both forms until such a time comes that we enforce low-s form on signature validation.
  158. fp2, err := CalculateAlternateFingerprint(c)
  159. if err != nil {
  160. return nil, fmt.Errorf("could not calculate alternate fingerprint to verify: %w", err)
  161. }
  162. if fp2 != "" && ncp.IsBlocklisted(fp2) {
  163. return nil, ErrBlockListed
  164. }
  165. // Blocking the v2 form of a version 3 certificate blocks the certificate too.
  166. var v2Fps []string
  167. if v2 := V2Form(c); v2 != nil {
  168. v2Fp, err := v2.Fingerprint()
  169. if err != nil {
  170. return nil, fmt.Errorf("could not calculate v2 fingerprint to verify: %w", err)
  171. }
  172. v2Fp2, err := CalculateAlternateFingerprint(v2)
  173. if err != nil {
  174. return nil, fmt.Errorf("could not calculate alternate v2 fingerprint to verify: %w", err)
  175. }
  176. v2Fps = append(v2Fps, v2Fp)
  177. if v2Fp2 != "" {
  178. v2Fps = append(v2Fps, v2Fp2)
  179. }
  180. for _, f := range v2Fps {
  181. if ncp.IsBlocklisted(f) {
  182. return nil, ErrBlockListed
  183. }
  184. }
  185. }
  186. cc := CachedCertificate{
  187. Certificate: c,
  188. InvertedGroups: make(map[string]struct{}),
  189. Fingerprint: fp,
  190. fingerprint2: fp2,
  191. v2Fingerprints: v2Fps,
  192. signerFingerprint: signer.Fingerprint,
  193. }
  194. for _, g := range c.Groups() {
  195. cc.InvertedGroups[g] = struct{}{}
  196. }
  197. return &cc, nil
  198. }
  199. // VerifyCachedCertificate is the same as VerifyCertificate other than it operates on a pre-verified structure and
  200. // is a cheaper operation to perform as a result.
  201. func (ncp *CAPool) VerifyCachedCertificate(now time.Time, c *CachedCertificate) error {
  202. // Check any available alternate fingerprint forms for this certificate, re P256 high-s/low-s
  203. if c.fingerprint2 != "" && ncp.IsBlocklisted(c.fingerprint2) {
  204. return ErrBlockListed
  205. }
  206. for _, f := range c.v2Fingerprints {
  207. if ncp.IsBlocklisted(f) {
  208. return ErrBlockListed
  209. }
  210. }
  211. _, err := ncp.verify(c.Certificate, now, c.Fingerprint, c.signerFingerprint)
  212. return err
  213. }
  214. func (ncp *CAPool) verify(c Certificate, now time.Time, certFp string, signerFp string) (*CachedCertificate, error) {
  215. if ncp.IsBlocklisted(certFp) {
  216. return nil, ErrBlockListed
  217. }
  218. signer, err := ncp.GetCAForCert(c)
  219. if err != nil {
  220. return nil, err
  221. }
  222. if signer.Certificate.Curve() != c.Curve() {
  223. return nil, ErrCurveMismatch
  224. }
  225. if PQSuiteOf(signer.Certificate) != PQSuiteOf(c) {
  226. return nil, ErrPQSuiteMismatch
  227. }
  228. if signer.Certificate.Expired(now) {
  229. return nil, ErrRootExpired
  230. }
  231. if c.Expired(now) {
  232. return nil, ErrExpired
  233. }
  234. // If we are checking a cached certificate then we can bail early here
  235. // Either the root is no longer trusted or everything is fine
  236. if len(signerFp) > 0 {
  237. if signerFp != signer.Fingerprint {
  238. return nil, ErrFingerprintMismatch
  239. }
  240. return signer, nil
  241. }
  242. if !c.CheckSignature(signer.Certificate.PublicKey()) {
  243. return nil, ErrSignatureMismatch
  244. }
  245. err = CheckCAConstraints(signer.Certificate, c)
  246. if err != nil {
  247. return nil, err
  248. }
  249. return signer, nil
  250. }
  251. // GetCAForCert attempts to return the signing certificate for the provided certificate.
  252. // No signature validation is performed
  253. func (ncp *CAPool) GetCAForCert(c Certificate) (*CachedCertificate, error) {
  254. issuer := c.Issuer()
  255. if issuer == "" {
  256. return nil, fmt.Errorf("no issuer in certificate")
  257. }
  258. cas := ncp.CAs
  259. if c.Version() == Version3 {
  260. cas = ncp.v3CAs
  261. }
  262. signer, ok := cas[issuer]
  263. if ok {
  264. return signer, nil
  265. }
  266. return nil, ErrCaNotFound
  267. }
  268. // GetFingerprints returns an array of trusted CA fingerprints
  269. func (ncp *CAPool) GetFingerprints() []string {
  270. fp := make([]string, len(ncp.CAs))
  271. i := 0
  272. for k := range ncp.CAs {
  273. fp[i] = k
  274. i++
  275. }
  276. return fp
  277. }
  278. // CheckCAConstraints returns an error if the sub certificate violates constraints present in the signer certificate.
  279. func CheckCAConstraints(signer Certificate, sub Certificate) error {
  280. return checkCAConstraints(signer, sub.NotBefore(), sub.NotAfter(), sub.Groups(), sub.Networks(), sub.UnsafeNetworks())
  281. }
  282. // checkCAConstraints is a very generic function allowing both Certificates and TBSCertificates to be tested.
  283. func checkCAConstraints(signer Certificate, notBefore, notAfter time.Time, groups []string, networks, unsafeNetworks []netip.Prefix) error {
  284. // Make sure this cert isn't valid after the root
  285. if notAfter.After(signer.NotAfter()) {
  286. return fmt.Errorf("certificate expires after signing certificate")
  287. }
  288. // Make sure this cert wasn't valid before the root
  289. if notBefore.Before(signer.NotBefore()) {
  290. return fmt.Errorf("certificate is valid before the signing certificate")
  291. }
  292. // If the signer has a limited set of groups make sure the cert only contains a subset
  293. signerGroups := signer.Groups()
  294. if len(signerGroups) > 0 {
  295. for _, g := range groups {
  296. if !slices.Contains(signerGroups, g) {
  297. return fmt.Errorf("certificate contained a group not present on the signing ca: %s", g)
  298. }
  299. }
  300. }
  301. // If the signer has a limited set of ip ranges to issue from make sure the cert only contains a subset
  302. signingNetworks := signer.Networks()
  303. if len(signingNetworks) > 0 {
  304. for _, certNetwork := range networks {
  305. found := false
  306. for _, signingNetwork := range signingNetworks {
  307. if signingNetwork.Contains(certNetwork.Addr()) && signingNetwork.Bits() <= certNetwork.Bits() {
  308. found = true
  309. break
  310. }
  311. }
  312. if !found {
  313. return fmt.Errorf("certificate contained a network assignment outside the limitations of the signing ca: %s", certNetwork.String())
  314. }
  315. }
  316. }
  317. // If the signer has a limited set of subnet ranges to issue from make sure the cert only contains a subset
  318. signingUnsafeNetworks := signer.UnsafeNetworks()
  319. if len(signingUnsafeNetworks) > 0 {
  320. for _, certUnsafeNetwork := range unsafeNetworks {
  321. found := false
  322. for _, caNetwork := range signingUnsafeNetworks {
  323. if caNetwork.Contains(certUnsafeNetwork.Addr()) && caNetwork.Bits() <= certUnsafeNetwork.Bits() {
  324. found = true
  325. break
  326. }
  327. }
  328. if !found {
  329. return fmt.Errorf("certificate contained an unsafe network assignment outside the limitations of the signing ca: %s", certUnsafeNetwork.String())
  330. }
  331. }
  332. }
  333. return nil
  334. }