aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Web/ZeroBin.hs36
-rw-r--r--src/Web/ZeroBin/SJCL.hs26
-rw-r--r--src/Web/ZeroBin/Utils.hs13
3 files changed, 60 insertions, 15 deletions
diff --git a/src/Web/ZeroBin.hs b/src/Web/ZeroBin.hs
index 94c2b17..f6ba051 100644
--- a/src/Web/ZeroBin.hs
+++ b/src/Web/ZeroBin.hs
@@ -1,3 +1,14 @@
+{-|
+High-level functions for posting to 0bin services like
+<http://0bin.net> or <http://paste.ec>.
+
+ >>> import Web.ZeroBin
+ >>> import Data.ByteString.Char8
+ >>> share "http://0bin.net" Day (pack "hello")
+"http://0bin.net/paste/ZH6VyKXjDHAiPT8J#C6LLidGyHO7xt3xuDtsNHjZ77luualukEuJ25S6w/K1m"
+
+-}
+
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
@@ -28,16 +39,22 @@ data Response = Response {
} deriving (Generic, Show)
instance JSON.FromJSON Response
+-- | 0bin error message
data ZeroBinError = ZeroBinError String
deriving (Show, Typeable)
instance Exception ZeroBinError
+-- | Expiration of a paste.
+-- "Burn after reading" really means "burn after two readings",
+-- because we do not redirect to the paste like a browser does.
+-- You can verify your paste before sharing the link.
+-- Original <http://0bin.net> does not support 'Week'.
data Expiration
- = Once
- | Day
- | Week
- | Month
- | Never
+ = Once -- ^ burn after reading
+ | Day -- ^ keep for 24 hours
+ | Week -- ^ for 7 days
+ | Month -- ^ for 30 days
+ | Never -- ^ for 100 years
form :: Expiration -> String
form Once = "burn_after_reading"
@@ -60,7 +77,14 @@ post bin ex ct = do
"ok" -> return $ bin ++ "/paste/" ++ (fromJust . paste) resp
_ -> throwIO . ZeroBinError $ (fromJust . message) resp
-share :: String -> Expiration -> ByteString -> IO String
+
+-- | Encrypts the plain data with a random password,
+-- post to 0bin and return the URI of a new paste.
+-- Can throw 'ZeroBinError' or 'Network.HTTP.Conduit.HttpException'.
+share :: String -- ^ the address of 0bin, e. g. <http://0bin.net> or <https://paste.ec>
+ -> Expiration
+ -> ByteString -- ^ the plain data to encrypt and paste
+ -> IO String -- ^ the URI of paste
share bin ex txt = do
pwd <- makePassword 33
cnt <- encrypt pwd (encode txt)
diff --git a/src/Web/ZeroBin/SJCL.hs b/src/Web/ZeroBin/SJCL.hs
index faa2db0..60e8737 100644
--- a/src/Web/ZeroBin/SJCL.hs
+++ b/src/Web/ZeroBin/SJCL.hs
@@ -1,3 +1,12 @@
+{-|
+Encryption compatible with <https://crypto.stanford.edu/sjcl/ SJCL>
+
+ >>> import Web.ZeroBin.SJCL
+ >>> import Data.ByteString.Char8
+ >>> encrypt "secret-word" (pack "hello")
+Content {iv = "VxyuJRVtKJqhG2iR/sPjAQ", salt = "AhnDuP1CkTCBlQTHgw", ct = "cqr7/pMRXrcROmcgwA"}
+-}
+
{-# LANGUAGE DeriveGeneric #-}
module Web.ZeroBin.SJCL (
@@ -23,10 +32,11 @@ import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
+-- | Encrypted content. Each field is a 'toWeb'-encoded byte-string
data Content = Content {
- iv :: String
- , salt :: String
- , ct :: String
+ iv :: String -- ^ random initialization vector (IV)
+ , salt :: String -- ^ random salt
+ , ct :: String -- ^ encrypted data
} deriving (Generic, Show)
-- FIXME: http://stackoverflow.com/questions/33045350/unexpected-haskell-aeson-warning-no-explicit-implementation-for-tojson
@@ -53,9 +63,13 @@ chunks sz = split
lengthOf :: Int -> Word8
lengthOf = ceiling . (logBase 256 :: Float -> Float) . fromIntegral
--- Ref. https://tools.ietf.org/html/rfc3610
--- SJCL uses 64-bit tag (8 bytes)
-encrypt :: String -> ByteString -> IO Content
+-- | <https://crypto.stanford.edu/sjcl/ SJCL>-compatible encryption function.
+-- Follows <https://tools.ietf.org/html/rfc3610 RFC3610> with a 8-bytes tag.
+-- Uses 16-bytes cipher key generated from the password and a random 'salt'
+-- by PBKDF2-HMAC-SHA256 with 1000 iterations.
+encrypt :: String -- ^ the password
+ -> ByteString -- ^ the plain data to encrypt
+ -> IO Content
encrypt password plaintext = do
ivd <- getEntropy 16 -- XXX it is truncated to get the nonce below
slt <- getEntropy 13 -- arbitrary length
diff --git a/src/Web/ZeroBin/Utils.hs b/src/Web/ZeroBin/Utils.hs
index 8f29ec4..559d9ec 100644
--- a/src/Web/ZeroBin/Utils.hs
+++ b/src/Web/ZeroBin/Utils.hs
@@ -1,3 +1,7 @@
+{-|
+Various utility functions
+-}
+
module Web.ZeroBin.Utils (
toWeb
, makePassword
@@ -8,10 +12,13 @@ import Data.ByteString (ByteString)
import Data.ByteString.Base64 (encode)
import Data.ByteString.Char8 (unpack)
-
-toWeb :: ByteString -> String
+-- | Encodes to base64 and drops padding '='.
+toWeb :: ByteString -- ^ the data to encode
+ -> String -- ^ base64 string without padding
toWeb = takeWhile (/= '=') . unpack . encode
-makePassword :: Int -> IO String
+-- | Makes a random password
+makePassword :: Int -- ^ the number of bytes of entropy
+ -> IO String -- ^ random byte-string encoded by 'toWeb'
makePassword n = toWeb `fmap` getEntropy n