382af95499
Change-Id: I1e7a83de36d5cf16eed8874091b15af1e0b73df7
132 lines
4.5 KiB
Go
132 lines
4.5 KiB
Go
// Copyright (C) 2022 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
"crypto/subtle"
|
|
"encoding/binary"
|
|
"time"
|
|
|
|
"github.com/flynn/noise"
|
|
|
|
"storj.io/common/identity"
|
|
"storj.io/common/pb"
|
|
"storj.io/common/signing"
|
|
)
|
|
|
|
// NoiseHeader is the drpcmigrate.Header prefix for DRPC over Noise.
|
|
const NoiseHeader = "DRPC!N!1"
|
|
|
|
// defaultNoiseProto is the protobuf enum value that specifies what noise
|
|
// protocol is in use.
|
|
// defaultNoiseInfo and defaultNoiseConfig should be changed together.
|
|
var defaultNoiseProto = pb.NoiseProtocol_NOISE_IK_25519_CHACHAPOLY_BLAKE2B
|
|
|
|
// defaultNoiseConfig returns the structure that tells this node what Noise
|
|
// settings to use.
|
|
// defaultNoiseProto and defaultNoiseConfig should be changed together.
|
|
func defaultNoiseConfig() noise.Config {
|
|
return noise.Config{
|
|
CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2b),
|
|
Pattern: noise.HandshakeIK,
|
|
}
|
|
}
|
|
|
|
func signableNoisePublicKey(ts time.Time, key []byte) []byte {
|
|
var buf [8]byte
|
|
tsnano := ts.UnixNano()
|
|
if tsnano < 0 {
|
|
tsnano = 0
|
|
}
|
|
binary.BigEndian.PutUint64(buf[:], uint64(tsnano))
|
|
return append(buf[:], key...)
|
|
}
|
|
|
|
// GenerateNoiseKeyAttestation will sign a given Noise public key using the
|
|
// Node's leaf key and certificate chain, generating a pb.NoiseKeyAttestation.
|
|
func GenerateNoiseKeyAttestation(ctx context.Context, ident *identity.FullIdentity, info *pb.NoiseInfo) (_ *pb.NoiseKeyAttestation, err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
ts := time.Now()
|
|
signature, err := signing.SignerFromFullIdentity(ident).HashAndSign(ctx,
|
|
append([]byte("noise-key-attestation-v1:"), signableNoisePublicKey(ts, info.PublicKey)...))
|
|
if err != nil {
|
|
return nil, Error.Wrap(err)
|
|
}
|
|
return &pb.NoiseKeyAttestation{
|
|
NodeId: ident.ID,
|
|
NodeCertchain: identity.EncodePeerIdentity(ident.PeerIdentity()),
|
|
NoiseProto: info.Proto,
|
|
NoisePublicKey: info.PublicKey,
|
|
Timestamp: ts,
|
|
Signature: signature,
|
|
}, nil
|
|
}
|
|
|
|
// ValidateNoiseKeyAttestation will confirm that a provided
|
|
// *pb.NoiseKeyAttestation was signed correctly.
|
|
func ValidateNoiseKeyAttestation(ctx context.Context, attestation *pb.NoiseKeyAttestation) (err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
peer, err := identity.DecodePeerIdentity(ctx, attestation.NodeCertchain)
|
|
if err != nil {
|
|
return Error.Wrap(err)
|
|
}
|
|
err = peer.Leaf.CheckSignatureFrom(peer.CA)
|
|
if err != nil {
|
|
return Error.New("certificate chain invalid: %w", err)
|
|
}
|
|
|
|
if subtle.ConstantTimeCompare(peer.ID.Bytes(), attestation.NodeId.Bytes()) != 1 {
|
|
return Error.New("node id mismatch")
|
|
}
|
|
signee := signing.SigneeFromPeerIdentity(peer)
|
|
unsigned := signableNoisePublicKey(attestation.Timestamp, attestation.NoisePublicKey)
|
|
err = signee.HashAndVerifySignature(ctx,
|
|
append([]byte("noise-key-attestation-v1:"), unsigned...),
|
|
attestation.Signature)
|
|
return Error.Wrap(err)
|
|
}
|
|
|
|
// GenerateNoiseSessionAttestation will sign a given Noise session handshake
|
|
// hash using the Node's leaf key and certificate chain, generating a
|
|
// pb.NoiseSessionAttestation.
|
|
func GenerateNoiseSessionAttestation(ctx context.Context, ident *identity.FullIdentity, handshakeHash []byte) (_ *pb.NoiseSessionAttestation, err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
signature, err := signing.SignerFromFullIdentity(ident).HashAndSign(ctx,
|
|
append([]byte("noise-session-attestation-v1:"), handshakeHash...))
|
|
if err != nil {
|
|
return nil, Error.Wrap(err)
|
|
}
|
|
return &pb.NoiseSessionAttestation{
|
|
NodeId: ident.ID,
|
|
NodeCertchain: identity.EncodePeerIdentity(ident.PeerIdentity()),
|
|
NoiseHandshakeHash: handshakeHash,
|
|
Signature: signature,
|
|
}, nil
|
|
}
|
|
|
|
// ValidateNoiseSessionAttestation will confirm that a provided
|
|
// *pb.NoiseSessionAttestation was signed correctly.
|
|
func ValidateNoiseSessionAttestation(ctx context.Context, attestation *pb.NoiseSessionAttestation) (err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
peer, err := identity.DecodePeerIdentity(ctx, attestation.NodeCertchain)
|
|
if err != nil {
|
|
return Error.Wrap(err)
|
|
}
|
|
err = peer.Leaf.CheckSignatureFrom(peer.CA)
|
|
if err != nil {
|
|
return Error.New("certificate chain invalid: %w", err)
|
|
}
|
|
|
|
if subtle.ConstantTimeCompare(peer.ID.Bytes(), attestation.NodeId.Bytes()) != 1 {
|
|
return Error.New("node id mismatch")
|
|
}
|
|
signee := signing.SigneeFromPeerIdentity(peer)
|
|
err = signee.HashAndVerifySignature(ctx,
|
|
append([]byte("noise-session-attestation-v1:"), attestation.NoiseHandshakeHash...),
|
|
attestation.Signature)
|
|
return Error.Wrap(err)
|
|
|
|
}
|