-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/
-- | Implementation of the SPAKE2 Password-Authenticated Key Exchange algorithm
--
-- This library implements the SPAKE2 password-authenticated key exchange
-- (PAKE) algorithm. This allows two parties, who share a weak
-- password, to safely derive a strong shared secret (and therefore build
-- an encrypted+authenticated channel).
@package spake2
@version 0.4.3
module Crypto.Spake2.Util
-- | Take an arbitrary sequence of bytes and expand it to be the given
-- number of bytes. Do this by extracting a pseudo-random key and
-- expanding it using HKDF.
expandData :: (ByteArrayAccess input, ByteArray output) => ByteString -> input -> Int -> output
-- | Given a seed value for an arbitrary element (see
-- arbitraryElement), expand it to be of the given length.
expandArbitraryElementSeed :: (ByteArrayAccess ikm, ByteArray out) => ikm -> Int -> out
-- | Deserialize a number according to the SPAKE2 protocol.
--
-- Just kidding, there isn't a SPAKE2 protocol. This just matches the
-- Python implementation.
--
-- Inverse of numberToBytes.
bytesToNumber :: ByteArrayAccess bytes => bytes -> Integer
-- | Serialize a number according to the SPAKE2 protocol.
--
-- Just kidding, there isn't a SPAKE2 protocol. This just matches the
-- Python implementation.
--
-- Inverse of bytesToNumber.
numberToBytes :: ByteArray bytes => Int -> Integer -> Maybe bytes
-- | Serialize a number according to the SPAKE2 protocol.
--
-- Panics if the number is too big to fit into the given number of bytes.
unsafeNumberToBytes :: ByteArray bytes => Int -> Integer -> bytes
module Crypto.Spake2.Group
-- | A group where elementAdd is commutative.
--
-- That is, where
--
--
-- \x y -> elementAdd group x y == elementAdd group y x
--
--
-- This property leads to a natural <math>-module, where scalar
-- multiplication is defined as repeatedly calling elementAdd.
--
-- Definitions
--
-- Warning: this gets algebraic.
--
-- A module is a ring <math> together with an abelian group
-- <math>, and a new operator <math> (i.e. scalar
-- multiplication) such that:
--
--
-- - <math>
-- - <math>
-- - <math>
-- - <math>
--
--
-- for all <math> in <math>, and <math> in
-- <math>, where <math> is the identity of the ring.
--
-- A ring <math> a set <math> with two operators such
-- that:
--
--
-- - <math> is an abelian group under <math>
-- - <math> is a monoid under <math>
-- - <math> is _distributive_ with respect to <math>. That
-- is,
-- - (a cdot (b + c) = (a cdot b) + (a cdot c) (left
-- distributivity)
-- - ((b + c) cdot a) = (b cdot a) + (c cdot a) (right
-- distributivity)
--
--
-- Note we have to define left & right distributivity, because
-- <math> might not be commutative.
--
-- A monoid is a group without the notion of inverse. See
-- Haskell's Monoid typeclass.
--
-- A <math>-module is a module where the ring <math> is the
-- integers with normal addition and multiplication.
class Group group => AbelianGroup group where {
-- | A scalar for this group. Mathematically equivalent to an integer, but
-- possibly stored differently for computational reasons.
type family Scalar group :: *;
}
-- | Multiply an element of the group with respect to a scalar.
--
-- This is equivalent to adding the element to itself N times, where N is
-- a scalar. The default implementation does exactly that.
scalarMultiply :: AbelianGroup group => group -> Scalar group -> Element group -> Element group
-- | Get the scalar that corresponds to an integer.
--
-- Note [Added for completeness]
--
--
-- \x -> scalarToInteger group (integerToScalar group x) == x
--
integerToScalar :: AbelianGroup group => group -> Integer -> Scalar group
-- | Get the integer that corresponds to a scalar.
--
-- Note [Added for completeness]
--
--
-- \x -> integerToScalar group (scalarToInteger group x) == x
--
scalarToInteger :: AbelianGroup group => group -> Scalar group -> Integer
-- | Size of scalars, in bits
scalarSizeBits :: AbelianGroup group => group -> Int
-- | Encode a scalar into bytes. | Generate a new random element of the
-- group, with corresponding scalar.
generateElement :: (AbelianGroup group, MonadRandom randomly) => group -> randomly (KeyPair group)
-- | A mathematical group intended to be used with SPAKE2.
class Group group where {
-- | An element of the group.
type family Element group :: *;
}
-- | Group addition.
--
--
-- \x y z -> elementAdd group (elementAdd group x y) z == elementAdd group x (elementAdd group y z)
--
elementAdd :: Group group => group -> Element group -> Element group -> Element group
-- | Inverse with respect to group addition.
--
--
-- \x -> (elementAdd group x (elementNegate group x)) == groupIdentity
--
--
--
-- \x -> (elementNegate group (elementNegate group x)) == x
--
elementNegate :: Group group => group -> Element group -> Element group
-- | Subtract one element from another.
--
--
-- \x y -> (elementSubtract group x y) == (elementAdd group x (elementNegate group y))
--
elementSubtract :: Group group => group -> Element group -> Element group -> Element group
-- | Identity of the group.
--
-- Note [Added for completeness]
--
--
-- \x -> (elementAdd group x groupIdentity) == x
--
--
--
-- \x -> (elementAdd group groupIdentity x) == x
--
groupIdentity :: Group group => group -> Element group
-- | Encode an element of the group into bytes.
--
-- Note [Byte encoding in Group]
--
--
-- \x -> decodeElement group (encodeElement group x) == CryptoPassed x
--
encodeElement :: (Group group, ByteArray bytes) => group -> Element group -> bytes
-- | Decode an element into the group from some bytes.
--
-- Note [Byte encoding in Group]
decodeElement :: (Group group, ByteArray bytes) => group -> bytes -> CryptoFailable (Element group)
-- | Size of elements, in bits
elementSizeBits :: Group group => group -> Int
-- | Deterministically create an arbitrary element from a seed bytestring.
--
-- XXX: jml would much rather this take a scalar, an element, or
-- even an integer, rather than bytes because bytes mean that the group
-- instances have to know about hash algorithms and HKDF. If the
-- IntegerGroup class in SPAKE2 also oversized its input, then it and the
-- ed25519 implementation would have identical decoding.
arbitraryElement :: (Group group, ByteArrayAccess bytes) => group -> bytes -> Element group
-- | Map some arbitrary bytes into a scalar in a group.
decodeScalar :: (ByteArrayAccess bytes, AbelianGroup group) => group -> bytes -> Scalar group
-- | Size of elements in a group, in bits.
elementSizeBytes :: Group group => group -> Int
-- | Size of scalars in a group, in bytes.
scalarSizeBytes :: AbelianGroup group => group -> Int
-- | A group key pair composed of the private part (a scalar) and a public
-- part (associated group element).
data KeyPair group
KeyPair :: !Element group -> !Scalar group -> KeyPair group
[keyPairPublic] :: KeyPair group -> !Element group
[keyPairPrivate] :: KeyPair group -> !Scalar group
-- | This module ignores everything about networks, bytes, encoding, hash
-- functions, and so forth. All it does is provide the mathematical
-- building blocks for SPAKE2, as per Simple Password-Based Encrypted
-- Key Exchange Protocols by Michel Abdalla and David Pointcheval.
--
-- How it works
--
-- Preliminaries
--
-- Let's say we have two users, user A and user B. They have already
-- agreed on the following public information:
--
--
-- - cyclic group, <math> of prime order, <math>
-- - generating element <math>, such that <math>
-- - hash algorithm to use, <math>
--
--
-- If the connection is asymmetric (e.g. if user A is a client and user B
-- is a server), then they will also have:
--
--
-- - two arbitrary elements in <math>, where <math> is
-- associated with user A and <math> with user B.
--
--
-- If the connection is symmetric (e.g. if user A and B are arbitrary
-- peers), then they will instead have:
--
--
-- - a single arbitrary element <math>
--
--
-- The discrete log of these arbitrary elements must be difficult to
-- guess.
--
-- And, they also have a secret password, which in practice will be an
-- arbitrary byte string, but for the purposes of this module is an
-- arbitrary scalar in the group that is a shared secret between
-- both parties (see Crypto.Spake2.Groups for more information on
-- scalars).
--
-- The protocol
--
-- This is derived from the paper linked above.
--
-- One side, A, initiates the exchange. They draw a random scalar,
-- <math>, and matching element, <math>, from the group. They
-- then "blind" <math> by adding it to <math> multiplied by
-- the password in scalar form. Call this <math>.
--
-- <math>
--
-- to the other side, side B.
--
-- Side B does the same thing, except they use <math> instead of
-- <math> to blind the result, and they call it <math>
-- instead of <math>.
--
-- <math>
--
-- After side A receives <math>, it calculates <math>, which
-- is the last missing input in calculating the session key.
--
-- <math>
--
-- That is, <math> is <math> subtracted from <math>
-- scalar multiplied by <math>, all of which is scalar multiplied
-- by <math>.
--
-- Side B likewise calculates:
--
-- <math>
--
-- If both parties were honest and knew the password, the keys will be
-- the same on both sides. That is:
--
-- <math>
--
-- How to use the keys
--
-- The keys <math> and <math> are not enough to securely
-- encrypt a session. They must be used as input to create a session key.
--
-- Constructing a session key is beyond the scope of this module. See
-- createSessionKey for more information.
module Crypto.Spake2.Math
-- | An instance of the SPAKE2 protocol. This represents one side of the
-- protocol.
data Spake2 group
Spake2 :: Params group -> Scalar group -> Spake2 group
[params] :: Spake2 group -> Params group
[password] :: Spake2 group -> Scalar group
-- | The parameters of the SPAKE2 protocol. The other side needs to be
-- using the same values, but with swapped values for ourBlind and
-- theirBlind.
data Params group
Params :: group -> Element group -> Element group -> Params group
-- | The cyclic group used for encrypting keys
[group] :: Params group -> group
-- | The "blind" we use when sending out values. Side A refers to this as
-- <math> in the protocol description.
[ourBlind] :: Params group -> Element group
-- | The "blind" the other side uses when sending values. Side A refers to
-- this as <math> in the protocol description.
[theirBlind] :: Params group -> Element group
-- | Initiate the SPAKE2 exchange. Generates a secret (xy) that
-- will be held by this side, and transmitted to the other side in
-- "blinded" form.
startSpake2 :: (AbelianGroup group, MonadRandom randomly) => Spake2 group -> randomly (Spake2Exchange group)
-- | A SPAKE2 exchange that has been initiated.
data Spake2Exchange group
-- | Determine the element (either <math> or <math>) to send to
-- the other side.
computeOutboundMessage :: AbelianGroup group => Spake2Exchange group -> Element group
-- | Generate key material, <math>, given a message from the other
-- side (either <math> or <math>).
--
-- This key material is the last piece of input required to make the
-- session key, <math>, which should be generated as:
--
-- <math>
--
-- Where:
--
--
-- - <math> is a hash function
-- - <math> identifies the initiating side
-- - <math> identifies the receiving side
-- - <math> is the outbound message from the initiating side
-- - <math> is the outbound message from the receiving side
-- - <math> is the result of this function
-- - <math> is the password (this is what makes it SPAKE2, not
-- SPAKE1)
--
generateKeyMaterial :: AbelianGroup group => Spake2Exchange group -> Element group -> Element group
-- | Each of these implements the Group typeclass.
module Crypto.Spake2.Groups
data Ed25519
Ed25519 :: Ed25519
-- | A finite group of integers with respect to multiplication modulo the
-- group order.
--
-- Construct with makeIntegerGroup.
data IntegerGroup
IntegerGroup :: !Integer -> !Integer -> !Integer -> IntegerGroup
[order] :: IntegerGroup -> !Integer
[subgroupOrder] :: IntegerGroup -> !Integer
[generator] :: IntegerGroup -> !Integer
-- | Construct an IntegerGroup.
--
-- Will fail if generator is '1', since having the identity for a
-- generator means the subgroup is the entire group.
--
-- TODO: Find other things to check for validity.
makeIntegerGroup :: Integer -> Integer -> Integer -> Maybe IntegerGroup
-- | 1024 bit integer group.
--
-- Originally from
-- http://haofeng66.googlepages.com/JPAKEDemo.java, via
-- python-spake2.
i1024 :: IntegerGroup
-- | 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, <math>
-- - group to use, <math>
-- - 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 <math> and <math>,
-- 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
--
--
module Crypto.Spake2
-- | 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.
data Password
-- | Construct a password.
makePassword :: ByteString -> Password
-- | 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.
data Protocol group hashAlgorithm
-- | Construct an asymmetric SPAKE2 protocol.
makeAsymmetricProtocol :: hashAlgorithm -> group -> Element group -> Element group -> SideID -> SideID -> WhichSide -> Protocol group hashAlgorithm
-- | Construct a symmetric SPAKE2 protocol.
makeSymmetricProtocol :: hashAlgorithm -> group -> Element group -> SideID -> Protocol group hashAlgorithm
-- | Perform an entire SPAKE2 exchange.
--
-- Given a SPAKE2 protocol that has all of the parameters for this
-- exchange, generate a one-off message from this side and receive a one
-- off message from the other.
--
-- Once we are done, return a key shared between both sides for a single
-- session.
--
-- Note: as per the SPAKE2 definition, the session key is not guaranteed
-- to actually work. If the other side has failed to authenticate,
-- you will still get a session key. Therefore, you must exchange some
-- other message that has been encrypted using this key in order to
-- confirm that the session key is indeed shared.
--
-- Note: the "send" and "receive" actions are performed
-- concurrently. If you have ordering requirements, consider using
-- a TVar or MVar to coordinate, or implementing your own
-- equivalent of spake2Exchange.
--
-- If the message received from the other side cannot be parsed, return a
-- MessageError.
--
-- Since 0.4.0.
spake2Exchange :: (AbelianGroup group, HashAlgorithm hashAlgorithm) => Protocol group hashAlgorithm -> Password -> (ByteString -> IO ()) -> IO (Either error ByteString) -> IO (Either (MessageError error) ByteString)
-- | Commence a SPAKE2 exchange.
startSpake2 :: (MonadRandom randomly, AbelianGroup group) => Protocol group hashAlgorithm -> Password -> randomly (Spake2Exchange group)
-- | Determine the element (either <math> or <math>) to send to
-- the other side.
computeOutboundMessage :: AbelianGroup group => Spake2Exchange group -> Element group
-- | Generate key material, <math>, given a message from the other
-- side (either <math> or <math>).
--
-- This key material is the last piece of input required to make the
-- session key, <math>, which should be generated as:
--
-- <math>
--
-- Where:
--
--
-- - <math> is a hash function
-- - <math> identifies the initiating side
-- - <math> identifies the receiving side
-- - <math> is the outbound message from the initiating side
-- - <math> is the outbound message from the receiving side
-- - <math> is the result of this function
-- - <math> is the password (this is what makes it SPAKE2, not
-- SPAKE1)
--
generateKeyMaterial :: AbelianGroup group => Spake2Exchange group -> Element group -> Element group
-- | 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.
extractElement :: Group group => Protocol group hashAlgorithm -> ByteString -> Either (MessageError error) (Element group)
-- | An error that occurs when interpreting messages from the other side of
-- the exchange.
data MessageError e
-- | Turn a MessageError into human-readable text.
formatError :: Show e => MessageError e -> Text
-- | Turn an element into a message from this side of the protocol.
elementToMessage :: Group group => Protocol group hashAlgorithm -> Element group -> ByteString
-- | Create a session key based on the output of SPAKE2.
--
-- <math>
--
-- Including <math> in the session key is what makes this SPAKE2,
-- not SPAKE1.
--
-- Note: In spake2 0.3 and earlier, The <math> and
-- <math> were expected to be from side A and side B respectively.
-- Since spake2 0.4, they are the outbound and inbound elements
-- respectively. This fixes an interoperability concern with the Python
-- library, and reduces the burden on the caller. Apologies for the
-- possibly breaking change to any users of older versions of spake2.
createSessionKey :: (Group group, HashAlgorithm hashAlgorithm) => Protocol group hashAlgorithm -> Element group -> Element group -> Element group -> Password -> ByteString
-- | Bytes that identify a side of the protocol
newtype SideID
SideID :: ByteString -> SideID
[unSideID] :: SideID -> ByteString
-- | Which side we are.
data WhichSide
SideA :: WhichSide
SideB :: WhichSide
instance GHC.Enum.Enum Crypto.Spake2.WhichSide
instance GHC.Enum.Bounded Crypto.Spake2.WhichSide
instance GHC.Show.Show Crypto.Spake2.WhichSide
instance GHC.Classes.Ord Crypto.Spake2.WhichSide
instance GHC.Classes.Eq Crypto.Spake2.WhichSide
instance GHC.Show.Show e => GHC.Show.Show (Crypto.Spake2.MessageError e)
instance GHC.Classes.Eq e => GHC.Classes.Eq (Crypto.Spake2.MessageError e)
instance GHC.Show.Show Crypto.Spake2.SideID
instance GHC.Classes.Ord Crypto.Spake2.SideID
instance GHC.Classes.Eq Crypto.Spake2.SideID
instance GHC.Classes.Ord Crypto.Spake2.Password
instance GHC.Classes.Eq Crypto.Spake2.Password