cpace

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: BSD-3-Clause Imports: 8 Imported by: 0

Documentation

Overview

Package cpace implements the CPace balanced password-authenticated key exchange, which lets two parties that share a low-entropy secret (a password) derive a strong shared key without exposing the password to offline dictionary attacks.

This package implements the cipher suite CPACE-X25519-SHA512 of “CPace, a balanced composable PAKE”, draft-irtf-cfrg-cpace-21, https://datatracker.ietf.org/doc/draft-irtf-cfrg-cpace/21/.

A CPace exchange is a single round trip. Each party calls Start with the shared password and the other inputs the two parties are expected to agree on, sends the resulting message and its associated data to the other party, and then calls State.Finish with the message and associated data it receives. Both parties derive the same intermediate session key if and only if they started with the same password, channel identifier, and session identifier, and each saw what the other sent.

state, msg, err := cpace.Start(&cpace.Config{
	Role:      cpace.Initiator,
	Password:  password,
	ChannelID: []byte("client.example\x00server.example"),
})
if err != nil {
	return err
}
if err := send(msg, nil); err != nil {
	return err
}
peerMsg, peerAD, err := recv()
if err != nil {
	return err
}
isk, err := state.Finish(peerMsg, peerAD)

Applications should not use the intermediate session key directly. Instead they should derive keys from it using a key derivation function such as crypto/hkdf.

By itself CPace provides implicit authentication only: the exchange always produces a key, and a party learns that the other party used the same password only by observing that the two keys match. Applications that need explicit authentication should add a key confirmation round using State.Tag and State.Verify, which also strengthens the forward secrecy of the exchange.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Role is this party's role in the exchange.
	// Both parties must agree on the pair of roles being used.
	Role Role

	// Password is the shared secret, called PRS in the draft.
	// It can be a password, or a value derived from one, for example by a
	// password hashing function. Clear text passwords should be encoded
	// according to RFC 8265.
	Password []byte

	// ChannelID identifies the channel connecting the two parties,
	// called CI in the draft. It might hold the identities of the two
	// parties, their network addresses, or a service name. It is not sent
	// on the wire, so it may contain confidential information, but both
	// parties must use the same value to derive the same key.
	//
	// If the exchange is meant to authenticate party identities, they
	// should be included here or in AD. If both identities are included
	// here, the encoding must distinguish the two roles, listing the
	// initiator's identity first.
	ChannelID []byte

	// SessionID identifies this exchange, called sid in the draft. If the
	// two parties can agree on a unique value before the exchange starts,
	// using it here binds the exchange to this session. It should not be
	// reused across exchanges. If it is left empty, [State.SessionID]
	// returns a unique identifier derived during the exchange instead.
	SessionID []byte

	// AD is this party's associated data, called ADa and ADb in the
	// draft. Unlike ChannelID, it is sent on the wire in the clear, so it
	// must not contain confidential information. It is authenticated by
	// the exchange: the two parties derive the same key only if each saw
	// what the other sent.
	AD []byte

	// Rand is the source of randomness used to sample the secret scalar.
	// If Rand is nil, [crypto/rand.Reader] is used.
	Rand io.Reader
}

A Config holds the inputs to a CPace exchange. All fields except Role and Password are optional.

type Role

type Role int

A Role identifies the part a party plays in a CPace exchange. The zero Role is invalid: every party must choose a role explicitly.

const (
	// Initiator and Responder are the roles of the two parties in an
	// exchange with a defined message ordering, in which the initiator's
	// message precedes the responder's.
	Initiator Role = 1 + iota
	Responder

	// Symmetric is the role of both parties in an exchange without a
	// defined message ordering, in which either party may speak first.
	//
	// Using Symmetric requires the associated data of the two parties to
	// differ in every exchange; otherwise an attacker running two
	// concurrent exchanges with the same party can relay each exchange's
	// message to the other, making that party derive a key with itself.
	// The message ordering of Initiator and Responder makes the two
	// transcripts differ, which prevents that relay, so use those roles if
	// the associated data cannot be guaranteed to differ.
	Symmetric
)

type State

type State struct {
	// contains filtered or unexported fields
}

A State holds one party's state during a CPace exchange.

func Start

func Start(cfg *Config) (*State, []byte, error)

Start starts a CPace exchange, returning the state needed to finish it and the message to send to the other party. The message must be sent along with cfg.AD, which the other party passes to State.Finish.

func (*State) Finish

func (s *State) Finish(msg, ad []byte) ([]byte, error)

Finish completes a CPace exchange using the message and associated data received from the other party, returning the intermediate session key.

Finish reports an error if the message is not a valid group element, encodes a low-order point, or is this party's own message relayed back to it, each of which indicates either a corrupted message or an attempt to force a known key. It can be called only once: the secret scalar of an exchange must never be reused.

func (*State) SessionID

func (s *State) SessionID() []byte

SessionID returns a public identifier for the exchange, derived from the exchange transcript. It is unique for honest parties and is meant for applications that had no session identifier available to pass to Start. SessionID returns nil if called before a successful State.Finish.

func (*State) Tag

func (s *State) Tag() []byte

Tag returns this party's key confirmation tag, to be sent to the other party after a successful State.Finish, for applications that add an explicit key confirmation round. It returns nil if called before a successful Finish.

Key confirmation gives the exchange explicit authentication: without it, a party that used the wrong password derives a key like any other and finds out only when the key fails to work. It also strengthens the forward secrecy of the exchange from weak to perfect. Each party sends its own tag and checks the other party's with State.Verify, aborting if the check fails.

func (*State) Verify

func (s *State) Verify(tag []byte) error

Verify checks the other party's key confirmation tag, reporting an error if it is not the expected one, which means the two parties did not derive the same key. It reports an error if called before a successful State.Finish.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL