-- 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.1.0 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 mathematical group intended to be used with SPAKE2. -- -- Notes: -- -- class Group group where type Element group :: * type Scalar group :: * elementSubtract group x y = elementAdd group x (elementNegate group y) where { type family Element group :: *; type family Scalar 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 -- | 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. scalarMultiply :: Group 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 :: Group 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 :: Group group => group -> Scalar group -> Integer -- | 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) -- | Encode a scalar into bytes. | Generate a new random element of the -- group, with corresponding scalar. generateElement :: (Group group, MonadRandom randomly) => group -> randomly (KeyPair group) -- | Size of elements, in bits elementSizeBits :: Group group => group -> Int -- | Size of scalars, in bits scalarSizeBits :: 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, Group 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 :: Group 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) -- | 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 -- | Simple integer addition group. -- -- Do NOT use this for anything cryptographic. newtype IntegerAddition IntegerAddition :: Integer -> IntegerAddition [modulus] :: IntegerAddition -> Integer -- | 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 :: (Group 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 :: Group 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 :: Group group => Spake2Exchange group -> Element group -> Element group -- | 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 -- | Do-nothing function so that we have something to import in our tests. -- TODO: Actually test something genuine and then remove this. something :: a -> a -- | 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 -- | Commence a SPAKE2 exchange. startSpake2 :: (MonadRandom randomly, Group group) => Protocol group hashAlgorithm -> Password -> randomly (Spake2Exchange group) -- | Determine the element (either <math> or <math>) to send to -- the other side. computeOutboundMessage :: Group 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 :: Group 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 (Element group) -- | An error that occurs when interpreting messages from the other side of -- the exchange. data MessageError -- | Turn a MessageError into human-readable text. formatError :: MessageError -> 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. 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 Crypto.Spake2.MessageError instance GHC.Classes.Eq Crypto.Spake2.MessageError 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