cert.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. package cert
  2. import (
  3. "crypto"
  4. "crypto/rand"
  5. "crypto/sha256"
  6. "encoding/binary"
  7. "encoding/hex"
  8. "encoding/pem"
  9. "fmt"
  10. "net"
  11. "time"
  12. "bytes"
  13. "encoding/json"
  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 the signer has a limited set of groups make sure the cert only contains a subset
  230. if len(signer.Details.InvertedGroups) > 0 {
  231. for _, g := range nc.Details.Groups {
  232. if _, ok := signer.Details.InvertedGroups[g]; !ok {
  233. return false, fmt.Errorf("certificate contained a group not present on the signing ca; %s", g)
  234. }
  235. }
  236. }
  237. return true, nil
  238. }
  239. // VerifyPrivateKey checks that the public key in the Nebula certificate and a supplied private key match
  240. func (nc *NebulaCertificate) VerifyPrivateKey(key []byte) error {
  241. var dst, key32 [32]byte
  242. copy(key32[:], key)
  243. curve25519.ScalarBaseMult(&dst, &key32)
  244. if !bytes.Equal(dst[:], nc.Details.PublicKey) {
  245. return fmt.Errorf("public key in cert and private key supplied don't match")
  246. }
  247. return nil
  248. }
  249. // String will return a pretty printed representation of a nebula cert
  250. func (nc *NebulaCertificate) String() string {
  251. if nc == nil {
  252. return "NebulaCertificate {}\n"
  253. }
  254. s := "NebulaCertificate {\n"
  255. s += "\tDetails {\n"
  256. s += fmt.Sprintf("\t\tName: %v\n", nc.Details.Name)
  257. if len(nc.Details.Ips) > 0 {
  258. s += "\t\tIps: [\n"
  259. for _, ip := range nc.Details.Ips {
  260. s += fmt.Sprintf("\t\t\t%v\n", ip.String())
  261. }
  262. s += "\t\t]\n"
  263. } else {
  264. s += "\t\tIps: []\n"
  265. }
  266. if len(nc.Details.Subnets) > 0 {
  267. s += "\t\tSubnets: [\n"
  268. for _, ip := range nc.Details.Subnets {
  269. s += fmt.Sprintf("\t\t\t%v\n", ip.String())
  270. }
  271. s += "\t\t]\n"
  272. } else {
  273. s += "\t\tSubnets: []\n"
  274. }
  275. if len(nc.Details.Groups) > 0 {
  276. s += "\t\tGroups: [\n"
  277. for _, g := range nc.Details.Groups {
  278. s += fmt.Sprintf("\t\t\t\"%v\"\n", g)
  279. }
  280. s += "\t\t]\n"
  281. } else {
  282. s += "\t\tGroups: []\n"
  283. }
  284. s += fmt.Sprintf("\t\tNot before: %v\n", nc.Details.NotBefore)
  285. s += fmt.Sprintf("\t\tNot After: %v\n", nc.Details.NotAfter)
  286. s += fmt.Sprintf("\t\tIs CA: %v\n", nc.Details.IsCA)
  287. s += fmt.Sprintf("\t\tIssuer: %s\n", nc.Details.Issuer)
  288. s += fmt.Sprintf("\t\tPublic key: %x\n", nc.Details.PublicKey)
  289. s += "\t}\n"
  290. fp, err := nc.Sha256Sum()
  291. if err == nil {
  292. s += fmt.Sprintf("\tFingerprint: %s\n", fp)
  293. }
  294. s += fmt.Sprintf("\tSignature: %x\n", nc.Signature)
  295. s += "}"
  296. return s
  297. }
  298. // getRawDetails marshals the raw details into protobuf ready struct
  299. func (nc *NebulaCertificate) getRawDetails() *RawNebulaCertificateDetails {
  300. rd := &RawNebulaCertificateDetails{
  301. Name: nc.Details.Name,
  302. Groups: nc.Details.Groups,
  303. NotBefore: nc.Details.NotBefore.Unix(),
  304. NotAfter: nc.Details.NotAfter.Unix(),
  305. PublicKey: make([]byte, len(nc.Details.PublicKey)),
  306. IsCA: nc.Details.IsCA,
  307. }
  308. for _, ipNet := range nc.Details.Ips {
  309. rd.Ips = append(rd.Ips, ip2int(ipNet.IP), ip2int(ipNet.Mask))
  310. }
  311. for _, ipNet := range nc.Details.Subnets {
  312. rd.Subnets = append(rd.Subnets, ip2int(ipNet.IP), ip2int(ipNet.Mask))
  313. }
  314. copy(rd.PublicKey, nc.Details.PublicKey[:])
  315. // I know, this is terrible
  316. rd.Issuer, _ = hex.DecodeString(nc.Details.Issuer)
  317. return rd
  318. }
  319. // Marshal will marshal a nebula cert into a protobuf byte array
  320. func (nc *NebulaCertificate) Marshal() ([]byte, error) {
  321. rc := RawNebulaCertificate{
  322. Details: nc.getRawDetails(),
  323. Signature: nc.Signature,
  324. }
  325. return proto.Marshal(&rc)
  326. }
  327. // MarshalToPEM will marshal a nebula cert into a protobuf byte array and pem encode the result
  328. func (nc *NebulaCertificate) MarshalToPEM() ([]byte, error) {
  329. b, err := nc.Marshal()
  330. if err != nil {
  331. return nil, err
  332. }
  333. return pem.EncodeToMemory(&pem.Block{Type: CertBanner, Bytes: b}), nil
  334. }
  335. // Sha256Sum calculates a sha-256 sum of the marshaled certificate
  336. func (nc *NebulaCertificate) Sha256Sum() (string, error) {
  337. b, err := nc.Marshal()
  338. if err != nil {
  339. return "", err
  340. }
  341. sum := sha256.Sum256(b)
  342. return hex.EncodeToString(sum[:]), nil
  343. }
  344. func (nc *NebulaCertificate) MarshalJSON() ([]byte, error) {
  345. toString := func(ips []*net.IPNet) []string {
  346. s := []string{}
  347. for _, ip := range ips {
  348. s = append(s, ip.String())
  349. }
  350. return s
  351. }
  352. fp, _ := nc.Sha256Sum()
  353. jc := m{
  354. "details": m{
  355. "name": nc.Details.Name,
  356. "ips": toString(nc.Details.Ips),
  357. "subnets": toString(nc.Details.Subnets),
  358. "groups": nc.Details.Groups,
  359. "notBefore": nc.Details.NotBefore,
  360. "notAfter": nc.Details.NotAfter,
  361. "publicKey": fmt.Sprintf("%x", nc.Details.PublicKey),
  362. "isCa": nc.Details.IsCA,
  363. "issuer": nc.Details.Issuer,
  364. },
  365. "fingerprint": fp,
  366. "signature": fmt.Sprintf("%x", nc.Signature),
  367. }
  368. return json.Marshal(jc)
  369. }
  370. func ip2int(ip []byte) uint32 {
  371. if len(ip) == 16 {
  372. return binary.BigEndian.Uint32(ip[12:16])
  373. }
  374. return binary.BigEndian.Uint32(ip)
  375. }
  376. func int2ip(nn uint32) net.IP {
  377. ip := make(net.IP, net.IPv4len)
  378. binary.BigEndian.PutUint32(ip, nn)
  379. return ip
  380. }