Signed-off-by: Tom DNetto <tom@tailscale.com>main
parent
7a74466998
commit
023d4e2216
@ -0,0 +1,74 @@ |
||||
// 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 key |
||||
|
||||
import ( |
||||
"crypto/ed25519" |
||||
|
||||
"go4.org/mem" |
||||
"tailscale.com/tka" |
||||
"tailscale.com/types/structs" |
||||
) |
||||
|
||||
const ( |
||||
// nlPrivateHexPrefix is the prefix used to identify a
|
||||
// hex-encoded network-lock key.
|
||||
nlPrivateHexPrefix = "nlpriv:" |
||||
) |
||||
|
||||
// NLPrivate is a node-managed network-lock key, used for signing
|
||||
// node-key signatures and authority update messages.
|
||||
type NLPrivate struct { |
||||
_ structs.Incomparable // because == isn't constant-time
|
||||
k [ed25519.PrivateKeySize]byte |
||||
} |
||||
|
||||
// NewNLPrivate creates and returns a new network-lock key.
|
||||
func NewNLPrivate() NLPrivate { |
||||
// ed25519.GenerateKey 'clamps' the key, not that it
|
||||
// matters given we don't do Diffie-Hellman.
|
||||
_, priv, err := ed25519.GenerateKey(nil) // nil == crypto/rand
|
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
var out NLPrivate |
||||
copy(out.k[:], priv) |
||||
return out |
||||
} |
||||
|
||||
// MarshalText implements encoding.TextUnmarshaler.
|
||||
func (k *NLPrivate) UnmarshalText(b []byte) error { |
||||
return parseHex(k.k[:], mem.B(b), mem.S(nlPrivateHexPrefix)) |
||||
} |
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (k NLPrivate) MarshalText() ([]byte, error) { |
||||
return toHex(k.k[:], nlPrivateHexPrefix), nil |
||||
} |
||||
|
||||
// Public returns the public component of this key.
|
||||
func (k NLPrivate) Public() ed25519.PublicKey { |
||||
return ed25519.PrivateKey(k.k[:]).Public().(ed25519.PublicKey) |
||||
} |
||||
|
||||
// KeyID returns an identifier for this key.
|
||||
func (k NLPrivate) KeyID() tka.KeyID { |
||||
return tka.Key{ |
||||
Kind: tka.Key25519, |
||||
Public: k.Public(), |
||||
}.ID() |
||||
} |
||||
|
||||
// SignAUM implements tka.UpdateSigner.
|
||||
func (k NLPrivate) SignAUM(a *tka.AUM) error { |
||||
sigHash := a.SigHash() |
||||
|
||||
a.Signatures = append(a.Signatures, tka.Signature{ |
||||
KeyID: k.KeyID(), |
||||
Signature: ed25519.Sign(k.k[:], sigHash[:]), |
||||
}) |
||||
return nil |
||||
} |
||||
@ -0,0 +1,47 @@ |
||||
// 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 key |
||||
|
||||
import ( |
||||
"bytes" |
||||
"testing" |
||||
|
||||
"tailscale.com/tka" |
||||
) |
||||
|
||||
func TestNLPrivate(t *testing.T) { |
||||
p := NewNLPrivate() |
||||
|
||||
encoded, err := p.MarshalText() |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
var decoded NLPrivate |
||||
if err := decoded.UnmarshalText(encoded); err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
if !bytes.Equal(decoded.k[:], p.k[:]) { |
||||
t.Error("decoded and generated NLPrivate bytes differ") |
||||
} |
||||
|
||||
// Test that NLPrivate implements tka.Signer by making a new
|
||||
// authority.
|
||||
k := tka.Key{Kind: tka.Key25519, Public: p.Public(), Votes: 1} |
||||
_, aum, err := tka.Create(&tka.Mem{}, tka.State{ |
||||
Keys: []tka.Key{k}, |
||||
DisablementSecrets: [][]byte{bytes.Repeat([]byte{1}, 32)}, |
||||
}, p) |
||||
if err != nil { |
||||
t.Fatalf("tka.Create() failed: %v", err) |
||||
} |
||||
|
||||
// Make sure the generated genesis AUM was signed.
|
||||
if got, want := len(aum.Signatures), 1; got != want { |
||||
t.Fatalf("len(signatures) = %d, want %d", got, want) |
||||
} |
||||
if err := aum.Signatures[0].Verify(aum.SigHash(), k); err != nil { |
||||
t.Errorf("signature did not verify: %v", err) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue