104 lines
2.5 KiB
Go
104 lines
2.5 KiB
Go
|
package nebula
|
||
|
|
||
|
import (
|
||
|
"crypto"
|
||
|
"crypto/ed25519"
|
||
|
"crypto/rand"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/slackhq/nebula/cert"
|
||
|
)
|
||
|
|
||
|
// ErrInvalidSignature is returned from functions when a signature validation
|
||
|
// fails.
|
||
|
var ErrInvalidSignature = errors.New("invalid signature")
|
||
|
|
||
|
func signCert(c *cert.NebulaCertificate, k SigningPrivateKey) error {
|
||
|
return c.Sign(ed25519.PrivateKey(k))
|
||
|
}
|
||
|
|
||
|
// Signed wraps an arbitrary value with a signature which was generated using a
|
||
|
// SigningPrivateKey. It can be JSON (un)marshaled while preserving all of its
|
||
|
// properties.
|
||
|
type Signed[T any] json.RawMessage
|
||
|
|
||
|
type signed[T any] struct {
|
||
|
Signature []byte
|
||
|
Body json.RawMessage
|
||
|
}
|
||
|
|
||
|
// Sign will generate a Signed of the given value by first JSON marshaling it,
|
||
|
// and then signing the resulting bytes.
|
||
|
func Sign[T any](v T, k SigningPrivateKey) (Signed[T], error) {
|
||
|
var res Signed[T]
|
||
|
|
||
|
b, err := json.Marshal(v)
|
||
|
if err != nil {
|
||
|
return res, fmt.Errorf("json marshaling: %w", err)
|
||
|
}
|
||
|
|
||
|
sig, err := ed25519.PrivateKey(k).Sign(rand.Reader, b, crypto.Hash(0))
|
||
|
if err != nil {
|
||
|
return res, fmt.Errorf("generating signature: %w", err)
|
||
|
}
|
||
|
|
||
|
return json.Marshal(signed[T]{Signature: sig, Body: json.RawMessage(b)})
|
||
|
}
|
||
|
|
||
|
// MarshalJSON implements the json.Marshaler interface.
|
||
|
func (s Signed[T]) MarshalJSON() ([]byte, error) {
|
||
|
return []byte(s), nil
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||
|
func (s *Signed[T]) UnmarshalJSON(b []byte) error {
|
||
|
*s = b
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Unwrap produces the original value which Sign was called on, or returns
|
||
|
// ErrInvalidSignature if the signature is not valid for the value and public
|
||
|
// key.
|
||
|
func (s Signed[T]) Unwrap(pubK SigningPublicKey) (T, error) {
|
||
|
var (
|
||
|
res T
|
||
|
into signed[T]
|
||
|
)
|
||
|
|
||
|
if err := json.Unmarshal(s, &into); err != nil {
|
||
|
return res, fmt.Errorf("json unmarshaling outer Signed: %w", err)
|
||
|
}
|
||
|
|
||
|
if !ed25519.Verify(
|
||
|
ed25519.PublicKey(pubK), []byte(into.Body), into.Signature,
|
||
|
) {
|
||
|
return res, ErrInvalidSignature
|
||
|
}
|
||
|
|
||
|
if err := json.Unmarshal(into.Body, &res); err != nil {
|
||
|
return res, fmt.Errorf("json unmarshaling: %w", err)
|
||
|
}
|
||
|
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
// UnwrapUnsafe is like Unwrap, but it will not check the signature.
|
||
|
func (s Signed[T]) UnwrapUnsafe() (T, error) {
|
||
|
var (
|
||
|
res T
|
||
|
into signed[T]
|
||
|
)
|
||
|
|
||
|
if err := json.Unmarshal(s, &into); err != nil {
|
||
|
return res, fmt.Errorf("json unmarshaling outer Signed: %w", err)
|
||
|
}
|
||
|
|
||
|
if err := json.Unmarshal(into.Body, &res); err != nil {
|
||
|
return res, fmt.Errorf("json unmarshaling: %w", err)
|
||
|
}
|
||
|
|
||
|
return res, nil
|
||
|
}
|