diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
 # Changelog
 
+- 0.2.1 (2025-01-07)
+  * Removes the 'primitive' dependency.
+
 - 0.2.0 (2025-01-04)
   * Adds bech32/bech32m/base32 decoding.
   * Fixes a bug in which mixed-case HRP's would result in encodings with
diff --git a/lib/Data/ByteString/Bech32/Internal.hs b/lib/Data/ByteString/Bech32/Internal.hs
--- a/lib/Data/ByteString/Bech32/Internal.hs
+++ b/lib/Data/ByteString/Bech32/Internal.hs
@@ -1,6 +1,7 @@
 {-# OPTIONS_HADDOCK hide, prune #-}
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE BinaryLiterals #-}
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ViewPatterns #-}
 
@@ -20,7 +21,6 @@
 import qualified Data.ByteString.Builder.Extra as BE
 import qualified Data.ByteString.Internal as BI
 import qualified Data.ByteString.Unsafe as BU
-import qualified Data.Primitive.PrimArray as PA
 import Data.Word (Word32)
 
 fi :: (Integral a, Num b) => a -> b
@@ -52,8 +52,14 @@
 
 polymod :: BS.ByteString -> Word32
 polymod = BS.foldl' alg 1 where
-  generator = PA.primArrayFromListN 5
-    [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
+  generator :: Int -> Word32
+  generator = \case
+    0 -> 0x3b6a57b2
+    1 -> 0x26508e6d
+    2 -> 0x1ea119fa
+    3 -> 0x3d4233dd
+    4 -> 0x2a1462b3
+    _ -> error "ppad-bech32: internal error (please report this as a bug!)"
 
   alg !chk v =
     let !b = chk `B.shiftR` 25
@@ -63,8 +69,7 @@
   loop_gen i b !chk
     | i > 4 = chk
     | otherwise =
-        let sor | B.testBit (b `B.shiftR` i) 0 =
-                    PA.indexPrimArray generator i
+        let sor | B.testBit (b `B.shiftR` i) 0 = generator i
                 | otherwise = 0
         in  loop_gen (succ i) b (chk `B.xor` sor)
 
diff --git a/ppad-bech32.cabal b/ppad-bech32.cabal
--- a/ppad-bech32.cabal
+++ b/ppad-bech32.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               ppad-bech32
-version:            0.2.0
+version:            0.2.1
 synopsis:           bech32 and bech32m encoding/decoding, per BIPs 173 & 350.
 license:            MIT
 license-file:       LICENSE
@@ -31,7 +31,6 @@
   build-depends:
       base >= 4.9 && < 5
     , bytestring >= 0.9 && < 0.13
-    , primitive >= 0.8 && < 0.10
 
 test-suite bech32-tests
   type:                exitcode-stdio-1.0
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -16,6 +16,9 @@
 data ValidInput = ValidInput BS.ByteString BS.ByteString
   deriving (Eq, Show)
 
+data InvalidInput = InvalidInput BS.ByteString BS.ByteString
+  deriving (Eq, Show)
+
 instance Q.Arbitrary ValidInput where
   arbitrary = do
     h <- hrp
@@ -24,6 +27,14 @@
     b <- bytes a
     pure (ValidInput h b)
 
+instance Q.Arbitrary InvalidInput where
+  arbitrary = do
+    h <- invalid_hrp
+    let l = 83 - BS.length h
+        a = l * 5 `quot` 8
+    b <- bytes a
+    pure (InvalidInput h b)
+
 instance Q.Arbitrary BS where
   arbitrary = do
     b <- bytes 1024
@@ -35,6 +46,12 @@
   v <- Q.vectorOf l (Q.choose (33, 126))
   pure (B8.map C.toLower (BS.pack v))
 
+invalid_hrp :: Q.Gen BS.ByteString
+invalid_hrp = do
+  l <- Q.oneof [pure 0, Q.chooseInt (84, 100)]
+  v <- Q.vectorOf l (Q.oneof [Q.choose (0, 32), Q.choose (127, 255)])
+  pure (B8.map C.toLower (BS.pack v))
+
 bytes :: Int -> Q.Gen BS.ByteString
 bytes k = do
   l <- Q.chooseInt (0, k)
@@ -66,6 +83,18 @@
   Nothing -> False
   Just b  -> b == bs
 
+bech32_invalid_input_fails_encode :: InvalidInput -> Bool
+bech32_invalid_input_fails_encode (InvalidInput h b) =
+  case Bech32.encode h b of
+    Nothing -> True
+    Just _ -> False
+
+bech32m_invalid_input_fails_encode :: InvalidInput -> Bool
+bech32m_invalid_input_fails_encode (InvalidInput h b) =
+  case Bech32m.encode h b of
+    Nothing -> True
+    Just _ -> False
+
 main :: IO ()
 main = defaultMain $ testGroup "ppad-bech32" [
     testGroup "base32" [
@@ -77,10 +106,14 @@
         Q.withMaxSuccess 1000 matches_reference
     , Q.testProperty "decode . encode ~ id" $
         Q.withMaxSuccess 1000 bech32_decode_inverts_encode
+    , Q.testProperty "invalid bech32 input fails to encode" $
+        Q.withMaxSuccess 1000 bech32_invalid_input_fails_encode
     ]
   , testGroup "bech32m" [
       Q.testProperty "decode . encode ~ id" $
         Q.withMaxSuccess 1000 bech32m_decode_inverts_encode
+    , Q.testProperty "invalid bech32m input fails to encode" $
+        Q.withMaxSuccess 1000 bech32_invalid_input_fails_encode
     ]
   ]
 
