Ensure all public method args are validated and documented
diff --git a/src/main/java/org/apache/commons/codec/digest/Blake3.java b/src/main/java/org/apache/commons/codec/digest/Blake3.java
index 08ff3af..6cc9173 100644
--- a/src/main/java/org/apache/commons/codec/digest/Blake3.java
+++ b/src/main/java/org/apache/commons/codec/digest/Blake3.java
@@ -115,6 +115,7 @@
      * Updates this hash state using the provided bytes.
      *
      * @param in source array to update data from
+     * @throws NullPointerException if in is null
      */
     public void update(final byte[] in) {
         Objects.requireNonNull(in);
@@ -127,9 +128,12 @@
      * @param in     source array to update data from
      * @param offset where in the array to begin reading bytes
      * @param length number of bytes to update
+     * @throws NullPointerException      if in is null
+     * @throws IndexOutOfBoundsException if offset or length are negative or if offset + length is greater than the
+     *                                   length of the provided array
      */
     public void update(final byte[] in, final int offset, final int length) {
-        Objects.requireNonNull(in);
+        checkBufferArgs(in, offset, length);
         engineState.inputData(in, offset, length);
     }
 
@@ -138,8 +142,10 @@
      * previously finalized bytes. Note that this can finalize up to 2<sup>64</sup> bytes per instance.
      *
      * @param out destination array to finalize bytes into
+     * @throws NullPointerException if out is null
      */
     public void doFinalize(final byte[] out) {
+        Objects.requireNonNull(out);
         doFinalize(out, 0, out.length);
     }
 
@@ -150,9 +156,12 @@
      * @param out    destination array to finalize bytes into
      * @param offset where in the array to begin writing bytes to
      * @param length number of bytes to finalize
+     * @throws NullPointerException      if out is null
+     * @throws IndexOutOfBoundsException if offset or length are negative or if offset + length is greater than the
+     *                                   length of the provided array
      */
     public void doFinalize(final byte[] out, final int offset, final int length) {
-        Objects.requireNonNull(out);
+        checkBufferArgs(out, offset, length);
         engineState.outputHash(out, offset, length);
     }
 
@@ -161,8 +170,12 @@
      *
      * @param nrBytes number of bytes to finalize
      * @return requested number of finalized bytes
+     * @throws IllegalArgumentException if nrBytes is negative
      */
     public byte[] doFinalize(final int nrBytes) {
+        if (nrBytes < 0) {
+            throw new IllegalArgumentException("Requested bytes must be non-negative");
+        }
         final byte[] hash = new byte[nrBytes];
         doFinalize(hash);
         return hash;
@@ -183,6 +196,8 @@
      *
      * @param key 32-byte secret key
      * @return fresh Blake3 instance in keyed mode using the provided key
+     * @throws NullPointerException     if key is null
+     * @throws IllegalArgumentException if key is not 32 bytes
      */
     public static Blake3 initKeyedHash(final byte[] key) {
         Objects.requireNonNull(key);
@@ -199,6 +214,7 @@
      *
      * @param kdfContext a globally unique key-derivation context byte string to separate key derivation contexts from each other
      * @return fresh Blake3 instance in key derivation mode
+     * @throws NullPointerException if kdfContext is null
      */
     public static Blake3 initKeyDerivationFunction(final byte[] kdfContext) {
         Objects.requireNonNull(kdfContext);
@@ -214,6 +230,7 @@
      *
      * @param data source array to absorb data from
      * @return 32-byte hash squeezed from the provided data
+     * @throws NullPointerException if data is null
      */
     public static byte[] hash(final byte[] data) {
         final Blake3 blake3 = Blake3.initHash();
@@ -227,6 +244,7 @@
      * @param key  32-byte secret key
      * @param data source array to absorb data from
      * @return 32-byte mac squeezed from the provided data
+     * @throws NullPointerException if key or data are null
      */
     public static byte[] keyedHash(final byte[] key, final byte[] data) {
         final Blake3 blake3 = Blake3.initKeyedHash(key);
@@ -234,6 +252,20 @@
         return blake3.doFinalize(OUT_LEN);
     }
 
+    private static void checkBufferArgs(byte[] buffer, int offset, int length) {
+        Objects.requireNonNull(buffer);
+        if (offset < 0) {
+            throw new IndexOutOfBoundsException("Offset must be non-negative");
+        }
+        if (length < 0) {
+            throw new IndexOutOfBoundsException("Length must be non-negative");
+        }
+        if (offset > buffer.length - length) {
+            throw new IndexOutOfBoundsException(
+                    "Offset " + offset + " and length " + length + " out of bounds with buffer length " + buffer.length);
+        }
+    }
+
     private static void packInt(final int value, final byte[] dst, final int off, final int len) {
         for (int i = 0; i < len; i++) {
             dst[off + i] = (byte) (value >>> i * Byte.SIZE);