storj/private/server/noise.go
JT Olio 382af95499 storagenode/contact: send noise key and settings as contact info
Change-Id: I1e7a83de36d5cf16eed8874091b15af1e0b73df7
2023-01-31 21:49:20 +00:00

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)
}