diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for the hashes package
 
+## 0.2.2.0 -- 2022-08-20
+
+*   Test suite for SHA hash functions
+*   Fix openssl-3.0 support
+
 ## 0.2.1.1 -- 2021-10-23
 
 *   Fixes for building with GHC-9.2
diff --git a/cbits/keccak.c b/cbits/keccak.c
--- a/cbits/keccak.c
+++ b/cbits/keccak.c
@@ -26,119 +26,77 @@
     printf("expected         : %s\n", expected);
 
 finally:
-    if (ctx) keccak256_freectx(ctx);
+    if (ctx) EVP_MD_CTX_free(ctx);
     return ! ok;
 }
 */
 
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
 /* *************************************************************************** */
-/* KECCAK-256 for OpenSSL 3.0 */
-
-extern const OSSL_DISPATCH ossl_sha3_256_functions[];
-extern const OSSL_DISPATCH ossl_sha3_512_functions[];
-extern int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen);
-
-typedef void (*VoidFunPtr) (void);
-
-VoidFunPtr dispatch(const OSSL_DISPATCH *fns, int fn_id) {
-    for (; fns->function_id != 0; fns++) {
-        if (fns->function_id == fn_id) {
-            return fns->function;
-        }
-    }
-    return NULL;
-}
-
-VoidFunPtr dispatch256(int fn_id) {
-    return dispatch(ossl_sha3_256_functions, fn_id);
-}
-
-VoidFunPtr dispatch512(int fn_id) {
-    return dispatch(ossl_sha3_512_functions, fn_id);
-}
-
-KECCAK256_CTX *keccak256_newctx()
-{
-    KECCAK256_CTX * ctx = ((OSSL_FUNC_digest_newctx_fn *) dispatch256(OSSL_FUNC_DIGEST_NEWCTX))(NULL);
-
-    // this has already be called once by the dispatch function. Here we update the pad character
-    if (ctx) ossl_sha3_init(ctx, '\x01', 256);
-    return ctx;
-}
-
-KECCAK512_CTX *keccak512_newctx()
-{
-    KECCAK512_CTX * ctx = ((OSSL_FUNC_digest_newctx_fn *) dispatch512(OSSL_FUNC_DIGEST_NEWCTX))(NULL);
-
-    // this has already be called once by the dispatch function. Here we update the pad character
-    if (ctx) ossl_sha3_init(ctx, '\x01', 512);
-    return ctx;
-}
-
-int keccak256_init(KECCAK256_CTX *ctx) {
-    return ((OSSL_FUNC_digest_init_fn *) dispatch256(OSSL_FUNC_DIGEST_INIT))(ctx, NULL);
-}
-
-int keccak512_init(KECCAK256_CTX *ctx) {
-    return ((OSSL_FUNC_digest_init_fn *) dispatch512(OSSL_FUNC_DIGEST_INIT))(ctx, NULL);
-}
-
-int keccak256_update(KECCAK256_CTX *ctx, const void *p, size_t l)
-{
-    return ((OSSL_FUNC_digest_update_fn *) dispatch256(OSSL_FUNC_DIGEST_UPDATE))(ctx, p, l);
-}
-
-int keccak512_update(KECCAK512_CTX *ctx, const void *p, size_t l)
-{
-    return ((OSSL_FUNC_digest_update_fn *) dispatch512(OSSL_FUNC_DIGEST_UPDATE))(ctx, p, l);
-}
-
-int keccak256_final(KECCAK256_CTX *ctx, unsigned char *md)
-{
-    size_t l;
-    return ((OSSL_FUNC_digest_final_fn *) dispatch256(OSSL_FUNC_DIGEST_FINAL))(ctx, md, &l, 32);
-}
-
-int keccak512_final(KECCAK512_CTX *ctx, unsigned char *md)
-{
-    size_t l;
-    return ((OSSL_FUNC_digest_final_fn *) dispatch512(OSSL_FUNC_DIGEST_FINAL))(ctx, md, &l, 64);
-}
-
-void keccak256_freectx(KECCAK256_CTX *ctx)
-{
-    return ((OSSL_FUNC_digest_freectx_fn *) dispatch256(OSSL_FUNC_DIGEST_FREECTX))(ctx);
-}
-
-void keccak512_freectx(KECCAK512_CTX *ctx)
-{
-    return ((OSSL_FUNC_digest_freectx_fn *) dispatch512(OSSL_FUNC_DIGEST_FREECTX))(ctx);
-}
-
-#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
-/* *************************************************************************** */
-/* OpenSSL 1.1 */
+/* OpenSSL 1.1 and OpenSSL 3.0 */
 
 /* The computation of the magic offset is base on the keccak_st structure in
- * OpenSSL-1.1.
+ * OpenSSL-1.1 and OpenSSL-3.0
  *
  * Assuming conventional alignment, the bytes offset is
  *
- * sizeof(uint64_t) * 40 + size_of(size_t) * 3 + (1600 / 8 - 32)
+ * sizeof(uint64_t) * 25 + size_of(size_t) * 3 + (1600 / 8 - 32)
  *
  * On a 64bit platform this number is 392
+ *
+ * // struct keccak_st {
+ * //     uint64_t A[5][5];
+ * //     size_t block_size;
+ * //     size_t md_size;
+ * //     size_t bufsz;
+ * //     unsigned char buf[KECCAK1600_WIDTH / 8 - 32];
+ * //     unsigned char pad;
+ * // };
+ *
+ * For OpenSSL 1.1 this context is stored in EVP_MD_CTX as data and is accessed
+ * via EVP_CTX_MD_md_data().
+ *
+ * For OpenSSL 3.0 it is stored in EVP_MD_CTX in the algctx field. The structure
+ * is a field of pointers and algctx is the 8th pointer.
+ *
+ * // struct evp_md_ctx_st {
+ * //   const EVP_MD *reqdigest;
+ * //   const EVP_MD *digest;
+ * //   ENGINE *engine;
+ * //   unsigned long flags;
+ * //   void *md_data;
+ * //   EVP_PKEY_CTX *pctx;
+ * //   int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
+ * //
+ * //   // Opaque ctx returned from a providers digest algorithm implementation
+ * //   // OSSL_FUNC_digest_newctx()
+ * //   //
+ * //   void *algctx;
+ * //   EVP_MD *fetched_digest;
+ * // }
  */
 
