-- 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: -- --
    --
  1. <math>
  2. --
  3. <math>
  4. --
  5. <math>
  6. --
  7. <math>
  8. --
-- -- 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: -- --
    --
  1. <math> is an abelian group under <math>
  2. --
  3. <math> is a monoid under <math>
  4. --
  5. <math> is _distributive_ with respect to <math>. That -- is,
  6. --
  7. (a cdot (b + c) = (a cdot b) + (a cdot c) (left -- distributivity)
  8. --
  9. ((b + c) cdot a) = (b cdot a) + (c cdot a) (right -- distributivity)
  10. --
-- -- 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: -- -- -- -- If the connection is asymmetric (e.g. if user A is a client and user B -- is a server), then they will also have: -- -- -- -- If the connection is symmetric (e.g. if user A and B are arbitrary -- peers), then they will instead have: -- -- -- -- 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: -- -- 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: -- -- -- -- 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: -- -- -- -- For a specific exchange: -- -- -- -- --

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

-- -- -- --

Open questions

-- -- -- --

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: -- -- 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