Implement key persistence for server
This commit is contained in:
parent
81ec84abaf
commit
f1b44d649d
4 changed files with 46 additions and 4 deletions
|
@ -1,4 +1,4 @@
|
||||||
# Revision history for hanafudapi
|
# Revision history for hanafuda-server
|
||||||
|
|
||||||
## 0.2.3.0 -- 2019-08-24
|
## 0.2.3.0 -- 2019-08-24
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
-- Initial hanafudapi.cabal generated by cabal init. For further
|
-- Initial hanafuda-server.cabal generated by cabal init. For further
|
||||||
-- documentation, see http://haskell.org/cabal/users-guide/
|
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||||
|
|
||||||
name: hanafuda-server
|
name: hanafuda-server
|
||||||
|
@ -35,6 +35,8 @@ executable hanafudapi
|
||||||
build-depends: base >=4.9 && <4.13
|
build-depends: base >=4.9 && <4.13
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers >= 0.5.9
|
, containers >= 0.5.9
|
||||||
|
, directory
|
||||||
|
, filepath
|
||||||
, unordered-containers
|
, unordered-containers
|
||||||
, hanafuda >= 0.3.3
|
, hanafuda >= 0.3.3
|
||||||
, hanafuda-protocol >= 0.1.0
|
, hanafuda-protocol >= 0.1.0
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
module Config (
|
module Config (
|
||||||
listenPort
|
libDir
|
||||||
|
, listenPort
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
libDir :: FilePath
|
||||||
|
libDir = "/var/lib/hanafuda-server"
|
||||||
|
|
||||||
listenPort :: Int
|
listenPort :: Int
|
||||||
listenPort = 3000
|
listenPort = 3000
|
||||||
|
|
38
src/Keys.hs
38
src/Keys.hs
|
@ -1,3 +1,4 @@
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
module Keys (
|
module Keys (
|
||||||
T(..)
|
T(..)
|
||||||
, getKeys
|
, getKeys
|
||||||
|
@ -5,19 +6,54 @@ module Keys (
|
||||||
, secret
|
, secret
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Config (libDir)
|
||||||
|
import Control.Monad.State (MonadState(..), StateT(..), evalStateT, lift)
|
||||||
|
import Crypto.Saltine.Class (IsEncoding(..))
|
||||||
import qualified Crypto.Saltine.Core.SecretBox as Encrypt (Key, newKey)
|
import qualified Crypto.Saltine.Core.SecretBox as Encrypt (Key, newKey)
|
||||||
import qualified Crypto.Saltine.Core.Sign as Sign (
|
import qualified Crypto.Saltine.Core.Sign as Sign (
|
||||||
Keypair, PublicKey, SecretKey, newKeypair
|
Keypair, PublicKey, SecretKey, newKeypair
|
||||||
)
|
)
|
||||||
|
import qualified Data.ByteString as BS (
|
||||||
|
ByteString, length, readFile, singleton, splitAt, uncons, writeFile
|
||||||
|
)
|
||||||
|
import System.Directory (createDirectoryIfMissing, doesFileExist)
|
||||||
|
import System.Exit (die)
|
||||||
|
import System.FilePath ((</>))
|
||||||
|
|
||||||
data T = T {
|
data T = T {
|
||||||
encrypt :: Encrypt.Key
|
encrypt :: Encrypt.Key
|
||||||
, sign :: Sign.Keypair
|
, sign :: Sign.Keypair
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serialize :: T -> BS.ByteString
|
||||||
|
serialize (T {encrypt, sign = (secretKey, publicKey)}) =
|
||||||
|
mconcat [encodeKey encrypt, encodeKey secretKey, encodeKey publicKey]
|
||||||
|
where
|
||||||
|
encodeKey key =
|
||||||
|
let encodedKey = encode key in
|
||||||
|
(BS.singleton . toEnum $ BS.length encodedKey) <> encodedKey
|
||||||
|
|
||||||
|
unserialize :: BS.ByteString -> Maybe T
|
||||||
|
unserialize = evalStateT $ T <$> decodeKey <*> ((,) <$> decodeKey <*> decodeKey)
|
||||||
|
where
|
||||||
|
decodeKey :: IsEncoding a => StateT BS.ByteString Maybe a
|
||||||
|
decodeKey = do
|
||||||
|
keyLength <- fromEnum <$> StateT BS.uncons
|
||||||
|
lift . decode =<< state (BS.splitAt keyLength)
|
||||||
|
|
||||||
getKeys :: IO T
|
getKeys :: IO T
|
||||||
getKeys = do
|
getKeys = do
|
||||||
T <$> Encrypt.newKey <*> Sign.newKeypair
|
fileExists <- doesFileExist keyRing
|
||||||
|
if fileExists
|
||||||
|
then BS.readFile keyRing >>= tryUnserialize
|
||||||
|
else do
|
||||||
|
newT <- T <$> Encrypt.newKey <*> Sign.newKeypair
|
||||||
|
createDirectoryIfMissing True libDir
|
||||||
|
BS.writeFile keyRing $ serialize newT
|
||||||
|
return newT
|
||||||
|
where
|
||||||
|
keyRing = libDir </> "keys"
|
||||||
|
tryUnserialize = maybe (die "Could not unserialize key") return . unserialize
|
||||||
|
|
||||||
public :: Sign.Keypair -> Sign.PublicKey
|
public :: Sign.Keypair -> Sign.PublicKey
|
||||||
public = snd
|
public = snd
|
||||||
|
|
Loading…
Reference in a new issue