Files
wireguard-go/device/noise-protocol.go
T

701 lines
17 KiB
Go
Raw Normal View History

2019-01-02 01:55:51 +01:00
/* SPDX-License-Identifier: MIT
2018-05-03 15:04:00 +02:00
*
2025-05-04 17:48:53 +02:00
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
2018-05-03 15:04:00 +02:00
*/
2019-03-03 04:04:41 +01:00
package device
2017-06-23 13:41:59 +02:00
import (
2024-12-26 20:36:53 +01:00
"encoding/binary"
2017-06-23 13:41:59 +02:00
"errors"
2020-03-04 20:58:39 -05:00
"fmt"
2019-05-14 09:09:52 +02:00
"sync"
"time"
2017-06-23 13:41:59 +02:00
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305"
2019-02-22 20:59:43 +01:00
"golang.zx2c4.com/wireguard/tai64n"
2017-06-23 13:41:59 +02:00
)
2020-03-04 20:58:39 -05:00
type handshakeState int
2017-06-23 13:41:59 +02:00
const (
2020-03-04 20:58:39 -05:00
handshakeZeroed = handshakeState(iota)
handshakeInitiationCreated
handshakeInitiationConsumed
handshakeResponseCreated
handshakeResponseConsumed
2017-06-23 13:41:59 +02:00
)
2020-03-04 20:58:39 -05:00
func (hs handshakeState) String() string {
switch hs {
case handshakeZeroed:
return "handshakeZeroed"
case handshakeInitiationCreated:
return "handshakeInitiationCreated"
case handshakeInitiationConsumed:
return "handshakeInitiationConsumed"
case handshakeResponseCreated:
return "handshakeResponseCreated"
case handshakeResponseConsumed:
return "handshakeResponseConsumed"
default:
return fmt.Sprintf("Handshake(UNKNOWN:%d)", int(hs))
}
}
2017-06-23 13:41:59 +02:00
const (
NoiseConstruction = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
WGIdentifier = "WireGuard v1 zx2c4 Jason@zx2c4.com"
WGLabelMAC1 = "mac1----"
WGLabelCookie = "cookie--"
)
const (
2017-06-27 17:33:06 +02:00
MessageInitiationType = 1
MessageResponseType = 2
MessageCookieReplyType = 3
MessageTransportType = 4
)
const (
MessageInitiationSize = 148 // size of handshake initiation message
2017-08-25 14:53:23 +02:00
MessageResponseSize = 92 // size of response message
MessageCookieReplySize = 64 // size of cookie reply message
MessageTransportHeaderSize = 16 // size of data preceding content in transport message
2025-07-25 17:56:07 +08:00
MessageTransportSize = MessageTransportHeaderSize // size of empty transport (no encryption tag)
2017-08-25 14:53:23 +02:00
MessageKeepaliveSize = MessageTransportSize // size of keepalive
MessageHandshakeSize = MessageInitiationSize // size of largest handshake related message
2017-07-01 23:29:22 +02:00
)
const (
MessageTransportOffsetReceiver = 4
MessageTransportOffsetCounter = 8
MessageTransportOffsetContent = 16
2017-06-23 13:41:59 +02:00
)
2017-06-26 13:14:02 +02:00
/* Type is an 8-bit field, followed by 3 nul bytes,
* by marshalling the messages in little-endian byteorder
2017-06-27 17:33:06 +02:00
* we can treat these as a 32-bit unsigned int (for now)
2017-06-26 13:14:02 +02:00
*
*/
type MessageInitiation struct {
2017-06-23 13:41:59 +02:00
Type uint32
Sender uint32
Ephemeral NoisePublicKey
2025-07-25 17:56:07 +08:00
Static [NoisePublicKeySize + Poly1305TagSize]byte
Timestamp [tai64n.TimestampSize + Poly1305TagSize]byte
2017-07-06 16:24:24 +02:00
MAC1 [blake2s.Size128]byte
MAC2 [blake2s.Size128]byte
2017-06-23 13:41:59 +02:00
}
type MessageResponse struct {
Type uint32
Sender uint32
2017-07-01 23:29:22 +02:00
Receiver uint32
2017-06-23 13:41:59 +02:00
Ephemeral NoisePublicKey
2025-07-25 17:56:07 +08:00
Empty [Poly1305TagSize]byte
2017-07-06 16:24:24 +02:00
MAC1 [blake2s.Size128]byte
MAC2 [blake2s.Size128]byte
2017-06-23 13:41:59 +02:00
}
type MessageTransport struct {
Type uint32
2017-07-01 23:29:22 +02:00
Receiver uint32
2017-06-23 13:41:59 +02:00
Counter uint64
Content []byte
}
2017-06-27 17:33:06 +02:00
type MessageCookieReply struct {
Type uint32
Receiver uint32
2018-12-10 04:23:17 +01:00
Nonce [chacha20poly1305.NonceSizeX]byte
2025-07-25 17:56:07 +08:00
Cookie [blake2s.Size128 + Poly1305TagSize]byte
2017-06-27 17:33:06 +02:00
}
2025-05-15 16:48:14 +02:00
var errMessageLengthMismatch = errors.New("message length mismatch")
2024-12-26 20:36:53 +01:00
func (msg *MessageInitiation) unmarshal(b []byte) error {
2025-05-15 16:48:14 +02:00
if len(b) != MessageInitiationSize {
return errMessageLengthMismatch
2024-12-26 20:36:53 +01:00
}
msg.Type = binary.LittleEndian.Uint32(b)
msg.Sender = binary.LittleEndian.Uint32(b[4:])
copy(msg.Ephemeral[:], b[8:])
copy(msg.Static[:], b[8+len(msg.Ephemeral):])
copy(msg.Timestamp[:], b[8+len(msg.Ephemeral)+len(msg.Static):])
copy(msg.MAC1[:], b[8+len(msg.Ephemeral)+len(msg.Static)+len(msg.Timestamp):])
copy(msg.MAC2[:], b[8+len(msg.Ephemeral)+len(msg.Static)+len(msg.Timestamp)+len(msg.MAC1):])
return nil
}
2025-05-17 11:34:30 +02:00
func (msg *MessageInitiation) marshal(b []byte) error {
if len(b) != MessageInitiationSize {
return errMessageLengthMismatch
}
binary.LittleEndian.PutUint32(b, msg.Type)
binary.LittleEndian.PutUint32(b[4:], msg.Sender)
copy(b[8:], msg.Ephemeral[:])
copy(b[8+len(msg.Ephemeral):], msg.Static[:])
copy(b[8+len(msg.Ephemeral)+len(msg.Static):], msg.Timestamp[:])
copy(b[8+len(msg.Ephemeral)+len(msg.Static)+len(msg.Timestamp):], msg.MAC1[:])
copy(b[8+len(msg.Ephemeral)+len(msg.Static)+len(msg.Timestamp)+len(msg.MAC1):], msg.MAC2[:])
return nil
}
2024-12-26 20:36:53 +01:00
func (msg *MessageResponse) unmarshal(b []byte) error {
2025-05-15 16:48:14 +02:00
if len(b) != MessageResponseSize {
return errMessageLengthMismatch
2024-12-26 20:36:53 +01:00
}
msg.Type = binary.LittleEndian.Uint32(b)
msg.Sender = binary.LittleEndian.Uint32(b[4:])
msg.Receiver = binary.LittleEndian.Uint32(b[8:])
copy(msg.Ephemeral[:], b[12:])
copy(msg.Empty[:], b[12+len(msg.Ephemeral):])
copy(msg.MAC1[:], b[12+len(msg.Ephemeral)+len(msg.Empty):])
copy(msg.MAC2[:], b[12+len(msg.Ephemeral)+len(msg.Empty)+len(msg.MAC1):])
return nil
}
2025-05-17 11:34:30 +02:00
func (msg *MessageResponse) marshal(b []byte) error {
if len(b) != MessageResponseSize {
return errMessageLengthMismatch
}
binary.LittleEndian.PutUint32(b, msg.Type)
binary.LittleEndian.PutUint32(b[4:], msg.Sender)
binary.LittleEndian.PutUint32(b[8:], msg.Receiver)
copy(b[12:], msg.Ephemeral[:])
copy(b[12+len(msg.Ephemeral):], msg.Empty[:])
copy(b[12+len(msg.Ephemeral)+len(msg.Empty):], msg.MAC1[:])
copy(b[12+len(msg.Ephemeral)+len(msg.Empty)+len(msg.MAC1):], msg.MAC2[:])
return nil
}
2024-12-26 20:36:53 +01:00
func (msg *MessageCookieReply) unmarshal(b []byte) error {
2025-05-15 16:48:14 +02:00
if len(b) != MessageCookieReplySize {
return errMessageLengthMismatch
2024-12-26 20:36:53 +01:00
}
msg.Type = binary.LittleEndian.Uint32(b)
msg.Receiver = binary.LittleEndian.Uint32(b[4:])
copy(msg.Nonce[:], b[8:])
copy(msg.Cookie[:], b[8+len(msg.Nonce):])
return nil
}
2025-05-17 11:34:30 +02:00
func (msg *MessageCookieReply) marshal(b []byte) error {
if len(b) != MessageCookieReplySize {
return errMessageLengthMismatch
}
binary.LittleEndian.PutUint32(b, msg.Type)
binary.LittleEndian.PutUint32(b[4:], msg.Receiver)
copy(b[8:], msg.Nonce[:])
copy(b[8+len(msg.Nonce):], msg.Cookie[:])
return nil
}
2017-06-23 13:41:59 +02:00
type Handshake struct {
2020-03-04 20:58:39 -05:00
state handshakeState
2017-08-14 17:09:25 +02:00
mutex sync.RWMutex
hash [blake2s.Size]byte // hash value
chainKey [blake2s.Size]byte // chain key
2021-01-28 14:44:51 +01:00
presharedKey NoisePresharedKey // psk
2017-08-14 17:09:25 +02:00
localEphemeral NoisePrivateKey // ephemeral secret key
localIndex uint32 // used to clear hash-table
remoteIndex uint32 // index for sending
remoteStatic NoisePublicKey // long term key
remoteEphemeral NoisePublicKey // ephemeral public key
precomputedStaticStatic [NoisePublicKeySize]byte // precomputed shared secret
2018-02-12 22:29:11 +01:00
lastTimestamp tai64n.Timestamp
2017-08-14 17:09:25 +02:00
lastInitiationConsumption time.Time
2018-05-13 23:14:43 +02:00
lastSentHandshake time.Time
2017-06-23 13:41:59 +02:00
}
var (
2017-06-30 14:41:08 +02:00
InitialChainKey [blake2s.Size]byte
InitialHash [blake2s.Size]byte
ZeroNonce [chacha20poly1305.NonceSize]byte
2017-06-23 13:41:59 +02:00
)
2021-12-09 17:55:50 +01:00
func mixKey(dst, c *[blake2s.Size]byte, data []byte) {
2017-09-01 14:21:53 +02:00
KDF1(dst, c[:], data)
2017-06-24 22:03:52 +02:00
}
2021-12-09 17:55:50 +01:00
func mixHash(dst, h *[blake2s.Size]byte, data []byte) {
2018-05-14 12:27:29 +02:00
hash, _ := blake2s.New256(nil)
hash.Write(h[:])
hash.Write(data)
hash.Sum(dst[:0])
hash.Reset()
2017-06-24 22:03:52 +02:00
}
func (h *Handshake) Clear() {
setZero(h.localEphemeral[:])
setZero(h.remoteEphemeral[:])
setZero(h.chainKey[:])
setZero(h.hash[:])
h.localIndex = 0
2020-03-04 20:58:39 -05:00
h.state = handshakeZeroed
}
2017-06-26 13:14:02 +02:00
func (h *Handshake) mixHash(data []byte) {
2017-09-01 14:21:53 +02:00
mixHash(&h.hash, &h.hash, data)
2017-06-23 13:41:59 +02:00
}
2017-06-26 13:14:02 +02:00
func (h *Handshake) mixKey(data []byte) {
2017-09-01 14:21:53 +02:00
mixKey(&h.chainKey, &h.chainKey, data)
2017-06-23 13:41:59 +02:00
}
2017-06-30 14:41:08 +02:00
/* Do basic precomputations
*/
func init() {
InitialChainKey = blake2s.Sum256([]byte(NoiseConstruction))
2017-09-01 14:21:53 +02:00
mixHash(&InitialHash, &InitialChainKey, []byte(WGIdentifier))
2017-06-30 14:41:08 +02:00
}
2017-06-26 13:14:02 +02:00
func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, error) {
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
2018-02-02 16:40:14 +01:00
2017-06-24 15:34:17 +02:00
handshake := &peer.handshake
handshake.mutex.Lock()
defer handshake.mutex.Unlock()
2017-06-23 13:41:59 +02:00
// create ephemeral key
2017-06-24 15:34:17 +02:00
var err error
2017-06-30 14:41:08 +02:00
handshake.hash = InitialHash
handshake.chainKey = InitialChainKey
2017-06-24 15:34:17 +02:00
handshake.localEphemeral, err = newPrivateKey()
if err != nil {
return nil, err
}
2017-06-30 14:41:08 +02:00
handshake.mixHash(handshake.remoteStatic[:])
msg := MessageInitiation{
Type: MessageInitiationType,
Ephemeral: handshake.localEphemeral.publicKey(),
}
2017-06-26 13:14:02 +02:00
handshake.mixKey(msg.Ephemeral[:])
handshake.mixHash(msg.Ephemeral[:])
2017-06-23 13:41:59 +02:00
2017-06-26 13:14:02 +02:00
// encrypt static key
ss, err := handshake.localEphemeral.sharedSecret(handshake.remoteStatic)
if err != nil {
return nil, err
2020-03-17 23:06:56 -06:00
}
var key [chacha20poly1305.KeySize]byte
KDF2(
&handshake.chainKey,
&key,
handshake.chainKey[:],
ss[:],
)
aead, _ := chacha20poly1305.New(key[:])
aead.Seal(msg.Static[:0], ZeroNonce[:], device.staticIdentity.publicKey[:], handshake.hash[:])
2017-06-26 13:14:02 +02:00
handshake.mixHash(msg.Static[:])
2017-06-23 13:41:59 +02:00
// encrypt timestamp
2020-03-17 23:06:56 -06:00
if isZero(handshake.precomputedStaticStatic[:]) {
return nil, errInvalidPublicKey
2020-03-17 23:06:56 -06:00
}
KDF2(
&handshake.chainKey,
&key,
handshake.chainKey[:],
handshake.precomputedStaticStatic[:],
)
2018-02-12 22:29:11 +01:00
timestamp := tai64n.Now()
2020-03-17 23:06:56 -06:00
aead, _ = chacha20poly1305.New(key[:])
aead.Seal(msg.Timestamp[:0], ZeroNonce[:], timestamp[:], handshake.hash[:])
// assign index
device.indexTable.Delete(handshake.localIndex)
msg.Sender, err = device.indexTable.NewIndexForHandshake(peer, handshake)
if err != nil {
return nil, err
}
handshake.localIndex = msg.Sender
2017-06-24 15:34:17 +02:00
2017-06-26 13:14:02 +02:00
handshake.mixHash(msg.Timestamp[:])
2020-03-04 20:58:39 -05:00
handshake.state = handshakeInitiationCreated
2017-06-23 13:41:59 +02:00
return &msg, nil
}
2017-06-26 13:14:02 +02:00
func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
2017-09-01 14:21:53 +02:00
var (
hash [blake2s.Size]byte
chainKey [blake2s.Size]byte
)
2018-02-02 16:40:14 +01:00
if msg.Type != MessageInitiationType {
return nil
}
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
2018-02-02 16:40:14 +01:00
2018-05-13 23:14:43 +02:00
mixHash(&hash, &InitialHash, device.staticIdentity.publicKey[:])
2017-09-01 14:21:53 +02:00
mixHash(&hash, &hash, msg.Ephemeral[:])
mixKey(&chainKey, &InitialChainKey, msg.Ephemeral[:])
2017-06-23 13:41:59 +02:00
2017-06-26 13:14:02 +02:00
// decrypt static key
2017-06-24 15:34:17 +02:00
var peerPK NoisePublicKey
2020-03-17 23:06:56 -06:00
var key [chacha20poly1305.KeySize]byte
ss, err := device.staticIdentity.privateKey.sharedSecret(msg.Ephemeral)
if err != nil {
2020-03-17 23:06:56 -06:00
return nil
}
KDF2(&chainKey, &key, chainKey[:], ss[:])
aead, _ := chacha20poly1305.New(key[:])
_, err = aead.Open(peerPK[:0], ZeroNonce[:], msg.Static[:], hash[:])
2017-06-23 13:41:59 +02:00
if err != nil {
2017-06-24 15:34:17 +02:00
return nil
2017-06-23 13:41:59 +02:00
}
2017-09-01 14:21:53 +02:00
mixHash(&hash, &hash, msg.Static[:])
2017-06-24 15:34:17 +02:00
2017-06-28 23:45:45 +02:00
// lookup peer
2017-06-24 15:34:17 +02:00
peer := device.LookupPeer(peerPK)
2022-08-30 07:43:11 -07:00
if peer == nil || !peer.isRunning.Load() {
2017-06-24 15:34:17 +02:00
return nil
}
2017-08-04 16:15:53 +02:00
2017-06-24 15:34:17 +02:00
handshake := &peer.handshake
2017-06-28 23:45:45 +02:00
// verify identity
2017-06-24 15:34:17 +02:00
2018-02-12 22:29:11 +01:00
var timestamp tai64n.Timestamp
2017-06-28 23:45:45 +02:00
2017-08-14 17:09:25 +02:00
handshake.mutex.RLock()
2020-03-17 23:06:56 -06:00
if isZero(handshake.precomputedStaticStatic[:]) {
handshake.mutex.RUnlock()
return nil
}
2017-09-01 14:21:53 +02:00
KDF2(
&chainKey,
&key,
2017-08-14 17:09:25 +02:00
chainKey[:],
handshake.precomputedStaticStatic[:],
)
2020-03-17 23:06:56 -06:00
aead, _ = chacha20poly1305.New(key[:])
2017-08-14 17:09:25 +02:00
_, err = aead.Open(timestamp[:0], ZeroNonce[:], msg.Timestamp[:], hash[:])
if err != nil {
handshake.mutex.RUnlock()
return nil
}
2017-09-01 14:21:53 +02:00
mixHash(&hash, &hash, msg.Timestamp[:])
2017-06-28 23:45:45 +02:00
2017-08-14 17:09:25 +02:00
// protect against replay & flood
2017-06-28 23:45:45 +02:00
replay := !timestamp.After(handshake.lastTimestamp)
flood := time.Since(handshake.lastInitiationConsumption) <= HandshakeInitationRate
2017-08-14 17:09:25 +02:00
handshake.mutex.RUnlock()
if replay {
device.log.Verbosef("%v - ConsumeMessageInitiation: handshake replay @ %v", peer, timestamp)
return nil
}
if flood {
device.log.Verbosef("%v - ConsumeMessageInitiation: handshake flood", peer)
2017-06-24 15:34:17 +02:00
return nil
}
2017-06-23 13:41:59 +02:00
// update handshake state
2017-06-28 23:45:45 +02:00
handshake.mutex.Lock()
2017-06-24 15:34:17 +02:00
handshake.hash = hash
handshake.chainKey = chainKey
handshake.remoteIndex = msg.Sender
handshake.remoteEphemeral = msg.Ephemeral
if timestamp.After(handshake.lastTimestamp) {
handshake.lastTimestamp = timestamp
}
now := time.Now()
if now.After(handshake.lastInitiationConsumption) {
handshake.lastInitiationConsumption = now
}
2020-03-04 20:58:39 -05:00
handshake.state = handshakeInitiationConsumed
2017-06-28 23:45:45 +02:00
handshake.mutex.Unlock()
2018-05-13 19:50:58 +02:00
setZero(hash[:])
setZero(chainKey[:])
2017-06-24 15:34:17 +02:00
return peer
2017-06-23 13:41:59 +02:00
}
2017-06-24 15:34:17 +02:00
func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error) {
handshake := &peer.handshake
handshake.mutex.Lock()
defer handshake.mutex.Unlock()
2017-06-23 13:41:59 +02:00
2020-03-04 20:58:39 -05:00
if handshake.state != handshakeInitiationConsumed {
2018-05-13 18:23:40 +02:00
return nil, errors.New("handshake initiation must be consumed first")
2017-06-24 15:34:17 +02:00
}
// assign index
var err error
2018-05-13 18:23:40 +02:00
device.indexTable.Delete(handshake.localIndex)
handshake.localIndex, err = device.indexTable.NewIndexForHandshake(peer, handshake)
2017-06-24 15:34:17 +02:00
if err != nil {
return nil, err
}
2017-06-24 22:03:52 +02:00
var msg MessageResponse
msg.Type = MessageResponseType
msg.Sender = handshake.localIndex
2017-07-01 23:29:22 +02:00
msg.Receiver = handshake.remoteIndex
2017-06-24 22:03:52 +02:00
2017-06-24 15:34:17 +02:00
// create ephemeral key
handshake.localEphemeral, err = newPrivateKey()
if err != nil {
return nil, err
}
msg.Ephemeral = handshake.localEphemeral.publicKey()
2017-06-26 13:14:02 +02:00
handshake.mixHash(msg.Ephemeral[:])
2017-07-01 23:29:22 +02:00
handshake.mixKey(msg.Ephemeral[:])
2017-06-24 15:34:17 +02:00
ss, err := handshake.localEphemeral.sharedSecret(handshake.remoteEphemeral)
if err != nil {
return nil, err
}
handshake.mixKey(ss[:])
ss, err = handshake.localEphemeral.sharedSecret(handshake.remoteStatic)
if err != nil {
return nil, err
}
handshake.mixKey(ss[:])
2017-06-24 15:34:17 +02:00
2018-05-13 19:50:58 +02:00
// add preshared key
2017-06-24 15:34:17 +02:00
var tau [blake2s.Size]byte
var key [chacha20poly1305.KeySize]byte
2017-09-01 14:21:53 +02:00
KDF3(
&handshake.chainKey,
&tau,
&key,
handshake.chainKey[:],
handshake.presharedKey[:],
)
2017-06-26 13:14:02 +02:00
handshake.mixHash(tau[:])
2017-06-24 15:34:17 +02:00
aead, _ := chacha20poly1305.New(key[:])
aead.Seal(msg.Empty[:0], ZeroNonce[:], nil, handshake.hash[:])
handshake.mixHash(msg.Empty[:])
2017-06-24 15:34:17 +02:00
2020-03-04 20:58:39 -05:00
handshake.state = handshakeResponseCreated
2017-09-01 14:21:53 +02:00
2017-06-24 15:34:17 +02:00
return &msg, nil
2017-06-23 13:41:59 +02:00
}
2017-06-24 22:03:52 +02:00
func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
if msg.Type != MessageResponseType {
2017-06-26 13:14:02 +02:00
return nil
2017-06-24 22:03:52 +02:00
}
2018-05-13 18:23:40 +02:00
// lookup handshake by receiver
2017-06-24 22:03:52 +02:00
2018-05-13 18:23:40 +02:00
lookup := device.indexTable.Lookup(msg.Receiver)
2017-06-26 22:07:29 +02:00
handshake := lookup.handshake
if handshake == nil {
2017-06-24 22:03:52 +02:00
return nil
}
2017-06-26 22:07:29 +02:00
2017-06-28 23:45:45 +02:00
var (
hash [blake2s.Size]byte
chainKey [blake2s.Size]byte
)
2017-06-24 22:03:52 +02:00
2017-06-28 23:45:45 +02:00
ok := func() bool {
// lock handshake state
2017-06-24 22:03:52 +02:00
2017-06-28 23:45:45 +02:00
handshake.mutex.RLock()
defer handshake.mutex.RUnlock()
2020-03-04 20:58:39 -05:00
if handshake.state != handshakeInitiationCreated {
2017-06-28 23:45:45 +02:00
return false
}
// lock private key for reading
device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock()
2017-06-28 23:45:45 +02:00
// finish 3-way DH
2017-09-01 14:21:53 +02:00
mixHash(&hash, &handshake.hash, msg.Ephemeral[:])
mixKey(&chainKey, &handshake.chainKey, msg.Ephemeral[:])
2017-06-28 23:45:45 +02:00
ss, err := handshake.localEphemeral.sharedSecret(msg.Ephemeral)
if err != nil {
return false
}
mixKey(&chainKey, &chainKey, ss[:])
setZero(ss[:])
2017-09-01 14:21:53 +02:00
ss, err = device.staticIdentity.privateKey.sharedSecret(msg.Ephemeral)
if err != nil {
return false
}
mixKey(&chainKey, &chainKey, ss[:])
setZero(ss[:])
2017-06-28 23:45:45 +02:00
// add preshared key (psk)
var tau [blake2s.Size]byte
var key [chacha20poly1305.KeySize]byte
2017-09-01 14:21:53 +02:00
KDF3(
&chainKey,
&tau,
&key,
chainKey[:],
handshake.presharedKey[:],
)
mixHash(&hash, &hash, tau[:])
2017-06-28 23:45:45 +02:00
// authenticate transcript
2017-06-28 23:45:45 +02:00
aead, _ := chacha20poly1305.New(key[:])
_, err = aead.Open(nil, ZeroNonce[:], msg.Empty[:], hash[:])
2017-06-28 23:45:45 +02:00
if err != nil {
return false
}
2017-09-01 14:21:53 +02:00
mixHash(&hash, &hash, msg.Empty[:])
2017-06-28 23:45:45 +02:00
return true
2017-06-24 22:03:52 +02:00
}()
2017-06-28 23:45:45 +02:00
if !ok {
2017-06-24 22:03:52 +02:00
return nil
}
// update handshake state
2017-06-28 23:45:45 +02:00
handshake.mutex.Lock()
2017-06-24 22:03:52 +02:00
handshake.hash = hash
handshake.chainKey = chainKey
handshake.remoteIndex = msg.Sender
2020-03-04 20:58:39 -05:00
handshake.state = handshakeResponseConsumed
2017-06-24 22:03:52 +02:00
2017-06-28 23:45:45 +02:00
handshake.mutex.Unlock()
2017-09-01 14:21:53 +02:00
setZero(hash[:])
setZero(chainKey[:])
2017-06-26 22:07:29 +02:00
return lookup.peer
2017-06-24 22:03:52 +02:00
}
2018-05-13 19:50:58 +02:00
/* Derives a new keypair from the current handshake state
2017-07-10 12:09:19 +02:00
*
*/
2018-05-13 23:14:43 +02:00
func (peer *Peer) BeginSymmetricSession() error {
2017-09-01 14:21:53 +02:00
device := peer.device
2017-06-24 22:03:52 +02:00
handshake := &peer.handshake
handshake.mutex.Lock()
defer handshake.mutex.Unlock()
2025-07-25 17:56:07 +08:00
// determine initiator role
2017-06-24 22:03:52 +02:00
2017-06-26 22:07:29 +02:00
var isInitiator bool
2017-06-24 22:03:52 +02:00
2020-03-04 20:58:39 -05:00
if handshake.state == handshakeResponseConsumed {
2017-06-26 22:07:29 +02:00
isInitiator = true
2020-03-04 20:58:39 -05:00
} else if handshake.state == handshakeResponseCreated {
2017-06-26 22:07:29 +02:00
isInitiator = false
2017-06-24 22:03:52 +02:00
} else {
2020-03-04 20:58:39 -05:00
return fmt.Errorf("invalid state for keypair derivation: %v", handshake.state)
2017-06-24 22:03:52 +02:00
}
2017-06-30 14:41:08 +02:00
// zero handshake
2017-09-01 14:21:53 +02:00
setZero(handshake.chainKey[:])
2025-07-25 17:56:07 +08:00
setZero(handshake.hash[:])
2017-09-01 14:21:53 +02:00
setZero(handshake.localEphemeral[:])
2020-03-04 20:58:39 -05:00
peer.handshake.state = handshakeZeroed
2017-06-30 14:41:08 +02:00
2025-07-25 17:56:07 +08:00
// create keypair without encryption
2017-06-24 22:03:52 +02:00
2018-05-13 18:23:40 +02:00
keypair := new(Keypair)
2025-07-25 17:56:07 +08:00
keypair.send = nil // no encryption
keypair.receive = nil // no decryption
2017-09-01 14:21:53 +02:00
2018-05-13 18:23:40 +02:00
keypair.created = time.Now()
keypair.replayFilter.Reset()
2018-05-13 18:23:40 +02:00
keypair.isInitiator = isInitiator
keypair.localIndex = peer.handshake.localIndex
keypair.remoteIndex = peer.handshake.remoteIndex
2017-06-24 22:03:52 +02:00
2017-06-26 22:07:29 +02:00
// remap index
2018-05-13 18:23:40 +02:00
device.indexTable.SwapIndexForKeypair(handshake.localIndex, keypair)
2017-06-26 22:07:29 +02:00
handshake.localIndex = 0
// rotate key pairs
2018-05-13 23:14:43 +02:00
keypairs := &peer.keypairs
keypairs.Lock()
defer keypairs.Unlock()
2017-08-14 17:09:25 +02:00
2018-05-13 23:14:43 +02:00
previous := keypairs.previous
2022-08-30 07:43:11 -07:00
next := keypairs.next.Load()
2018-05-13 23:14:43 +02:00
current := keypairs.current
2018-05-07 22:27:03 +02:00
2017-09-20 09:26:08 +02:00
if isInitiator {
2018-05-07 22:27:03 +02:00
if next != nil {
2022-08-30 07:43:11 -07:00
keypairs.next.Store(nil)
2018-05-13 23:14:43 +02:00
keypairs.previous = next
2018-05-07 22:27:03 +02:00
device.DeleteKeypair(current)
2017-09-20 09:26:08 +02:00
} else {
2018-05-13 23:14:43 +02:00
keypairs.previous = current
2017-09-20 09:26:08 +02:00
}
2018-05-07 22:27:03 +02:00
device.DeleteKeypair(previous)
2018-05-13 23:14:43 +02:00
keypairs.current = keypair
2017-09-20 09:26:08 +02:00
} else {
2022-08-30 07:43:11 -07:00
keypairs.next.Store(keypair)
2018-05-07 22:27:03 +02:00
device.DeleteKeypair(next)
2018-05-13 23:14:43 +02:00
keypairs.previous = nil
2018-05-07 22:27:03 +02:00
device.DeleteKeypair(previous)
2017-09-20 09:26:08 +02:00
}
2017-06-26 22:07:29 +02:00
2018-05-13 19:50:58 +02:00
return nil
}
func (peer *Peer) ReceivedWithKeypair(receivedKeypair *Keypair) bool {
2018-05-13 23:14:43 +02:00
keypairs := &peer.keypairs
2022-08-30 07:43:11 -07:00
if keypairs.next.Load() != receivedKeypair {
2018-05-13 19:50:58 +02:00
return false
}
keypairs.Lock()
defer keypairs.Unlock()
2022-08-30 07:43:11 -07:00
if keypairs.next.Load() != receivedKeypair {
2018-05-13 19:50:58 +02:00
return false
}
2018-05-13 23:14:43 +02:00
old := keypairs.previous
keypairs.previous = keypairs.current
2018-05-13 19:50:58 +02:00
peer.device.DeleteKeypair(old)
2022-08-30 07:43:11 -07:00
keypairs.current = keypairs.next.Load()
keypairs.next.Store(nil)
2018-05-13 19:50:58 +02:00
return true
2017-06-24 22:03:52 +02:00
}