Skip to content

Commit

Permalink
Add in v2 certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
nbrownus committed Sep 13, 2024
1 parent 2626ff9 commit e5f059f
Show file tree
Hide file tree
Showing 14 changed files with 1,072 additions and 267 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ bench-cpu-long:
go test -bench=. -benchtime=60s -cpuprofile=cpu.pprof
go tool pprof go-audit.test cpu.pprof

proto: nebula.pb.go cert/cert.pb.go
proto: nebula.pb.go cert/cert_v1.pb.go

nebula.pb.go: nebula.proto .FORCE
go build github.com/gogo/protobuf/protoc-gen-gogofaster
Expand Down
52 changes: 52 additions & 0 deletions cert/asn1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cert

import (
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
)

// readOptionalASN1Boolean reads an asn.1 boolean with a specific tag instead of a asn.1 tag wrapping a boolean with a value
// https://github.com/golang/go/issues/64811#issuecomment-1944446920
func readOptionalASN1Boolean(b *cryptobyte.String, out *bool, tag asn1.Tag, defaultValue bool) bool {
var present bool
var child cryptobyte.String
if !b.ReadOptionalASN1(&child, &present, tag) {
return false
}

if !present {
*out = defaultValue
return true
}

// Ensure we have 1 byte
if len(child) == 1 {
*out = child[0] > 0
return true
}

return false
}

// readOptionalASN1Byte reads an asn.1 uint8 with a specific tag instead of a asn.1 tag wrapping a uint8 with a value
// Similar issue as with readOptionalASN1Boolean
func readOptionalASN1Byte(b *cryptobyte.String, out *byte, tag asn1.Tag, defaultValue byte) bool {
var present bool
var child cryptobyte.String
if !b.ReadOptionalASN1(&child, &present, tag) {
return false
}

if !present {
*out = defaultValue
return true
}

// Ensure we have 1 byte
if len(child) == 1 {
*out = child[0]
return true
}

return false
}
63 changes: 53 additions & 10 deletions cert/cert.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package cert

import (
"fmt"
"net/netip"
"time"
)

type Version int
type Version uint32

const (
Version1 Version = 1
Version2 Version = 2
VersionPre1 Version = 0
Version1 Version = 1
Version2 Version = 2
)

type Certificate interface {
Expand Down Expand Up @@ -109,21 +111,62 @@ type CachedCertificate struct {

// UnmarshalCertificate will attempt to unmarshal a wire protocol level certificate.
func UnmarshalCertificate(b []byte) (Certificate, error) {
c, err := unmarshalCertificateV1(b, true)
if err != nil {
return nil, err
//TODO: you left off here, no one uses this function but it might be beneficial to export _something_ that someone can use, maybe the Versioned unmarshallsers?
var c Certificate
c, err := unmarshalCertificateV2(b, nil)
if err == nil {
return c, nil
}
return c, nil

c, err = unmarshalCertificateV1(b, nil)
if err == nil {
return c, nil
}

return nil, fmt.Errorf("could not unmarshal certificate")
}

// UnmarshalCertificateFromHandshake will attempt to unmarshal a certificate received in a handshake.
// Handshakes save space by placing the peers public key in a different part of the packet, we have to
// reassemble the actual certificate structure with that in mind.
func UnmarshalCertificateFromHandshake(b []byte, publicKey []byte) (Certificate, error) {
c, err := unmarshalCertificateV1(b, false)
func UnmarshalCertificateFromHandshake(v Version, b []byte, publicKey []byte) (Certificate, error) {
var c Certificate
var err error

switch v {
case VersionPre1, Version1:
c, err = unmarshalCertificateV1(b, publicKey)
case Version2:
c, err = unmarshalCertificateV2(b, publicKey)
default:
//TODO: make a static var
return nil, fmt.Errorf("unknown certificate version %d", v)
}

if err != nil {
return nil, err
}
c.details.PublicKey = publicKey
return c, nil
}

func RecombineAndValidate(v Version, rawCertBytes, publicKey []byte, caPool *CAPool) (*CachedCertificate, error) {
if publicKey == nil {
return nil, ErrNoPeerStaticKey
}

if rawCertBytes == nil {
return nil, ErrNoPayload
}

c, err := UnmarshalCertificateFromHandshake(v, rawCertBytes, publicKey)
if err != nil {
return nil, fmt.Errorf("error unmarshaling cert: %w", err)
}

cc, err := caPool.VerifyCertificate(time.Now(), c)
if err != nil {
return nil, fmt.Errorf("certificate validation failed: %w", err)
}

return cc, nil
}
4 changes: 2 additions & 2 deletions cert/cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestMarshalingNebulaCertificate(t *testing.T) {
assert.Nil(t, err)
//t.Log("Cert size:", len(b))

nc2, err := unmarshalCertificateV1(b, true)
nc2, err := unmarshalCertificateV1(b, nil)
assert.Nil(t, err)

assert.Equal(t, nc.signature, nc2.Signature())
Expand Down Expand Up @@ -534,7 +534,7 @@ func TestNebulaCertificate_Copy(t *testing.T) {
func TestUnmarshalNebulaCertificate(t *testing.T) {
// Test that we don't panic with an invalid certificate (#332)
data := []byte("\x98\x00\x00")
_, err := unmarshalCertificateV1(data, true)
_, err := unmarshalCertificateV1(data, nil)
assert.EqualError(t, err, "encoded Details was nil")
}

Expand Down
Loading

0 comments on commit e5f059f

Please sign in to comment.