Skip to content

Commit

Permalink
docs in expansion and crypt modules
Browse files Browse the repository at this point in the history
  • Loading branch information
oxarbitrage committed Oct 23, 2023
1 parent d787302 commit c483e05
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 123 deletions.
17 changes: 16 additions & 1 deletion scripts/prologue
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,19 @@

[skip to module list](#module-list)

This site contains Haskell documentation of the category theory implementation of the salsa20 crypto cipher.
Salsa20 is a widely used stream cipher designed for fast and secure encryption. This project contains a pure Haskell implementation of the Salsa20 cipher, organized into several sub-modules.

The implementation is organized into the following modules:

- 'Quarterround': Defines the quarterround operation used in the Salsa20 cipher.
- 'Rowround': Implements the rowround operation, a component of the Salsa20 cipher.
- 'Columnround': Implements the columnround operation, treating it as rowround expressions with the input transposed.
- 'Doubleround': Defines the doubleround function as the composition of rowround and columnround. Also, provides a variant 'doubleroundR' for a specified number of rounds.
- 'Hash': Implements the Salsa20 core function and the extended hash expressions.
- 'Utils': Provides utility functions used in the Salsa20 cipher, such as little-endian encoding, matrix operations, and reduction functions.
- 'Expansion': Implements functions for Salsa20 key expansion and matrix generation.
- 'Crypt': Provides functions for Salsa20 encryption and decryption.

The Salsa20 cipher itself is a stream cipher that operates on 64-byte blocks, producing a keystream that is XORed with the plaintext to generate ciphertext. It is known for its efficiency and security.

For usage instructions and additional details, refer to the documentation in each module.
228 changes: 140 additions & 88 deletions src/Crypt.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@ License : MIT
Stability : experimental
Portability : POSIX
The salsa20 encryption and decryption.
The 'Crypt' module provides functions for Salsa20 encryption and decryption. Salsa20 is a symmetric key stream cipher designed for fast and secure encryption of data.
This module includes functions to calculate an index over 64, generate Salsa20 expansion matrices,
encrypt or decrypt messages using 16-byte and 32-byte keys, and display encryption details as strings or equations.
The module ensures the uniqueness and security of encryption keys by utilizing nonces and augmented keys.
The Salsa20 algorithm is widely used in various cryptographic applications due to its strong security and high performance.
Users can employ the functions provided by this module to secure their data with Salsa20 encryption and, if needed,
perform decryption to retrieve the original content.
Please refer to the individual function documentation for specific details and usage guidelines.
-}
module Crypt
(
cryptBlockV1, cryptBlockV1Display, cryptBlockV1Equations,
cryptBlockV2, cryptBlockV2Display, cryptBlockV2Equations,
cryptBlock16Compute, cryptBlock16Display, cryptBlock16Equations,
cryptBlock32Compute, cryptBlock32Display, cryptBlock32Equations,
)
where

Expand All @@ -22,114 +31,157 @@ import Data.Bits
import Data.Word
import Text.Printf

