commit dc9d806977074e3a8dffed3df9b0785ace902264
parent 1ef48519658f06f2e5bf400ded6cd04e8c7fcfb6
Author: Jared Tobin <jared@jtobin.io>
Date: Wed, 31 May 2023 16:26:12 +0400
Add a few Haskell implementations.
Diffstat:
8 files changed, 102 insertions(+), 29 deletions(-)
diff --git a/README.md b/README.md
@@ -2,6 +2,10 @@
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jtobin/cryptopals/blob/master/LICENSE)
+Use e.g. `cabal build cryptopals:exec:fixed-xor` in a Nix shell
+to build binaries. Use `cabal install` to (I think) dump them in
+`$HOME/.cabal/bin`.
+
## Problems
* [Problem Set 1](docs/s1.md)
diff --git a/cryptopals.cabal b/cryptopals.cabal
@@ -12,18 +12,28 @@ cabal-version: >= 1.10
library
default-language: Haskell2010
default-extensions:
- DeriveFoldable
- DeriveFunctor
- DeriveTraversable
LambdaCase
- RankNTypes
- ScopedTypeVariables
- TypeFamilies
- ViewPatterns
+ OverloadedStrings
+ RecordWildCards
hs-source-dirs:
lib
exposed-modules:
- Cryptopals
+ Cryptopals.Util
build-depends:
base
+ , base16
+ , base64
+ , bytestring
+ , text
+executable fixed-xor
+ main-is: FixedXor.hs
+ ghc-options: -Wall -O2
+ default-language: Haskell2010
+ hs-source-dirs: src
+ build-depends:
+ base
+ , bytestring
+ , cryptopals
+ , optparse-applicative
+ , text
diff --git a/data/s1/q2_input.txt b/data/s1/q2_input0.txt
diff --git a/data/s1/q2_against.txt b/data/s1/q2_input1.txt
diff --git a/docs/s1.md b/docs/s1.md
@@ -28,9 +28,6 @@ equality:
$ diff <(xxd -r -p data/s1/q1_input.txt | base64) data/s1/q1_output.txt
-In Rust it's easy enough to just use the appropriate functionality from the
-`hex` and `base64` crates.
-
#### 1.2
Fixed-xor just encrypts by xoring every bit with some corresponding bit.
@@ -54,24 +51,15 @@ zero-padded:
$ echo 'obase=16; ibase=2; 000011110000001000000111' | bc
F0207
-The `fixed_xor` binary in `./bin` will perform the reverse task on the
-zero-padded string here:
+The `fixed_xor` executable included will perform the reverse task on the
+zero-padded string:
- $ ./bin/fixed_xor '0f0207' $(echo -n ICE | xxd -p) | xxd -r -p
+ $ fixed_xor '0f0207' $(echo -n ICE | xxd -p) | xxd -r -p
FAB
-The Rust implementation is trivial:
-
- fn fixed_xor(target: &[u8], partner: &[u8]) -> Vec<u8> {
- target.iter()
- .zip(partner)
- .map(|(l, r)| l ^ r)
- .collect()
- }
-
-And running `fixed_xor` on the question input yields the following:
+and running `fixed_xor` on the question input yields the following:
- $ SOLUTION=$(./bin/fixed_xor $(< data/s1/q2_input.txt) $(< data/s1/q2_against.txt))
+ $ SOLUTION=$(fixed_xor $(< data/s1/q2_input0.txt) $(< data/s1/q2_input1.txt))
746865206b696420646f6e277420706c6179
The ASCII encoding is fun:
diff --git a/lib/Cryptopals.hs b/lib/Cryptopals.hs
@@ -1,4 +0,0 @@
-
-module Cryptopals where
-
-
diff --git a/lib/Cryptopals/Util.hs b/lib/Cryptopals/Util.hs
@@ -0,0 +1,33 @@
+module Cryptopals.Util (
+ Hex(..)
+ , Base64(..)
+
+ , hexToB64
+ , fixedXor
+ ) where
+
+import qualified Data.Bits as B
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Base16 as B16
+import qualified Data.ByteString.Base64 as B64
+import qualified Data.Text as T
+
+newtype Hex = Hex BS.ByteString
+ deriving (Eq, Show)
+
+newtype Base64 = Base64 BS.ByteString
+ deriving (Eq, Show)
+
+hexToB64 :: Hex -> Either T.Text Base64
+hexToB64 (Hex b) = do
+ b16 <- B16.decodeBase16 b
+ pure $ Base64 (B64.encodeBase64' b16)
+
+fixedXor :: Hex -> Hex -> Either T.Text Hex
+fixedXor (Hex a) (Hex b) = do
+ l <- B16.decodeBase16 a
+ r <- B16.decodeBase16 b
+ if BS.length l /= BS.length r
+ then Left "fixedXor: unequal-length buffers"
+ else pure $ Hex (B16.encodeBase16' . BS.pack $ BS.zipWith B.xor l r)
+
diff --git a/src/FixedXor.hs b/src/FixedXor.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Main where
+
+import Cryptopals.Util (Hex(..))
+import qualified Cryptopals.Util as CU
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+import qualified Data.Text.Encoding as TE
+import qualified Options.Applicative as O
+
+data Args = Args {
+ argsKey :: T.Text
+ , argsInp :: T.Text
+ }
+
+ops :: O.Parser Args
+ops = Args
+ <$> O.argument O.str (O.metavar "KEY")
+ <*> O.argument O.str (O.metavar "INPUT")
+
+fxor :: Args -> IO ()
+fxor Args {..} = do
+ let k = Hex (TE.encodeUtf8 argsKey)
+ v = Hex (TE.encodeUtf8 argsInp)
+ r = CU.fixedXor k v
+
+ case r of
+ Left e -> TIO.putStrLn e
+ Right (Hex b) -> TIO.putStrLn (TE.decodeUtf8 b)
+
+main :: IO ()
+main = do
+ let pars = O.info (O.helper <*> ops) $
+ O.fullDesc
+ <> O.progDesc "compute fixed-xor KEY on INPUT"
+ <> O.header "fixed-xor"
+
+ args <- O.execParser pars
+
+ fxor args
+