spake2-0.2.0: Implementation of the SPAKE2 Password-Authenticated Key Exchange algorithm

Safe HaskellNone
LanguageHaskell2010

Crypto.Spake2

Contents

Description

Say that you and someone else share a secret password, and you want to use this password to arrange some secure channel of communication. You want:

  • to know that the other party also knows the secret password (maybe they're an imposter!)
  • the password to be secure against offline dictionary attacks
  • probably some other things

SPAKE2 is an algorithm for agreeing on a key exchange that meets these criteria. See Simple Password-Based Encrypted Key Exchange Protocols by Michel Abdalla and David Pointcheval for more details.

How it works

Preliminaries

Before exchanging, two nodes need to agree on the following, out-of-band:

In general:

  • hash algorithm, \(H\)
  • group to use, \(G\)
  • arbitrary members of group to use for blinding
  • a means of converting this password to a scalar of group

For a specific exchange:

  • whether the connection is symmetric or asymmetric
  • the IDs of the respective sides
  • a shared, secret password in bytes

Protocol

How we map the password to a scalar

Use HKDF expansion (see expandData) to expand the password by 16 bytes, using an empty salt, and "SPAKE2 pw" as the info.

Then, use a group-specific mapping from bytes to scalars. Since scalars are normally isomorphic to integers, this will normally be a matter of converting the bytes to an integer using standard deserialization and then turning the integer into a scalar.

How we exchange information

See Math for details on the mathematics of the exchange.

How python-spake2 works

  • Message to other side is prepended with a single character, A, B, or S, to indicate which side it came from
  • The hash function for generating the session key has a few interesting properties:
  • uses SHA256 for hashing
  • does not include password or IDs directly, but rather uses their SHA256 digests as inputs to the hash
  • for the symmetric version, it sorts \(X^{\star}\) and \(Y^{\star}\), because neither side knows which is which
  • By default, the ID of either side is the empty bytestring

Open questions

  • how does endianness come into play?
  • what is Shallue-Woestijne-Ulas and why is it relevant?

References

Synopsis

Documentation

data Password Source #

Shared secret password used to negotiate the connection.

Constructor deliberately not exported, so that once a Password has been created, the actual password cannot be retrieved by other modules.

Construct with makePassword.

makePassword :: ByteString -> Password Source #

Construct a password.

The SPAKE2 protocol

data Protocol group hashAlgorithm Source #

Everything required for the SPAKE2 protocol.

Both sides must agree on these values for the protocol to work. This mostly means value equality, except for us, where each side must have complementary values.

Construct with makeAsymmetricProtocol or makeSymmetricProtocol.

makeAsymmetricProtocol :: hashAlgorithm -> group -> Element group -> Element group -> SideID -> SideID -> WhichSide -> Protocol group hashAlgorithm Source #

Construct an asymmetric SPAKE2 protocol.

makeSymmetricProtocol :: hashAlgorithm -> group -> Element group -> SideID -> Protocol group hashAlgorithm Source #

Construct a symmetric SPAKE2 protocol.

startSpake2 :: (MonadRandom randomly, AbelianGroup group) => Protocol group hashAlgorithm -> Password -> randomly (Spake2Exchange group) Source #

Commence a SPAKE2 exchange.

computeOutboundMessage :: AbelianGroup group => Spake2Exchange group -> Element group Source #

Determine the element (either \(X^{\star}\) or \(Y^{\star}\)) to send to the other side.

generateKeyMaterial Source #

Arguments

:: AbelianGroup group 
=> Spake2Exchange group

An initiated SPAKE2 exchange

-> Element group

The outbound message from the other side (i.e. inbound to us)

-> Element group

The final piece of key material to generate the session key.

Generate key material, \(K\), given a message from the other side (either \(Y^{\star}\) or \(X^{\star}\)).

This key material is the last piece of input required to make the session key, \(SK\), which should be generated as:

\[SK \leftarrow H(A, B, X^{\star}, Y^{\star}, K, pw)\]

Where:

  • \(H\) is a hash function
  • \(A\) identifies the initiating side
  • \(B\) identifies the receiving side
  • \(X^{star}\) is the outbound message from the initiating side
  • \(Y^{star}\) is the outbound message from the receiving side
  • \(K\) is the result of this function
  • \(pw\) is the password (this is what makes it SPAKE2, not SPAKE1)

extractElement :: Group group => Protocol group hashAlgorithm -> ByteString -> Either MessageError (Element group) Source #

Extract an element on the group from an incoming message.

Returns a MessageError if we cannot decode the message, or the other side does not appear to be the expected other side.

TODO: Need to protect against reflection attack at some point.

data MessageError Source #

An error that occurs when interpreting messages from the other side of the exchange.

formatError :: MessageError -> Text Source #

Turn a MessageError into human-readable text.

elementToMessage :: Group group => Protocol group hashAlgorithm -> Element group -> ByteString Source #

Turn an element into a message from this side of the protocol.

createSessionKey Source #

Arguments

:: (Group group, HashAlgorithm hashAlgorithm) 
=> Protocol group hashAlgorithm

The protocol used for this exchange

-> Element group

The message from side A, \(X^{\star}\), or either side if symmetric

-> Element group

The message from side B, \(Y^{\star}\), or either side if symmetric

-> Element group

The calculated key material, \(K\)

-> Password

The shared secret password

-> ByteString

A session key to use for further communication

Create a session key based on the output of SPAKE2.

\[SK \leftarrow H(A, B, X^{\star}, Y^{\star}, K, pw)\]

Including \(pw\) in the session key is what makes this SPAKE2, not SPAKE1.

newtype SideID Source #

Bytes that identify a side of the protocol

Constructors

SideID 

Fields