-// struct keccak_st {
-//     uint64_t A[5][5];
-//     size_t block_size;          /* cached ctx->digest->block_size */
-//     size_t md_size;             /* output length, variable in XOF */
-//     size_t bufsz;               /* used bytes in below buffer */
-//     unsigned char buf[KECCAK1600_WIDTH / 8 - 32];
-//     unsigned char pad;
-// };
+#define PAD_BYTE_OFFSET (25 * sizeof(uint64_t) + 3 * sizeof(size_t) + 1600/8 - 32)
 
+/* OPENSSL 3.1 */
+#if OPENSSL_VERSION_NUMBER >= 0x31000000L
+#define SET_PAD_BYTE 
+
+/* OPENSSL 3.0 */
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+#define GET_CTX(ctx) (*(((uint8_t **) ctx) + 7))
+#define SET_PAD_BYTE (((uint8_t *) GET_CTX(ctx))[PAD_BYTE_OFFSET] = 0x01)
+
+/* OPENSSL 1.1 */
+#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
+#define GET_CTX(ctx) ((uint8_t *) EVP_MD_CTX_md_data(ctx))
+#define SET_PAD_BYTE (GET_CTX(ctx)[PAD_BYTE_OFFSET] = 0x01)
+
+#endif
+
+/* *************************************************************************** */
+/* Implementation */
+
+
 KECCAK256_CTX *keccak256_newctx()
 {
     return EVP_MD_CTX_new();
@@ -152,12 +110,9 @@
 int keccak256_init(KECCAK256_CTX *ctx) {
     int ok = 1;
     const EVP_MD *md = NULL;
-    int padByteOffset = 25 * sizeof(uint64_t) + 3 * sizeof(size_t) + 1600/8 - 32;
-    CHECKED(md = EVP_sha3_256());
+    CHECKED(md = EVP_get_digestbyname("SHA3-256"));
     CHECKED(EVP_DigestInit(ctx, md));
-
-    // MAGIC (set padding char to 0x1)
-    ((uint8_t *) EVP_MD_CTX_md_data(ctx))[padByteOffset] = 0x01;
+    SET_PAD_BYTE;
 finally:
     return ok;
 }
@@ -165,12 +120,9 @@
 int keccak512_init(KECCAK512_CTX *ctx) {
     int ok = 1;
     const EVP_MD *md = NULL;
-    int padByteOffset = 25 * sizeof(uint64_t) + 3 * sizeof(size_t) + 1600/8 - 32;
-    CHECKED(md = EVP_sha3_512());
+    CHECKED(md = EVP_get_digestbyname("SHA3-512"));
     CHECKED(EVP_DigestInit(ctx, md));
-
-    // MAGIC (set padding char to 0x1)
-    ((uint8_t *) EVP_MD_CTX_md_data(ctx))[padByteOffset] = 0x01;
+    SET_PAD_BYTE;
 finally:
     return ok;
 }
@@ -206,6 +158,4 @@
 {
     return EVP_MD_CTX_free(ctx);
 }
-
-#endif
 
diff --git a/cbits/keccak.h b/cbits/keccak.h
--- a/cbits/keccak.h
+++ b/cbits/keccak.h
@@ -9,20 +9,10 @@
     }
 
 /* *************************************************************************** */
-/* Keccak-256 */
-
-#if OPENSSL_VERSION_NUMBER >= 0x31000000L
-
-#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
-typedef struct keccak1600_ctx_st KECCAK1600_CTX;
-typedef KECCAK1600_CTX KECCAK256_CTX;
-typedef KECCAK1600_CTX KECCAK512_CTX;
+/* Keccak */
 
-#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
 typedef EVP_MD_CTX KECCAK256_CTX;
 typedef EVP_MD_CTX KECCAK512_CTX;
-
-#endif
 
 // KECCAK-256
 KECCAK256_CTX *keccak256_newctx();
diff --git a/hashes.cabal b/hashes.cabal
--- a/hashes.cabal
+++ b/hashes.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.4
 name: hashes
-version: 0.2.1.1
+version: 0.2.2.0
 synopsis: Hash functions
 Description: Efficient implementations of hash functions
 homepage: https://github.com/larskuhtz/hs-hashes
@@ -12,9 +12,9 @@
 copyright: Copyright (c) 2019-2021 Lars Kuhtz <lakuhtz@gmail.com>
 category: Data
 tested-with:
+    GHC==9.2
     GHC==9.0.1
     GHC==8.10.7
-    GHC==8.8.4
 extra-source-files:
     README.md
     CHANGELOG.md
@@ -51,13 +51,13 @@
 common openssl-common
     if flag(with-openssl)
         c-sources:
-            cbits/keccak.h
             cbits/keccak.c
         cpp-options: -DWITH_OPENSSL=1
         if flag(openssl-use-pkg-config)
             pkgconfig-depends: libcrypto
         else
-            extra-Libraries: crypto
+            extra-libraries: crypto
+        include-dirs: cbits
 
 library
     import: openssl-common
@@ -110,6 +110,13 @@
         , base >=4.11 && <5
         , bytestring >=0.10
         , sydtest >=0.4
+        , vector >=0.12
+
+    if flag(with-openssl)
+        build-depends:
+            , sha-validation >=0.1
+        other-modules:
+            Test.Data.Hash.SHA3
 
     if flag(test-cryptonite)
         build-depends:
diff --git a/src/Data/Hash/Class/Mutable.hs b/src/Data/Hash/Class/Mutable.hs
--- a/src/Data/Hash/Class/Mutable.hs
+++ b/src/Data/Hash/Class/Mutable.hs
@@ -64,36 +64,36 @@
 
 hashByteString :: forall a . Hash a => B.ByteString -> a
 hashByteString b = unsafeDupablePerformIO $ do
