Dust (empty) → 2.0
raw patch · 30 files changed
+2842/−0 lines, 30 filesdep +Dustdep +HUnitdep +QuickChecksetup-changed
Dependencies added: Dust, HUnit, QuickCheck, base, binary, bytestring, cereal, cipher-aes, containers, directory, entropy, ghc-prim, network, random, random-extras, random-fu, random-source, split, test-framework, test-framework-hunit, test-framework-quickcheck2, test-framework-th
Files
- Dust.cabal +124/−0
- Dust/Core/CryptoProtocol.hs +51/−0
- Dust/Core/DustPacket.hs +70/−0
- Dust/Core/Invite.hs +25/−0
- Dust/Core/WireProtocol.hs +53/−0
- Dust/Core/WireProtocolHandler.hs +46/−0
- Dust/Crypto/Curve25519.hs +39/−0
- Dust/Crypto/DustCipher.hs +45/−0
- Dust/Crypto/ECDH.hs +47/−0
- Dust/Crypto/ECDSA.hs +42/−0
- Dust/Crypto/Ed25519.hs +85/−0
- Dust/Crypto/Keys.hs +76/−0
- Dust/Model/Content.hs +55/−0
- Dust/Model/Huffman.hs +153/−0
- Dust/Model/Observations.hs +185/−0
- Dust/Model/Packet.hs +172/−0
- Dust/Model/PacketLength.hs +47/−0
- Dust/Model/Port.hs +25/−0
- Dust/Model/Stats.hs +16/−0
- Dust/Network/DustServer.hs +69/−0
- Dust/Network/TcpClient.hs +20/−0
- Dust/Network/TcpServer.hs +37/−0
- Dust/Network/UdpServer.hs +15/−0
- Dust/Network/Util.hs +25/−0
- LICENSE +339/−0
- Setup.hs +3/−0
- lib/curve25519-donna.c +730/−0
- lib/ed25519-donna/ed25519.c +109/−0
- tests/TestCore.hs +35/−0
- tests/TestCrypto.hs +104/−0
+ Dust.cabal view
@@ -0,0 +1,124 @@+Name: Dust+Version: 2.0+Description: Dust is a polymorphic protocol engine designed to circumvent Internet filtering based on protocol identification+Synopsis: Polymorphic protocol engine+Category: Network+License: GPL+License-file: LICENSE+Author: Brandon Wiley+Maintainer: brandon@ischool.utexas.edu+Build-Type: Simple+Cabal-Version: >=1.8++Library+ Build-Depends:+ base >= 3 && < 5,+ bytestring,+ entropy,+ network,+ cereal,+ ghc-prim,+ binary,+ random,+ random-extras,+ random-source,+ random-fu,+ containers,+ directory,+ split,+ cipher-aes+ Extensions:+ ForeignFunctionInterface++ Exposed-modules:+ Dust.Network.TcpServer+ Dust.Network.UdpServer+ Dust.Network.TcpClient+ Dust.Network.Util+ Dust.Model.PacketLength+ Dust.Model.Huffman+ Dust.Model.Content+ Dust.Core.Invite+ Dust.Model.Observations+ Dust.Model.Stats+ Dust.Crypto.Keys+ Dust.Crypto.ECDSA+ Dust.Crypto.ECDH+ Dust.Crypto.DustCipher+++-- if os(windows)+-- build-depends: Win32+-- C-sources: lib/curve25519-donna.c, lib/ed25519-donna/ed25519.c+-- extra-lib-dirs=C:\OpenSSL-Win64 /OpenSSL-Win64 C:\OpenSSL-Win64\lib C:\OpenSSL-Win64\bin+-- extra-libraries: ssleay32 eay32++ if os(linux) || os(darwin)+ C-sources: lib/curve25519-donna.c, lib/ed25519-donna/ed25519.c+ Include-Dirs: lib+ extra-libraries: crypto+ Exposed-modules:+ Dust.Crypto.Curve25519+ Dust.Crypto.Ed25519+ Dust.Core.DustPacket+ Dust.Core.CryptoProtocol+ Dust.Core.WireProtocol+ Dust.Core.WireProtocolHandler+ Dust.Network.DustServer+ Dust.Model.Port+ Dust.Model.Packet++test-suite crypto+ type: exitcode-stdio-1.0+ main-is: TestCrypto.hs+ ghc-options: -w -threaded -rtsopts -with-rtsopts=-N+ hs-source-dirs: tests++ build-depends:+ base,+ Dust,+ HUnit >= 1.2,+ test-framework >= 0.6,+ test-framework-hunit >= 0.2,+ test-framework-quickcheck2,+-- test-framework-th,+ bytestring,+ cipher-aes,+ cereal,+ ghc-prim,+ QuickCheck++test-suite core+ type: exitcode-stdio-1.0+ main-is: TestCore.hs+ ghc-options: -w -threaded -rtsopts -with-rtsopts=-N+ hs-source-dirs: tests++ build-depends:+ base,+ Dust,+ HUnit >= 1.2,+ test-framework >= 0.6,+ test-framework-hunit >= 0.2,+ test-framework-quickcheck2,+ test-framework-th,+ bytestring,+ cipher-aes,+ cereal,+ ghc-prim,+ QuickCheck++-- test-suite model+-- type: exitcode-stdio-1.0+-- main-is: TestModel.hs+-- ghc-options: -w -threaded -rtsopts -with-rtsopts=-N+-- hs-source-dirs: tests++-- build-depends:+-- base,+-- Dust,+-- HUnit >= 1.2,+-- test-framework >= 0.6,+-- test-framework-hunit >= 0.2,+-- test-framework-th,+-- base >= 3 && < 5, bytestring, cipher-aes, entropy, network, cereal, ghc-prim, binary, random, random-extras, random-source, random-fu, containers
+ Dust/Core/CryptoProtocol.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Core.CryptoProtocol+(+ Session(..),+ Stream(..),+ StreamHeader(..),+ makeSession,+ makeEncrypt,+ makeDecrypt,+ makeHeader,+ makeStream,+ makeEncoder+) where++import Dust.Core.DustPacket+import Dust.Crypto.DustCipher+import Dust.Crypto.ECDH+import Dust.Crypto.Keys+import Dust.Model.TrafficModel++data Session = Session Keypair PublicKey IV deriving (Show)+data Stream = Stream StreamHeader CipherDataPacket deriving (Show)+data StreamHeader = StreamHeader PublicKey IV deriving (Show)++makeSession :: Keypair -> PublicKey -> IV -> Session+makeSession keypair publicKey iv = Session keypair publicKey iv++makeEncrypt :: Session -> (Plaintext -> Ciphertext)+makeEncrypt (Session (Keypair myPublic myPrivate) otherPublic iv) =+ let key = createShared myPrivate otherPublic+ in encrypt key iv++makeDecrypt :: Session -> (Ciphertext -> Plaintext)+makeDecrypt (Session (Keypair myPublic myPrivate) otherPublic iv) =+ let key = createShared myPrivate otherPublic+ in decrypt key iv++makeHeader :: PublicKey -> IV -> StreamHeader+makeHeader publicKey iv = StreamHeader publicKey iv++makeStream :: StreamHeader -> CipherDataPacket -> Stream+makeStream header cipherPacket = Stream header cipherPacket++makeEncoder :: Session -> (Plaintext -> Stream)+makeEncoder session@(Session (Keypair myPublic _) _ iv) =+ let header = makeHeader myPublic iv+ cipher = makeEncrypt session+ encrypter = encryptData cipher+ stream = makeStream header+ in stream . encrypter . makePlainPacket
+ Dust/Core/DustPacket.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Core.DustPacket+(+ PlainHeader(..),+ CipherHeader(..),+ PlainDataPacket(..),+ CipherDataPacket(..),+ makePlainPacket,+ makeCipherPacket,+ encryptData,+ decryptData,+ decryptHeader,+ length32+) where++import GHC.Generics+import Dust.Crypto.DustCipher+import Data.ByteString+import qualified Data.ByteString as B+import Data.Serialize+import Data.Int++data PlainHeader = PlainHeader {+ payloadLength :: Int32+} deriving (Show, Eq, Generic)++data CipherHeader = CipherHeader {+ encryptedPayloadLength :: Ciphertext+} deriving (Show, Eq, Generic)++data PlainDataPacket = PlainDataPacket PlainHeader Plaintext deriving (Show, Eq, Generic)+data CipherDataPacket = CipherDataPacket CipherHeader Ciphertext deriving (Show, Eq, Generic)++instance Serialize PlainHeader+instance Serialize CipherHeader+instance Serialize PlainDataPacket+instance Serialize CipherDataPacket++makePlainPacket :: Plaintext -> PlainDataPacket+makePlainPacket (Plaintext bs) = PlainDataPacket (PlainHeader (length32 bs)) (Plaintext bs)++makeCipherPacket :: Ciphertext -> Ciphertext -> CipherDataPacket+makeCipherPacket lengthCiphertext ciphertext = CipherDataPacket (CipherHeader lengthCiphertext) ciphertext++decryptHeader :: (Ciphertext -> Plaintext) -> CipherHeader -> PlainHeader+decryptHeader cipher (CipherHeader ciphertext) =+ let (Plaintext lengthBytes) = cipher ciphertext+ lengthValue = decodeHeader lengthBytes+ in PlainHeader lengthValue++decodeHeader :: ByteString -> Int32+decodeHeader bs =+ case (decode bs)::(Either String Int32) of+ Left _ -> 0+ Right value -> value++encryptData :: (Plaintext -> Ciphertext) -> PlainDataPacket -> CipherDataPacket+encryptData cipher (PlainDataPacket header plaintext) =+ let cipherheader = cipher (Plaintext (encode header))+ ciphertext = cipher plaintext+ in CipherDataPacket (CipherHeader cipherheader) ciphertext++decryptData :: (Ciphertext -> Plaintext) -> CipherDataPacket -> PlainDataPacket+decryptData cipher (CipherDataPacket header ciphertext) =+ let plaintext@(Plaintext bs) = cipher ciphertext+ in PlainDataPacket (PlainHeader (length32 bs)) plaintext++length32 :: ByteString -> Int32+length32 bs = (fromIntegral (B.length bs)) :: Int32
+ Dust/Core/Invite.hs view
@@ -0,0 +1,25 @@+module Dust.Core.Invite+(+)+where++import Network+import Data.ByteString++import Dust.Crypto.Keys+import Dust.Crypto.DustCipher+import Dust.Model.TrafficModel++data Invite = Invite {+ address :: Address,+ encryptionKey :: PublicKey,+ signingKey :: PublicKey,+ model :: TrafficModel,+ nonce :: ByteString+}++data Address = Address {+ v6 :: Bool,+ host :: String,+ port :: PortNumber+}
+ Dust/Core/WireProtocol.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Core.WireProtocol+(+ getSession,+ getPacket,+ putSession,+ putPacket+) where++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import GHC.Int+import Data.Serialize.Put (Put, putByteString)+import Data.Serialize.Get (Get, getByteString)++import Dust.Core.DustPacket+import Dust.Crypto.DustCipher+import Dust.Crypto.ECDH+import Dust.Crypto.Keys+import Dust.Model.TrafficModel+import Dust.Core.CryptoProtocol++getSession :: Keypair -> Get Session+getSession keypair = do+ public <- getByteString 32+ iv <- getByteString 16+ return $ Session keypair (PublicKey public) (IV iv)++putSession :: Session -> Put+putSession (Session (Keypair (PublicKey public) _) _ (IV iv)) = do+ putByteString public+ putByteString iv++getPacket :: Session -> Get Plaintext+getPacket session = do+ packetBytes <- getByteString 4+ let cipherHeader = CipherHeader (Ciphertext packetBytes)+ let cipher = makeDecrypt session+ let plainPacketHeader = decryptHeader cipher cipherHeader+ let PlainHeader packetLength = plainPacketHeader+ let packetLen = (fromIntegral packetLength)::Int++ payloadBytes <- getByteString packetLen+ let ciphertext = Ciphertext payloadBytes+ return $ cipher ciphertext++putPacket :: Session -> Plaintext -> Put+putPacket session plaintext = do+ let packet = makePlainPacket plaintext+ let (CipherDataPacket (CipherHeader (Ciphertext header)) (Ciphertext payload)) = encryptData (makeEncrypt session) packet+ putByteString header+ putByteString payload
+ Dust/Core/WireProtocolHandler.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Core.WireProtocolHandler+(+ Packets(..),+ encodeMessage,+ decodeMessage,+ decodeSession,+ decodePacket+) where++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import GHC.Int+import Data.Serialize.Put (Put, runPut)+import Data.Serialize.Get (runGetState)++import Dust.Core.DustPacket+import Dust.Crypto.DustCipher+import Dust.Crypto.ECDH+import Dust.Crypto.Keys+import Dust.Model.TrafficModel+import Dust.Core.CryptoProtocol+import Dust.Core.WireProtocol++data Packets = Packets [ByteString]++encodeMessage :: Session -> Plaintext -> Packets+encodeMessage session plaintext = do+ let header = runPut (putSession session)+ let packet = runPut (putPacket session plaintext)+ Packets [B.append header packet]++decodeMessage :: Keypair -> ByteString -> Either String (Plaintext, ByteString)+decodeMessage keypair buffer = do+ let eitherSession = decodeSession keypair buffer+ case eitherSession of+ Left error -> Left error+ Right (session, rest) -> decodePacket session rest++decodeSession :: Keypair -> ByteString -> Either String (Session, ByteString)+decodeSession keypair buffer = runGetState (getSession keypair) buffer 0++decodePacket :: Session -> ByteString -> Either String (Plaintext, ByteString)+decodePacket session buffer = runGetState (getPacket session) buffer 0
+ Dust/Crypto/Curve25519.hs view
@@ -0,0 +1,39 @@+{-# CFILES lib/curve25519-donna.c #-}+{-# LANGUAGE ForeignFunctionInterface #-}++module Dust.Crypto.Curve25519 (curve25519) where++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.ByteString.Unsafe (unsafeUseAsCString)+import Foreign.C.String (CString)++import Foreign.Marshal.Unsafe (unsafeLocalState)++curve25519 :: ByteString -> ByteString -> ByteString+curve25519 bs1 bs2 = curve+ where+ Just curve = unsafeLocalState $ unsafe_curve25519 bs1 bs2++unsafe_curve25519 :: ByteString -> ByteString -> IO (Maybe ByteString)+unsafe_curve25519 secret basepoint+ | B.length secret >= 32 && B.length basepoint >= 32 =+ -- This ByteString will be overwritten by the C call, but so long as+ -- the C side does not keep a reference to it afterward, it does what we+ -- expect and saves us a copy+ unsafeUseAsCString outBS $ \output ->+ B.useAsCString secret $ \csecret ->+ B.useAsCString basepoint $ \cbasepoint -> do+ result <- c_curve25519_donna output csecret cbasepoint+ case result of+ 0 -> return $ Just outBS+ _ -> return Nothing+ | otherwise = return Nothing+ where+ outBS = B.replicate 32 0xAB++-- Should be Ptr Word8 (not CString), but this should be safe if we just+-- use them as bytes++foreign import ccall unsafe "curve25519_donna" c_curve25519_donna ::+ CString -> CString -> CString -> IO Int
+ Dust/Crypto/DustCipher.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Crypto.DustCipher+(+ EncryptionKey(..),+ IV(..),+ Plaintext(..),+ Ciphertext(..),++ encrypt,+ decrypt,+ createIV+) where++import GHC.Generics+import Data.ByteString+import Data.Serialize+import qualified Crypto.Cipher.AES as AES+import System.Entropy++import Dust.Crypto.Keys++data EncryptionKey = EncryptionKey ByteString deriving (Show, Eq)+newtype IV = IV ByteString deriving (Show, Eq, Generic)+newtype Plaintext = Plaintext ByteString deriving (Show, Eq, Generic)+newtype Ciphertext = Ciphertext ByteString deriving (Show, Eq, Generic)++instance Serialize IV+instance Serialize Plaintext+instance Serialize Ciphertext++encrypt :: EncryptionKey -> IV -> Plaintext -> Ciphertext+encrypt (EncryptionKey keyBytes) (IV iv) (Plaintext plaintext) =+ let aesKey = AES.initAES keyBytes+ in Ciphertext $ AES.encryptCTR aesKey iv plaintext++decrypt :: EncryptionKey -> IV -> Ciphertext -> Plaintext+decrypt (EncryptionKey keyBytes) (IV iv) (Ciphertext ciphertext) =+ let aesKey = AES.initAES keyBytes+ in Plaintext $ AES.decryptCTR aesKey iv ciphertext++createIV :: IO (IV)+createIV = do+ entropy <- getEntropy 16+ return (IV entropy)
+ Dust/Crypto/ECDH.hs view
@@ -0,0 +1,47 @@+module Dust.Crypto.ECDH+(+ splitSecret,+ createPrivate,+ createPublic,+ createKeypair,+ createShared,+ createEphemeral+) where++import Data.ByteString as B+import Data.Word+import Data.Bits+import System.Entropy++import Dust.Crypto.Keys+import Dust.Crypto.DustCipher+import Dust.Crypto.Curve25519++createEphemeral :: IO (Keypair)+createEphemeral = do+ entropy <- getEntropy 32+ return (createKeypair entropy)++splitSecret :: ByteString -> (Word8,ByteString,Word8)+splitSecret bs = let firstByte = B.head bs+ lastByte = B.last bs+ middle = B.tail (B.init bs)+ in (firstByte,middle,lastByte)++createPrivate :: ByteString -> PrivateKey+createPrivate bs = let (firstByte,middle,lastByte) = splitSecret bs+ firstByte' = firstByte .&. 248+ lastByte' = (lastByte .&. 127) .|. 64+ in PrivateKey (firstByte' `cons` middle `snoc` lastByte')++createPublic :: PrivateKey -> PublicKey+createPublic private = let bps = pack [9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]+ in PublicKey (curve25519 (privateBytes private) bps)++createKeypair :: ByteString -> Keypair+createKeypair entropy = let private = createPrivate entropy+ public = createPublic private+ in Keypair public private++createShared :: PrivateKey -> PublicKey -> EncryptionKey+createShared private public = EncryptionKey (curve25519 (privateBytes private) (publicBytes public))
+ Dust/Crypto/ECDSA.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Crypto.ECDSA+(+ Signature,+ Signedtext(..),+ createPrivate,+ createPublic,+ createSigningKeypair,+ sign,+ verify+) where++import GHC.Generics (Generic)+import Data.Serialize (Serialize)+import Data.ByteString (ByteString)++import Dust.Crypto.Keys+import Dust.Crypto.Ed25519++newtype Signature = Signature { signatureBytes :: ByteString } deriving (Show, Eq, Generic)+instance Serialize Signature++data Signedtext = Signedtext PublicKey Signature ByteString deriving (Eq, Show, Generic)+instance Serialize Signedtext++createPrivate :: ByteString -> PrivateKey+createPrivate = PrivateKey++createPublic :: PrivateKey -> PublicKey+createPublic = PublicKey . ed25519_publickey . privateBytes++createSigningKeypair :: ByteString -> Keypair+createSigningKeypair entropy = Keypair (createPublic private) private+ where+ private = createPrivate entropy++sign :: ByteString -> Keypair -> Signedtext+sign msg (Keypair pubkey private) = Signedtext pubkey (Signature $ ed25519_sign msg (privateBytes private) (publicBytes pubkey)) msg++verify :: Signedtext -> Bool+verify (Signedtext pubkey signature msg) = ed25519_sign_open msg (publicBytes pubkey) (signatureBytes signature)
+ Dust/Crypto/Ed25519.hs view
@@ -0,0 +1,85 @@+{-# CFILES lib/ed25519-donna/ed25519.c #-}+{-# LANGUAGE ForeignFunctionInterface #-}++module Dust.Crypto.Ed25519+(+ ed25519_publickey,+ ed25519_sign_open,+ ed25519_sign+) where++import qualified Data.ByteString as B+import Data.ByteString (ByteString)+import Data.ByteString.Unsafe (unsafeUseAsCString)++import Foreign.C+import Foreign.Marshal.Unsafe (unsafeLocalState)++ed25519_publickey :: ByteString -> ByteString+ed25519_publickey privkey = pubkey+ where+ Just pubkey = fmap unsafeLocalState (unsafe_ed25519_publickey privkey)++ed25519_sign_open :: ByteString -> ByteString -> ByteString -> Bool+ed25519_sign_open msg pubkey signature = result+ where+ Just result = fmap unsafeLocalState $ unsafe_ed25519_sign_open msg pubkey signature++ed25519_sign :: ByteString -> ByteString -> ByteString -> ByteString+ed25519_sign msg secret pubkey = signature+ where+ Just signature = fmap unsafeLocalState $ unsafe_ed25519_sign msg secret pubkey++unsafe_ed25519_publickey :: ByteString -> Maybe (IO ByteString)+unsafe_ed25519_publickey input+ | B.length input <= 32 = Just $+ -- This ByteString will be overwritten by the C call, but so long as+ -- the C side does not keep a reference to it afterward, it does what we+ -- expect and saves us a copy+ unsafeUseAsCString outBS $ \output ->+ B.useAsCString input $ \cinput -> do+ c_ed25519_publickey cinput output+ return outBS+ | otherwise = Nothing+ where+ outBS = B.replicate 32 0xAB+ {-# NOINLINE outBS #-}++unsafe_ed25519_sign_open :: ByteString -> ByteString -> ByteString -> Maybe (IO Bool)+unsafe_ed25519_sign_open msg pubkey signature+ | B.length pubkey <= 32 && B.length signature <= 64 = Just $+ B.useAsCStringLen msg $ \(cmsg, cmsglen) ->+ B.useAsCString pubkey $ \cpubkey ->+ B.useAsCString signature $ \csignature ->+ let result = c_ed25519_sign_open cmsg (fromIntegral cmsglen) cpubkey csignature in+ return (result == 0)+ | otherwise = Nothing++unsafe_ed25519_sign :: ByteString -> ByteString -> ByteString -> Maybe (IO ByteString)+unsafe_ed25519_sign msg secret pubkey+ | B.length secret <= 32 && B.length pubkey <= 32 = Just $+ -- This ByteString will be overwritten by the C call, but so long as+ -- the C side does not keep a reference to it afterward, it does what we+ -- expect and saves us a copy+ unsafeUseAsCString outBS $ \signature ->+ B.useAsCStringLen msg $ \(cmsg, cmsglen) ->+ B.useAsCString secret $ \csecret ->+ B.useAsCString pubkey $ \cpubkey -> do+ c_ed25519_sign cmsg (fromIntegral cmsglen) csecret cpubkey signature+ return outBS+ | otherwise = Nothing+ where+ outBS = B.replicate 64 0xAB+ {-# NOINLINE outBS #-}++-- Should be Ptr CUChar (not CString), but this should be safe if we just+-- use them as bytes++foreign import ccall unsafe "ed25519_publickey" c_ed25519_publickey ::+ CString -> CString -> IO ()++foreign import ccall unsafe "ed25519_sign_open" c_ed25519_sign_open ::+ CString -> CSize -> CString -> CString -> CInt++foreign import ccall unsafe "ed25519_sign" c_ed25519_sign ::+ CString -> CSize -> CString -> CString -> CString -> IO ()
+ Dust/Crypto/Keys.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Crypto.Keys+(+ PublicKey(..),+ PrivateKey(..),+ Keypair(..),++ loadKeypair,+ saveKeypair,+ loadPublic,++ loadSigningKeypair,+ saveSigningKeypair+) where++import GHC.Generics+import Data.Serialize+import Data.ByteString++newtype PublicKey = PublicKey { publicBytes :: ByteString } deriving (Show, Eq, Generic)+newtype PrivateKey = PrivateKey { privateBytes :: ByteString } deriving (Show, Eq, Generic)++instance Serialize PublicKey++data Keypair = Keypair {+ public :: PublicKey,+ private :: PrivateKey+} deriving (Show, Eq)++loadKeypair :: IO (Keypair)+loadKeypair = do+ public <- loadPublic "id.pub"+ private <- loadPrivate "id.priv"+ return (Keypair public private)++loadPrivate :: FilePath -> IO (PrivateKey)+loadPrivate path = do+ key <- loadKey path+ return (PrivateKey key)++loadPublic :: FilePath -> IO (PublicKey)+loadPublic path = do+ key <- loadKey path+ return (PublicKey key)++loadKey :: FilePath -> IO (ByteString)+loadKey path = Data.ByteString.readFile path+++saveKeypair :: Keypair -> IO ()+saveKeypair (Keypair public private) = do+ savePublic "id.pub" public+ savePrivate "id.priv" private++savePrivate :: FilePath -> PrivateKey -> IO ()+savePrivate path (PrivateKey bs) = do+ saveKey path bs++savePublic :: FilePath -> PublicKey -> IO ()+savePublic path (PublicKey bs) = do+ saveKey path bs++saveKey :: FilePath -> ByteString -> IO ()+saveKey path bs = Data.ByteString.writeFile path bs++loadSigningKeypair :: IO (Keypair)+loadSigningKeypair = do+ public <- loadPublic "sign.pub"+ private <- loadPrivate "sign.priv"+ return (Keypair public private)++saveSigningKeypair :: Keypair -> IO ()+saveSigningKeypair (Keypair public private) = do+ savePublic "sign.pub" public+ savePrivate "sign.priv" private
+ Dust/Model/Content.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Model.Content+(+ ContentModel(..),+ loadContentModel,+ makeContentModel,+ encodeContent,+ decodeContent+)+where++import Data.Word (Word8)+import System.Random+import GHC.Generics+import Data.Serialize+import Data.Random.Shuffle.Weighted+import Data.Random.RVar+import Data.Random+import Data.Random.Source.IO+import Data.Random.Source.Std+import qualified Data.Map as M+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL++import Dust.Model.Huffman (HuffmanTree)+import Dust.Model.Huffman as H++data ContentModel = ContentModel (HuffmanTree Word8) deriving (Generic)+instance Serialize ContentModel++loadContentModel :: FilePath -> IO ContentModel+loadContentModel path = do+ tree <- H.fileToTree path+ return $ ContentModel $ tree++makeContentModel :: [(Word8, Int)] -> ContentModel+makeContentModel counts =+ let tree = H.countsToTree counts+ in ContentModel $ tree++encodeContent :: (HuffmanTree Word8) -> B.ByteString -> B.ByteString+encodeContent tree input =+-- let codez = H.codes tree+-- bytes = B.unpack input+-- bits = H.padToEight $ H.encode codez bytes+-- in (B.concat . BL.toChunks) $ H.bitpack bits+ input++decodeContent :: (HuffmanTree Word8) -> B.ByteString -> B.ByteString+decodeContent tree input =+-- let bits = H.bitunpack input+-- bytes = H.decode tree bits+-- in B.pack bytes+ input
+ Dust/Model/Huffman.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE BangPatterns, DeriveGeneric, DefaultSignatures #-}+module Dust.Model.Huffman+(+ HuffmanTree,+ encode,+ decode,+ bitpack,+ bitunpack,+ padToEight,+ codes,+ fileToTree,+ countsToTree+)+where++import GHC.Generics+import Data.Serialize hiding (encode, decode)++import Data.Word+import Data.Char (intToDigit)+import Data.Bits ((.|.), shiftL, testBit)+import Data.List (insertBy, foldl', sortBy, unfoldr)+import Data.Maybe (fromJust)+import Data.Ord (comparing)+import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as B+import qualified Data.Map as M++import Dust.Model.Stats (histogram)++--------------------------------------------------++data HuffmanTree a+ = LeafNode a Int+ | InternalNode Int (HuffmanTree a) (HuffmanTree a)+ deriving (Generic)+instance (Serialize a) => Serialize (HuffmanTree a)++-- build a multiline string representation of a huffman tree+instance Show a => Show (HuffmanTree a) where+ show =+ go ""+ where+ spaces = map (const ' ')+ paren s = "(" ++ s ++ ")"+ go ss (LeafNode s o) = "--" ++ paren (show o) ++ show s ++ "\n"+ go ss (InternalNode o l r) =+ let root = "--" ++ paren (show o) ++ "-+"+ ss' = ss ++ tail (spaces root)+ lbranch = go (ss' ++ "|") l+ rbranch = go (ss' ++ " ") r+ in root ++ lbranch+ ++ ss' ++ "|\n"+ ++ ss' ++ "`"+ ++ rbranch++frequency :: HuffmanTree a -> Int+frequency (LeafNode _ x ) = x+frequency (InternalNode x _ _) = x++-- build a huffman tree bototm-up from a list of symbols sorted by frequency+sortedHuffman :: [(a,Int)] -> HuffmanTree a+sortedHuffman =+ -- first, convert each tuple into a Leaf, then combine+ combine . map toLeaf+ where+ -- repeatedly combine lowest frequency trees and reinsert the result into+ -- the frequency ordered list+ -- note: a priority queue could help+ combine [t] = t+ combine (ta:tb:ts) = combine . insertBy (comparing frequency) (merge ta tb) $ ts+ -- make an internal node from two trees. the frequency is the sum of the+ -- two trees frequencies+ merge ta tb = InternalNode (frequency ta + frequency tb) ta tb+ -- make a Leaf from a symbol,freq tuple+ toLeaf = uncurry LeafNode++-- traverse the huffman tree generating a map from the symbol to its huffman+-- tree path (where False is left, and True is right)+codes :: Ord a => HuffmanTree a -> M.Map a [Bool]+codes =+ M.fromList . go []+ where+ -- leaf nodes mark the end of a path to a symbol+ go p (LeafNode s _) = [(s,reverse p)]+ -- traverse both branches and accumulate a reverse path+ go p (InternalNode _ l r) = go (False:p) l ++ go (True:p) r++-- from a table mapping symbols to their corresponding huffman tree bit paths,+-- replace each instance of a symbol with its bit path+encode :: Ord a => M.Map a [Bool] -> [a] -> [Bool]+encode tbl =+ concatMap get+ where+ get x = fromJust (M.lookup x tbl)++-- from a list of bits, navigate a given huffman tree and emit its decoded+-- symbol when reaching a Leaf+decode :: HuffmanTree a -> [Bool] -> [a]+decode t0 xs0 =+ go t0 xs0+ where+ -- reached leaf, emit symbol+ go (LeafNode s _) bs = s : go t0 bs+ -- choose path based on bit+ go (InternalNode _ l r) (b:bs)+ | not b = go l bs+ | otherwise = go r bs+ go _ [] = []++--------------------------------------------------++swap :: (a,b) -> (b,a)+swap ~(a,b) = (b,a)++showBits :: [Bool] -> String+showBits = map (intToDigit . fromEnum)++--------------------------------------------------++bitpack :: [Bool] -> B.ByteString+bitpack = B.pack . map packByte . takeWhile (not . null) . unfoldr (Just . splitAt 8)+ where+ packByte = foldl' (\i b -> (i `shiftL` 1) .|. (fromIntegral $ fromEnum b)) 0++bitunpack :: S.ByteString -> [Bool]+bitunpack = concatMap (\byte -> map (testBit byte) [7,6..0]) . S.unpack++--------------------------------------------------++padToEight :: [Bool] -> [Bool]+padToEight bits =+ let len = length bits+ rem = len `mod` 8+ extra = 8-rem+ padding = replicate extra False+ in bits ++ padding++fileToTree :: FilePath -> IO (HuffmanTree Word8)+fileToTree path = do+ contents <- B.readFile path+ return $ bytesToTree $ B.unpack contents++bytesToTree :: [Word8] -> (HuffmanTree Word8)+bytesToTree text =+ let frequencies = histogram text+ sortedFrequencies = sortBy (comparing swap) frequencies+ in sortedHuffman sortedFrequencies++countsToTree :: [(Word8, Int)] -> (HuffmanTree Word8)+countsToTree frequencies =+ let sortedFrequencies = sortBy (comparing swap) frequencies+ in sortedHuffman sortedFrequencies
+ Dust/Model/Observations.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Model.Observations+(+ Observations(..),+ LengthObservations(..),+ ContentObservations(..),+ SubstringObservations(..),+ emptyObservations,+ loadObservations,+ saveObservations,+ ensureObservations,+ observePacket,+ observePort,+ observeSubstrings,+ makeModel+)+where++import GHC.Generics+import Data.Serialize+import Data.ByteString (ByteString, unpack)+import qualified Data.ByteString as B+import Data.Int+import Data.Word+import System.Directory+import Data.List (nub)+import Data.Map (Map(..), alter, empty)++import Dust.Model.PacketLength+import qualified Dust.Model.Content as C+import Dust.Model.Port+import Dust.Model.TrafficModel++data Observations = Observations {+ lengths :: LengthObservations,+ content :: ContentObservations,+ ports :: PortObservations,+ strings :: SubstringObservations+} deriving (Generic, Show)+instance Serialize Observations++data LengthObservations = LengthObservations [Int] deriving (Generic, Show)+instance Serialize LengthObservations++data ContentObservations = ContentObservations [Int] deriving (Generic, Show)+instance Serialize ContentObservations++data PortObservations = PortObservations [Int] deriving (Generic, Show)+instance Serialize PortObservations++data SubstringObservations = SubstringObservations [(Map ByteString Int)] deriving (Generic, Show)+instance Serialize SubstringObservations++observePacket :: Observations -> ByteString -> Observations+observePacket (Observations lobs cobs pobs sobs) bs =+ let bsl = (fromIntegral $ B.length bs)::Int+ lobs' = observeLengths lobs (bsl, 1)+ cobs' = observeByteString cobs bs+ in Observations lobs' cobs' pobs sobs++observePort :: Observations -> Int -> Observations+observePort (Observations lobs cobs (PortObservations ports) sobs) port =+ let pobs' = PortObservations $ nub $ port : ports+ in Observations lobs cobs pobs' sobs++observeSubstrings :: Observations -> ByteString -> Observations+observeSubstrings obs bs =+ let windows = windowed 17 bs + in observeSubstringList obs 0 windows++observeSubstringList :: Observations -> Int -> [ByteString] -> Observations+observeSubstringList obs offset [] = obs+observeSubstringList obs offset (bs:bss) = observeSubstringList (observeSubstring obs offset bs) (offset+1) bss++observeSubstring :: Observations -> Int -> ByteString -> Observations+observeSubstring (Observations lobs cobs pobs (SubstringObservations subs)) offset bs =+ let sobs' = SubstringObservations $ updateSubstringCount subs offset bs+ in Observations lobs cobs pobs sobs' ++updateSubstringCount :: [(Map ByteString Int)] -> Int -> ByteString -> [(Map ByteString Int)]+updateSubstringCount items index bs =+ let (a, rest) = splitAt index items+ in case rest of+ [] -> items+ (item:b) -> a ++ ((alter updateMapCount bs item):b) ++updateMapCount :: Maybe Int -> Maybe Int+updateMapCount Nothing = Just 1+updateMapCount (Just x) = Just (x+1)++windowed :: Int -> ByteString -> [ByteString]+windowed size ls = + if B.null ls+ then []+ else+ if B.length ls < size+ then [ls]+ else (B.take size ls) : (windowed size $ B.tail ls)++observeLengths :: LengthObservations -> (Int, Int) -> LengthObservations+observeLengths (LengthObservations items) item = LengthObservations $ updateCounts items item++updateCounts :: [Int] -> (Int, Int) -> [Int]+updateCounts items (index,count) = + let (a, (item:b)) = splitAt index items+ in a ++ ((item+count):b)++observeByteString :: ContentObservations -> ByteString -> ContentObservations+observeByteString obs bs = observeBytes obs $ unpack bs++observeBytes :: ContentObservations -> [Word8] -> ContentObservations+observeBytes obs [] = obs+observeBytes obs (b:rest) =+ let bint = (fromIntegral b)::Int+ newObs = observeByte obs (bint,1)+ in observeBytes newObs rest++observeByte :: ContentObservations -> (Int, Int) -> ContentObservations+observeByte (ContentObservations items) item = ContentObservations $ updateCounts items item++emptyObservations :: Observations+emptyObservations = Observations emptyLengthObservations emptyContentObservations emptyPortObservations emptySubstringObservations++emptyLengthObservations :: LengthObservations+emptyLengthObservations = LengthObservations (take 1520 $ repeat 0)++emptyContentObservations :: ContentObservations+emptyContentObservations = ContentObservations (take 256 $ repeat 0)++emptyPortObservations :: PortObservations+emptyPortObservations = PortObservations []++emptySubstringObservations :: SubstringObservations+emptySubstringObservations = SubstringObservations $ take 1500 $ repeat empty++loadObservations :: FilePath -> IO (Either String Observations)+loadObservations path = do+ s <- B.readFile path+ return ((decode s)::(Either String Observations))++saveObservations :: FilePath -> Observations -> IO()+saveObservations path obs = do+ putStrLn "Saving observations..."+ let s = encode obs+ putStrLn $ "Writing " ++ (show $ B.length s) ++ " bytes"+ B.writeFile path s+ putStrLn "Done."++ensureObservations :: FilePath -> IO Observations+ensureObservations path = do+ exists <- doesFileExist path+ case exists of+ True -> do+ eitherObs <- loadObservations path+ case eitherObs of+ Right obs -> return obs+ Left _ -> return emptyObservations+ False -> return emptyObservations++makeModel :: Observations -> TrafficModel+makeModel (Observations lengthObs contentObs portObs stringObs) =+ let lengthModel = makeLengthModel lengthObs+ contentModel = makeContentModel contentObs+ portModel = makePortModel portObs+ in TrafficModel lengthModel contentModel portModel++makeLengthModel :: LengthObservations -> PacketLengthModel+makeLengthModel (LengthObservations counts) =+ let total = sum counts+ probs = map (divideBy total) counts+ in PacketLengthModel probs++makeContentModel :: ContentObservations -> C.ContentModel+makeContentModel (ContentObservations obs) =+ C.makeContentModel $ zip ([0..255]::[Word8]) obs++makePortModel :: PortObservations -> PortModel+makePortModel (PortObservations ports) = PortModel ports++divideBy :: Int -> Int -> Double+divideBy d n = + let fd = (fromIntegral d)::Double+ fn = (fromIntegral n)::Double+ in fn / fd
+ Dust/Model/Packet.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-}++module Dust.Model.Packet+(+ IP(..),+ Transport(..),+ Packet(..),+ Stream(..),+ Protocol(..),+ parsePacket+)+where++import GHC.Generics+import Data.Serialize (Serialize)+import Data.Serialize.Get+import Data.ByteString (ByteString, unpack)+import qualified Data.ByteString as B+import Data.Int+import Data.Word+import System.Directory+import Data.List (nub)+import Data.Map (Map(..), alter, empty)+import Data.Bits++import Dust.Model.PacketLength+import qualified Dust.Model.Content as C+import Dust.Model.Port+import Dust.Model.TrafficModel++data Ethernet = Ethernet {+ ethDest :: ByteString, -- 6 bytes+ ethSrc :: ByteString, -- 6 bytes+ ethtype :: Word16+} deriving (Generic, Show)+instance Serialize Ethernet++parseEthernet :: ByteString -> Either String (Ethernet, ByteString)+parseEthernet bs = runGetState getEthernet bs 0++getEthernet :: Get Ethernet+getEthernet = do+ bs0 <- getByteString 6+ bs7 <- getByteString 6+ s13 <- getWord16be++ return $ Ethernet bs0 bs7 s13++data IP = IP {+ version :: Word8,+ ihl :: Word8,+ tos :: Word8,+ tl :: Word16,+ id :: Word16,+ ipflags :: Word8,+ fragment:: Word16,+ ttl :: Word8,+ prot :: Word8,+ checksum:: Word16,+ source :: Word32,+ dest :: Word32,+ ipopts :: ByteString+} deriving (Generic, Show)+instance Serialize IP++parseIP :: ByteString -> Either String (IP, ByteString)+parseIP bs = runGetState getIP bs 0++getIP :: Get IP+getIP = do+ b0 <- getWord8+ b1 <- getWord8+ s2 <- getWord16be+ s4 <- getWord16be+ s6 <- getWord16be+ b8 <- getWord8+ b9 <- getWord8+ s10<- getWord16be+ l12<- getWord32be+ l14<- getWord32be+ let v20 = B.empty++ let v = shift b0 (-4)+ let hl = shift (shift b0 4) (-4)+ let f = (fromIntegral $ shift s6 (-13))::Word8+ let fo = shift (shift s6 3) (-3)+ + return $ IP v hl b1 s2 s4 f fo b8 b9 s10 l12 l14 v20++data Transport = + TCP {+ srcport :: Word16,+ destport:: Word16,+ seqnum :: Word32,+ acknum :: Word32,+ offset :: Word8,+ reserved:: Word8,+ tcpflags:: Word8,+ window :: Word16,+ tcpchk :: Word16,+ urgent :: Word16,+ tcptops :: ByteString+ } + | UDP {+ srcport :: Word16,+ destport:: Word16,+ len :: Word16,+ udpchk :: Word16+ }+ deriving (Generic, Show)+instance Serialize Transport++parseTCP :: ByteString -> Either String (Transport, ByteString)+parseTCP bs = runGetState getTCP bs 0++getTCP :: Get Transport+getTCP = do+ s0 <- getWord16be+ s2 <- getWord16be+ l4 <- getWord32be+ l8 <- getWord32be+ b12<- getWord8+ b13<- getWord8+ s14<- getWord16be+ s16<- getWord16be+ s18<- getWord16be++ let off = shift b12 (-4)+ let rsv = shift (shift b12 4) (-4)+ + v20 <- getByteString (fromIntegral ((off - 5) * 4)::Int)++ return $ TCP s0 s2 l4 l8 off rsv b13 s14 s16 s18 v20++parseUDP :: ByteString -> Either String (Transport, ByteString)+parseUDP bs = runGetState getUDP bs 0++getUDP :: Get Transport+getUDP = do+ s0 <- getWord16be+ s2 <- getWord16be+ s4 <- getWord16be+ s6 <- getWord16be++ return $ UDP s0 s2 s4 s6++data Packet = Packet Ethernet IP Transport ByteString deriving (Generic, Show)+instance Serialize Packet ++data Stream = Stream Protocol Word16 [Packet] deriving (Generic, Show)+instance Serialize Stream++data Protocol = ProtocolTCP | ProtocolUDP deriving (Generic, Show)+instance Serialize Protocol++parsePacket :: ByteString -> (Either String Packet)+parsePacket bs =+ case parseEthernet bs of+ Left etherError -> Left etherError+ Right (ether, noether) ->+ case parseIP noether of+ Left ipError -> Left ipError+ Right (ip, noip) -> case (prot ip) of+ 6 ->+ case parseTCP noip of+ Left tcpError -> Left tcpError+ Right (tcp, notcp) -> Right $ Packet ether ip tcp notcp+ 17 ->+ case parseUDP noip of+ Left udpError -> Left udpError+ Right (udp, noudp) -> Right $ Packet ether ip udp noudp+ otherwise -> Left $ "Unknown protocol " ++ (show $ prot ip)
+ Dust/Model/PacketLength.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Model.PacketLength+(+ PacketLengthModel(..),+ loadLengthModel,+ probsToCDF,+ nextLength+)+where++import System.Random+import GHC.Generics+import Data.Serialize+import Data.Random.Shuffle.Weighted+import Data.Random.RVar+import Data.Random+import Data.Random.Source.IO+import Data.Random.Source.Std+import Data.Map+import qualified Data.ByteString as B++data PacketLengthModel = PacketLengthModel [Double] deriving (Eq, Show, Generic)++instance Serialize PacketLengthModel++loadLengthModel :: FilePath -> IO (Map Double Int)+loadLengthModel path = do+ probs <- loadProbs path+ return $ probsToCDF probs++loadProbs :: FilePath -> IO [Double]+loadProbs path = do+ s <- B.readFile path+ let result = (decode s)::(Either String [Double])+ case result of+ Left error -> return ([])+ Right arr -> return(arr)++probsToCDF :: [Double] -> Map Double Int+probsToCDF probs = cdfMapFromList $ zip probs [1..(length probs)]++nextLength :: Map Double Int -> IO Int+nextLength cdf = do+ let dist = weightedSampleCDF 1 cdf+ arr <- runRVar dist StdRandom :: IO [Int]+ return (head arr)
+ Dust/Model/Port.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE DeriveGeneric, DefaultSignatures #-} -- For automatic generation of cereal put and get++module Dust.Model.Port+(+ PortModel(..),+ nextPort+)+where++import GHC.Generics+import Data.Serialize+import Data.Random+import Data.Random.RVar+import Data.Random.Extras+import Data.Random.Source.IO+import Data.Random.Source.Std++data PortModel = PortModel [Int] deriving (Eq, Show, Generic)+instance Serialize PortModel++nextPort :: [Int] -> IO Int+nextPort ports =+ let dist = choice ports+ in runRVar dist StdRandom :: IO Int+
+ Dust/Model/Stats.hs view
@@ -0,0 +1,16 @@+module Dust.Model.Stats+(+ histogram+)+where++import qualified Data.Map as M+import Data.List (foldl')++-- count the number of instances each symbol occurs in a list+histogram :: Ord a => [a] -> [(a,Int)]+histogram xs =+ M.toList . foldl' insert M.empty $ xs+ where+ insert a k = M.insertWith' (+) k 1 a+
+ Dust/Network/DustServer.hs view
@@ -0,0 +1,69 @@+module Dust.Network.DustServer+(+ dustServer+)+where++import Data.ByteString.Lazy (ByteString)+import Data.ByteString.Char8 (pack, unpack)+import System.IO.Error+import System.Entropy+import Data.Binary.Get (runGetState)+import Data.Binary.Put (runPut)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import Network.Socket++import Dust.Crypto.Keys+import Dust.Crypto.ECDH+import Dust.Core.WireProtocolHandler+import Dust.Network.TcpServer+import Dust.Crypto.DustCipher+import Dust.Model.TrafficModel+import Dust.Network.ProtocolSocket+import Dust.Core.CryptoProtocol++dustServer :: TrafficGenerator -> (Plaintext -> IO(Plaintext)) -> IO()+dustServer gen proxyAction = do+ putStrLn "Loading keys..."+ (keypair, newKeys) <- ensureKeys++ if newKeys+ then putStrLn "Generating new keys..."+ else putStrLn "Loaded keys."++ let host = "0.0.0.0"+ let port = 6885++ iv <- createIV++ server host port (reencode keypair iv gen proxyAction)++ensureKeys :: IO (Keypair, Bool)+ensureKeys = do+ result <- try loadKeypair+ case result of+ Left e -> do+ entropy <- getEntropy 32+ let keys = createKeypair entropy+ saveKeypair keys+ return (keys, True)+ Right keypair -> return (keypair, False)++reencode :: Keypair -> IV -> TrafficGenerator -> (Plaintext -> IO(Plaintext)) -> Socket -> IO()+reencode keypair iv gen proxyAction sock = do+ (session, rest) <- getSession B.empty keypair sock+ let (Session _ otherPublic _) = session+ (plaintext, rest') <- getPacket rest session sock++ putStrLn $ "Request:" ++ (show plaintext)++ result <- proxyAction plaintext+ let Plaintext resultBytes = result++ putStrLn $ "Response:" ++ (show (B.length resultBytes))++ let otherSession = makeSession keypair otherPublic iv+ encode otherSession result sock++ return ()
+ Dust/Network/TcpClient.hs view
@@ -0,0 +1,20 @@+module Dust.Network.TcpClient+(+ client+) where++import Network.Socket+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL++import Dust.Network.Util+import Dust.Crypto.DustCipher++client :: String -> PortNumber -> (Socket -> IO(Plaintext)) -> IO(Plaintext)+client host port handleRequest = withSocketsDo $ do+ sock <- socket AF_INET Stream defaultProtocol+ addr <- inet_addr host+ connect sock (SockAddrInet port addr)+ setSocketOption sock NoDelay 1++ handleRequest sock
+ Dust/Network/TcpServer.hs view
@@ -0,0 +1,37 @@+module Dust.Network.TcpServer+(+ server+) where++import Network (listenOn, PortID(PortNumber))+import Network.Socket+import qualified Network.Socket.ByteString as NSB+import qualified Network.Socket.ByteString.Lazy as NSBL+import Network.Socket.ByteString (sendAll)+import Data.ByteString.Lazy (ByteString, fromChunks)+import Control.Monad (forever)++import Dust.Network.Util++type Host = SockAddr++server :: String -> PortNumber -> (Socket -> IO()) -> IO()+server host port handleRequest = withSocketsDo $ do+ sock <- initSocket host port+ forever $ acceptAndProcess sock handleRequest+ sClose sock++initSocket :: String -> PortNumber -> IO(Socket)+initSocket host port = listenOn $ PortNumber port++acceptAndProcess :: Socket -> (Socket-> IO()) -> IO()+acceptAndProcess sock handleRequest = do+ (s, _) <- accept sock+ setSocketOption s NoDelay 1+ process handleRequest s++process :: (Socket -> IO()) -> Socket -> IO()+process handleRequest sock = do+ handleRequest sock+ sClose sock+ return ()
+ Dust/Network/UdpServer.hs view
@@ -0,0 +1,15 @@+module Dust.Network.UdpServer+(+ server+) where++import Network (PortID(PortNumber))+import Network.Socket+import Control.Monad (forever)++server :: String -> PortNumber -> (Socket -> IO()) -> IO()+server host port@(PortNum iport) handleRequest = withSocketsDo $ do+ sock <- socket AF_INET Datagram defaultProtocol+ putStrLn $ "Binding to " ++ (show iport)+ bindSocket sock (SockAddrInet port iNADDR_ANY)+ handleRequest sock
+ Dust/Network/Util.hs view
@@ -0,0 +1,25 @@+module Dust.Network.Util+(+ recvAll+)+where++import Data.ByteString+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import Network.Socket hiding (recv, send)+import Network.Socket.ByteString (recv, send)++recvAll :: Socket -> IO(BL.ByteString)+recvAll sock = do+ list <- recvList sock+ return (BL.fromChunks list)++recvList :: Socket -> IO([B.ByteString])+recvList sock = do+ input <- recv sock 4096+ if B.null input+ then return ([input])+ else do+ next <- recvList sock+ return (input:next)
+ LICENSE view
@@ -0,0 +1,339 @@+ GNU GENERAL PUBLIC LICENSE+ Version 2, June 1991++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The licenses for most software are designed to take away your+freedom to share and change it. By contrast, the GNU General Public+License is intended to guarantee your freedom to share and change free+software--to make sure the software is free for all its users. This+General Public License applies to most of the Free Software+Foundation's software and to any other program whose authors commit to+using it. (Some other Free Software Foundation software is covered by+the GNU Lesser General Public License instead.) You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+this service if you wish), that you receive source code or can get it+if you want it, that you can change the software or use pieces of it+in new free programs; and that you know you can do these things.++ To protect your rights, we need to make restrictions that forbid+anyone to deny you these rights or to ask you to surrender the rights.+These restrictions translate to certain responsibilities for you if you+distribute copies of the software, or if you modify it.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must give the recipients all the rights that+you have. You must make sure that they, too, receive or can get the+source code. And you must show them these terms so they know their+rights.++ We protect your rights with two steps: (1) copyright the software, and+(2) offer you this license which gives you legal permission to copy,+distribute and/or modify the software.++ Also, for each author's protection and ours, we want to make certain+that everyone understands that there is no warranty for this free+software. If the software is modified by someone else and passed on, we+want its recipients to know that what they have is not the original, so+that any problems introduced by others will not reflect on the original+authors' reputations.++ Finally, any free program is threatened constantly by software+patents. We wish to avoid the danger that redistributors of a free+program will individually obtain patent licenses, in effect making the+program proprietary. To prevent this, we have made it clear that any+patent must be licensed for everyone's free use or not licensed at all.++ The precise terms and conditions for copying, distribution and+modification follow.++ GNU GENERAL PUBLIC LICENSE+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++ 0. This License applies to any program or other work which contains+a notice placed by the copyright holder saying it may be distributed+under the terms of this General Public License. The "Program", below,+refers to any such program or work, and a "work based on the Program"+means either the Program or any derivative work under copyright law:+that is to say, a work containing the Program or a portion of it,+either verbatim or with modifications and/or translated into another+language. (Hereinafter, translation is included without limitation in+the term "modification".) Each licensee is addressed as "you".++Activities other than copying, distribution and modification are not+covered by this License; they are outside its scope. The act of+running the Program is not restricted, and the output from the Program+is covered only if its contents constitute a work based on the+Program (independent of having been made by running the Program).+Whether that is true depends on what the Program does.++ 1. You may copy and distribute verbatim copies of the Program's+source code as you receive it, in any medium, provided that you+conspicuously and appropriately publish on each copy an appropriate+copyright notice and disclaimer of warranty; keep intact all the+notices that refer to this License and to the absence of any warranty;+and give any other recipients of the Program a copy of this License+along with the Program.++You may charge a fee for the physical act of transferring a copy, and+you may at your option offer warranty protection in exchange for a fee.++ 2. You may modify your copy or copies of the Program or any portion+of it, thus forming a work based on the Program, and copy and+distribute such modifications or work under the terms of Section 1+above, provided that you also meet all of these conditions:++ a) You must cause the modified files to carry prominent notices+ stating that you changed the files and the date of any change.++ b) You must cause any work that you distribute or publish, that in+ whole or in part contains or is derived from the Program or any+ part thereof, to be licensed as a whole at no charge to all third+ parties under the terms of this License.++ c) If the modified program normally reads commands interactively+ when run, you must cause it, when started running for such+ interactive use in the most ordinary way, to print or display an+ announcement including an appropriate copyright notice and a+ notice that there is no warranty (or else, saying that you provide+ a warranty) and that users may redistribute the program under+ these conditions, and telling the user how to view a copy of this+ License. (Exception: if the Program itself is interactive but+ does not normally print such an announcement, your work based on+ the Program is not required to print an announcement.)++These requirements apply to the modified work as a whole. If+identifiable sections of that work are not derived from the Program,+and can be reasonably considered independent and separate works in+themselves, then this License, and its terms, do not apply to those+sections when you distribute them as separate works. But when you+distribute the same sections as part of a whole which is a work based+on the Program, the distribution of the whole must be on the terms of+this License, whose permissions for other licensees extend to the+entire whole, and thus to each and every part regardless of who wrote it.++Thus, it is not the intent of this section to claim rights or contest+your rights to work written entirely by you; rather, the intent is to+exercise the right to control the distribution of derivative or+collective works based on the Program.++In addition, mere aggregation of another work not based on the Program+with the Program (or with a work based on the Program) on a volume of+a storage or distribution medium does not bring the other work under+the scope of this License.++ 3. You may copy and distribute the Program (or a work based on it,+under Section 2) in object code or executable form under the terms of+Sections 1 and 2 above provided that you also do one of the following:++ a) Accompany it with the complete corresponding machine-readable+ source code, which must be distributed under the terms of Sections+ 1 and 2 above on a medium customarily used for software interchange; or,++ b) Accompany it with a written offer, valid for at least three+ years, to give any third party, for a charge no more than your+ cost of physically performing source distribution, a complete+ machine-readable copy of the corresponding source code, to be+ distributed under the terms of Sections 1 and 2 above on a medium+ customarily used for software interchange; or,++ c) Accompany it with the information you received as to the offer+ to distribute corresponding source code. (This alternative is+ allowed only for noncommercial distribution and only if you+ received the program in object code or executable form with such+ an offer, in accord with Subsection b above.)++The source code for a work means the preferred form of the work for+making modifications to it. For an executable work, complete source+code means all the source code for all modules it contains, plus any+associated interface definition files, plus the scripts used to+control compilation and installation of the executable. However, as a+special exception, the source code distributed need not include+anything that is normally distributed (in either source or binary+form) with the major components (compiler, kernel, and so on) of the+operating system on which the executable runs, unless that component+itself accompanies the executable.++If distribution of executable or object code is made by offering+access to copy from a designated place, then offering equivalent+access to copy the source code from the same place counts as+distribution of the source code, even though third parties are not+compelled to copy the source along with the object code.++ 4. You may not copy, modify, sublicense, or distribute the Program+except as expressly provided under this License. Any attempt+otherwise to copy, modify, sublicense or distribute the Program is+void, and will automatically terminate your rights under this License.+However, parties who have received copies, or rights, from you under+this License will not have their licenses terminated so long as such+parties remain in full compliance.++ 5. You are not required to accept this License, since you have not+signed it. However, nothing else grants you permission to modify or+distribute the Program or its derivative works. These actions are+prohibited by law if you do not accept this License. Therefore, by+modifying or distributing the Program (or any work based on the+Program), you indicate your acceptance of this License to do so, and+all its terms and conditions for copying, distributing or modifying+the Program or works based on it.++ 6. Each time you redistribute the Program (or any work based on the+Program), the recipient automatically receives a license from the+original licensor to copy, distribute or modify the Program subject to+these terms and conditions. You may not impose any further+restrictions on the recipients' exercise of the rights granted herein.+You are not responsible for enforcing compliance by third parties to+this License.++ 7. If, as a consequence of a court judgment or allegation of patent+infringement or for any other reason (not limited to patent issues),+conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot+distribute so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you+may not distribute the Program at all. For example, if a patent+license would not permit royalty-free redistribution of the Program by+all those who receive copies directly or indirectly through you, then+the only way you could satisfy both it and this License would be to+refrain entirely from distribution of the Program.++If any portion of this section is held invalid or unenforceable under+any particular circumstance, the balance of the section is intended to+apply and the section as a whole is intended to apply in other+circumstances.++It is not the purpose of this section to induce you to infringe any+patents or other property right claims or to contest validity of any+such claims; this section has the sole purpose of protecting the+integrity of the free software distribution system, which is+implemented by public license practices. Many people have made+generous contributions to the wide range of software distributed+through that system in reliance on consistent application of that+system; it is up to the author/donor to decide if he or she is willing+to distribute software through any other system and a licensee cannot+impose that choice.++This section is intended to make thoroughly clear what is believed to+be a consequence of the rest of this License.++ 8. If the distribution and/or use of the Program is restricted in+certain countries either by patents or by copyrighted interfaces, the+original copyright holder who places the Program under this License+may add an explicit geographical distribution limitation excluding+those countries, so that distribution is permitted only in or among+countries not thus excluded. In such case, this License incorporates+the limitation as if written in the body of this License.++ 9. The Free Software Foundation may publish revised and/or new versions+of the General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++Each version is given a distinguishing version number. If the Program+specifies a version number of this License which applies to it and "any+later version", you have the option of following the terms and conditions+either of that version or of any later version published by the Free+Software Foundation. If the Program does not specify a version number of+this License, you may choose any version ever published by the Free Software+Foundation.++ 10. If you wish to incorporate parts of the Program into other free+programs whose distribution conditions are different, write to the author+to ask for permission. For software which is copyrighted by the Free+Software Foundation, write to the Free Software Foundation; we sometimes+make exceptions for this. Our decision will be guided by the two goals+of preserving the free status of all derivatives of our free software and+of promoting the sharing and reuse of software generally.++ NO WARRANTY++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,+REPAIR OR CORRECTION.++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE+POSSIBILITY OF SUCH DAMAGES.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+convey the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software; you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation; either version 2 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License along+ with this program; if not, write to the Free Software Foundation, Inc.,+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.++Also add information on how to contact you by electronic and paper mail.++If the program is interactive, make it output a short notice like this+when it starts in an interactive mode:++ Gnomovision version 69, Copyright (C) year name of author+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, the commands you use may+be called something other than `show w' and `show c'; they could even be+mouse-clicks or menu items--whatever suits your program.++You should also get your employer (if you work as a programmer) or your+school, if any, to sign a "copyright disclaimer" for the program, if+necessary. Here is a sample; alter the names:++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program+ `Gnomovision' (which makes passes at compilers) written by James Hacker.++ <signature of Ty Coon>, 1 April 1989+ Ty Coon, President of Vice++This General Public License does not permit incorporating your program into+proprietary programs. If your program is a subroutine library, you may+consider it more useful to permit linking proprietary applications with the+library. If this is what you want to do, use the GNU Lesser General+Public License instead of this License.
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple+main = defaultMain+
+ lib/curve25519-donna.c view
@@ -0,0 +1,730 @@+/* Copyright 2008, Google Inc.+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions are+ * met:+ *+ * * Redistributions of source code must retain the above copyright+ * notice, this list of conditions and the following disclaimer.+ * * Redistributions in binary form must reproduce the above+ * copyright notice, this list of conditions and the following disclaimer+ * in the documentation and/or other materials provided with the+ * distribution.+ * * Neither the name of Google Inc. nor the names of its+ * contributors may be used to endorse or promote products derived from+ * this software without specific prior written permission.+ *+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * curve25519-donna: Curve25519 elliptic curve, public key function+ *+ * http://code.google.com/p/curve25519-donna/+ *+ * Adam Langley <agl@imperialviolet.org>+ *+ * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>+ *+ * More information about curve25519 can be found here+ * http://cr.yp.to/ecdh.html+ *+ * djb's sample implementation of curve25519 is written in a special assembly+ * language called qhasm and uses the floating point registers.+ *+ * This is, almost, a clean room reimplementation from the curve25519 paper. It+ * uses many of the tricks described therein. Only the crecip function is taken+ * from the sample implementation.+ */++#include <string.h>+#include <stdint.h>++typedef uint8_t u8;+typedef int32_t s32;+typedef int64_t limb;++/* Field element representation:+ *+ * Field elements are written as an array of signed, 64-bit limbs, least+ * significant first. The value of the field element is:+ * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...+ *+ * i.e. the limbs are 26, 25, 26, 25, ... bits wide.+ */++/* Sum two numbers: output += in */+static void fsum(limb *output, const limb *in) {+ unsigned i;+ for (i = 0; i < 10; i += 2) {+ output[0+i] = (output[0+i] + in[0+i]);+ output[1+i] = (output[1+i] + in[1+i]);+ }+}++/* Find the difference of two numbers: output = in - output+ * (note the order of the arguments!)+ */+static void fdifference(limb *output, const limb *in) {+ unsigned i;+ for (i = 0; i < 10; ++i) {+ output[i] = (in[i] - output[i]);+ }+}++/* Multiply a number by a scalar: output = in * scalar */+static void fscalar_product(limb *output, const limb *in, const limb scalar) {+ unsigned i;+ for (i = 0; i < 10; ++i) {+ output[i] = in[i] * scalar;+ }+}++/* Multiply two numbers: output = in2 * in+ *+ * output must be distinct to both inputs. The inputs are reduced coefficient+ * form, the output is not.+ */+static void fproduct(limb *output, const limb *in2, const limb *in) {+ output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]);+ output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) ++ ((limb) ((s32) in2[1])) * ((s32) in[0]);+ output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) ++ ((limb) ((s32) in2[0])) * ((s32) in[2]) ++ ((limb) ((s32) in2[2])) * ((s32) in[0]);+ output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) ++ ((limb) ((s32) in2[2])) * ((s32) in[1]) ++ ((limb) ((s32) in2[0])) * ((s32) in[3]) ++ ((limb) ((s32) in2[3])) * ((s32) in[0]);+ output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) ++ 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) ++ ((limb) ((s32) in2[3])) * ((s32) in[1])) ++ ((limb) ((s32) in2[0])) * ((s32) in[4]) ++ ((limb) ((s32) in2[4])) * ((s32) in[0]);+ output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) ++ ((limb) ((s32) in2[3])) * ((s32) in[2]) ++ ((limb) ((s32) in2[1])) * ((s32) in[4]) ++ ((limb) ((s32) in2[4])) * ((s32) in[1]) ++ ((limb) ((s32) in2[0])) * ((s32) in[5]) ++ ((limb) ((s32) in2[5])) * ((s32) in[0]);+ output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) ++ ((limb) ((s32) in2[1])) * ((s32) in[5]) ++ ((limb) ((s32) in2[5])) * ((s32) in[1])) ++ ((limb) ((s32) in2[2])) * ((s32) in[4]) ++ ((limb) ((s32) in2[4])) * ((s32) in[2]) ++ ((limb) ((s32) in2[0])) * ((s32) in[6]) ++ ((limb) ((s32) in2[6])) * ((s32) in[0]);+ output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) ++ ((limb) ((s32) in2[4])) * ((s32) in[3]) ++ ((limb) ((s32) in2[2])) * ((s32) in[5]) ++ ((limb) ((s32) in2[5])) * ((s32) in[2]) ++ ((limb) ((s32) in2[1])) * ((s32) in[6]) ++ ((limb) ((s32) in2[6])) * ((s32) in[1]) ++ ((limb) ((s32) in2[0])) * ((s32) in[7]) ++ ((limb) ((s32) in2[7])) * ((s32) in[0]);+ output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) ++ 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) ++ ((limb) ((s32) in2[5])) * ((s32) in[3]) ++ ((limb) ((s32) in2[1])) * ((s32) in[7]) ++ ((limb) ((s32) in2[7])) * ((s32) in[1])) ++ ((limb) ((s32) in2[2])) * ((s32) in[6]) ++ ((limb) ((s32) in2[6])) * ((s32) in[2]) ++ ((limb) ((s32) in2[0])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[0]);+ output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) ++ ((limb) ((s32) in2[5])) * ((s32) in[4]) ++ ((limb) ((s32) in2[3])) * ((s32) in[6]) ++ ((limb) ((s32) in2[6])) * ((s32) in[3]) ++ ((limb) ((s32) in2[2])) * ((s32) in[7]) ++ ((limb) ((s32) in2[7])) * ((s32) in[2]) ++ ((limb) ((s32) in2[1])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[1]) ++ ((limb) ((s32) in2[0])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[0]);+ output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) ++ ((limb) ((s32) in2[3])) * ((s32) in[7]) ++ ((limb) ((s32) in2[7])) * ((s32) in[3]) ++ ((limb) ((s32) in2[1])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[1])) ++ ((limb) ((s32) in2[4])) * ((s32) in[6]) ++ ((limb) ((s32) in2[6])) * ((s32) in[4]) ++ ((limb) ((s32) in2[2])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[2]);+ output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) ++ ((limb) ((s32) in2[6])) * ((s32) in[5]) ++ ((limb) ((s32) in2[4])) * ((s32) in[7]) ++ ((limb) ((s32) in2[7])) * ((s32) in[4]) ++ ((limb) ((s32) in2[3])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[3]) ++ ((limb) ((s32) in2[2])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[2]);+ output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) ++ 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) ++ ((limb) ((s32) in2[7])) * ((s32) in[5]) ++ ((limb) ((s32) in2[3])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[3])) ++ ((limb) ((s32) in2[4])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[4]);+ output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) ++ ((limb) ((s32) in2[7])) * ((s32) in[6]) ++ ((limb) ((s32) in2[5])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[5]) ++ ((limb) ((s32) in2[4])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[4]);+ output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) ++ ((limb) ((s32) in2[5])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[5])) ++ ((limb) ((s32) in2[6])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[6]);+ output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) ++ ((limb) ((s32) in2[8])) * ((s32) in[7]) ++ ((limb) ((s32) in2[6])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[6]);+ output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) ++ 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[7]));+ output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) ++ ((limb) ((s32) in2[9])) * ((s32) in[8]);+ output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]);+}++/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */+static void freduce_degree(limb *output) {+ /* Each of these shifts and adds ends up multiplying the value by 19. */+ output[8] += output[18] << 4;+ output[8] += output[18] << 1;+ output[8] += output[18];+ output[7] += output[17] << 4;+ output[7] += output[17] << 1;+ output[7] += output[17];+ output[6] += output[16] << 4;+ output[6] += output[16] << 1;+ output[6] += output[16];+ output[5] += output[15] << 4;+ output[5] += output[15] << 1;+ output[5] += output[15];+ output[4] += output[14] << 4;+ output[4] += output[14] << 1;+ output[4] += output[14];+ output[3] += output[13] << 4;+ output[3] += output[13] << 1;+ output[3] += output[13];+ output[2] += output[12] << 4;+ output[2] += output[12] << 1;+ output[2] += output[12];+ output[1] += output[11] << 4;+ output[1] += output[11] << 1;+ output[1] += output[11];+ output[0] += output[10] << 4;+ output[0] += output[10] << 1;+ output[0] += output[10];+}++#if (-1 & 3) != 3+#error "This code only works on a two's complement system"+#endif++/* return v / 2^26, using only shifts and adds. */+static inline limb+div_by_2_26(const limb v)+{+ /* High word of v; no shift needed*/+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);+ /* Set to all 1s if v was negative; else set to 0s. */+ const int32_t sign = ((int32_t) highword) >> 31;+ /* Set to 0x3ffffff if v was negative; else set to 0. */+ const int32_t roundoff = ((uint32_t) sign) >> 6;+ /* Should return v / (1<<26) */+ return (v + roundoff) >> 26;+}++/* return v / (2^25), using only shifts and adds. */+static inline limb+div_by_2_25(const limb v)+{+ /* High word of v; no shift needed*/+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);+ /* Set to all 1s if v was negative; else set to 0s. */+ const int32_t sign = ((int32_t) highword) >> 31;+ /* Set to 0x1ffffff if v was negative; else set to 0. */+ const int32_t roundoff = ((uint32_t) sign) >> 7;+ /* Should return v / (1<<25) */+ return (v + roundoff) >> 25;+}++static inline s32+div_s32_by_2_25(const s32 v)+{+ const s32 roundoff = ((uint32_t)(v >> 31)) >> 7;+ return (v + roundoff) >> 25;+}++/* Reduce all coefficients of the short form input so that |x| < 2^26.+ *+ * On entry: |output[i]| < 2^62+ */+static void freduce_coefficients(limb *output) {+ unsigned i;++ output[10] = 0;++ for (i = 0; i < 10; i += 2) {+ limb over = div_by_2_26(output[i]);+ output[i] -= over << 26;+ output[i+1] += over;++ over = div_by_2_25(output[i+1]);+ output[i+1] -= over << 25;+ output[i+2] += over;+ }+ /* Now |output[10]| < 2 ^ 38 and all other coefficients are reduced. */+ output[0] += output[10] << 4;+ output[0] += output[10] << 1;+ output[0] += output[10];++ output[10] = 0;++ /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19 * 2^38+ * So |over| will be no more than 77825 */+ {+ limb over = div_by_2_26(output[0]);+ output[0] -= over << 26;+ output[1] += over;+ }++ /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 77825+ * So |over| will be no more than 1. */+ {+ /* output[1] fits in 32 bits, so we can use div_s32_by_2_25 here. */+ s32 over32 = div_s32_by_2_25((s32) output[1]);+ output[1] -= over32 << 25;+ output[2] += over32;+ }++ /* Finally, output[0,1,3..9] are reduced, and output[2] is "nearly reduced":+ * we have |output[2]| <= 2^26. This is good enough for all of our math,+ * but it will require an extra freduce_coefficients before fcontract. */+}++/* A helpful wrapper around fproduct: output = in * in2.+ *+ * output must be distinct to both inputs. The output is reduced degree and+ * reduced coefficient.+ */+static void+fmul(limb *output, const limb *in, const limb *in2) {+ limb t[19];+ fproduct(t, in, in2);+ freduce_degree(t);+ freduce_coefficients(t);+ memcpy(output, t, sizeof(limb) * 10);+}++static void fsquare_inner(limb *output, const limb *in) {+ output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]);+ output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]);+ output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) ++ ((limb) ((s32) in[0])) * ((s32) in[2]));+ output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) ++ ((limb) ((s32) in[0])) * ((s32) in[3]));+ output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) ++ 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) ++ 2 * ((limb) ((s32) in[0])) * ((s32) in[4]);+ output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) ++ ((limb) ((s32) in[1])) * ((s32) in[4]) ++ ((limb) ((s32) in[0])) * ((s32) in[5]));+ output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) ++ ((limb) ((s32) in[2])) * ((s32) in[4]) ++ ((limb) ((s32) in[0])) * ((s32) in[6]) ++ 2 * ((limb) ((s32) in[1])) * ((s32) in[5]));+ output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) ++ ((limb) ((s32) in[2])) * ((s32) in[5]) ++ ((limb) ((s32) in[1])) * ((s32) in[6]) ++ ((limb) ((s32) in[0])) * ((s32) in[7]));+ output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) ++ 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) ++ ((limb) ((s32) in[0])) * ((s32) in[8]) ++ 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) ++ ((limb) ((s32) in[3])) * ((s32) in[5])));+ output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) ++ ((limb) ((s32) in[3])) * ((s32) in[6]) ++ ((limb) ((s32) in[2])) * ((s32) in[7]) ++ ((limb) ((s32) in[1])) * ((s32) in[8]) ++ ((limb) ((s32) in[0])) * ((s32) in[9]));+ output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) ++ ((limb) ((s32) in[4])) * ((s32) in[6]) ++ ((limb) ((s32) in[2])) * ((s32) in[8]) ++ 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) ++ ((limb) ((s32) in[1])) * ((s32) in[9])));+ output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) ++ ((limb) ((s32) in[4])) * ((s32) in[7]) ++ ((limb) ((s32) in[3])) * ((s32) in[8]) ++ ((limb) ((s32) in[2])) * ((s32) in[9]));+ output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) ++ 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) ++ 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) ++ ((limb) ((s32) in[3])) * ((s32) in[9])));+ output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) ++ ((limb) ((s32) in[5])) * ((s32) in[8]) ++ ((limb) ((s32) in[4])) * ((s32) in[9]));+ output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) ++ ((limb) ((s32) in[6])) * ((s32) in[8]) ++ 2 * ((limb) ((s32) in[5])) * ((s32) in[9]));+ output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) ++ ((limb) ((s32) in[6])) * ((s32) in[9]));+ output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) ++ 4 * ((limb) ((s32) in[7])) * ((s32) in[9]);+ output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]);+ output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]);+}++static void+fsquare(limb *output, const limb *in) {+ limb t[19];+ fsquare_inner(t, in);+ freduce_degree(t);+ freduce_coefficients(t);+ memcpy(output, t, sizeof(limb) * 10);+}++/* Take a little-endian, 32-byte number and expand it into polynomial form */+static void+fexpand(limb *output, const u8 *input) {+#define F(n,start,shift,mask) \+ output[n] = ((((limb) input[start + 0]) | \+ ((limb) input[start + 1]) << 8 | \+ ((limb) input[start + 2]) << 16 | \+ ((limb) input[start + 3]) << 24) >> shift) & mask;+ F(0, 0, 0, 0x3ffffff);+ F(1, 3, 2, 0x1ffffff);+ F(2, 6, 3, 0x3ffffff);+ F(3, 9, 5, 0x1ffffff);+ F(4, 12, 6, 0x3ffffff);+ F(5, 16, 0, 0x1ffffff);+ F(6, 19, 1, 0x3ffffff);+ F(7, 22, 3, 0x1ffffff);+ F(8, 25, 4, 0x3ffffff);+ F(9, 28, 6, 0x1ffffff);+#undef F+}++#if (-32 >> 1) != -16+#error "This code only works when >> does sign-extension on negative numbers"+#endif++/* Take a fully reduced polynomial form number and contract it into a+ * little-endian, 32-byte array+ */+static void+fcontract(u8 *output, limb *input) {+ int i;+ int j;++ for (j = 0; j < 2; ++j) {+ for (i = 0; i < 9; ++i) {+ if ((i & 1) == 1) {+ /* This calculation is a time-invariant way to make input[i] positive+ by borrowing from the next-larger limb.+ */+ const s32 mask = (s32)(input[i]) >> 31;+ const s32 carry = -(((s32)(input[i]) & mask) >> 25);+ input[i] = (s32)(input[i]) + (carry << 25);+ input[i+1] = (s32)(input[i+1]) - carry;+ } else {+ const s32 mask = (s32)(input[i]) >> 31;+ const s32 carry = -(((s32)(input[i]) & mask) >> 26);+ input[i] = (s32)(input[i]) + (carry << 26);+ input[i+1] = (s32)(input[i+1]) - carry;+ }+ }+ {+ const s32 mask = (s32)(input[9]) >> 31;+ const s32 carry = -(((s32)(input[9]) & mask) >> 25);+ input[9] = (s32)(input[9]) + (carry << 25);+ input[0] = (s32)(input[0]) - (carry * 19);+ }+ }++ /* The first borrow-propagation pass above ended with every limb+ except (possibly) input[0] non-negative.++ Since each input limb except input[0] is decreased by at most 1+ by a borrow-propagation pass, the second borrow-propagation pass+ could only have wrapped around to decrease input[0] again if the+ first pass left input[0] negative *and* input[1] through input[9]+ were all zero. In that case, input[1] is now 2^25 - 1, and this+ last borrow-propagation step will leave input[1] non-negative.+ */+ {+ const s32 mask = (s32)(input[0]) >> 31;+ const s32 carry = -(((s32)(input[0]) & mask) >> 26);+ input[0] = (s32)(input[0]) + (carry << 26);+ input[1] = (s32)(input[1]) - carry;+ }++ /* Both passes through the above loop, plus the last 0-to-1 step, are+ necessary: if input[9] is -1 and input[0] through input[8] are 0,+ negative values will remain in the array until the end.+ */++ input[1] <<= 2;+ input[2] <<= 3;+ input[3] <<= 5;+ input[4] <<= 6;+ input[6] <<= 1;+ input[7] <<= 3;+ input[8] <<= 4;+ input[9] <<= 6;+#define F(i, s) \+ output[s+0] |= input[i] & 0xff; \+ output[s+1] = (input[i] >> 8) & 0xff; \+ output[s+2] = (input[i] >> 16) & 0xff; \+ output[s+3] = (input[i] >> 24) & 0xff;+ output[0] = 0;+ output[16] = 0;+ F(0,0);+ F(1,3);+ F(2,6);+ F(3,9);+ F(4,12);+ F(5,16);+ F(6,19);+ F(7,22);+ F(8,25);+ F(9,28);+#undef F+}++/* Input: Q, Q', Q-Q'+ * Output: 2Q, Q+Q'+ *+ * x2 z3: long form+ * x3 z3: long form+ * x z: short form, destroyed+ * xprime zprime: short form, destroyed+ * qmqp: short form, preserved+ */+static void fmonty(limb *x2, limb *z2, /* output 2Q */+ limb *x3, limb *z3, /* output Q + Q' */+ limb *x, limb *z, /* input Q */+ limb *xprime, limb *zprime, /* input Q' */+ const limb *qmqp /* input Q - Q' */) {+ limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],+ zzprime[19], zzzprime[19], xxxprime[19];++ memcpy(origx, x, 10 * sizeof(limb));+ fsum(x, z);+ fdifference(z, origx); // does x - z++ memcpy(origxprime, xprime, sizeof(limb) * 10);+ fsum(xprime, zprime);+ fdifference(zprime, origxprime);+ fproduct(xxprime, xprime, z);+ fproduct(zzprime, x, zprime);+ freduce_degree(xxprime);+ freduce_coefficients(xxprime);+ freduce_degree(zzprime);+ freduce_coefficients(zzprime);+ memcpy(origxprime, xxprime, sizeof(limb) * 10);+ fsum(xxprime, zzprime);+ fdifference(zzprime, origxprime);+ fsquare(xxxprime, xxprime);+ fsquare(zzzprime, zzprime);+ fproduct(zzprime, zzzprime, qmqp);+ freduce_degree(zzprime);+ freduce_coefficients(zzprime);+ memcpy(x3, xxxprime, sizeof(limb) * 10);+ memcpy(z3, zzprime, sizeof(limb) * 10);++ fsquare(xx, x);+ fsquare(zz, z);+ fproduct(x2, xx, zz);+ freduce_degree(x2);+ freduce_coefficients(x2);+ fdifference(zz, xx); // does zz = xx - zz+ memset(zzz + 10, 0, sizeof(limb) * 9);+ fscalar_product(zzz, zz, 121665);+ /* No need to call freduce_degree here:+ fscalar_product doesn't increase the degree of its input. */+ freduce_coefficients(zzz);+ fsum(zzz, xx);+ fproduct(z2, zz, zzz);+ freduce_degree(z2);+ freduce_coefficients(z2);+}++/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave+ * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid+ * side-channel attacks.+ *+ * NOTE that this function requires that 'iswap' be 1 or 0; other values give+ * wrong results. Also, the two limb arrays must be in reduced-coefficient,+ * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,+ * and all all values in a[0..9],b[0..9] must have magnitude less than+ * INT32_MAX.+ */+static void+swap_conditional(limb a[19], limb b[19], limb iswap) {+ unsigned i;+ const s32 swap = (s32) -iswap;++ for (i = 0; i < 10; ++i) {+ const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) );+ a[i] = ((s32)a[i]) ^ x;+ b[i] = ((s32)b[i]) ^ x;+ }+}++/* Calculates nQ where Q is the x-coordinate of a point on the curve+ *+ * resultx/resultz: the x coordinate of the resulting curve point (short form)+ * n: a little endian, 32-byte number+ * q: a point of the curve (short form)+ */+static void+cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {+ limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};+ limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;+ limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};+ limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;++ unsigned i, j;++ memcpy(nqpqx, q, sizeof(limb) * 10);++ for (i = 0; i < 32; ++i) {+ u8 byte = n[31 - i];+ for (j = 0; j < 8; ++j) {+ const limb bit = byte >> 7;++ swap_conditional(nqx, nqpqx, bit);+ swap_conditional(nqz, nqpqz, bit);+ fmonty(nqx2, nqz2,+ nqpqx2, nqpqz2,+ nqx, nqz,+ nqpqx, nqpqz,+ q);+ swap_conditional(nqx2, nqpqx2, bit);+ swap_conditional(nqz2, nqpqz2, bit);++ t = nqx;+ nqx = nqx2;+ nqx2 = t;+ t = nqz;+ nqz = nqz2;+ nqz2 = t;+ t = nqpqx;+ nqpqx = nqpqx2;+ nqpqx2 = t;+ t = nqpqz;+ nqpqz = nqpqz2;+ nqpqz2 = t;++ byte <<= 1;+ }+ }++ memcpy(resultx, nqx, sizeof(limb) * 10);+ memcpy(resultz, nqz, sizeof(limb) * 10);+}++// -----------------------------------------------------------------------------+// Shamelessly copied from djb's code+// -----------------------------------------------------------------------------+static void+crecip(limb *out, const limb *z) {+ limb z2[10];+ limb z9[10];+ limb z11[10];+ limb z2_5_0[10];+ limb z2_10_0[10];+ limb z2_20_0[10];+ limb z2_50_0[10];+ limb z2_100_0[10];+ limb t0[10];+ limb t1[10];+ int i;++ /* 2 */ fsquare(z2,z);+ /* 4 */ fsquare(t1,z2);+ /* 8 */ fsquare(t0,t1);+ /* 9 */ fmul(z9,t0,z);+ /* 11 */ fmul(z11,z9,z2);+ /* 22 */ fsquare(t0,z11);+ /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);++ /* 2^6 - 2^1 */ fsquare(t0,z2_5_0);+ /* 2^7 - 2^2 */ fsquare(t1,t0);+ /* 2^8 - 2^3 */ fsquare(t0,t1);+ /* 2^9 - 2^4 */ fsquare(t1,t0);+ /* 2^10 - 2^5 */ fsquare(t0,t1);+ /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);++ /* 2^11 - 2^1 */ fsquare(t0,z2_10_0);+ /* 2^12 - 2^2 */ fsquare(t1,t0);+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);++ /* 2^21 - 2^1 */ fsquare(t0,z2_20_0);+ /* 2^22 - 2^2 */ fsquare(t1,t0);+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);++ /* 2^41 - 2^1 */ fsquare(t1,t0);+ /* 2^42 - 2^2 */ fsquare(t0,t1);+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }+ /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);++ /* 2^51 - 2^1 */ fsquare(t0,z2_50_0);+ /* 2^52 - 2^2 */ fsquare(t1,t0);+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);++ /* 2^101 - 2^1 */ fsquare(t1,z2_100_0);+ /* 2^102 - 2^2 */ fsquare(t0,t1);+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }+ /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);++ /* 2^201 - 2^1 */ fsquare(t0,t1);+ /* 2^202 - 2^2 */ fsquare(t1,t0);+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);++ /* 2^251 - 2^1 */ fsquare(t1,t0);+ /* 2^252 - 2^2 */ fsquare(t0,t1);+ /* 2^253 - 2^3 */ fsquare(t1,t0);+ /* 2^254 - 2^4 */ fsquare(t0,t1);+ /* 2^255 - 2^5 */ fsquare(t1,t0);+ /* 2^255 - 21 */ fmul(out,t1,z11);+}++int curve25519_donna(u8 *, const u8 *, const u8 *);++int+curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {+ limb bp[10], x[10], z[11], zmone[10];+ uint8_t e[32];+ int i;++ for (i = 0; i < 32; ++i) e[i] = secret[i];+ e[0] &= 248;+ e[31] &= 127;+ e[31] |= 64;++ fexpand(bp, basepoint);+ cmult(x, z, e, bp);+ crecip(zmone, z);+ fmul(z, x, zmone);+ freduce_coefficients(z);+ fcontract(mypublic, z);+ return 0;+}
+ lib/ed25519-donna/ed25519.c view
@@ -0,0 +1,109 @@+/*+ Public domain by Andrew M. <liquidsun@gmail.com>++ Ed25519 reference implementation using Ed25519-donna+*/+++#include "ed25519-donna.h"+#include "ed25519.h"+#include "ed25519-randombytes.h"+#include <openssl/sha.h>++/*+ Generates a (extsk[0..31]) and aExt (extsk[32..63])+*/++static void DONNA_INLINE+ed25519_extsk(hash_512bits extsk, const ed25519_secret_key sk) {+ SHA512(sk, 32, extsk);+ extsk[0] &= 248;+ extsk[31] &= 127;+ extsk[31] |= 64;+}++static void+ed25519_hram(hash_512bits hram, const ed25519_signature RS, const ed25519_public_key pk, const unsigned char *m, size_t mlen) {+ SHA512_CTX shactx;+ SHA512_Init(&shactx);+ SHA512_Update(&shactx, RS, 32);+ SHA512_Update(&shactx, pk, 32);+ SHA512_Update(&shactx, m, mlen);+ SHA512_Final(hram, &shactx);+}++void+ed25519_publickey(const ed25519_secret_key sk, ed25519_public_key pk) {+ bignum256modm a;+ ge25519 MM16 A;+ hash_512bits extsk;++ /* A = aB */+ ed25519_extsk(extsk, sk);+ expand256_modm(a, extsk, 32);+ ge25519_scalarmult_base_niels(&A, a);+ ge25519_pack(pk, &A);+}+++void+ed25519_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS) {+ SHA512_CTX shactx;+ bignum256modm r, S, a;+ ge25519 MM16 R;+ hash_512bits extsk, hashr, hram;++ ed25519_extsk(extsk, sk);++ /* r = H(aExt[32..64], m) */+ SHA512_Init(&shactx);+ SHA512_Update(&shactx, extsk + 32, 32);+ SHA512_Update(&shactx, m, mlen);+ SHA512_Final(hashr, &shactx);+ expand256_modm(r, hashr, 64);++ /* R = rB */+ ge25519_scalarmult_base_niels(&R, r);+ ge25519_pack(RS, &R);++ /* S = H(R,A,m).. */+ ed25519_hram(hram, RS, pk, m, mlen);+ expand256_modm(S, hram, 64);++ /* S = H(R,A,m)a */+ expand256_modm(a, extsk, 32);+ mul256_modm(S, S, a);++ /* S = (r + H(R,A,m)a) */+ add256_modm(S, S, r);++ /* S = (r + H(R,A,m)a) mod L */ + contract256_modm(RS + 32, S);+}++int+ed25519_sign_open(const unsigned char *m, size_t mlen, const ed25519_public_key pk, const ed25519_signature RS) {+ ge25519 MM16 R, A;+ hash_512bits hash;+ bignum256modm hram, S;+ unsigned char checkR[32];++ if ((RS[63] & 224) || !ge25519_unpack_negative_vartime(&A, pk))+ return -1;++ /* hram = H(R,A,m) */+ ed25519_hram(hash, RS, pk, m, mlen);+ expand256_modm(hram, hash, 64);++ /* S */+ expand256_modm(S, RS + 32, 32);++ /* SB - H(R,A,m)A */+ ge25519_double_scalarmult_vartime(&R, &A, hram, S);+ ge25519_pack(checkR, &R);++ /* check that R = SB - H(R,A,m)A */+ return ed25519_verify(RS, checkR, 32) ? 0 : -1;+}++#include "ed25519-donna-batchverify.h"
+ tests/TestCore.hs view
@@ -0,0 +1,35 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+{-# LANGUAGE TemplateHaskell #-}++module Main where++import Data.Monoid+import qualified Data.ByteString.Lazy as LZ++import Test.QuickCheck+import Test.Framework.Providers.QuickCheck2+import Test.Framework++import Dust.Model.Huffman (bitpack, bitunpack, padToEight)++-- | Bits of the sort that can be bitpack'd and then bitunpack'd+newtype Bits = Bits [Bool] deriving (Show)++instance Arbitrary Bits where+ arbitrary = fmap (Bits . padToEight) $ arbitrary `suchThat` startsWithTrue+ where+ startsWithTrue (True:_) = True+ startsWithTrue _ = False++prop_bitpack_unpack_loop :: Bits -> Bool+prop_bitpack_unpack_loop (Bits bits) = bitunpack packed == bits+ where+ packed = mconcat $ LZ.toChunks $ bitpack bits+ {-# NOINLINE packed #-}++main :: IO ()+main = defaultMain [+ testGroup "Huffman" [+ testProperty "bit pack/unpack loop" prop_bitpack_unpack_loop+ ]+ ]
+ tests/TestCrypto.hs view
@@ -0,0 +1,104 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+{-# LANGUAGE TemplateHaskell #-}++module Main where++import Test.Framework.Providers.HUnit+import Test.Framework.Providers.QuickCheck2+import Test.Framework+import Test.HUnit hiding (test)+import Test.QuickCheck+import Test.QuickCheck.Gen++import Data.ByteString as B (ByteString, length)+import Data.ByteString.Char8 (pack, useAsCString, packCString)++import Dust.Crypto.DustCipher+import qualified Dust.Crypto.ECDH as ECDH+import qualified Dust.Crypto.ECDSA as ECDSA+import Dust.Crypto.Keys++gen_privkeys :: Gen String+gen_privkeys =+ let e = elements ['\00'..'\FF']+ in vectorOf 32 e++prop_ECDH_pubkey_size :: Property+prop_ECDH_pubkey_size =+ forAll gen_privkeys $ \privBytes ->+ let priv = PrivateKey $ pack privBytes+ (PublicKey pub) = ECDH.createPublic priv+ in B.length pub == 32++prop_ECDSA_pubkey_size :: Property+prop_ECDSA_pubkey_size =+ forAll gen_privkeys $ \privBytes ->+ let priv = PrivateKey $ pack privBytes+ (PublicKey pub) = ECDSA.createPublic priv+ in B.length pub == 32++case_ECDH_keypair_save_load = do+ let input = (pack "1234567890123457890123456789012")+ let keypair1 = ECDH.createKeypair input+ saveKeypair keypair1++ keypair2 <- loadKeypair++ keypair1 @?= keypair2++case_ECDH_createShared = do+ let entropy1 = (pack "1234567890123457890123456789012")+ let private1 = ECDH.createPrivate entropy1+ let public1 = ECDH.createPublic private1++ let entropy2 = (pack "2109876543210987543210987654321")+ let private2 = ECDH.createPrivate entropy2+ let public2 = ECDH.createPublic private2++ let shared1 = ECDH.createShared private1 public2+ let shared2 = ECDH.createShared private2 public1++ shared1 @?= shared2++case_ECDH_createKeypair = do+ let input = (pack "1234567890123457890123456789012")+ let keys = ECDH.createKeypair input+ let Keypair public private = keys+ publicBytes public @?= pack "\164x}\156n\177\164\240\187F\163\202\232}\221\207/x\ETX\178\SOH\208\238\213q\138%\137\159\253+{"+ privateBytes private @?= pack "023456789012345789012345678901r"++case_ECDH_createPublic = do+ let input = (pack "1234567890123457890123456789012")+ let private = ECDH.createPrivate input+ let public = ECDH.createPublic private+ publicBytes public @?= pack "\164x}\156n\177\164\240\187F\163\202\232}\221\207/x\ETX\178\SOH\208\238\213q\138%\137\159\253+{"++case_ECDH_createPrivate = do+ let input = (pack "1234567890123457890123456789012")+ let output = ECDH.createPrivate input+ privateBytes output @?= pack "023456789012345789012345678901r"++case_encrypt_decrypt = let key = EncryptionKey (pack "1234567890123456")+ iv = IV (pack "1234567890123456")++ plain1 = Plaintext (pack "majestic")+ cipher = encrypt key iv plain1+ plain2 = decrypt key iv cipher++ in plain1 @?= plain2++main :: IO ()+main =+ defaultMain [+ testGroup "ECDH25519" [ + testCase "ECDH_createPrivate" case_ECDH_createPrivate,+ testCase "ECDH_createPublic" case_ECDH_createPublic,+ testCase "ECDH_createKeypair" case_ECDH_createKeypair,+ testCase "ECDH_createShared" case_ECDH_createShared,+ testCase "ECDH_keypair_save_load" case_ECDH_keypair_save_load,+ testCase "encrypt_decrypt" case_encrypt_decrypt,+ + testProperty "ECDH_pubkey_size" prop_ECDH_pubkey_size,+ testProperty "ECDSA_pubkey_size" prop_ECDSA_pubkey_size+ ]+ ]