2 A Session represents a pair of Noise CipherState objects, one for receiving and
3 one for sending, resulting from a handshake. The Encrypt and Decrypt methods of
4 Session deal in Noise transport messages with prepended 64-bit big-endian
7 The procedure for getting a Session differs depending on whether you are the
8 initiator or the responder. As an initiator, call InitiateHandshake to get a
9 PreSession and an initiator handshake message to send, then call FinishHandshake
10 with the responder's handshake message to get a Session.
11 pre, initMsg, err := InitiateHandshake(nil, pubkey)
13 // send initMsg to the responder
14 // receive respMsg from the responder
15 session, err := pre.FinishHandshake(respMsg)
17 As a responder, receive the initiator's handshake message and call
18 AcceptHandshake on it to get a Session and a handshake message to send back to
20 // receive initMsg from the initiator
21 session, respMsg, err := AcceptHandshake(nil, initMsg, privkey)
23 // send respMsg to the initiator
32 "github.com/flynn/noise"
35 var errPayload
= errors
.New("unexpected payload in handshake message")
36 var errMissingNonce
= errors
.New("slice is too short to contain a nonce")
37 var errInvalidNonce
= errors
.New("nonce is already used or out of window")
39 // PreSession represents a partially initialized Session, from the point of view
40 // of an initiator that has sent its handshake message but has not yet received
41 // the responder's handshake message. Call FinishHandshake with the responder's
42 // handshake message to convert the PreSession into a full Session.
43 type PreSession
struct {
44 handshakeState
*noise
.HandshakeState
47 // Session represents an initialized Noise session, post-handshake with all
48 // necessary key material.
50 recv
, send
*noise
.CipherState
51 recvLock
, sendLock sync
.Mutex
55 // InitiateHandshake prepares a PreSession and returns an initiator handshake
56 // message to be sent to the responder. out is a byte slice (may be nil) to
57 // which the handshake message will be appended. pubkey is the responder's
59 func InitiateHandshake(out
, pubkey
[]byte) (*PreSession
, []byte, error
) {
61 config
.Initiator
= true
62 config
.PeerStatic
= pubkey
63 handshakeState
, err
:= noise
.NewHandshakeState(config
)
69 out
, _
, _
, err
= handshakeState
.WriteMessage(out
, nil)
74 return &PreSession
{handshakeState
: handshakeState
}, out
, nil
77 // FinishHandshake completes a handshake with the responder's handshake message
78 // and converts a PreSession into a Session. The PreSession should not be used
79 // after calling this method.
80 func (pre
*PreSession
) FinishHandshake(msg
[]byte) (*Session
, error
) {
82 payload
, send
, recv
, err
:= pre
.handshakeState
.ReadMessage(nil, msg
)
86 if len(payload
) != 0 {
87 return nil, errPayload
90 return &Session
{recv
: recv
, send
: send
}, nil
93 // AcceptHandshake accepts an initiator handshake message, and returns a Session
94 // along with a handshake message to be sent back to the initiator. out is a
95 // byte slice (may be nil) to which the handshake message will be appended.
96 // privkey is the responder's private key.
97 func AcceptHandshake(out
, msg
, privkey
[]byte) (*Session
, []byte, error
) {
99 config
.Initiator
= false
100 config
.StaticKeypair
= noise
.DHKey
{
102 Public
: PubkeyFromPrivkey(privkey
),
104 handshakeState
, err
:= noise
.NewHandshakeState(config
)
110 payload
, _
, _
, err
:= handshakeState
.ReadMessage(nil, msg
)
114 if len(payload
) != 0 {
115 return nil, nil, errPayload
119 out
, recv
, send
, err
:= handshakeState
.WriteMessage(out
, nil)
124 return &Session
{recv
: recv
, send
: send
}, out
, nil
127 // Encrypt produces an encrypted Noise transport message with an explicit nonce.
128 // It encrypts the plaintext p, prepends the nonce, appends the message to out
130 func (session
*Session
) Encrypt(out
, p
[]byte) ([]byte, error
) {
131 session
.sendLock
.Lock()
132 defer session
.sendLock
.Unlock()
134 // Prepend the nonce.
136 binary
.BigEndian
.PutUint64(buf
[:], session
.send
.Nonce())
137 out
= append(out
, buf
[:]...)
139 // Encrypt the message.
140 return session
.send
.Encrypt(out
, nil, p
)
143 // Decrypt decrypts a Noise transport message that has an explicit nonce. It
144 // appends the plaintext to out and returns out. It returns a non-nil error when
145 // the message cannot be authenticated or the nonce has already been used or is
147 func (session
*Session
) Decrypt(out
, msg
[]byte) ([]byte, error
) {
148 // Decode the prepended nonce.
150 return nil, errMissingNonce
152 var nonce
uint64 = binary
.BigEndian
.Uint64(msg
[:8])
154 session
.recvLock
.Lock()
155 defer session
.recvLock
.Unlock()
157 // Decrypt the message.
158 session
.recv
.SetNonce(nonce
)
159 p
, err
:= session
.recv
.Decrypt(out
, nil, msg
[8:])
164 // The message was authenticated; is its nonce acceptable (i.e., in a
165 // recent window and not a replay)? It is important to do this check
166 // only after successful decryption+authentication.
167 if !session
.replay
.CheckAndUpdate(nonce
) {
168 return nil, errInvalidNonce