Signed-off-by: Tom DNetto <tom@tailscale.com>main
parent
c13fab2a67
commit
8cfd775885
@ -0,0 +1,99 @@ |
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tka |
||||
|
||||
import ( |
||||
"bytes" |
||||
"crypto/ed25519" |
||||
"errors" |
||||
"fmt" |
||||
|
||||
"github.com/fxamacker/cbor/v2" |
||||
"github.com/hdevalence/ed25519consensus" |
||||
"golang.org/x/crypto/blake2s" |
||||
) |
||||
|
||||
// SigKind describes valid NodeKeySignature types.
|
||||
type SigKind uint8 |
||||
|
||||
const ( |
||||
SigInvalid SigKind = iota |
||||
// SigDirect describes a signature over a specific node key, using
|
||||
// the keyID specified.
|
||||
SigDirect |
||||
) |
||||
|
||||
func (s SigKind) String() string { |
||||
switch s { |
||||
case SigInvalid: |
||||
return "invalid" |
||||
case SigDirect: |
||||
return "direct" |
||||
default: |
||||
return fmt.Sprintf("Sig?<%d>", int(s)) |
||||
} |
||||
} |
||||
|
||||
// NodeKeySignature encapsulates a signature that authorizes a specific
|
||||
// node key, based on verification from keys in the tailnet key authority.
|
||||
type NodeKeySignature struct { |
||||
// SigKind identifies the variety of signature.
|
||||
SigKind SigKind `cbor:"1,keyasint"` |
||||
// Pubkey identifies the public key which is being certified.
|
||||
Pubkey []byte `cbor:"2,keyasint"` |
||||
|
||||
// KeyID identifies which key in the tailnet key authority should
|
||||
// be used to verify this signature. Only set for SigDirect and
|
||||
// SigCredential signature kinds.
|
||||
KeyID []byte `cbor:"3,keyasint,omitempty"` |
||||
|
||||
// Signature is the packed (R, S) ed25519 signature over the rest
|
||||
// of the structure.
|
||||
Signature []byte `cbor:"4,keyasint,omitempty"` |
||||
} |
||||
|
||||
// sigHash returns the cryptographic digest which a signature
|
||||
// is over.
|
||||
//
|
||||
// This is a hash of the serialized structure, sans the signature.
|
||||
// Without this exclusion, the hash used for the signature
|
||||
// would be circularly dependent on the signature.
|
||||
func (s NodeKeySignature) sigHash() [blake2s.Size]byte { |
||||
dupe := s |
||||
dupe.Signature = nil |
||||
return blake2s.Sum256(dupe.Serialize()) |
||||
} |
||||
|
||||
// Serialize returns the given NKS in a serialized format.
|
||||
func (s *NodeKeySignature) Serialize() []byte { |
||||
out := bytes.NewBuffer(make([]byte, 0, 128)) // 64byte sig + 32byte keyID + 32byte headroom
|
||||
encoder, err := cbor.CTAP2EncOptions().EncMode() |
||||
if err != nil { |
||||
// Deterministic validation of encoding options, should
|
||||
// never fail.
|
||||
panic(err) |
||||
} |
||||
if err := encoder.NewEncoder(out).Encode(s); err != nil { |
||||
// Writing to a bytes.Buffer should never fail.
|
||||
panic(err) |
||||
} |
||||
return out.Bytes() |
||||
} |
||||
|
||||
// verifySignature checks that the NodeKeySignature is authentic and certified
|
||||
// by the given verificationKey.
|
||||
func (s *NodeKeySignature) verifySignature(verificationKey Key) error { |
||||
sigHash := s.sigHash() |
||||
switch verificationKey.Kind { |
||||
case Key25519: |
||||
if ed25519consensus.Verify(ed25519.PublicKey(verificationKey.Public), sigHash[:], s.Signature) { |
||||
return nil |
||||
} |
||||
return errors.New("invalid signature") |
||||
|
||||
default: |
||||
return fmt.Errorf("unhandled key type: %v", verificationKey.Kind) |
||||
} |
||||
} |
||||
@ -0,0 +1,34 @@ |
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tka |
||||
|
||||
import ( |
||||
"crypto/ed25519" |
||||
"testing" |
||||
) |
||||
|
||||
func TestSigDirect(t *testing.T) { |
||||
nodeKeyPub := []byte{1, 2, 3, 4} |
||||
|
||||
// Verification key (the key used to sign)
|
||||
pub, priv := testingKey25519(t, 1) |
||||
key := Key{Kind: Key25519, Public: pub, Votes: 2} |
||||
|
||||
sig := NodeKeySignature{ |
||||
SigKind: SigDirect, |
||||
KeyID: key.ID(), |
||||
Pubkey: nodeKeyPub, |
||||
} |
||||
sigHash := sig.sigHash() |
||||
sig.Signature = ed25519.Sign(priv, sigHash[:]) |
||||
|
||||
if sig.sigHash() != sigHash { |
||||
t.Errorf("sigHash changed after signing: %x != %x", sig.sigHash(), sigHash) |
||||
} |
||||
|
||||
if err := sig.verifySignature(key); err != nil { |
||||
t.Fatalf("verifySignature() failed: %v", err) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue