Skip to content

Commit

Permalink
Switch from base64-bytestring to base64
Browse files Browse the repository at this point in the history
  • Loading branch information
cotrone committed Sep 20, 2023
1 parent b0b7f5e commit c196668
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 15 deletions.
8 changes: 4 additions & 4 deletions src/Web/WebPush.hs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ sendPushNotification vapidKeys httpManager pushNotification = do
, (hAuthorization, BSL.toStrict $ "WebPush " <> jwt)
, ("Crypto-Key", cryptoKeyHeaderContents)
, (hContentEncoding, "aesgcm")
, ("Encryption", "salt=" <> (b64UrlNoPadding randSalt))
, ("Encryption", "salt=" <> (B64.URL.encodeBase64Unpadded' randSalt))
]

request = initReq {
Expand All @@ -173,7 +173,7 @@ sendPushNotification vapidKeys httpManager pushNotification = do
toPushNotificationError (PushEncodeApplicationPublicKeyError err) = ApplicationKeyEncodeError err
cryptoKeyHeader :: ECDSA.PublicKey -> ECC.Point -> Either String C8.ByteString
cryptoKeyHeader vapidPublic ecdhServerPublic = do
let encodePublic = fmap b64UrlNoPadding . ecPublicKeyToBytes
let encodePublic = fmap B64.URL.encodeBase64Unpadded' . ecPublicKeyToBytes
dh <- encodePublic ecdhServerPublic
ecdsa <- encodePublic (ECDSA.public_q vapidPublic)
pure $ BS.concat [ "dh=", dh, ";", "p256ecdsa=", ecdsa]
Expand All @@ -189,9 +189,9 @@ sendPushNotification vapidKeys httpManager pushNotification = do
410 -> RecepientEndpointNotFound
_ -> PushRequestFailed err
| otherwise = PushRequestFailed err
authSecretBytes = B64.URL.decodeLenient . TE.encodeUtf8 $ pushNotification ^. pushAuth
authSecretBytes = B64.URL.decodeBase64Lenient . TE.encodeUtf8 $ pushNotification ^. pushAuth
-- extract the 65 bytes of ECDH uncompressed public key received from browser in subscription
subscriptionPublicKeyBytes = B64.URL.decodeLenient . TE.encodeUtf8 $ pushNotification ^. pushP256dh
subscriptionPublicKeyBytes = B64.URL.decodeBase64Lenient . TE.encodeUtf8 $ pushNotification ^. pushP256dh
-- encode the message to a safe representation like base64URL before sending it to encryption algorithms
-- decode the message through service workers on browsers before trying to read the JSON
plainMessage64Encoded = A.encode $ pushNotification ^. pushMessage
Expand Down
13 changes: 3 additions & 10 deletions src/Web/WebPush/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@ instance A.ToJSON PushNotificationPayload where
webPushJWT :: MonadIO m => VAPIDKeys -> PushNotificationPayload -> m LB.ByteString
webPushJWT vapidKeys payload = do
let
encodedJWTPayload = b64UrlNoPadding . LB.toStrict . A.encode $ payload
encodedJWTPayload = B64.URL.encodeBase64Unpadded' . LB.toStrict . A.encode $ payload
messageForJWTSignature = encodedJWTHeader <> "." <> encodedJWTPayload
-- JWT only accepts SHA256 hash with ECDSA for ES256 signed token
-- ECDSA signing vulnerable to timing attacks
ECDSA.Signature signR signS <- liftIO $ ECDSA.sign (ECDSA.toPrivateKey vapidKeys) SHA256 messageForJWTSignature
-- 32 bytes of R followed by 32 bytes of S
let encodedJWTSignature = b64UrlNoPadding $ LB.toStrict $ (Binary.encode $ int32Bytes signR) <> (Binary.encode $ int32Bytes signS)
let encodedJWTSignature = B64.URL.encodeBase64Unpadded' $ LB.toStrict $ (Binary.encode $ int32Bytes signR) <> (Binary.encode $ int32Bytes signS)
pure . LB.fromStrict $ messageForJWTSignature <> "." <> encodedJWTSignature
where
encodedJWTHeader = b64UrlNoPadding . LB.toStrict . A.encode $ A.object [
encodedJWTHeader = B64.URL.encodeBase64Unpadded' . LB.toStrict . A.encode $ A.object [
"typ" .= ("JWT" :: Text)
, "alg" .= ("ES256" :: Text)
]
Expand Down Expand Up @@ -147,8 +147,6 @@ webPushEncrypt EncryptionInput {..} = do
where
handleCryptoError = first PushEncryptCryptoError . eitherCryptoError
curveP256 = ECCTypes.getCurveByName ECCTypes.SEC_p256r1



-- Conversions among integers and bytes
-- The bytes are in network/big endian order.
Expand Down Expand Up @@ -189,8 +187,3 @@ bytes32Int (d,c,b,a) = (Bits.shiftL (fromIntegral d) (64*3)) +
(Bits.shiftL (fromIntegral c) (64*2)) +
(Bits.shiftL (fromIntegral b) (64 )) +
(fromIntegral a)

-- at most places we do not need the padding in base64 url encoding
-- TODO this could be removed
b64UrlNoPadding :: ByteString -> ByteString
b64UrlNoPadding = fst . BS.breakSubstring "=" . B64.URL.encode
2 changes: 1 addition & 1 deletion web-push.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ library
ghc-options: -Wall -fwarn-tabs -O2
build-depends: base >= 4.7 && < 5
, aeson >= 2.0 && < 3.0
, base64-bytestring >= 1.0.0.1 && < 1.3
, base64 >= 0.4 && < 0.5
, binary >= 0.7.5 && < 0.9
, bytestring >= 0.9 && < 0.13
, crypton >= 0.30
Expand Down

0 comments on commit c196668

Please sign in to comment.