cert.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. package cert
  2. import (
  3. "bytes"
  4. "crypto"
  5. "crypto/rand"
  6. "crypto/sha256"
  7. "encoding/binary"
  8. "encoding/hex"
  9. "encoding/json"
  10. "encoding/pem"
  11. "fmt"
  12. "net"
  13. "time"
  14. "github.com/golang/protobuf/proto"
  15. "golang.org/x/crypto/curve25519"
  16. "golang.org/x/crypto/ed25519"
  17. )
  18. const publicKeyLen = 32
  19. const (
  20. CertBanner = "NEBULA CERTIFICATE"
  21. X25519PrivateKeyBanner = "NEBULA X25519 PRIVATE KEY"
  22. X25519PublicKeyBanner = "NEBULA X25519 PUBLIC KEY"
  23. Ed25519PrivateKeyBanner = "NEBULA ED25519 PRIVATE KEY"
  24. Ed25519PublicKeyBanner = "NEBULA ED25519 PUBLIC KEY"
  25. )
  26. type NebulaCertificate struct {
  27. Details NebulaCertificateDetails
  28. Signature []byte
  29. }
  30. type NebulaCertificateDetails struct {
  31. Name string
  32. Ips []*net.IPNet
  33. Subnets []*net.IPNet
  34. Groups []string
  35. NotBefore time.Time
  36. NotAfter time.Time
  37. PublicKey []byte
  38. IsCA bool
  39. Issuer string
  40. // Map of groups for faster lookup
  41. InvertedGroups map[string]struct{}
  42. }
  43. type m map[string]interface{}
  44. // UnmarshalNebulaCertificate will unmarshal a protobuf byte representation of a nebula cert
  45. func UnmarshalNebulaCertificate(b []byte) (*NebulaCertificate, error) {
  46. if len(b) == 0 {
  47. return nil, fmt.Errorf("nil byte array")
  48. }
  49. var rc RawNebulaCertificate
  50. err := proto.Unmarshal(b, &rc)
  51. if err != nil {
  52. return nil, err
  53. }
  54. if len(rc.Details.Ips)%2 != 0 {
  55. return nil, fmt.Errorf("encoded IPs should be in pairs, an odd number was found")
  56. }
  57. if len(rc.Details.Subnets)%2 != 0 {
  58. return nil, fmt.Errorf("encoded Subnets should be in pairs, an odd number was found")
  59. }
  60. nc := NebulaCertificate{
  61. Details: NebulaCertificateDetails{
  62. Name: rc.Details.Name,
  63. Groups: make([]string, len(rc.Details.Groups)),
  64. Ips: make([]*net.IPNet, len(rc.Details.Ips)/2),
  65. Subnets: make([]*net.IPNet, len(rc.Details.Subnets)/2),
  66. NotBefore: time.Unix(rc.Details.NotBefore, 0),
  67. NotAfter: time.Unix(rc.Details.NotAfter, 0),
  68. PublicKey: make([]byte, len(rc.Details.PublicKey)),
  69. IsCA: rc.Details.IsCA,
  70. InvertedGroups: make(map[string]struct{}),
  71. },
  72. Signature: make([]byte, len(rc.Signature)),
  73. }
  74. copy(nc.Signature, rc.Signature)
  75. copy(nc.Details.Groups, rc.Details.Groups)
  76. nc.Details.Issuer = hex.EncodeToString(rc.Details.Issuer)
  77. if len(rc.Details.PublicKey) < publicKeyLen {
  78. return nil, fmt.Errorf("Public key was fewer than 32 bytes; %v", len(rc.Details.PublicKey))
  79. }
  80. copy(nc.Details.PublicKey, rc.Details.PublicKey)
  81. for i, rawIp := range rc.Details.Ips {
  82. if i%2 == 0 {
  83. nc.Details.Ips[i/2] = &net.IPNet{IP: int2ip(rawIp)}
  84. } else {
  85. nc.Details.Ips[i/2].Mask = net.IPMask(int2ip(rawIp))
  86. }
  87. }
  88. for i, rawIp := range rc.Details.Subnets {
  89. if i%2 == 0 {
  90. nc.Details.Subnets[i/2] = &net.IPNet{IP: int2ip(rawIp)}
  91. } else {
  92. nc.Details.Subnets[i/2].Mask = net.IPMask(int2ip(rawIp))
  93. }
  94. }
  95. for _, g := range rc.Details.Groups {
  96. nc.Details.InvertedGroups[g] = struct{}{}
  97. }
  98. return &nc, nil
  99. }
  100. // UnmarshalNebulaCertificateFromPEM will unmarshal the first pem block in a byte array, returning any non consumed data
  101. // or an error on failure
  102. func UnmarshalNebulaCertificateFromPEM(b []byte) (*NebulaCertificate, []byte, error) {
  103. p, r := pem.Decode(b)
  104. if p == nil {
  105. return nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
  106. }
  107. nc, err := UnmarshalNebulaCertificate(p.Bytes)
  108. return nc, r, err
  109. }
  110. // MarshalX25519PrivateKey is a simple helper to PEM encode an X25519 private key
  111. func MarshalX25519PrivateKey(b []byte) []byte {
  112. return pem.EncodeToMemory(&pem.Block{Type: X25519PrivateKeyBanner, Bytes: b})
  113. }
  114. // MarshalEd25519PrivateKey is a simple helper to PEM encode an Ed25519 private key
  115. func MarshalEd25519PrivateKey(key ed25519.PrivateKey) []byte {
  116. return pem.EncodeToMemory(&pem.Block{Type: Ed25519PrivateKeyBanner, Bytes: key})
  117. }
  118. // UnmarshalX25519PrivateKey will try to pem decode an X25519 private key, returning any other bytes b
  119. // or an error on failure
  120. func UnmarshalX25519PrivateKey(b []byte) ([]byte, []byte, error) {
  121. k, r := pem.Decode(b)
  122. if k == nil {
  123. return nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
  124. }
  125. if k.Type != X25519PrivateKeyBanner {
  126. return nil, r, fmt.Errorf("bytes did not contain a proper nebula X25519 private key banner")
  127. }
  128. if len(k.Bytes) != publicKeyLen {
  129. return nil, r, fmt.Errorf("key was not 32 bytes, is invalid X25519 private key")
  130. }
  131. return k.Bytes, r, nil
  132. }
  133. // UnmarshalEd25519PrivateKey will try to pem decode an Ed25519 private key, returning any other bytes b
  134. // or an error on failure
  135. func UnmarshalEd25519PrivateKey(b []byte) (ed25519.PrivateKey, []byte, error) {
  136. k, r := pem.Decode(b)
  137. if k == nil {
  138. return nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
  139. }
  140. if k.Type != Ed25519PrivateKeyBanner {
  141. return nil, r, fmt.Errorf("bytes did not contain a proper nebula Ed25519 private key banner")
  142. }
  143. if len(k.Bytes) != ed25519.PrivateKeySize {
  144. return nil, r, fmt.Errorf("key was not 64 bytes, is invalid ed25519 private key")
  145. }
  146. return k.Bytes, r, nil
  147. }
  148. // MarshalX25519PublicKey is a simple helper to PEM encode an X25519 public key
  149. func MarshalX25519PublicKey(b []byte) []byte {
  150. return pem.EncodeToMemory(&pem.Block{Type: X25519PublicKeyBanner, Bytes: b})
  151. }
  152. // MarshalEd25519PublicKey is a simple helper to PEM encode an Ed25519 public key
  153. func MarshalEd25519PublicKey(key ed25519.PublicKey) []byte {
  154. return pem.EncodeToMemory(&pem.Block{Type: Ed25519PublicKeyBanner, Bytes: key})
  155. }
  156. // UnmarshalX25519PublicKey will try to pem decode an X25519 public key, returning any other bytes b
  157. // or an error on failure
  158. func UnmarshalX25519PublicKey(b []byte) ([]byte, []byte, error) {
  159. k, r := pem.Decode(b)
  160. if k == nil {
  161. return nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
  162. }
  163. if k.Type != X25519PublicKeyBanner {
  164. return nil, r, fmt.Errorf("bytes did not contain a proper nebula X25519 public key banner")
  165. }
  166. if len(k.Bytes) != publicKeyLen {
  167. return nil, r, fmt.Errorf("key was not 32 bytes, is invalid X25519 public key")
  168. }
  169. return k.Bytes, r, nil
  170. }
  171. // UnmarshalEd25519PublicKey will try to pem decode an Ed25519 public key, returning any other bytes b
  172. // or an error on failure
  173. func UnmarshalEd25519PublicKey(b []byte) (ed25519.PublicKey, []byte, error) {
  174. k, r := pem.Decode(b)
  175. if k == nil {
  176. return nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
  177. }
  178. if k.Type != Ed25519PublicKeyBanner {
  179. return nil, r, fmt.Errorf("bytes did not contain a proper nebula Ed25519 public key banner")
  180. }
  181. if len(k.Bytes) != ed25519.PublicKeySize {
  182. return nil, r, fmt.Errorf("key was not 32 bytes, is invalid ed25519 public key")
  183. }
  184. return k.Bytes, r, nil
  185. }
  186. // Sign signs a nebula cert with the provided private key
  187. func (nc *NebulaCertificate) Sign(key ed25519.PrivateKey) error {
  188. b, err := proto.Marshal(nc.getRawDetails())
  189. if err != nil {
  190. return err
  191. }
  192. sig, err := key.Sign(rand.Reader, b, crypto.Hash(0))
  193. if err != nil {
  194. return err
  195. }
  196. nc.Signature = sig
  197. return nil
  198. }
  199. // CheckSignature verifies the signature against the provided public key
  200. func (nc *NebulaCertificate) CheckSignature(key ed25519.PublicKey) bool {
  201. b, err := proto.Marshal(nc.getRawDetails())
  202. if err != nil {
  203. return false
  204. }
  205. return ed25519.Verify(key, b, nc.Signature)
  206. }
  207. // Expired will return true if the nebula cert is too young or too old compared to the provided time, otherwise false
  208. func (nc *NebulaCertificate) Expired(t time.Time) bool {
  209. return nc.Details.NotBefore.After(t) || nc.Details.NotAfter.Before(t)
  210. }
  211. // Verify will ensure a certificate is good in all respects (expiry, group membership, signature, cert blacklist, etc)
  212. func (nc *NebulaCertificate) Verify(t time.Time, ncp *NebulaCAPool) (bool, error) {
  213. if ncp.IsBlacklisted(nc) {
  214. return false, fmt.Errorf("certificate has been blacklisted")
  215. }
  216. signer, err := ncp.GetCAForCert(nc)
  217. if err != nil {
  218. return false, err
  219. }
  220. if signer.Expired(t) {
  221. return false, fmt.Errorf("root certificate is expired")
  222. }
  223. if nc.Expired(t) {
  224. return false, fmt.Errorf("certificate is expired")
  225. }
  226. if !nc.CheckSignature(signer.Details.PublicKey) {
  227. return false, fmt.Errorf("certificate signature did not match")
  228. }
  229. if err := nc.CheckRootConstrains(signer); err != nil {
  230. return false, err
  231. }
  232. return true, nil
  233. }
  234. // CheckRootConstrains returns an error if the certificate violates constraints set on the root (groups, ips, subnets)
  235. func (nc *NebulaCertificate) CheckRootConstrains(signer *NebulaCertificate) error {
  236. // Make sure this cert wasn't valid before the root
  237. if signer.Details.NotAfter.Before(nc.Details.NotAfter) {
  238. return fmt.Errorf("certificate expires after signing certificate")
  239. }
  240. // Make sure this cert isn't valid after the root
  241. if signer.Details.NotBefore.After(nc.Details.NotBefore) {
  242. return fmt.Errorf("certificate is valid before the signing certificate")
  243. }
  244. // If the signer has a limited set of groups make sure the cert only contains a subset
  245. if len(signer.Details.InvertedGroups) > 0 {
  246. for _, g := range nc.Details.Groups {
  247. if _, ok := signer.Details.InvertedGroups[g]; !ok {
  248. return fmt.Errorf("certificate contained a group not present on the signing ca: %s", g)
  249. }
  250. }
  251. }
  252. // If the signer has a limited set of ip ranges to issue from make sure the cert only contains a subset
  253. if len(signer.Details.Ips) > 0 {
  254. for _, ip := range nc.Details.Ips {
  255. if !netMatch(ip, signer.Details.Ips) {
  256. return fmt.Errorf("certificate contained an ip assignment outside the limitations of the signing ca: %s", ip.String())
  257. }
  258. }
  259. }
  260. // If the signer has a limited set of subnet ranges to issue from make sure the cert only contains a subset
  261. if len(signer.Details.Subnets) > 0 {
  262. for _, subnet := range nc.Details.Subnets {
  263. if !netMatch(subnet, signer.Details.Subnets) {
  264. return fmt.Errorf("certificate contained a subnet assignment outside the limitations of the signing ca: %s", subnet)
  265. }
  266. }
  267. }
  268. return nil
  269. }
  270. // VerifyPrivateKey checks that the public key in the Nebula certificate and a supplied private key match
  271. func (nc *NebulaCertificate) VerifyPrivateKey(key []byte) error {
  272. var dst, key32 [32]byte
  273. copy(key32[:], key)
  274. curve25519.ScalarBaseMult(&dst, &key32)
  275. if !bytes.Equal(dst[:], nc.Details.PublicKey) {
  276. return fmt.Errorf("public key in cert and private key supplied don't match")
  277. }
  278. return nil
  279. }
  280. // String will return a pretty printed representation of a nebula cert
  281. func (nc *NebulaCertificate) String() string {
  282. if nc == nil {
  283. return "NebulaCertificate {}\n"
  284. }
  285. s := "NebulaCertificate {\n"
  286. s += "\tDetails {\n"
  287. s += fmt.Sprintf("\t\tName: %v\n", nc.Details.Name)
  288. if len(nc.Details.Ips) > 0 {
  289. s += "\t\tIps: [\n"
  290. for _, ip := range nc.Details.Ips {
  291. s += fmt.Sprintf("\t\t\t%v\n", ip.String())
  292. }
  293. s += "\t\t]\n"
  294. } else {
  295. s += "\t\tIps: []\n"
  296. }
  297. if len(nc.Details.Subnets) > 0 {
  298. s += "\t\tSubnets: [\n"
  299. for _, ip := range nc.Details.Subnets {
  300. s += fmt.Sprintf("\t\t\t%v\n", ip.String())
  301. }
  302. s += "\t\t]\n"
  303. } else {
  304. s += "\t\tSubnets: []\n"
  305. }
  306. if len(nc.Details.Groups) > 0 {
  307. s += "\t\tGroups: [\n"
  308. for _, g := range nc.Details.Groups {
  309. s += fmt.Sprintf("\t\t\t\"%v\"\n", g)
  310. }
  311. s += "\t\t]\n"
  312. } else {
  313. s += "\t\tGroups: []\n"
  314. }
  315. s += fmt.Sprintf("\t\tNot before: %v\n", nc.Details.NotBefore)
  316. s += fmt.Sprintf("\t\tNot After: %v\n", nc.Details.NotAfter)
  317. s += fmt.Sprintf("\t\tIs CA: %v\n", nc.Details.IsCA)
  318. s += fmt.Sprintf("\t\tIssuer: %s\n", nc.Details.Issuer)
  319. s += fmt.Sprintf("\t\tPublic key: %x\n", nc.Details.PublicKey)
  320. s += "\t}\n"
  321. fp, err := nc.Sha256Sum()
  322. if err == nil {
  323. s += fmt.Sprintf("\tFingerprint: %s\n", fp)
  324. }
  325. s += fmt.Sprintf("\tSignature: %x\n", nc.Signature)
  326. s += "}"
  327. return s
  328. }
  329. // getRawDetails marshals the raw details into protobuf ready struct
  330. func (nc *NebulaCertificate) getRawDetails() *RawNebulaCertificateDetails {
  331. rd := &RawNebulaCertificateDetails{
  332. Name: nc.Details.Name,
  333. Groups: nc.Details.Groups,
  334. NotBefore: nc.Details.NotBefore.Unix(),
  335. NotAfter: nc.Details.NotAfter.Unix(),
  336. PublicKey: make([]byte, len(nc.Details.PublicKey)),
  337. IsCA: nc.Details.IsCA,
  338. }
  339. for _, ipNet := range nc.Details.Ips {
  340. rd.Ips = append(rd.Ips, ip2int(ipNet.IP), ip2int(ipNet.Mask))
  341. }
  342. for _, ipNet := range nc.Details.Subnets {
  343. rd.Subnets = append(rd.Subnets, ip2int(ipNet.IP), ip2int(ipNet.Mask))
  344. }
  345. copy(rd.PublicKey, nc.Details.PublicKey[:])
  346. // I know, this is terrible
  347. rd.Issuer, _ = hex.DecodeString(nc.Details.Issuer)
  348. return rd
  349. }
  350. // Marshal will marshal a nebula cert into a protobuf byte array
  351. func (nc *NebulaCertificate) Marshal() ([]byte, error) {
  352. rc := RawNebulaCertificate{
  353. Details: nc.getRawDetails(),
  354. Signature: nc.Signature,
  355. }
  356. return proto.Marshal(&rc)
  357. }
  358. // MarshalToPEM will marshal a nebula cert into a protobuf byte array and pem encode the result
  359. func (nc *NebulaCertificate) MarshalToPEM() ([]byte, error) {
  360. b, err := nc.Marshal()
  361. if err != nil {
  362. return nil, err
  363. }
  364. return pem.EncodeToMemory(&pem.Block{Type: CertBanner, Bytes: b}), nil
  365. }
  366. // Sha256Sum calculates a sha-256 sum of the marshaled certificate
  367. func (nc *NebulaCertificate) Sha256Sum() (string, error) {
  368. b, err := nc.Marshal()
  369. if err != nil {
  370. return "", err
  371. }
  372. sum := sha256.Sum256(b)
  373. return hex.EncodeToString(sum[:]), nil
  374. }
  375. func (nc *NebulaCertificate) MarshalJSON() ([]byte, error) {
  376. toString := func(ips []*net.IPNet) []string {
  377. s := []string{}
  378. for _, ip := range ips {
  379. s = append(s, ip.String())
  380. }
  381. return s
  382. }
  383. fp, _ := nc.Sha256Sum()
  384. jc := m{
  385. "details": m{
  386. "name": nc.Details.Name,
  387. "ips": toString(nc.Details.Ips),
  388. "subnets": toString(nc.Details.Subnets),
  389. "groups": nc.Details.Groups,
  390. "notBefore": nc.Details.NotBefore,
  391. "notAfter": nc.Details.NotAfter,
  392. "publicKey": fmt.Sprintf("%x", nc.Details.PublicKey),
  393. "isCa": nc.Details.IsCA,
  394. "issuer": nc.Details.Issuer,
  395. },
  396. "fingerprint": fp,
  397. "signature": fmt.Sprintf("%x", nc.Signature),
  398. }
  399. return json.Marshal(jc)
  400. }
  401. func netMatch(certIp *net.IPNet, rootIps []*net.IPNet) bool {
  402. for _, net := range rootIps {
  403. if net.Contains(certIp.IP) && maskContains(net.Mask, certIp.Mask) {
  404. return true
  405. }
  406. }
  407. return false
  408. }
  409. func maskContains(caMask, certMask net.IPMask) bool {
  410. caM := maskTo4(caMask)
  411. cM := maskTo4(certMask)
  412. // Make sure forcing to ipv4 didn't nuke us
  413. if caM == nil || cM == nil {
  414. return false
  415. }
  416. // Make sure the cert mask is not greater than the ca mask
  417. for i := 0; i < len(caMask); i++ {
  418. if caM[i] > cM[i] {
  419. return false
  420. }
  421. }
  422. return true
  423. }
  424. func maskTo4(ip net.IPMask) net.IPMask {
  425. if len(ip) == net.IPv4len {
  426. return ip
  427. }
  428. if len(ip) == net.IPv6len && isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff {
  429. return ip[12:16]
  430. }
  431. return nil
  432. }
  433. func isZeros(b []byte) bool {
  434. for i := 0; i < len(b); i++ {
  435. if b[i] != 0 {
  436. return false
  437. }
  438. }
  439. return true
  440. }
  441. func ip2int(ip []byte) uint32 {
  442. if len(ip) == 16 {
  443. return binary.BigEndian.Uint32(ip[12:16])
  444. }
  445. return binary.BigEndian.Uint32(ip)
  446. }
  447. func int2ip(nn uint32) net.IP {
  448. ip := make(net.IP, net.IPv4len)
  449. binary.BigEndian.PutUint32(ip, nn)
  450. return ip
  451. }