diff --git a/messages/uniworx/de.msg b/messages/uniworx/de.msg index 86c1d0cd5..44209ffb9 100644 --- a/messages/uniworx/de.msg +++ b/messages/uniworx/de.msg @@ -491,11 +491,12 @@ ErrorResponseBadMethod requestMethodText@Text: Ihr Browser kann auf mehrere vers ErrorResponseEncrypted: Um keine sensiblen Daten preiszugeben wurden nähere Details verschlüsselt. Wenn Sie eine Anfrage an den Support schicken fügen Sie bitte die unten aufgeführten verschlüsselten Daten mit an. ErrMsgCiphertext: Verschlüsselte Fehlermeldung -ErrMsgCiphertextTooShort: Verschlüsselte Daten zu kurz um valide zu sein -ErrMsgInvalidBase64 base64Err@String: Verschlüsselte Daten nicht korrekt base64url-kodiert: #{base64Err} -ErrMsgCouldNotDecodeNonce: Konnte secretbox-nonce nicht dekodieren -ErrMsgCouldNotOpenSecretbox: Konnte libsodium-secretbox nicht öffnen (Verschlüsselte Daten sind nicht authentisch) -ErrMsgCouldNotDecodePlaintext utf8Err@Text: Konnte Klartext nicht UTF8-dekodieren: #{utf8Err} +EncodedSecretBoxCiphertextTooShort: Verschlüsselte Daten zu kurz um valide zu sein +EncodedSecretBoxInvalidBase64 base64Err@String: Verschlüsselte Daten nicht korrekt base64url-kodiert: #{base64Err} +EncodedSecretBoxInvalidPadding: Verschlüsselte Daten sind nicht korrekt padded +EncodedSecretBoxCouldNotDecodeNonce: Konnte secretbox-nonce nicht dekodieren +EncodedSecretBoxCouldNotOpenSecretBox: Konnte libsodium-secretbox nicht öffnen (Verschlüsselte Daten sind nicht authentisch) +EncodedSecretBoxCouldNotDecodePlaintext aesonErr@String: Konnte Klartext nicht JSON-dekodieren: #{aesonErr} ErrMsgHeading: Fehlermeldung entschlüsseln ErrorCryptoIdMismatch: Verschlüsselte Id der Abgabe passte nicht zu anderen Daten diff --git a/package.yaml b/package.yaml index 0853fdd38..4e09e10e4 100644 --- a/package.yaml +++ b/package.yaml @@ -110,6 +110,7 @@ dependencies: - monad-memo - xss-sanitize - text-metrics + - pkcs7 other-extensions: - GeneralizedNewtypeDeriving diff --git a/src/Application.hs b/src/Application.hs index b1f17147b..90792b4f5 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -137,7 +137,7 @@ makeFoundation appSettings@AppSettings{..} = do -- logging function. To get out of this loop, we initially create a -- temporary foundation without a real connection pool, get a log function -- from there, and then create the real foundation. - let mkFoundation appConnPool appSmtpPool appCryptoIDKey appSessionKey appErrorMsgKey = UniWorX {..} + let mkFoundation appConnPool appSmtpPool appCryptoIDKey appSessionKey appSecretBoxKey = UniWorX {..} -- The UniWorX {..} syntax is an example of record wild cards. For more -- information, see: -- https://ocharles.org.uk/blog/posts/2014-12-04-record-wildcards.html @@ -146,7 +146,7 @@ makeFoundation appSettings@AppSettings{..} = do (error "smtpPool forced in tempFoundation") (error "cryptoIDKey forced in tempFoundation") (error "sessionKey forced in tempFoundation") - (error "errorMsgKey forced in tempFoundation") + (error "secretBoxKey forced in tempFoundation") logFunc loc src lvl str = do f <- messageLoggerSource tempFoundation <$> readTVarIO (snd appLogger) f loc src lvl str @@ -166,9 +166,9 @@ makeFoundation appSettings@AppSettings{..} = do migrateAll `runSqlPool` sqlPool appCryptoIDKey <- clusterSetting (Proxy :: Proxy 'ClusterCryptoIDKey) `runSqlPool` sqlPool appSessionKey <- clusterSetting (Proxy :: Proxy 'ClusterClientSessionKey) `runSqlPool` sqlPool - appErrorMsgKey <- clusterSetting (Proxy :: Proxy 'ClusterErrorMessageKey) `runSqlPool` sqlPool + appSecretBoxKey <- clusterSetting (Proxy :: Proxy 'ClusterSecretBoxKey) `runSqlPool` sqlPool - let foundation = mkFoundation sqlPool smtpPool appCryptoIDKey appSessionKey appErrorMsgKey + let foundation = mkFoundation sqlPool smtpPool appCryptoIDKey appSessionKey appSecretBoxKey handleJobs foundation diff --git a/src/Foundation.hs b/src/Foundation.hs index 9b899765f..ea504444d 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -66,8 +66,6 @@ import Utils.Lens import Utils.Form import Utils.SystemMessage -import Data.Aeson hiding (Error, Success) - import Text.Shakespeare.Text (st) import Yesod.Form.I18n.German @@ -76,7 +74,6 @@ import qualified Yesod.Auth.Message as Auth import qualified Data.Conduit.List as C import qualified Crypto.Saltine.Core.SecretBox as SecretBox -import qualified Crypto.Saltine.Class as Saltine instance DisplayAble b => DisplayAble (E.CryptoID a b) where @@ -99,19 +96,19 @@ instance DisplayAble SchoolId where -- starts running, such as database connections. Every handler will have -- access to the data present here. data UniWorX = UniWorX - { appSettings :: AppSettings - , appStatic :: Static -- ^ Settings for static file serving. - , appConnPool :: ConnectionPool -- ^ Database connection pool. - , appSmtpPool :: Maybe SMTPPool - , appHttpManager :: Manager - , appLogger :: (ReleaseKey, TVar Logger) - , appLogSettings :: TVar LogSettings - , appCryptoIDKey :: CryptoIDKey - , appInstanceID :: InstanceId - , appJobCtl :: TVar (Map ThreadId (TMChan JobCtl)) - , appCronThread :: TMVar (ReleaseKey, ThreadId) - , appErrorMsgKey :: SecretBox.Key - , appSessionKey :: ClientSession.Key + { appSettings :: AppSettings + , appStatic :: Static -- ^ Settings for static file serving. + , appConnPool :: ConnectionPool -- ^ Database connection pool. + , appSmtpPool :: Maybe SMTPPool + , appHttpManager :: Manager + , appLogger :: (ReleaseKey, TVar Logger) + , appLogSettings :: TVar LogSettings + , appCryptoIDKey :: CryptoIDKey + , appInstanceID :: InstanceId + , appJobCtl :: TVar (Map ThreadId (TMChan JobCtl)) + , appCronThread :: TMVar (ReleaseKey, ThreadId) + , appSessionKey :: ClientSession.Key + , appSecretBoxKey :: SecretBox.Key } type SMTPPool = Pool SMTPConnection @@ -201,6 +198,7 @@ embedRenderMessage ''UniWorX ''RatingException id embedRenderMessage ''UniWorX ''SheetGrading ("SheetGrading" <>) embedRenderMessage ''UniWorX ''AuthTag $ ("AuthTag" <>) . concat . drop 1 . splitCamel embedRenderMessage ''UniWorX ''SheetSubmissionMode ("Sheet" <>) +embedRenderMessage ''UniWorX ''EncodedSecretBoxException id newtype SheetTypeHeader = SheetTypeHeader SheetType embedRenderMessageVariant ''UniWorX ''SheetTypeHeader ("SheetType" <>) @@ -681,15 +679,12 @@ instance Yesod UniWorX where if | shouldEncrypt , not canDecrypt -> do - errKey <- getsYesod appErrorMsgKey - nonce <- liftIO SecretBox.newNonce - let ciphertext = SecretBox.secretbox errKey nonce . Lazy.ByteString.toStrict $ encode plaintextJson - encoded = decodeUtf8 . Base64.encode $ Saltine.encode nonce <> ciphertext - formatted = Text.intercalate "\n" $ Text.chunksOf 76 encoded + ciphertext <- encodedSecretBox SecretBoxPretty plaintextJson + [whamlet|
_{MsgErrorResponseEncrypted}
- #{formatted}
+ #{ciphertext}
|]
| otherwise -> plaintext
@@ -1722,6 +1717,9 @@ instance (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => MonadCrypto
type MonadCryptoKey m = CryptoIDKey
cryptoIDKey f = getsYesod appCryptoIDKey >>= f
+instance {-# OVERLAPPING #-} (Monad m, MonadHandler m, HandlerSite m ~ UniWorX) => MonadSecretBox m where
+ secretBoxKey = getsYesod appSecretBoxKey
+
-- Note: Some functionality previously present in the scaffolding has been
-- moved to documentation in the Wiki. Following are some hopefully helpful
-- links:
diff --git a/src/Handler/Admin.hs b/src/Handler/Admin.hs
index da8a8aed8..feea45783 100644
--- a/src/Handler/Admin.hs
+++ b/src/Handler/Admin.hs
@@ -4,16 +4,7 @@ import Import
import Handler.Utils
import Jobs
-import qualified Data.ByteString as BS
-
-import qualified Crypto.Saltine.Internal.ByteSizes as Saltine
-import qualified Data.ByteString.Base64.URL as Base64
-import Crypto.Saltine.Core.SecretBox (secretboxOpen)
-import qualified Crypto.Saltine.Class as Saltine
-
-import qualified Data.Text as Text
-import qualified Data.Text.Encoding as Text
-import Data.Char (isSpace)
+import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder)
import Control.Monad.Trans.Except
@@ -110,31 +101,17 @@ getAdminUserR uuid = do
getAdminErrMsgR, postAdminErrMsgR :: Handler Html
getAdminErrMsgR = postAdminErrMsgR
postAdminErrMsgR = do
- errKey <- getsYesod appErrorMsgKey
-
((ctResult, ctView), ctEncoding) <- runFormPost . renderAForm FormStandard $
(unTextarea <$> areq textareaField (fslpI MsgErrMsgCiphertext "Ciphertext") Nothing)
<* submitButton
- plaintext <- formResultMaybe ctResult $ \(encodeUtf8 . Text.filter (not . isSpace) -> inputBS) ->
- exceptT (\err -> Nothing <$ addMessageI Error err) (return . Just) $ do
- ciphertext <- either (throwE . MsgErrMsgInvalidBase64) return $ Base64.decode inputBS
-
- unless (BS.length ciphertext >= Saltine.secretBoxNonce + Saltine.secretBoxMac) $
- throwE MsgErrMsgCiphertextTooShort
- let (nonceBS, secretbox) = BS.splitAt Saltine.secretBoxNonce ciphertext
-
- nonce <- maybe (throwE MsgErrMsgCouldNotDecodeNonce) return $ Saltine.decode nonceBS
-
- plainBS <- maybe (throwE MsgErrMsgCouldNotOpenSecretbox) return $ secretboxOpen errKey nonce secretbox
-
- either (throwE . MsgErrMsgCouldNotDecodePlaintext . tshow) return $ Text.decodeUtf8' plainBS
+ plaintext <- formResultMaybe ctResult $ exceptT (\err -> Nothing <$ addMessageI Error err) (return . Just) . (encodedSecretBoxOpen :: Text -> ExceptT EncodedSecretBoxException Handler Value)
defaultLayout
[whamlet|
$maybe t <- plaintext
- #{t}
+ #{encodePrettyToTextBuilder t}