diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for u2f
+
+## 0.1.0.0  -- 2016-09-15
+
+* Mass-renamed everything. No changes of value
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2016, Eugene Butler
+
+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 Eugene Butler nor the names of other
+      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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# U2F
+
+Haskell library for conveniently processing U2F registration/sign-in requests.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/src/U2F.hs b/src/U2F.hs
new file mode 100644
--- /dev/null
+++ b/src/U2F.hs
@@ -0,0 +1,278 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module U2F where
+
+import GHC.Generics
+import Data.Bits
+
+import Data.Either.Unwrap
+
+import Data.ASN1.BinaryEncoding
+import Data.ASN1.Types
+import qualified Data.ByteString.Lazy.Char8 as LBS
+import qualified Data.ByteString.Char8 as BS
+
+import Data.Text.Encoding (encodeUtf8)
+import Data.Aeson ((.:), (.:?), decode, FromJSON(..),
+  ToJSON(..), Value(..), genericParseJSON, defaultOptions)
+import Data.Binary.Get
+import Data.Aeson.Types
+import Control.Applicative ((<$>), (<*>))
+import Data.ASN1.BitArray
+import Data.ASN1.Encoding
+import Data.ByteString (pack)
+import Data.ByteString.Base64.URL (encode, decodeLenient)
+import qualified Data.Text as T
+import Data.Text.Encoding (encodeUtf8, decodeUtf8)
+import Data.List
+
+import qualified Crypto.Hash.SHA256 as SHA256
+
+-- Cryptonite stuff
+import Crypto.Error
+import Crypto.PubKey.ECC.Types
+import qualified Crypto.PubKey.ECC.P256 as P256
+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA
+import Crypto.Hash.Algorithms
+
+{-
+  HOW TO USE
+  ==========
+  To Register
+    - Generate yourself a Request, consisting of your site/service uri, u2f version number, etc, send it to the client.
+    - Assuming the client returned a registration response (Registration), parse it with parseRegistration.
+    - Use verifyRegistration Request Registration to verify that the Registration is valid. (Challenge bytes match, were signed by key described in cert)
+    - Stash the publicKey and keyHandle somewhere, so you can use them for signin. verifyRegistration returns a Request, with added keyHandle, for convenience.
+  To Signin
+    - Make a Request.
+    - Parse whatever signin json you have with parseSignin.
+    - Dig out the publicKey for the relevant keyHandle.
+    - Verify signin with verifySignin publicKey Request Signin
+-}
+
+-- Curve
+ourCurve = getCurveByName SEC_p256r1
+
+-- Errors
+data U2FError =
+  RegistrationParseError |
+  RegistrationDataParseError |
+  RegistrationCertificateParseError |
+  PubKeyParsingError |
+  SignatureParseError |
+  ChallengeMismatchError |
+  FailedVerificationError |
+  SigninParseError |
+  ClientDataParseError |
+  RequestParseError
+  deriving (Show, Eq)
+
+data Request = Request {
+  appId :: T.Text,
+  version :: T.Text,
+  challenge :: T.Text,
+  keyHandle :: Maybe T.Text
+} deriving (Show, Generic, Eq)
+instance FromJSON Request
+instance ToJSON Request
+
+-- Registration Flow --
+data Registration = Registration {
+  registration_registrationData :: T.Text,
+  registration_challenge :: T.Text,
+  registration_version :: Maybe T.Text,
+  registration_appId :: T.Text,
+  registration_clientData :: T.Text,
+  registration_sessionID :: Maybe T.Text
+} deriving (Show, Generic)
+instance ToJSON Registration where
+  toJSON = genericToJSON defaultOptions {
+                fieldLabelModifier = Prelude.drop 13 }
+
+instance FromJSON Registration where
+  parseJSON = genericParseJSON defaultOptions {
+                fieldLabelModifier = Prelude.drop 13 }
+
+
+data RegistrationData = RegistrationData {
+  registrationData_reserved :: BS.ByteString,
+  registrationData_publicKey :: BS.ByteString,
+  registrationData_keyHandle :: BS.ByteString,
+  registrationData_certificate :: BS.ByteString,
+  registrationData_signature :: BS.ByteString
+} deriving (Show, Generic)
+
+parseRequest :: String -> Either U2FError Request
+parseRequest x = case (Data.Aeson.decode (LBS.pack x) :: Maybe Request) of
+  Just request -> Right request
+  Nothing -> Left RequestParseError
+
+parseRegistration :: String -> Either U2FError Registration
+parseRegistration x = case (Data.Aeson.decode (LBS.pack x) :: Maybe Registration) of
+  Just registration -> Right registration
+  Nothing -> Left RegistrationParseError
+
+parseRegistrationData :: BS.ByteString -> Either U2FError RegistrationData
+parseRegistrationData r = Right $ runGet unpackRegistrationData ( LBS.fromStrict $ decodeLenient r)
+
+getPubKeyFromCertificate :: BS.ByteString -> Either U2FError ECDSA.PublicKey
+getPubKeyFromCertificate cert = case (decodeASN1' DER cert) of
+  Right certParse -> case (findPubKey certParse) of
+    Just key -> Right key
+    Nothing -> Left PubKeyParsingError
+  Left _ -> Left RegistrationCertificateParseError
+
+findPubKey :: Foldable t => t ASN1 -> Maybe ECDSA.PublicKey
+findPubKey parsedCert = case (find pubKeyShape parsedCert) of
+  Just (BitString (BitArray len x)) -> parsePublicKey $ BS.tail x
+  _ -> Nothing
+
+pubKeyShape :: ASN1 -> Bool
+pubKeyShape (BitString (BitArray len _)) = len == 520
+pubKeyShape _ = False
+
+getSignatureBase :: BS.ByteString -> BS.ByteString -> BS.ByteString -> BS.ByteString -> BS.ByteString
+getSignatureBase appId clientData keyHandle publicKey = sigBase
+  where sigBase = BS.concat([BS.pack "\NUL", SHA256.hash(appId), SHA256.hash(decodeLenient clientData), keyHandle, publicKey])
+
+getSignatureBaseFromRegistration :: Registration -> RegistrationData -> Either U2FError BS.ByteString
+getSignatureBaseFromRegistration registration registrationData = do
+  appId <- pure $ BS.pack $ T.unpack $ registration_appId registration
+  clientData <- pure $ BS.pack $ T.unpack $ registration_clientData registration
+  keyHandle <- pure $ registrationData_keyHandle registrationData
+  publicKey <- pure $ registrationData_publicKey registrationData
+  pure $ getSignatureBase appId clientData keyHandle publicKey
+
+verifyRegistration :: Request -> Registration -> Either U2FError Request
+verifyRegistration request registration = do
+  challengesEqual <- u2fComparator (challenge request) (registration_challenge registration) ChallengeMismatchError
+  registrationData <- parseRegistrationData $ encodeUtf8 $ registration_registrationData registration
+  pkey <- getPubKeyFromCertificate $ registrationData_certificate registrationData
+  signature <- parseSignature $ registrationData_signature registrationData
+  signatureBase <- getSignatureBaseFromRegistration registration registrationData
+  case (verifySignature signatureBase pkey signature) of
+    True -> Right (request {keyHandle = Just $ formatOutputBase64 $ registrationData_keyHandle registrationData})
+    False -> Left FailedVerificationError
+
+-- Signin Flow --
+data Signin = Signin {
+  signin_keyHandle :: T.Text,
+  signin_clientData :: T.Text,
+  signin_signatureData :: T.Text
+} deriving (Show, Generic, Eq)
+
+instance FromJSON Signin where
+  parseJSON = genericParseJSON defaultOptions {
+                fieldLabelModifier = Prelude.drop 7 }
+
+data ClientData = ClientData {
+  clientData_typ :: T.Text,
+  clientData_challenge :: T.Text,
+  clientData_origin :: T.Text,
+  clientData_cid_pubkey :: T.Text
+  } deriving (Show, Generic, Eq)
+
+instance FromJSON ClientData where
+  parseJSON = genericParseJSON defaultOptions {
+                fieldLabelModifier = Prelude.drop 11 }
+
+data SignatureData = SignatureData {
+  signatureData_userPresenceFlag :: BS.ByteString,
+  signatureData_counter :: BS.ByteString,
+  signatureData_signature :: BS.ByteString
+} deriving (Show, Generic)
+
+parseSignin :: String -> Either U2FError Signin
+parseSignin x = case (Data.Aeson.decode (LBS.pack x) :: Maybe Signin) of
+  Just signin -> Right signin
+  Nothing -> Left SigninParseError
+
+parseClientData :: BS.ByteString -> Either U2FError ClientData
+parseClientData x = case (Data.Aeson.decode (LBS.fromStrict $ decodeLenient x) :: Maybe ClientData) of
+  Just clientData -> Right clientData
+  Nothing -> Left ClientDataParseError
+
+verifySignin :: BS.ByteString -> Request -> Signin -> Either U2FError Bool
+verifySignin savedPubkey request signin = do
+  clientData <- parseClientData $ encodeUtf8 $ signin_clientData signin
+  challengesEqual <- u2fComparator (challenge request) (clientData_challenge clientData) ChallengeMismatchError
+  signatureData <- parseSignatureData $ encodeUtf8 $ signin_signatureData signin
+  signature <- parseSignature $ signatureData_signature signatureData
+  signatureBase <- getSigninSignatureBase request signin signatureData
+  -- So Gross. TODO: write function that checks first byte for compression state, parses each pubkey format
+  publicKey <- case (parsePublicKey $ BS.tail $ savedPubkey) of
+    Just key -> Right key
+    Nothing -> Left PubKeyParsingError
+  case (verifySignature signatureBase publicKey signature) of
+    True -> Right True
+    False -> Left FailedVerificationError
+
+-- Other stuff
+
+parseSignatureData :: BS.ByteString -> Either U2FError SignatureData
+parseSignatureData s = Right $ runGet unpackSignatureData ( LBS.fromStrict $ decodeLenient s)
+
+parseSignature :: BS.ByteString -> Either U2FError ECDSA.Signature
+parseSignature possibleSig = case (decodeASN1' DER possibleSig) of
+  Right ([_, IntVal r, IntVal s, _]) -> Right $ ECDSA.Signature r s
+  _ -> Left SignatureParseError
+
+getSigninSignatureBase :: Request -> Signin -> SignatureData -> Either U2FError BS.ByteString
+getSigninSignatureBase request signin signatureData = do
+  appId <- pure $ encodeUtf8 $ appId request
+  userPresenceFlag <- pure $ signatureData_userPresenceFlag signatureData
+  counter <- pure $ signatureData_counter signatureData
+  clientData <- pure $ encodeUtf8 $ signin_clientData signin
+  Right $ BS.concat([SHA256.hash(appId), userPresenceFlag, counter, SHA256.hash(decodeLenient clientData)])
+
+parsePublicKey :: BS.ByteString -> Maybe ECDSA.PublicKey
+parsePublicKey keyByteString = case P256.pointFromBinary keyByteString of
+  CryptoPassed key -> Just $ ECDSA.PublicKey ourCurve $ Point (fst $ P256.pointToIntegers key) (snd $ P256.pointToIntegers key)
+  CryptoFailed err -> Nothing
+
+-- URL-friendly base64 encoding may or may not contain padding. Delete it here
+-- https://tools.ietf.org/html/rfc4648#section-3.2
+formatOutputBase64 :: BS.ByteString -> T.Text
+formatOutputBase64 byteString = T.replace (T.pack "=") (T.pack "") (decodeUtf8 $ encode byteString)
+
+verifySignature :: BS.ByteString -> ECDSA.PublicKey -> ECDSA.Signature -> Bool
+verifySignature sigBase pubKey signature = ECDSA.verify Crypto.Hash.Algorithms.SHA256 pubKey signature sigBase
+
+u2fComparator :: (Eq a) => a -> a -> U2FError -> Either U2FError Bool
+u2fComparator firstThing secondThing theError = case (firstThing == secondThing) of
+    True -> Right True
+    False -> Left theError
+
+unpackRegistrationData :: Get RegistrationData
+unpackRegistrationData = do
+  reserved <- getByteString 1
+  publicKey <- getByteString 65
+  keyHandleLen <- getWord8
+  keyHandle <- getByteString $ fromIntegral keyHandleLen
+  cert <- unpackASN1
+  sign <- unpackASN1
+  return $ RegistrationData reserved publicKey keyHandle cert sign
+
+unpackSignatureData :: Get SignatureData
+unpackSignatureData = do
+  userPresenceFlag <- getByteString 1
+  counter <- getByteString 4
+  signature <- unpackASN1
+  return $ SignatureData userPresenceFlag counter signature
+
+unpackASN1 :: Get BS.ByteString
+unpackASN1 = do
+  asnPadding <- getWord8
+  asnLen <- getWord8
+  if ((.&.) asnLen 128) /= 0
+    then do
+      firstByte <- getWord8
+      secondByte <- getWord8
+      let firstLen = (fromIntegral firstByte :: Int)
+      let secondLen = (fromIntegral secondByte :: Int)
+      let asnLength = (firstLen * 256) + secondLen
+      asnBody <- getByteString asnLength
+      return $ BS.concat([pack([asnPadding, asnLen, firstByte, secondByte]), asnBody])
+    else do
+      asnBody <- getByteString (fromIntegral asnLen)
+      return $ BS.concat([pack([asnPadding, asnLen]), asnBody])
diff --git a/tests/test.hs b/tests/test.hs
new file mode 100644
--- /dev/null
+++ b/tests/test.hs
@@ -0,0 +1,50 @@
+import Test.Hspec
+import Control.Exception (evaluate)
+import U2F
+import Data.Maybe
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.Text as T
+
+rawRegRequest = "{\"appId\":\"https://localhost:4000\",\"version\":\"U2F_V2\",\"challenge\":\"7uy5uxqHXeCGg7K1cWmO3bVlr_KY1U5dQkPP_wOvlWg\",\"keyHandle\":\"9Hgjem2ZlnKKuKVnHepmuSzMHnoyCX0idccYCwztgtpXllNSr-xv_oSuQnyVCN8HYa5JgIsawOxhtp0CIIcBGQ\"}"
+registrationRequest = Request (T.pack "https://localhost:4000") (T.pack "U2F_V2") (T.pack "7uy5uxqHXeCGg7K1cWmO3bVlr_KY1U5dQkPP_wOvlWg") Nothing
+registrationResponse = justRight $ parseRegistration "{\"registrationData\":\"BQQ65jfw5zT6MqdV81UYQ6btGV-dK8wRyxAGuYcFAKvFp1HubXnyI9wG5Vw8zdmE8sjBNK58GNrJ3woQ-e_nnV9kQPR4I3ptmZZyirilZx3qZrkszB56Mgl9InXHGAsM7YLaV5ZTUq_sb_6ErkJ8lQjfB2GuSYCLGsDsYbadAiCHARkwggJEMIIBLqADAgECAgR4wN8OMAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWNvIFUyRiBFRSBTZXJpYWwgMjAyNTkwNTkzNDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLW4cVyD_f4OoVxFd6yFjfSMF2_eh53K9Lg9QNMg8m-t5iX89_XIr9g1GPjbniHsCDsYRYDHF-xKRwuWim-6P2-jOzA5MCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgUgMAsGCSqGSIb3DQEBCwOCAQEAPvar9kqRawv5lJON3JU04FRAAmhWeKcsQ6er5l2QZf9h9FHOijru2GaJ0ZC5UK8AelTRMe7wb-JrTqe7PjK3kgWl36dgBDRT40r4RMN81KhfjFwthw4KKLK37UQCQf2zeSsgdrDhivqbQy7u_CZYugkFxBskqTxuyLum1W8z6NZT189r1QFUVaJll0D33MUcwDFgnNA-ps3pOZ7KCHYykHY_tMjQD1aQaaElSQBq67BqIaIU5JmYN7Qp6B1-VtM6VJLdOhYcgpOVQIGqfu90nDpWPb3X26OVzEc-RGltQZGFwkN6yDrAZMHL5HIn_3obd8fV6gw2fUX2ML2ZjVmybjBEAiBGwwt4P70-8E1KmmKQBtVQkvi-w16gSYLECB68b8nDNgIgCiRB1ATDXuWQ7m2DfNnsEq3bs3haITTa4ssHWB8PG-0\",\"challenge\":\"7uy5uxqHXeCGg7K1cWmO3bVlr_KY1U5dQkPP_wOvlWg\",\"version\":\"U2F_V2\",\"appId\":\"https://localhost:4000\",\"sessionId\":\"444\",\"clientData\":\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6Ijd1eTV1eHFIWGVDR2c3SzFjV21PM2JWbHJfS1kxVTVkUWtQUF93T3ZsV2ciLCJvcmlnaW4iOiJodHRwczovL2xvY2FsaG9zdDo0MDAwIiwiY2lkX3B1YmtleSI6InVudXNlZCJ9\"}"
+registrationGoodSig = BS.pack "0D\STX F\195\vx?\189>\240MJ\154b\144\ACK\213P\146\248\190\195^\160I\130\196\b\RS\188o\201\195\&6\STX \n$A\212\EOT\195^\229\144\238m\131|\217\236\DC2\173\219\179xZ!4\218\226\203\aX\US\SI\ESC\237"
+registrationGoodSigBase = BS.pack "\NUL\219\214S\135\176*\251\174\&8J\132g\177\209\ETX\192y\136s/L\136si\RSA\255\140\184/%\129Q\208\243\CAN!\204\&1\202\144j\153\246\&4\171\SOH\251@\165\244%\202=\ETB>\217\160\a\STX!\158\246W\244x#zm\153\150r\138\184\165g\GS\234f\185,\204\RSz2\t}\"u\199\CAN\v\f\237\130\218W\150SR\175\236o\254\132\174B|\149\b\223\aa\174I\128\139\SUB\192\236a\182\157\STX \135\SOH\EM\EOT:\230\&7\240\231\&4\250\&2\167U\243U\CANC\166\237\EM_\157+\204\DC1\203\DLE\ACK\185\135\ENQ\NUL\171\197\167Q\238my\242#\220\ACK\229\\<\205\217\132\242\200\193\&4\174|\CAN\218\201\223\n\DLE\249\239\231\157_d"
+
+signinRequest = Request (T.pack "https://localhost:4000") (T.pack "U2F_V2") (T.pack "7uy5uxqHXeCGg7K1cWmO3bVlr_KY1U5dQkPP_wOvlWg") (Just $ T.pack "9Hgjem2ZlnKKuKVnHepmuSzMHnoyCX0idccYCwztgtpXllNSr-xv_oSuQnyVCN8HYa5JgIsawOxhtp0CIIcBGQ")
+signinResponse = justRight $ parseSignin "{\"keyHandle\":\"9Hgjem2ZlnKKuKVnHepmuSzMHnoyCX0idccYCwztgtpXllNSr-xv_oSuQnyVCN8HYa5JgIsawOxhtp0CIIcBGQ\",\"clientData\":\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiN3V5NXV4cUhYZUNHZzdLMWNXbU8zYlZscl9LWTFVNWRRa1BQX3dPdmxXZyIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjQwMDAiLCJjaWRfcHVia2V5IjoidW51c2VkIn0\",\"signatureData\":\"AQAAAAAwRgIhAI4jLgLewiaFzuyyuaxlToF1OHmkItaOeASUtIStic5HAiEA8X16pOe_Sugk8O8AAh2iQdlx_98cR9UwwCwDmr_bbXo\"}"
+signinSavedPubkey = BS.pack "\EOT:\230\&7\240\231\&4\250\&2\167U\243U\CANC\166\237\EM_\157+\204\DC1\203\DLE\ACK\185\135\ENQ\NUL\171\197\167Q\238my\242#\220\ACK\229\\<\205\217\132\242\200\193\&4\174|\CAN\218\201\223\n\DLE\249\239\231\157_d"
+justRight (Right x) = x
+
+main :: IO ()
+main = hspec $ do
+  describe "Request/Response Parsing" $ do
+    it "should work for registration requests" $ do
+      regRequest <- pure $ justRight $ parseRequest rawRegRequest
+      (appId regRequest) `shouldBe` (T.pack "https://localhost:4000")
+
+  describe "Registration Flow" $ do
+    it "should check that request and response challenges are equivalent" $ do
+      registrationVerificationFail <- return $ do
+            registration <- pure $ registrationResponse {registration_challenge = (T.pack "")}
+            verifyRegistration registrationRequest registration
+      registrationVerificationFail `shouldBe` (Left ChallengeMismatchError)
+
+    it "should validate properly formed request and response" $ do
+      registrationVerificationSuccess <- return $ do
+            verifyRegistration registrationRequest registrationResponse
+      --TODO: Find a better way to test for this
+      (registrationVerificationSuccess) `shouldBe` (Right $ justRight registrationVerificationSuccess)
+
+  describe "Signin Flow" $ do
+    it "should check that request and response challenges are equivalent" $ do
+      signinVerificationFail <- return $ do
+            signinRequest <- pure $ signinRequest {challenge = (T.pack "")}
+            verifySignin signinSavedPubkey signinRequest signinResponse
+      signinVerificationFail `shouldBe` (Left ChallengeMismatchError)
+
+    it "should validate properly formed request and response" $ do
+      signinVerificationSuccess <- return $ do
+            verifySignin signinSavedPubkey signinRequest signinResponse
+      --TODO: Find a better way to test for this
+      (signinVerificationSuccess) `shouldBe` (Right $ justRight signinVerificationSuccess)
diff --git a/u2f.cabal b/u2f.cabal
new file mode 100644
--- /dev/null
+++ b/u2f.cabal
@@ -0,0 +1,44 @@
+-- Initial u2f.cabal generated by cabal init.  For further documentation,
+-- see http://haskell.org/cabal/users-guide/
+
+name:                u2f
+version:             0.1.0.0
+synopsis:            Haskell Universal Two Factor helper toolbox library thing
+description:         Library useful for server-side U2F Registration and Signin flows.
+homepage:            https://github.com/EButlerIV/u2f
+license:             BSD3
+license-file:        LICENSE
+author:              Eugene Butler
+maintainer:          eugene@eugene4.com
+-- copyright:
+category:            Web
+build-type:          Simple
+extra-source-files:  ChangeLog.md, README.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     U2F
+  -- other-modules:
+  -- other-extensions:
+  build-depends:       base >=4.8 && <4.9
+                       , cryptonite >= 0.17
+                       , binary >= 0.8.4.0
+                       , text
+                       , asn1-encoding
+                       , asn1-types
+                       , base64-bytestring
+                       , cryptohash
+                       , aeson
+                       , bytestring
+                       , either-unwrap
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+test-suite test
+  type:       exitcode-stdio-1.0
+  main-is:    tests/test.hs
+  build-depends:  base
+                , hspec
+                , u2f
+                , text
+                , bytestring
