module Crypto.RNCryptor.V3.Decrypt
( parseHeader
, decrypt
, decryptBlock
, decryptStream
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Word
import Control.Monad.State
import Crypto.RNCryptor.Types
import Crypto.RNCryptor.V3.Stream
import Crypto.Cipher.AES
import Data.Monoid
import qualified System.IO.Streams as S
parseHeader :: ByteString -> RNCryptorHeader
parseHeader input = flip evalState input $ do
v <- parseVersion
o <- parseOptions
eSalt <- parseEncryptionSalt
hmacSalt <- parseHMACSalt
iv <- parseIV
return RNCryptorHeader {
rncVersion = v
, rncOptions = o
, rncEncryptionSalt = eSalt
, rncHMACSalt = hmacSalt
, rncIV = iv
, rncHMAC = parseHMAC
}
parseSingleWord8 :: String -> State ByteString Word8
parseSingleWord8 err = do
bs <- get
let (v,vs) = B.splitAt 1 bs
put vs
case B.unpack v of
x:[] -> return x
_ -> fail err
parseBSOfSize :: Int -> String -> State ByteString ByteString
parseBSOfSize sz err = do
bs <- get
let (v,vs) = B.splitAt sz bs
put vs
case B.unpack v of
[] -> fail err
_ -> return v
parseVersion :: State ByteString Word8
parseVersion = parseSingleWord8 "parseVersion: not enough bytes."
parseOptions :: State ByteString Word8
parseOptions = parseSingleWord8 "parseOptions: not enough bytes."
parseEncryptionSalt :: State ByteString ByteString
parseEncryptionSalt = parseBSOfSize 8 "parseEncryptionSalt: not enough bytes."
parseHMACSalt :: State ByteString ByteString
parseHMACSalt = parseBSOfSize 8 "parseHMACSalt: not enough bytes."
parseIV :: State ByteString ByteString
parseIV = parseBSOfSize 16 "parseIV: not enough bytes."
parseHMAC :: ByteString -> ByteString
parseHMAC leftover = flip evalState leftover $ parseBSOfSize 32 "parseHMAC: not enough bytes."
removePaddingSymbols :: ByteString -> ByteString
removePaddingSymbols input =
let lastWord = B.last input
in B.take (B.length input fromEnum lastWord) input
decryptBlock :: RNCryptorContext
-> ByteString
-> (RNCryptorContext, ByteString)
decryptBlock ctx cipherText =
let clearText = decryptCBC (ctxCipher ctx) (aesIV_ . rncIV . ctxHeader $ ctx) cipherText
!sz = B.length cipherText
!newHeader = (ctxHeader ctx) { rncIV = (B.drop (sz 16) cipherText) }
in (ctx { ctxHeader = newHeader }, clearText)
decrypt :: ByteString -> ByteString -> ByteString
decrypt input pwd =
let (rawHdr, rest) = B.splitAt 34 input
(toDecrypt, _) = B.splitAt (B.length rest 32) rest
hdr = parseHeader rawHdr
ctx = newRNCryptorContext pwd hdr
clearText = decryptCBC (ctxCipher ctx) (aesIV_ . rncIV . ctxHeader $ ctx) toDecrypt
in removePaddingSymbols clearText
decryptStream :: ByteString
-> S.InputStream ByteString
-> S.OutputStream ByteString
-> IO ()
decryptStream userKey inS outS = do
rawHdr <- S.readExactly 34 inS
let hdr = parseHeader rawHdr
let ctx = newRNCryptorContext userKey hdr
processStream ctx inS outS decryptBlock finaliseDecryption
where
finaliseDecryption lastBlock ctx = do
let (rest, _) = B.splitAt (B.length lastBlock 32) lastBlock
S.write (Just $ removePaddingSymbols (snd $ decryptBlock ctx rest)) outS