-        ctx <- initialize @a
-        updateByteString @a ctx b
-        finalize ctx
+    ctx <- initialize @a
+    updateByteString @a ctx b
+    finalize ctx
 {-# INLINE hashByteString #-}
 
 hashByteStringLazy :: forall a . Hash a => BL.ByteString -> a
 hashByteStringLazy b = unsafeDupablePerformIO $ do
-        ctx <- initialize @a
-        updateByteStringLazy @a ctx b
-        finalize ctx
+    ctx <- initialize @a
+    updateByteStringLazy @a ctx b
+    finalize ctx
 {-# INLINE hashByteStringLazy #-}
 
 hashShortByteString :: forall a . Hash a => BS.ShortByteString -> IO a
 hashShortByteString b = do
-        ctx <- initialize @a
-        updateShortByteString @a ctx b
-        finalize ctx
+    ctx <- initialize @a
+    updateShortByteString @a ctx b
+    finalize ctx
 {-# INLINE hashShortByteString #-}
 
 hashStorable :: forall a b . Hash a => Storable b => b -> IO a
 hashStorable b = do
-        ctx <- initialize @a
-        updateStorable @a ctx b
-        finalize ctx
+    ctx <- initialize @a
+    updateStorable @a ctx b
+    finalize ctx
 {-# INLINE hashStorable #-}
 
 hashByteArray :: forall a . Hash a => ByteArray# -> IO a
 hashByteArray b = do
-        ctx <- initialize @a
-        updateByteArray @a ctx b
-        finalize ctx
+    ctx <- initialize @a
+    updateByteArray @a ctx b
+    finalize ctx
 {-# INLINE hashByteArray #-}
 
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -15,6 +15,10 @@
 import qualified Test.Data.Hash.SipHash
 import qualified Test.Data.Hash.Class.Pure
 
+#if defined(WITH_OPENSSL)
+import qualified Test.Data.Hash.SHA3
+#endif
+
 #if defined(TEST_CRYPTONITE)
 import qualified Cryptonite
 #endif
@@ -29,6 +33,10 @@
     describe "Test.Data.Hash.FNV1.tests" Test.Data.Hash.FNV1.tests
     describe "Test.Data.Hash.SipHash.tests" Test.Data.Hash.SipHash.tests
     describe "Test.Data.Hash.Class.Pure" Test.Data.Hash.Class.Pure.tests
+
+#if defined(WITH_OPENSSL)
+    describe "Test.Data.Hash.SHA3" Test.Data.Hash.SHA3.tests
+#endif
 
 #if defined(TEST_CRYPTONITE)
     describe "Cryptonite" Cryptonite.tests
diff --git a/test/Test/Data/Hash/FNV1.hs b/test/Test/Data/Hash/FNV1.hs
--- a/test/Test/Data/Hash/FNV1.hs
+++ b/test/Test/Data/Hash/FNV1.hs
@@ -1,6 +1,4 @@
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE MagicHash #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
diff --git a/test/Test/Data/Hash/SHA3.hs b/test/Test/Data/Hash/SHA3.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Data/Hash/SHA3.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+
+-- |
+-- Module: Test.Data.Hash.SHA3
+-- Copyright: Copyright © 2022 Kadena LLC.
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lars@kadena.io>
+-- Stability: experimental
+--
+-- TODO
+--
+module Test.Data.Hash.SHA3
+( tests
+) where
+
+import qualified Data.ByteString.Short as BS
+import Data.Coerce
+
+import Test.Syd
+import Test.Hash.SHA3
+
+-- internal modules
+
+import Data.Hash.SHA3
+
+-- -------------------------------------------------------------------------- --
+--
+
+tests :: Spec
+tests = describe "SHA3 Test Vectors" $ do
+    shortMsgTests
+    longMsgTests
+    monteTests
+
+-- -------------------------------------------------------------------------- --
+-- Msg Tests
+
+shortMsgTests :: Spec
+shortMsgTests = describe "ShortMsg" $ do
+    describe "224" $ runMsgTest @Sha3_224 sha3_224ShortMsg
+    describe "256" $ runMsgTest @Sha3_256 sha3_256ShortMsg
+    describe "384" $ runMsgTest @Sha3_384 sha3_384ShortMsg
+    describe "512" $ runMsgTest @Sha3_512 sha3_512ShortMsg
+
+longMsgTests :: Spec
+longMsgTests = describe "LongMsg" $ do
+    describe "224" $ runMsgTest @Sha3_224 sha3_224LongMsg
+    describe "256" $ runMsgTest @Sha3_256 sha3_256LongMsg
+    describe "384" $ runMsgTest @Sha3_384 sha3_384LongMsg
+    describe "512" $ runMsgTest @Sha3_512 sha3_512LongMsg
+
+runMsgTest
+    :: forall a
+    . Hash a
+    => Coercible a BS.ShortByteString
+    => MsgFile
+    -> Spec
+runMsgTest = msgAssert
+    (\l a b -> it l (a == b))
+    (BS.fromShort . coerce . hashByteString @a)
+
+-- -------------------------------------------------------------------------- --
+-- Monte Tests
+
+monteTests :: Spec
+monteTests = describe "Monte" $ do
+    describe "224" $ runMonteTest @Sha3_224 sha3_224Monte
+    describe "256" $ runMonteTest @Sha3_256 sha3_256Monte
+    describe "384" $ runMonteTest @Sha3_384 sha3_384Monte
+    describe "512" $ runMonteTest @Sha3_512 sha3_512Monte
+
+runMonteTest
+    :: forall a
+    . Hash a
+    => Coercible a BS.ShortByteString
+    => MonteFile
+    -> Spec
+runMonteTest = monteAssert
+    (\l a b -> it l (a == b))
+    (BS.fromShort . coerce . hashByteString @a)
+
