Refactor how signing/encryption keys are typed and (un)marshaled
This commit is contained in:
parent
65fa208a34
commit
c645a8c767
@ -69,7 +69,7 @@ in rec {
|
||||
'';
|
||||
};
|
||||
|
||||
vendorHash = "sha256-P1TXG0fG8/6n37LmM5ApYctqoZzJFlvFAO2Zl85SVvk=";
|
||||
vendorHash = "sha256-33gwBj+6x9I/yz0Qf4G8YXRgC/HfwHCedqzrCE4FHHk=";
|
||||
|
||||
subPackages = [
|
||||
"./cmd/entrypoint"
|
||||
|
@ -78,10 +78,5 @@ type Host struct {
|
||||
// This assumes that the Host and its data has already been verified against the
|
||||
// CA signing key.
|
||||
func (h Host) IP() net.IP {
|
||||
ip, err := nebula.IPFromHostCertPEM(h.PublicCredentials.CertPEM)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not parse IP out of cert for host %q: %w", h.Name, err))
|
||||
}
|
||||
|
||||
return ip
|
||||
return h.PublicCredentials.Cert.Details.Ips[0].IP
|
||||
}
|
||||
|
@ -381,13 +381,23 @@ var subCmdAdminCreateNebulaCert = subCmd{
|
||||
return fmt.Errorf("reading public key from %q: %w", *pubKeyPath, err)
|
||||
}
|
||||
|
||||
nebulaHostCertPEM, err := nebula.NewHostCertPEM(
|
||||
adm.Nebula.CACredentials, string(hostPubPEM), *hostName, ip,
|
||||
var hostPub nebula.EncryptingPublicKey
|
||||
if err := hostPub.UnmarshalNebulaPEM(hostPubPEM); err != nil {
|
||||
return fmt.Errorf("unmarshaling public key as PEM: %w", err)
|
||||
}
|
||||
|
||||
nebulaHostCert, err := nebula.NewHostCert(
|
||||
adm.Nebula.CACredentials, hostPub, *hostName, ip,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating cert: %w", err)
|
||||
}
|
||||
|
||||
nebulaHostCertPEM, err := nebulaHostCert.MarshalToPEM()
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling cert to PEM: %w", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stdout.Write([]byte(nebulaHostCertPEM)); err != nil {
|
||||
return fmt.Errorf("writing to stdout: %w", err)
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"isle/jsonutil"
|
||||
"os"
|
||||
|
||||
"github.com/slackhq/nebula/cert"
|
||||
)
|
||||
|
||||
var subCmdNebulaShow = subCmd{
|
||||
@ -23,10 +21,10 @@ var subCmdNebulaShow = subCmd{
|
||||
return fmt.Errorf("loading host bootstrap: %w", err)
|
||||
}
|
||||
|
||||
caPublicCreds := hostBootstrap.CAPublicCredentials
|
||||
caCert, _, err := cert.UnmarshalNebulaCertificateFromPEM([]byte(caPublicCreds.CertPEM))
|
||||
caCert := hostBootstrap.CAPublicCredentials.Cert
|
||||
caCertPEM, err := caCert.MarshalToPEM()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling ca.crt: %w", err)
|
||||
return fmt.Errorf("marshaling CA cert to PEM: %w", err)
|
||||
}
|
||||
|
||||
if len(caCert.Details.Subnets) != 1 {
|
||||
@ -48,7 +46,7 @@ var subCmdNebulaShow = subCmd{
|
||||
SubnetCIDR string
|
||||
Lighthouses []outLighthouse
|
||||
}{
|
||||
CACert: caPublicCreds.CertPEM,
|
||||
CACert: string(caCertPEM),
|
||||
SubnetCIDR: subnet.String(),
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"code.betamike.com/micropelago/pmux/pmuxlib"
|
||||
"github.com/slackhq/nebula/cert"
|
||||
)
|
||||
|
||||
// waitForNebula waits for the nebula interface to have been started up. It does
|
||||
@ -56,11 +57,29 @@ func nebulaPmuxProcConfig(
|
||||
staticHostMap[ip] = []string{host.Nebula.PublicAddr}
|
||||
}
|
||||
|
||||
caCertPEM, err := hostBootstrap.CAPublicCredentials.Cert.MarshalToPEM()
|
||||
if err != nil {
|
||||
return pmuxlib.ProcessConfig{}, fmt.Errorf(
|
||||
"marshaling CA cert to PEM: :%w", err,
|
||||
)
|
||||
}
|
||||
|
||||
hostCertPEM, err := hostBootstrap.PublicCredentials.Cert.MarshalToPEM()
|
||||
if err != nil {
|
||||
return pmuxlib.ProcessConfig{}, fmt.Errorf(
|
||||
"marshaling host cert to PEM: :%w", err,
|
||||
)
|
||||
}
|
||||
|
||||
hostKeyPEM := cert.MarshalX25519PrivateKey(
|
||||
hostBootstrap.PrivateCredentials.EncryptingPrivateKey.Bytes(),
|
||||
)
|
||||
|
||||
config := map[string]interface{}{
|
||||
"pki": map[string]string{
|
||||
"ca": hostBootstrap.CAPublicCredentials.CertPEM,
|
||||
"cert": hostBootstrap.PublicCredentials.CertPEM,
|
||||
"key": hostBootstrap.PrivateCredentials.PrivateKeyPEM,
|
||||
"ca": string(caCertPEM),
|
||||
"cert": string(hostCertPEM),
|
||||
"key": string(hostKeyPEM),
|
||||
},
|
||||
"static_host_map": staticHostMap,
|
||||
"punchy": map[string]bool{
|
||||
|
@ -23,6 +23,7 @@ require (
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||
github.com/jxskiss/base62 v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.13.5 // indirect
|
||||
github.com/klauspost/cpuid v1.3.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.0 // indirect
|
||||
|
@ -24,6 +24,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
|
||||
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
|
||||
github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4=
|
||||
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
|
117
go/nebula/encrypting_key.go
Normal file
117
go/nebula/encrypting_key.go
Normal file
@ -0,0 +1,117 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/slackhq/nebula/cert"
|
||||
)
|
||||
|
||||
var (
|
||||
encPrivKeyPrefix = []byte("x0")
|
||||
encPubKeyPrefix = []byte("X0")
|
||||
|
||||
x25519 = ecdh.X25519()
|
||||
)
|
||||
|
||||
// EncryptingPublicKey wraps an X25519-based ECDH public key to provide
|
||||
// convenient text (un)marshaling methods.
|
||||
type EncryptingPublicKey struct{ inner *ecdh.PublicKey }
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (pk EncryptingPublicKey) MarshalText() ([]byte, error) {
|
||||
return encodeWithPrefix(encPubKeyPrefix, pk.inner.Bytes()), nil
|
||||
}
|
||||
|
||||
// Bytes returns the raw bytes of the EncryptingPublicKey.
|
||||
func (k EncryptingPublicKey) Bytes() []byte {
|
||||
return k.inner.Bytes()
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (pk *EncryptingPublicKey) UnmarshalText(b []byte) error {
|
||||
b, err := decodeWithPrefix(encPubKeyPrefix, b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling: %w", err)
|
||||
}
|
||||
|
||||
if pk.inner, err = x25519.NewPublicKey(b); err != nil {
|
||||
return fmt.Errorf("converting bytes to public key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalNebulaPEM unmarshals the EncryptingPublicKey as a nebula host public
|
||||
// key PEM.
|
||||
func (pk *EncryptingPublicKey) UnmarshalNebulaPEM(b []byte) error {
|
||||
b, _, err := cert.UnmarshalEd25519PublicKey(b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling: %w", err)
|
||||
}
|
||||
|
||||
if pk.inner, err = x25519.NewPublicKey(b); err != nil {
|
||||
return fmt.Errorf("converting bytes to public key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk EncryptingPublicKey) String() string {
|
||||
b, err := pk.MarshalText()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// EncryptingPrivateKey wraps an X25519-based ECDH private key to provide
|
||||
// convenient text (un)marshaling methods.
|
||||
type EncryptingPrivateKey struct{ inner *ecdh.PrivateKey }
|
||||
|
||||
// NewEncryptingPrivateKey generates and returns a fresh EncryptingPrivateKey.
|
||||
func NewEncryptingPrivateKey() EncryptingPrivateKey {
|
||||
k, err := x25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return EncryptingPrivateKey{k}
|
||||
}
|
||||
|
||||
// PublicKey returns the public key which corresponds with this private key.
|
||||
func (k EncryptingPrivateKey) PublicKey() EncryptingPublicKey {
|
||||
return EncryptingPublicKey{k.inner.PublicKey()}
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (k EncryptingPrivateKey) MarshalText() ([]byte, error) {
|
||||
return encodeWithPrefix(encPrivKeyPrefix, k.inner.Bytes()), nil
|
||||
}
|
||||
|
||||
// Bytes returns the raw bytes of the EncryptingPrivateKey.
|
||||
func (k EncryptingPrivateKey) Bytes() []byte {
|
||||
return k.inner.Bytes()
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (k *EncryptingPrivateKey) UnmarshalText(b []byte) error {
|
||||
b, err := decodeWithPrefix(encPrivKeyPrefix, b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling: %w", err)
|
||||
}
|
||||
|
||||
if k.inner, err = x25519.NewPrivateKey(b); err != nil {
|
||||
return fmt.Errorf("converting bytes to private key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k EncryptingPrivateKey) String() string {
|
||||
b, err := k.MarshalText()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/slackhq/nebula/cert"
|
||||
)
|
||||
|
||||
// SigningPrivateKey wraps an ed25519.PrivateKey to provide convenient JSON
|
||||
// (un)marshaling methods.
|
||||
type SigningPrivateKey ed25519.PrivateKey
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (k SigningPrivateKey) MarshalJSON() ([]byte, error) {
|
||||
pemStr := cert.MarshalEd25519PrivateKey(ed25519.PrivateKey(k))
|
||||
return json.Marshal(string(pemStr))
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (k *SigningPrivateKey) UnmarshalJSON(b []byte) error {
|
||||
var pemStr string
|
||||
if err := json.Unmarshal(b, &pemStr); err != nil {
|
||||
return fmt.Errorf("unmarshaling into string: %w", err)
|
||||
}
|
||||
|
||||
key, _, err := cert.UnmarshalEd25519PrivateKey([]byte(pemStr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling from PEM: %w", err)
|
||||
}
|
||||
|
||||
*k = SigningPrivateKey(key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SigningPublicKey wraps an ed25519.PublicKey to provide convenient JSON
|
||||
// (un)marshaling methods.
|
||||
type SigningPublicKey ed25519.PublicKey
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (k SigningPublicKey) MarshalJSON() ([]byte, error) {
|
||||
pemStr := cert.MarshalEd25519PublicKey(ed25519.PublicKey(k))
|
||||
return json.Marshal(string(pemStr))
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Unmarshaler interface.
|
||||
func (k *SigningPublicKey) UnmarshalJSON(b []byte) error {
|
||||
var pemStr string
|
||||
if err := json.Unmarshal(b, &pemStr); err != nil {
|
||||
return fmt.Errorf("unmarshaling into string: %w", err)
|
||||
}
|
||||
|
||||
key, _, err := cert.UnmarshalEd25519PublicKey([]byte(pemStr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling from PEM: %w", err)
|
||||
}
|
||||
|
||||
*k = SigningPublicKey(key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateSigningPair generates and returns a new key pair which can be used
|
||||
// for signing arbitrary blobs of bytes.
|
||||
func GenerateSigningPair() (SigningPublicKey, SigningPrivateKey) {
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("generating ed25519 key: %w", err))
|
||||
}
|
||||
return SigningPublicKey(pub), SigningPrivateKey(priv)
|
||||
}
|
@ -3,35 +3,32 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/slackhq/nebula/cert"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
// HostPublicCredentials contains certificate and signing public keys which are
|
||||
// able to be broadcast publicly.
|
||||
type HostPublicCredentials struct {
|
||||
CertPEM string
|
||||
Cert cert.NebulaCertificate
|
||||
SigningKey SigningPublicKey
|
||||
}
|
||||
|
||||
// HostPrivateCredentials contains the private key files which will
|
||||
// need to be present on a particular host.
|
||||
type HostPrivateCredentials struct {
|
||||
PrivateKeyPEM string
|
||||
SigningPrivateKey SigningPrivateKey
|
||||
EncryptingPrivateKey EncryptingPrivateKey
|
||||
SigningPrivateKey SigningPrivateKey
|
||||
}
|
||||
|
||||
// CAPublicCredentials contains certificate and signing public keys which are
|
||||
// able to be broadcast publicly. The signing public key is the same one which
|
||||
// is embedded into the certificate.
|
||||
type CAPublicCredentials struct {
|
||||
CertPEM string
|
||||
Cert cert.NebulaCertificate
|
||||
SigningKey SigningPublicKey
|
||||
}
|
||||
|
||||
@ -42,33 +39,28 @@ type CACredentials struct {
|
||||
SigningPrivateKey SigningPrivateKey
|
||||
}
|
||||
|
||||
// NewHostCertPEM generates and signs a new host certificate containing the
|
||||
// given public key.
|
||||
func NewHostCertPEM(
|
||||
caCreds CACredentials, hostPubPEM string, hostName string, ip net.IP,
|
||||
// NewHostCert generates and signs a new host certificate containing the given
|
||||
// public key.
|
||||
func NewHostCert(
|
||||
caCreds CACredentials,
|
||||
hostPub EncryptingPublicKey,
|
||||
hostName string,
|
||||
ip net.IP,
|
||||
) (
|
||||
string, error,
|
||||
cert.NebulaCertificate, error,
|
||||
) {
|
||||
hostPub, _, err := cert.UnmarshalX25519PublicKey([]byte(hostPubPEM))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unmarshaling public key PEM: %w", err)
|
||||
}
|
||||
|
||||
caCert, _, err := cert.UnmarshalNebulaCertificateFromPEM([]byte(caCreds.Public.CertPEM))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unmarshaling ca.crt: %w", err)
|
||||
}
|
||||
caCert := caCreds.Public.Cert
|
||||
|
||||
issuer, err := caCert.Sha256Sum()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting ca.crt issuer: %w", err)
|
||||
return cert.NebulaCertificate{}, fmt.Errorf("getting ca.crt issuer: %w", err)
|
||||
}
|
||||
|
||||
expireAt := caCert.Details.NotAfter.Add(-1 * time.Second)
|
||||
|
||||
subnet := caCert.Details.Subnets[0]
|
||||
if !subnet.Contains(ip) {
|
||||
return "", fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet)
|
||||
return cert.NebulaCertificate{}, fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet)
|
||||
}
|
||||
|
||||
hostCert := cert.NebulaCertificate{
|
||||
@ -80,26 +72,21 @@ func NewHostCertPEM(
|
||||
}},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: expireAt,
|
||||
PublicKey: hostPub,
|
||||
PublicKey: hostPub.Bytes(),
|
||||
IsCA: false,
|
||||
Issuer: issuer,
|
||||
},
|
||||
}
|
||||
|
||||
if err := hostCert.CheckRootConstrains(caCert); err != nil {
|
||||
return "", fmt.Errorf("validating certificate constraints: %w", err)
|
||||
if err := hostCert.CheckRootConstrains(&caCert); err != nil {
|
||||
return cert.NebulaCertificate{}, fmt.Errorf("validating certificate constraints: %w", err)
|
||||
}
|
||||
|
||||
if err := signCert(&hostCert, caCreds.SigningPrivateKey); err != nil {
|
||||
return "", fmt.Errorf("signing host cert with ca.key: %w", err)
|
||||
return cert.NebulaCertificate{}, fmt.Errorf("signing host cert with ca.key: %w", err)
|
||||
}
|
||||
|
||||
hostCertPEM, err := hostCert.MarshalToPEM()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshalling host.crt: %w", err)
|
||||
}
|
||||
|
||||
return string(hostCertPEM), nil
|
||||
return hostCert, nil
|
||||
}
|
||||
|
||||
// NewHostCredentials generates a new key/cert for a nebula host using the CA
|
||||
@ -110,39 +97,27 @@ func NewHostCredentials(
|
||||
pub HostPublicCredentials, priv HostPrivateCredentials, err error,
|
||||
) {
|
||||
|
||||
// The logic here is largely based on
|
||||
// https://github.com/slackhq/nebula/blob/v1.4.0/cmd/nebula-cert/sign.go
|
||||
var (
|
||||
encPrivKey = NewEncryptingPrivateKey()
|
||||
encPubKey = encPrivKey.PublicKey()
|
||||
|
||||
var hostPub, hostKey []byte
|
||||
{
|
||||
var pubkey, privkey [32]byte
|
||||
if _, err = io.ReadFull(rand.Reader, privkey[:]); err != nil {
|
||||
err = fmt.Errorf("reading random bytes to form private key: %w", err)
|
||||
return
|
||||
}
|
||||
curve25519.ScalarBaseMult(&pubkey, &privkey)
|
||||
hostPub, hostKey = pubkey[:], privkey[:]
|
||||
}
|
||||
signingPubKey, signingPrivKey = GenerateSigningPair()
|
||||
)
|
||||
|
||||
signingPubKey, signingPrivKey := GenerateSigningPair()
|
||||
|
||||
hostPubPEM := cert.MarshalX25519PublicKey(hostPub)
|
||||
hostKeyPEM := cert.MarshalX25519PrivateKey(hostKey)
|
||||
|
||||
hostCertPEM, err := NewHostCertPEM(caCreds, string(hostPubPEM), hostName, ip)
|
||||
hostCert, err := NewHostCert(caCreds, encPubKey, hostName, ip)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("creating host certificate: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
pub = HostPublicCredentials{
|
||||
CertPEM: hostCertPEM,
|
||||
Cert: hostCert,
|
||||
SigningKey: signingPubKey,
|
||||
}
|
||||
|
||||
priv = HostPrivateCredentials{
|
||||
PrivateKeyPEM: string(hostKeyPEM),
|
||||
SigningPrivateKey: signingPrivKey,
|
||||
EncryptingPrivateKey: encPrivKey,
|
||||
SigningPrivateKey: signingPrivKey,
|
||||
}
|
||||
|
||||
return
|
||||
@ -151,14 +126,11 @@ func NewHostCredentials(
|
||||
// NewCACredentials generates a CACredentials. The domain should be the network's root domain,
|
||||
// and is included in the signing certificate's Name field.
|
||||
func NewCACredentials(domain string, subnet *net.IPNet) (CACredentials, error) {
|
||||
|
||||
// The logic here is largely based on
|
||||
// https://github.com/slackhq/nebula/blob/v1.4.0/cmd/nebula-cert/ca.go
|
||||
|
||||
signingPubKey, signingPrivKey := GenerateSigningPair()
|
||||
|
||||
now := time.Now()
|
||||
expireAt := now.Add(2 * 365 * 24 * time.Hour)
|
||||
var (
|
||||
signingPubKey, signingPrivKey = GenerateSigningPair()
|
||||
now = time.Now()
|
||||
expireAt = now.Add(2 * 365 * 24 * time.Hour)
|
||||
)
|
||||
|
||||
caCert := cert.NebulaCertificate{
|
||||
Details: cert.NebulaCertificateDetails{
|
||||
@ -175,33 +147,11 @@ func NewCACredentials(domain string, subnet *net.IPNet) (CACredentials, error) {
|
||||
return CACredentials{}, fmt.Errorf("signing caCert: %w", err)
|
||||
}
|
||||
|
||||
certPEM, err := caCert.MarshalToPEM()
|
||||
if err != nil {
|
||||
return CACredentials{}, fmt.Errorf("marshaling caCert: %w", err)
|
||||
}
|
||||
|
||||
return CACredentials{
|
||||
Public: CAPublicCredentials{
|
||||
CertPEM: string(certPEM),
|
||||
Cert: caCert,
|
||||
SigningKey: signingPubKey,
|
||||
},
|
||||
SigningPrivateKey: signingPrivKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IPFromHostCertPEM is a convenience function for parsing the IP of a host out
|
||||
// of its nebula cert.
|
||||
func IPFromHostCertPEM(hostCertPEM string) (net.IP, error) {
|
||||
|
||||
hostCert, _, err := cert.UnmarshalNebulaCertificateFromPEM([]byte(hostCertPEM))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling host certificate as PEM: %w", err)
|
||||
}
|
||||
|
||||
ips := hostCert.Details.Ips
|
||||
if len(ips) == 0 {
|
||||
return nil, fmt.Errorf("malformed nebula host cert: no IPs")
|
||||
}
|
||||
|
||||
return ips[0].IP, nil
|
||||
}
|
||||
|
78
go/nebula/signing_key.go
Normal file
78
go/nebula/signing_key.go
Normal file
@ -0,0 +1,78 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
sigPrivKeyPrefix = []byte("s0")
|
||||
sigPubKeyPrefix = []byte("S0")
|
||||
)
|
||||
|
||||
// SigningPrivateKey wraps an ed25519.PrivateKey to provide convenient text
|
||||
// (un)marshaling methods.
|
||||
type SigningPrivateKey ed25519.PrivateKey
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (k SigningPrivateKey) MarshalText() ([]byte, error) {
|
||||
return encodeWithPrefix(sigPrivKeyPrefix, k), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (k *SigningPrivateKey) UnmarshalText(b []byte) error {
|
||||
b, err := decodeWithPrefix(sigPrivKeyPrefix, b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling: %w", err)
|
||||
}
|
||||
|
||||
*k = SigningPrivateKey(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k SigningPrivateKey) String() string {
|
||||
b, err := k.MarshalText()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// SigningPublicKey wraps an ed25519.PublicKey to provide convenient text
|
||||
// (un)marshaling methods.
|
||||
type SigningPublicKey ed25519.PublicKey
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (pk SigningPublicKey) MarshalText() ([]byte, error) {
|
||||
return encodeWithPrefix(sigPubKeyPrefix, pk), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (pk *SigningPublicKey) UnmarshalText(b []byte) error {
|
||||
b, err := decodeWithPrefix(sigPubKeyPrefix, b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling: %w", err)
|
||||
}
|
||||
|
||||
*pk = SigningPublicKey(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk SigningPublicKey) String() string {
|
||||
b, err := pk.MarshalText()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// GenerateSigningPair generates and returns a new key pair which can be used
|
||||
// for signing arbitrary blobs of bytes.
|
||||
func GenerateSigningPair() (SigningPublicKey, SigningPrivateKey) {
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("generating ed25519 key: %w", err))
|
||||
}
|
||||
return SigningPublicKey(pub), SigningPrivateKey(priv)
|
||||
}
|
25
go/nebula/util.go
Normal file
25
go/nebula/util.go
Normal file
@ -0,0 +1,25 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jxskiss/base62"
|
||||
)
|
||||
|
||||
func encodeWithPrefix(prefix []byte, b []byte) []byte {
|
||||
res := make([]byte, 0, len(prefix)+len(b)*4)
|
||||
res = append(res, prefix...)
|
||||
res = base62.EncodeToBuf(res, b)
|
||||
return res
|
||||
}
|
||||
|
||||
func decodeWithPrefix(prefix []byte, b []byte) ([]byte, error) {
|
||||
if len(b) < len(prefix) {
|
||||
return nil, errors.New("input is too short")
|
||||
} else if !bytes.HasPrefix(b, prefix) {
|
||||
return nil, fmt.Errorf("missing expected prefix %q", prefix)
|
||||
}
|
||||
return base62.Decode(b[len(prefix):])
|
||||
}
|
Loading…
Reference in New Issue
Block a user