// Package noise implements the Noise Protocol Framework. // // Noise is a low-level framework for building crypto protocols. Noise protocols // support mutual and optional authentication, identity hiding, forward secrecy, // zero round-trip encryption, and other advanced features. For more details, // visit https://noiseprotocol.org.
package noise import ( ) // A CipherState provides symmetric encryption and decryption after a successful // handshake. type CipherState struct { cs CipherSuite c Cipher k [32]byte n uint64 invalid bool } // MaxNonce is the maximum value of n that is allowed. ErrMaxNonce is returned // by Encrypt and Decrypt after this has been reached. 2^64-1 is reserved for rekeys. const MaxNonce = uint64(math.MaxUint64) - 1 var ErrMaxNonce = errors.New("noise: cipherstate has reached maximum n, a new handshake must be performed") var ErrCipherSuiteCopied = errors.New("noise: CipherSuite has been copied, state is invalid") // UnsafeNewCipherState reconstructs a CipherState from exported components. // It is important that, when resuming from an exported state, care is taken // to synchronize the nonce state and not allow rollbacks. func ( CipherSuite, [32]byte, uint64) *CipherState { return &CipherState{ cs: , c: .Cipher(), k: , n: , } } // Encrypt encrypts the plaintext and then appends the ciphertext and an // authentication tag across the ciphertext and optional authenticated data to // out. This method automatically increments the nonce after every call, so // messages must be decrypted in the same order. ErrMaxNonce is returned after // the maximum nonce of 2^64-2 is reached. func ( *CipherState) (, , []byte) ([]byte, error) { if .invalid { return nil, ErrCipherSuiteCopied } if .n > MaxNonce { return nil, ErrMaxNonce } = .c.Encrypt(, .n, , ) .n++ return , nil } // Decrypt checks the authenticity of the ciphertext and authenticated data and // then decrypts and appends the plaintext to out. This method automatically // increments the nonce after every call, messages must be provided in the same // order that they were encrypted with no missing messages. ErrMaxNonce is // returned after the maximum nonce of 2^64-2 is reached. func ( *CipherState) (, , []byte) ([]byte, error) { if .invalid { return nil, ErrCipherSuiteCopied } if .n > MaxNonce { return nil, ErrMaxNonce } , := .c.Decrypt(, .n, , ) if != nil { return nil, } .n++ return , nil } // Cipher returns the low-level symmetric encryption primitive. It should only // be used if nonces need to be managed manually, for example with a network // protocol that can deliver out-of-order messages. This is dangerous, users // must ensure that they are incrementing a nonce after every encrypt operation. // After calling this method, it is an error to call Encrypt/Decrypt on the // CipherState. func ( *CipherState) () Cipher { .invalid = true return .c } // Nonce returns the current value of n. This can be used to determine if a // new handshake should be performed due to approaching MaxNonce. func ( *CipherState) () uint64 { return .n } // SetNonce sets the current value of n. func ( *CipherState) ( uint64) { .n = } // UnsafeKey returns the current value of k. This exports the current key for the // CipherState. Intended to be used alongside UnsafeNewCipherState to resume a // CipherState at a later point. func ( *CipherState) () [32]byte { return .k } func ( *CipherState) () { var [32]byte var []byte = .c.Encrypt(, math.MaxUint64, []byte{}, [:]) copy(.k[:], [:32]) .c = .cs.Cipher(.k) } type symmetricState struct { CipherState hasK bool ck []byte h []byte prevCK []byte prevH []byte } func ( *symmetricState) ( []byte) { := .cs.Hash() if len() <= .Size() { .h = make([]byte, .Size()) copy(.h, ) } else { .Write() .h = .Sum(nil) } .ck = make([]byte, len(.h)) copy(.ck, .h) } func ( *symmetricState) ( []byte) { .n = 0 .hasK = true var []byte .ck, , _ = hkdf(.cs.Hash, 2, .ck[:0], .k[:0], nil, .ck, ) copy(.k[:], ) .c = .cs.Cipher(.k) } func ( *symmetricState) ( []byte) { := .cs.Hash() .Write(.h) .Write() .h = .Sum(.h[:0]) } func ( *symmetricState) ( []byte) { var []byte var []byte .ck, , = hkdf(.cs.Hash, 3, .ck[:0], , .k[:0], .ck, ) .MixHash() copy(.k[:], ) .c = .cs.Cipher(.k) .n = 0 .hasK = true } func ( *symmetricState) (, []byte) ([]byte, error) { if !.hasK { .MixHash() return append(, ...), nil } , := .Encrypt(, .h, ) if != nil { return nil, } .MixHash([len():]) return , nil } func ( *symmetricState) (, []byte) ([]byte, error) { if !.hasK { .MixHash() return append(, ...), nil } , := .Decrypt(, .h, ) if != nil { return nil, } .MixHash() return , nil } func ( *symmetricState) () (*CipherState, *CipherState) { , := &CipherState{cs: .cs}, &CipherState{cs: .cs} , , := hkdf(.cs.Hash, 2, .k[:0], .k[:0], nil, .ck, nil) copy(.k[:], ) copy(.k[:], ) .c = .cs.Cipher(.k) .c = .cs.Cipher(.k) return , } func ( *symmetricState) () { if len(.ck) > cap(.prevCK) { .prevCK = make([]byte, len(.ck)) } .prevCK = .prevCK[:len(.ck)] copy(.prevCK, .ck) if len(.h) > cap(.prevH) { .prevH = make([]byte, len(.h)) } .prevH = .prevH[:len(.h)] copy(.prevH, .h) } func ( *symmetricState) () { .ck = .ck[:len(.prevCK)] copy(.ck, .prevCK) .h = .h[:len(.prevH)] copy(.h, .prevH) } // A MessagePattern is a single message or operation used in a Noise handshake. type MessagePattern int // A HandshakePattern is a list of messages and operations that are used to // perform a specific Noise handshake. type HandshakePattern struct { Name string InitiatorPreMessages []MessagePattern ResponderPreMessages []MessagePattern Messages [][]MessagePattern } const ( MessagePatternS MessagePattern = iota MessagePatternE MessagePatternDHEE MessagePatternDHES MessagePatternDHSE MessagePatternDHSS MessagePatternPSK ) // MaxMsgLen is the maximum number of bytes that can be sent in a single Noise // message. const MaxMsgLen = 65535 // A HandshakeState tracks the state of a Noise handshake. It may be discarded // after the handshake is complete. type HandshakeState struct { ss symmetricState s DHKey // local static keypair e DHKey // local ephemeral keypair rs []byte // remote party's static public key re []byte // remote party's ephemeral public key psk []byte // preshared key, maybe zero length willPsk bool // indicates if preshared key will be used (even if not yet set) messagePatterns [][]MessagePattern shouldWrite bool initiator bool msgIdx int rng io.Reader } // A Config provides the details necessary to process a Noise handshake. It is // never modified by this package, and can be reused. type Config struct { // CipherSuite is the set of cryptographic primitives that will be used. CipherSuite CipherSuite // Random is the source for cryptographically appropriate random bytes. If // zero, it is automatically configured. Random io.Reader // Pattern is the pattern for the handshake. Pattern HandshakePattern // Initiator must be true if the first message in the handshake will be sent // by this peer. Initiator bool // Prologue is an optional message that has already be communicated and must // be identical on both sides for the handshake to succeed. Prologue []byte // PresharedKey is the optional preshared key for the handshake. PresharedKey []byte // PresharedKeyPlacement specifies the placement position of the PSK token // when PresharedKey is specified PresharedKeyPlacement int // StaticKeypair is this peer's static keypair, required if part of the // handshake. StaticKeypair DHKey // EphemeralKeypair is this peer's ephemeral keypair that was provided as // a pre-message in the handshake. EphemeralKeypair DHKey // PeerStatic is the static public key of the remote peer that was provided // as a pre-message in the handshake. PeerStatic []byte // PeerEphemeral is the ephemeral public key of the remote peer that was // provided as a pre-message in the handshake. PeerEphemeral []byte } // NewHandshakeState starts a new handshake using the provided configuration. func ( Config) (*HandshakeState, error) { := &HandshakeState{ s: .StaticKeypair, e: .EphemeralKeypair, rs: .PeerStatic, messagePatterns: .Pattern.Messages, shouldWrite: .Initiator, initiator: .Initiator, rng: .Random, } if .rng == nil { .rng = rand.Reader } if len(.PeerEphemeral) > 0 { .re = make([]byte, len(.PeerEphemeral)) copy(.re, .PeerEphemeral) } .ss.cs = .CipherSuite := "" // NB: for psk{0,1} we must have preshared key set in configuration as its needed in the first // message. For psk{2+} we may not know the correct psk yet so it might not be set. if len(.PresharedKey) > 0 || .PresharedKeyPlacement >= 2 { .willPsk = true if len(.PresharedKey) > 0 { if := .SetPresharedKey(.PresharedKey); != nil { return nil, } } = fmt.Sprintf("psk%d", .PresharedKeyPlacement) .messagePatterns = append([][]MessagePattern(nil), .messagePatterns...) if .PresharedKeyPlacement == 0 { .messagePatterns[0] = append([]MessagePattern{MessagePatternPSK}, .messagePatterns[0]...) } else { .messagePatterns[.PresharedKeyPlacement-1] = append(.messagePatterns[.PresharedKeyPlacement-1], MessagePatternPSK) } } .ss.InitializeSymmetric([]byte("Noise_" + .Pattern.Name + + "_" + string(.ss.cs.Name()))) .ss.MixHash(.Prologue) for , := range .Pattern.InitiatorPreMessages { switch { case .Initiator && == MessagePatternS: .ss.MixHash(.s.Public) case .Initiator && == MessagePatternE: .ss.MixHash(.e.Public) case !.Initiator && == MessagePatternS: .ss.MixHash(.rs) case !.Initiator && == MessagePatternE: .ss.MixHash(.re) } } for , := range .Pattern.ResponderPreMessages { switch { case !.Initiator && == MessagePatternS: .ss.MixHash(.s.Public) case !.Initiator && == MessagePatternE: .ss.MixHash(.e.Public) case .Initiator && == MessagePatternS: .ss.MixHash(.rs) case .Initiator && == MessagePatternE: .ss.MixHash(.re) } } return , nil } // WriteMessage appends a handshake message to out. The message will include the // optional payload if provided. If the handshake is completed by the call, two // CipherStates will be returned, one is used for encryption of messages to the // remote peer, the other is used for decryption of messages from the remote // peer. It is an error to call this method out of sync with the handshake // pattern. func ( *HandshakeState) (, []byte) ([]byte, *CipherState, *CipherState, error) { if !.shouldWrite { return nil, nil, nil, errors.New("noise: unexpected call to WriteMessage should be ReadMessage") } if .msgIdx > len(.messagePatterns)-1 { return nil, nil, nil, errors.New("noise: no handshake messages left") } if len() > MaxMsgLen { return nil, nil, nil, errors.New("noise: message is too long") } var error for , := range .messagePatterns[.msgIdx] { switch { case MessagePatternE: , := .ss.cs.GenerateKeypair(.rng) if != nil { return nil, nil, nil, } .e = = append(, .e.Public...) .ss.MixHash(.e.Public) if .willPsk { .ss.MixKey(.e.Public) } case MessagePatternS: if len(.s.Public) == 0 { return nil, nil, nil, errors.New("noise: invalid state, s.Public is nil") } , = .ss.EncryptAndHash(, .s.Public) if != nil { return nil, nil, nil, } case MessagePatternDHEE: , := .ss.cs.DH(.e.Private, .re) if != nil { return nil, nil, nil, } .ss.MixKey() case MessagePatternDHES: if .initiator { , := .ss.cs.DH(.e.Private, .rs) if != nil { return nil, nil, nil, } .ss.MixKey() } else { , := .ss.cs.DH(.s.Private, .re) if != nil { return nil, nil, nil, } .ss.MixKey() } case MessagePatternDHSE: if .initiator { , := .ss.cs.DH(.s.Private, .re) if != nil { return nil, nil, nil, } .ss.MixKey() } else { , := .ss.cs.DH(.e.Private, .rs) if != nil { return nil, nil, nil, } .ss.MixKey() } case MessagePatternDHSS: , := .ss.cs.DH(.s.Private, .rs) if != nil { return nil, nil, nil, } .ss.MixKey() case MessagePatternPSK: if len(.psk) == 0 { return nil, nil, nil, errors.New("noise: cannot send psk message without psk set") } .ss.MixKeyAndHash(.psk) } } .shouldWrite = false .msgIdx++ , = .ss.EncryptAndHash(, ) if != nil { return nil, nil, nil, } if .msgIdx >= len(.messagePatterns) { , := .ss.Split() return , , , nil } return , nil, nil, nil } // ErrShortMessage is returned by ReadMessage if a message is not as long as it should be. var ErrShortMessage = errors.New("noise: message is too short") func ( *HandshakeState) ( []byte) error { if len() != 32 { return errors.New("noise: specification mandates 256-bit preshared keys") } .psk = make([]byte, 32) copy(.psk, ) return nil } // ReadMessage processes a received handshake message and appends the payload, // if any to out. If the handshake is completed by the call, two CipherStates // will be returned, one is used for encryption of messages to the remote peer, // the other is used for decryption of messages from the remote peer. It is an // error to call this method out of sync with the handshake pattern. func ( *HandshakeState) (, []byte) ([]byte, *CipherState, *CipherState, error) { if .shouldWrite { return nil, nil, nil, errors.New("noise: unexpected call to ReadMessage should be WriteMessage") } if .msgIdx > len(.messagePatterns)-1 { return nil, nil, nil, errors.New("noise: no handshake messages left") } := false .ss.Checkpoint() var error for , := range .messagePatterns[.msgIdx] { switch { case MessagePatternE, MessagePatternS: := .ss.cs.DHLen() if == MessagePatternS && .ss.hasK { += 16 } if len() < { return nil, nil, nil, ErrShortMessage } switch { case MessagePatternE: if cap(.re) < .ss.cs.DHLen() { .re = make([]byte, .ss.cs.DHLen()) } .re = .re[:.ss.cs.DHLen()] copy(.re, ) .ss.MixHash(.re) if .willPsk { .ss.MixKey(.re) } case MessagePatternS: if len(.rs) > 0 { return nil, nil, nil, errors.New("noise: invalid state, rs is not nil") } .rs, = .ss.DecryptAndHash(.rs[:0], [:]) = true } if != nil { .ss.Rollback() if { .rs = nil } return nil, nil, nil, } = [:] case MessagePatternDHEE: , := .ss.cs.DH(.e.Private, .re) if != nil { return nil, nil, nil, } .ss.MixKey() case MessagePatternDHES: if .initiator { , := .ss.cs.DH(.e.Private, .rs) if != nil { return nil, nil, nil, } .ss.MixKey() } else { , := .ss.cs.DH(.s.Private, .re) if != nil { return nil, nil, nil, } .ss.MixKey() } case MessagePatternDHSE: if .initiator { , := .ss.cs.DH(.s.Private, .re) if != nil { return nil, nil, nil, } .ss.MixKey() } else { , := .ss.cs.DH(.e.Private, .rs) if != nil { return nil, nil, nil, } .ss.MixKey() } case MessagePatternDHSS: , := .ss.cs.DH(.s.Private, .rs) if != nil { return nil, nil, nil, } .ss.MixKey() case MessagePatternPSK: .ss.MixKeyAndHash(.psk) } } , = .ss.DecryptAndHash(, ) if != nil { .ss.Rollback() if { .rs = nil } return nil, nil, nil, } .shouldWrite = true .msgIdx++ if .msgIdx >= len(.messagePatterns) { , := .ss.Split() return , , , nil } return , nil, nil, nil } // ChannelBinding provides a value that uniquely identifies the session and can // be used as a channel binding. It is an error to call this method before the // handshake is complete. func ( *HandshakeState) () []byte { return .ss.h } // PeerStatic returns the static key provided by the remote peer during // a handshake. It is an error to call this method if a handshake message // containing a static key has not been read. func ( *HandshakeState) () []byte { return .rs } // MessageIndex returns the current handshake message id func ( *HandshakeState) () int { return .msgIdx } // PeerEphemeral returns the ephemeral key provided by the remote peer during // a handshake. It is an error to call this method if a handshake message // containing a static key has not been read. func ( *HandshakeState) () []byte { return .re } // LocalEphemeral returns the local ephemeral key pair generated during // a handshake. func ( *HandshakeState) () DHKey { return .e }