{- |Calculate an index over 64 as described in the spec.
`i` is the index of a provided l-length message that we want to encrypt. This index is then divided by 64 and the
floor of it is taken.
The resulting number is returned expressed as a unique 8 bytes sequence.
{-|
Calculate an index over the scalar 64 as described in the spec.
Given an index `i` representing the position of a message in a sequence of length `l`,
this function computes the floor of `i / 64` and expresses the result as a unique sequence of 8 bytes.
The resulting sequence is used in the Salsa20 encryption process.
-}
iOver64 :: Integral a => a -> [Word32]
iOver64 index = extractBytes 8 $ floor (fromIntegral index / 64 :: Double)
iOver64Compute :: Integral a => a -> [Word32]
iOver64Compute index = extractBytes 8 $ floor (fromIntegral index / 64 :: Double)

-- |Display the calculation of an index over 64. See `iOver64`.
-- |Display the calculation of an index over the scalar 64. See `iOver64Compute`.
iOver64Display :: String -> [String]
-- TODO: is this the same?
--iOver64Display index = [printf "_(%s/64)" index]
iOver64Display index = displayBytes 8 $ printf "_(%s/64)" index

{- |Join the nonce and the calculated `iOver64`.
Nonce is 8 bytes and index 8 bytes more for a total of 16 as the result.
{-|
Join the nonce with the calculated `iOver64Compute` to create an extended nonce.
The `nonce` is an 8-byte sequence, and the `iOver64Compute` function produces an additional 8-byte sequence.
When combined, they form a 16-byte extended nonce. This extended nonce is used in Salsa20 encryption to ensure the
uniqueness of the encryption keys.
-}
nonceAndiOver64 :: [Word32] -> Int -> [Word32]
nonceAndiOver64 nonce index
| length nonce == 8 = nonce ++ iOver64 index
| otherwise = error "First input to `nonceAndiOver64` must be a list of 8 `Word32` numbers"
nonceAndiOver64Compute :: [Word32] -> Int -> [Word32]
nonceAndiOver64Compute nonce index
| length nonce == 8 = nonce ++ iOver64Compute index
| otherwise = error "First input to `nonceAndiOver64Compute` must be a list of 8 `Word32` numbers"

-- |Join the nonce and the calculated `iOver64Display`.
-- |Join the nonce with the calculated `iOver64Display`. See `nonceAndiOver64Compute`.
nonceAndiOver64Display :: [String] -> String -> [String]
nonceAndiOver64Display nonce index
| length nonce == 8 = nonce ++ iOver64Display index
| otherwise = error "first input to `nonceAndiOver64Display` must be a list of 8 `String` strings"

-- |Given a single 16 bytes key, a nonce and an index of a message byte, get the salsa20 expanded matrix of it.
cryptV1 :: [Word32] -> [Word32] -> Int -> [Word32]
cryptV1 key nonce index
| length key == 16 && length nonce == 8 = expand16Compute key $ nonceAndiOver64 nonce index
| otherwise = error "first input to `cryptV1` must be a list of 16 `Word32` numbers and the second a list of 8 `Word32` numbers"

-- |Given a single 16 bytes key, a nonce and an index get the salsa20 expanded matrix of it.
cryptV1Display :: [String] -> [String] -> String -> [String]
cryptV1Display key nonce index
| length key == 16 && length nonce == 8 = expand16Display key $ nonceAndiOver64Display nonce index
| otherwise = error "first input to `cryptV1Display` must be a list of 16 `String` strings and the second a list of 8 `String` strings"

-- |Given two 16 bytes keys, a nonce and an index get the salsa20 expanded matrix of it.
cryptV2 :: [Word32] -> [Word32] -> [Word32] -> Int -> [Word32]
cryptV2 key0 key1 nonce index
| length key0 == 16 && length key1 == 16 && length nonce == 8 = expand32Compute key0 key1 $ nonceAndiOver64 nonce index
| otherwise = error "first input to `cryptV2` must be a list of 16 `Word32` numbers, the second a list of 16 `Word32` numbers and the third a list of 8 `Word32` numbers"

-- |Given two 16 bytes keys, a nonce and an index get the salsa20 expanded matrix of it.
cryptV2Display :: [String] -> [String] -> [String] -> String -> [String]
cryptV2Display key0 key1 nonce index
| length key0 == 16 && length key1 == 16 && length nonce == 8 = expand32Display key0 key1 $ nonceAndiOver64Display nonce index
| otherwise = error "first input to `cryptV2Display` must be a list of 16 `String` strings, the second a list of 16 `String` strings and the third a list of 8 `String` strings"

-- |Given an aumented key and an index of it, returns the number corresponding to that index.
keybyte :: [Word32] -> Int -> Word32
keybyte aumented_key index
{-|
Retrieve a specific byte from the augmented key.
Given an augmented key, this function extracts the byte at the specified index and returns it as a `Word32`.
The augmented key is an array of 64 `Word32` values, and the index should be within the range [0, 63] to access
a valid byte.
-}
keybyteCompute :: [Word32] -> Int -> Word32
keybyteCompute aumented_key index
| length aumented_key == 64 = aumented_key!!index
| otherwise = error "first input to `keybyte` must be a list of 64 `Word32` numbers"
| otherwise = error "first input to `keybyteCompute` must be a list of 64 `Word32` numbers"

-- |Given an aumented key as a list of strings and an index of it, returns the string corresponding to that index.
-- |Display a specific byte from the augmented key. See `keybyteCompute`.
keybyteDisplay :: [String] -> Int -> String
keybyteDisplay aumented_key index
| length aumented_key == 64 = aumented_key!!index
| otherwise = error "first input to `keybyteDisplay` must be a list of 64 `String` strings"

-- |Encrypt or decrypt a message with a single 16 bytes key resulting in a list of the same length.
cryptBlockV1 :: [Word32] -> [Word32] -> [Word32] -> Int -> [Word32]
cryptBlockV1 (x:xs) key nonce index
| length key == 16 && length nonce == 8 = xor x (keybyte (cryptV1 key nonce index) (index `mod` 64)) :
cryptBlockV1 xs key nonce (index+1)
| otherwise = error "first input to `cryptBlockV1` must be a list of 16 `Word32` numbers and the second a list of 8 `Word32` numbers"
cryptBlockV1 _ _ _ _ = []

-- |Encrypt or decrypt a message with a single 16 bytes key resulting in a list of the same length.
cryptBlockV1Display :: [String] -> [String] -> [String] -> Int -> [String]
cryptBlockV1Display (x:xs) key nonce index
| length key == 16 && length nonce == 8 = printf "%s ⊕ %s" x (keybyteDisplay (cryptV1Display key nonce (show index)) (index `mod` 64)) :
cryptBlockV1Display xs key nonce (index+1)
| otherwise = error "first input to `cryptBlockV1Display` must be a list of 16 `String` strings and the second a list of 8 `String` strings"
cryptBlockV1Display _ _ _ _ = []

-- |Display the output of `cryptBlockV1` as string equations.
cryptBlockV1Equations :: [String] -> [String] -> [String] -> Int -> [String]
cryptBlockV1Equations message key nonce index
{-|
Generate the Salsa20 expansion matrix for a single 16-byte key.
This function computes the Salsa20 expansion matrix for a given 16-byte key and nonce,
using an index to represent a message byte.
The resulting matrix is generated based on the provided key and nonce, where the key should be a list of
16 `Word32` values, and the nonce should be a list of 8 `Word32` values.
-}
crypt16Compute :: [Word32] -> [Word32] -> Int -> [Word32]
crypt16Compute key nonce index
| length key == 16 && length nonce == 8 = expand16Compute key $ nonceAndiOver64Compute nonce index
| otherwise = error "first input to `crypt16Compute` must be a list of 16 `Word32` numbers and the second a list of 8 `Word32` numbers"

{-|
Generate the Salsa20 expansion matrix as a list of strings for a single 16-byte key.
This function computes the Salsa20 expansion matrix as a list of strings for a given 16-byte key and nonce,
using an index to represent a message byte. The resulting matrix is generated based on the provided key and nonce,
where the key should be a list of 16 `String` values, and the nonce should be a list of 8 `String` values.
The resulting matrix is returned as a list of strings.
-}
crypt16Display :: [String] -> [String] -> String -> [String]
crypt16Display key nonce index
| length key == 16 && length nonce == 8 = expand16Display key $ nonceAndiOver64Display nonce index
| otherwise = error "first input to `crypt16Display` must be a list of 16 `String` strings and the second a list of 8 `String` strings"

{-|
Encrypt or decrypt a message using a single 16-byte key and return a encrypted/decrypted message of the same length.
It takes a list of `Word32` values of any length (message block), a 16-byte key, an 8-byte nonce, and an index representing
the position of the message byte.
The function performs XOR operations on the message block and the corresponding Salsa20 expansion matrix entry.
-}
cryptBlock16Compute :: [Word32] -> [Word32] -> [Word32] -> Int -> [Word32]
cryptBlock16Compute (x:xs) key nonce index
| length key == 16 && length nonce == 8 = xor x (keybyteCompute (crypt16Compute key nonce index) (index `mod` 64)) :
cryptBlock16Compute xs key nonce (index+1)
| otherwise = error "first input to `cryptBlock16Compute` must be a list of 16 `Word32` numbers and the second a list of 8 `Word32` numbers"
cryptBlock16Compute _ _ _ _ = []

-- |Display the encryption or decryption of a message with a single 16 bytes key as a list of strings.
cryptBlock16Display :: [String] -> [String] -> [String] -> Int -> [String]
cryptBlock16Display (x:xs) key nonce index
| length key == 16 && length nonce == 8 = printf "%s ⊕ %s" x (keybyteDisplay (crypt16Display key nonce (show index)) (index `mod` 64)) :
cryptBlock16Display xs key nonce (index+1)
| otherwise = error "first input to `cryptBlock16Display` must be a list of 16 `String` strings and the second a list of 8 `String` strings"
cryptBlock16Display _ _ _ _ = []

-- |Display the encryption or decryption of a message with a single 16 bytes key as a list of equations.
cryptBlock16Equations :: [String] -> [String] -> [String] -> Int -> [String]
cryptBlock16Equations message key nonce index
| length key == 16 && length nonce == 8 =
[printf "z%d = %s" (idx :: Int) eq | (idx, eq) <- zip [0..] (cryptBlockV1Display message key nonce index)]
| otherwise = error "first input to `cryptBlockV1Equations` must be a list of 16 `String` strings and the second a list of 8 `String` strings"
[printf "z%d = %s" (idx :: Int) eq | (idx, eq) <- zip [0..] (cryptBlock16Display message key nonce index)]
| otherwise = error "first input to `cryptBlock16Equations` must be a list of 16 `String` strings and the second a list of 8 `String` strings"

-- |Encrypt or decrypt a message with two 16 bytes key resulting in a list of the same length.
cryptBlockV2 :: [Word32] -> [Word32] -> [Word32] -> [Word32] -> Int -> [Word32]
cryptBlockV2 (x:xs) key0 key1 nonce index
{-|
Generate the Salsa20 expansion matrix for a 32-byte key.
This function computes the Salsa20 expansion matrix for two given 16-byte keys and nonce,
using an index to represent a message byte.
The resulting matrix is generated based on the provided key and nonce, where the key should be two lists of
16 `Word32` values, and the nonce should be a list of 8 `Word32` values.
-}
crypt32Compute :: [Word32] -> [Word32] -> [Word32] -> Int -> [Word32]
crypt32Compute key0 key1 nonce index
| length key0 == 16 && length key1 == 16 && length nonce == 8 = expand32Compute key0 key1 $ nonceAndiOver64Compute nonce index
| otherwise = error "first input to `crypt32Compute` must be a list of 16 `Word32` numbers, the second a list of 16 `Word32` numbers and the third a list of 8 `Word32` numbers"

{-|
Generate the Salsa20 expansion matrix as a list of strings for a 32-byte key.
This function computes the Salsa20 expansion matrix as a list of strings for two given 16-byte key and nonce,
using an index to represent a message byte. The resulting matrix is generated based on the provided key and nonce,
where the key should be two lists of 16 `String` values, and the nonce should be a list of 8 `String` values.
The resulting matrix is returned as a list of strings.
-}
crypt32Display :: [String] -> [String] -> [String] -> String -> [String]
crypt32Display key0 key1 nonce index
| length key0 == 16 && length key1 == 16 && length nonce == 8 = expand32Display key0 key1 $ nonceAndiOver64Display nonce index
| otherwise = error "first input to `crypt32Display` must be a list of 16 `String` strings, the second a list of 16 `String` strings and the third a list of 8 `String` strings"

{-|
Encrypt or decrypt a message using a 32-byte key and return a encrypted/decrypted message of the same length.
It takes a list of `Word32` values of any length (message block), two 16-byte keys, an 8-byte nonce, and an index representing
the position of the message byte.
The function performs XOR operations on the message block and the corresponding Salsa20 expansion matrix entry.
-}
cryptBlock32Compute :: [Word32] -> [Word32] -> [Word32] -> [Word32] -> Int -> [Word32]
cryptBlock32Compute (x:xs) key0 key1 nonce index
| length key0 == 16 && length key1 == 16 && length nonce == 8 =
xor x (keybyte (cryptV2 key0 key1 nonce index) (index `mod` 64)) :
cryptBlockV2 xs key0 key1 nonce (index+1)
| otherwise = error "first input to `cryptBlockV2` must be a list of 16 `Word32` numbers, the second a list of 16 `Word32` numbers and the third a list of 8 `Word32` numbers"
cryptBlockV2 _ _ _ _ _ = []

-- |Encrypt or decrypt a message with two 16 bytes key resulting in a list of the same length.
cryptBlockV2Display :: [String] -> [String] -> [String] -> [String] -> Int -> [String]
cryptBlockV2Display (x:xs) key0 key1 nonce index
xor x (keybyteCompute (crypt32Compute key0 key1 nonce index) (index `mod` 64)) :
cryptBlock32Compute xs key0 key1 nonce (index+1)
| otherwise = error "first input to `cryptBlock32Compute` must be a list of 16 `Word32` numbers, the second a list of 16 `Word32` numbers and the third a list of 8 `Word32` numbers"
cryptBlock32Compute _ _ _ _ _ = []

-- |Display the encryption or decryption of a message with a two 16 bytes key as a list of strings.
cryptBlock32Display :: [String] -> [String] -> [String] -> [String] -> Int -> [String]
cryptBlock32Display (x:xs) key0 key1 nonce index
| length key0 == 16 && length key1 == 16 && length nonce == 8 =
printf "%s ⊕ %s" x (keybyteDisplay (cryptV2Display key0 key1 nonce (show index)) (index `mod` 64)) :
cryptBlockV2Display xs key0 key1 nonce (index+1)
| otherwise = error "first input to `cryptBlockV2Display` must be a list of 16 `String` strings, the second a list of 16 `String` strings and the third a list of 8 `String` strings"
cryptBlockV2Display _ _ _ _ _ = []

-- |Display the output of `cryptBlockV2` as string equations.
cryptBlockV2Equations :: [String] -> [String] -> [String] -> [String] -> Int -> [String]
cryptBlockV2Equations message key0 key1 nonce index
printf "%s ⊕ %s" x (keybyteDisplay (crypt32Display key0 key1 nonce (show index)) (index `mod` 64)) :
cryptBlock32Display xs key0 key1 nonce (index+1)
| otherwise = error "first input to `cryptBlock32Display` must be a list of 16 `String` strings, the second a list of 16 `String` strings and the third a list of 8 `String` strings"
cryptBlock32Display _ _ _ _ _ = []

-- |Display the encryption or decryption of a message with a two 16 bytes key as a list of equations.
cryptBlock32Equations :: [String] -> [String] -> [String] -> [String] -> Int -> [String]
cryptBlock32Equations message key0 key1 nonce index
| length key0 == 16 && length key1 == 16 && length nonce == 8 =
[printf "z%d = %s" (idx :: Int) eq | (idx, eq) <- zip [0..] (cryptBlockV2Display message key0 key1 nonce index)]
| otherwise = error "first input to `cryptBlockV2Equations` must be a list of 16 `String` strings, the second a list of 16 `String` strings and the third a list of 8 `String` strings"
[printf "z%d = %s" (idx :: Int) eq | (idx, eq) <- zip [0..] (cryptBlock32Display message key0 key1 nonce index)]
| otherwise = error "first input to `cryptBlock32Equations` must be a list of 16 `String` strings, the second a list of 16 `String` strings and the third a list of 8 `String` strings"
Loading

0 comments on commit c483e05

Please sign in to comment.