Python · Hashing and Checksums

Hashing and Checksums

import stringzillafrom stringzilla import Aes256CtrKey, Aes256GcmKey, Aes256GcmEncryptor, … 6.0.0-rc.1 · 91a27bdd

§ One Shot Hashes and Checksums

  • sz.hash(text, seed=0) — seeded 64-bit AES-accelerated hash, returned as an unsigned int. This differs from Python’s built-in hash(), which returns a platform-dependent Py_hash_t.
  • sz.hash_multiseed(text, seeds, out=None) — hash one string under many seeds at once. seeds is a contiguous buffer of uint64, such as a numpy.uint64 array or array('Q', ...); plain int lists are not accepted. Returns a tuple of ints, or fills the optional contiguous uint64 out buffer in place and returns None. This is much faster than looping hash for short strings under many seeds, and is useful for feature hashing, Count-Min sketches, Bloom/cuckoo filters, and MinHash/LSH.
  • sz.bytesum(text) — additive checksum of the individual byte values, as an int.
  • sz.sha256(text) — 32-byte SHA-256 digest as bytes.
  • sz.hmac_sha256(key, message) — 32-byte HMAC-SHA256 digest as bytes.

hash, hash_multiseed, bytesum, and sha256 are also available as String methods.

import stringzilla as sz
import numpy as np

sz.hash("hello")                  # seeded 64-bit value
sz.hash("hello", seed=42)         # a different value
assert sz.bytesum("abc") == 294
assert sz.sha256("abc").hex()[:8] == "ba7816bf"
assert len(sz.hmac_sha256(b"key", b"message")) == 32

seeds = np.arange(8, dtype=np.uint64)
assert len(sz.hash_multiseed("hello", seeds)) == 8   # tuple of 8 ints

§ Incremental 64-bit Hasher

Hasher(seed=0) is an incremental, AES-accelerated hasher producing the same 64-bit value as sz.hash.

  • update(data) — absorb a str/bytes chunk; returns self for chaining.
  • digest() — current hash as an unsigned 64-bit int, without consuming state.
  • hexdigest() — current hash as a 16-character lowercase hex str.
  • reset() — reset to the initial seed; returns self.
import stringzilla as sz

h = sz.Hasher()
h.update(b"hello").update(b" world")
assert h.digest() == sz.hash(b"hello world")
assert len(h.hexdigest()) == 16

§ Incremental Sha256 Compatible with hashlib

Sha256() is an incremental SHA-256 hasher with a hashlib-compatible interface, hardware-accelerated where available.

  • update(data) — absorb a str/bytes chunk; returns self.
  • digest() — current 32-byte digest as bytes.
  • hexdigest() — current digest as a 64-character lowercase hex str.
  • reset() — reset to the initial SHA-256 constants; returns self.
  • copy() — return an independent copy with identical internal state.
import stringzilla as sz
import hashlib

assert sz.Sha256().update(b"abc").hexdigest() == hashlib.sha256(b"abc").hexdigest()

a = sz.Sha256().update(b"ab")
assert a.copy().update(b"c").hexdigest() == sz.Sha256().update(b"abc").hexdigest()

§ Sha256s for Many Messages at Once

Digesting one message is a serial dependency chain, but independent messages compress in parallel lanes — sixteen at a time on AVX-512, eight on AVX2. Sha256s(lanes) holds one hasher per lane, with update(chunks) taking exactly one chunk per lane and digest(out=None), hexdigest(), reset(), copy() and len() behaving as on Sha256. Lanes are workers rather than messages: lanes[i] reads one as a Sha256, and lanes[i] = sz.Sha256() retires a finished lane while the others keep streaming.

sz.hmac_sha256(key, message, out=None) takes either one message or a collection, authenticating a batch of tokens sharing a secret through the same kernels.

Both accept a Strings, read in place, and an out buffer of bytes — a (count, 32) numpy.uint8 matrix receives one digest per row with no allocation, since that is byte-for-byte the layout the kernel writes. The GIL is released around the kernel, so several threads digest concurrently.

Lanes advance in lockstep within a group, so a group costs as much as its longest message. Every length is handled correctly, but throughput is best when messages of similar length share a group, which Strings.argsort arranges.

import numpy as np
import stringzilla as sz

texts = sz.Strings(["alpha", "beta", "gamma"])
lanes = sz.Sha256s(len(texts))
digests = np.empty((len(lanes), sz.Sha256.digest_length), np.uint8)
lanes.update(texts).digest(out=digests)

tags = sz.hmac_sha256(b"secret", texts)   # one 32-byte tag per message
assert tags[0] == sz.hmac_sha256(b"secret", "alpha")

API

Types

AuthenticationErrorRaised when a Galois/counter mode tag does not authenticate the bytes it accompanies.
__init__.pyi:43 ↗
class AuthenticationError(ValueError)

Derives from ValueError, so code guarding only against malformed input still stops a forgery, and the plaintext is never returned alongside it.

Examples

>>> issubclass(sz.AuthenticationError, ValueError)
True
ClassAes256CtrKeyAES-256 in counter mode, abbreviated CTR: unauthenticated, seekable, and its own inverse.2
__init__.pyi:54 ↗
class Aes256CtrKey

The 32-byte secret is expanded once into a round-key schedule the object holds and wipes when it is collected.

Raises: ValueError: If the secret is not exactly 32 bytes long. Note: A nonce must never repeat under one secret; reuse exposes the exclusive-or of two texts.

Examples

>>> key = sz.Aes256CtrKey(bytes(32))
>>> key.xor(key.xor(b'hello', bytes(12)), bytes(12))
b'hello'
Aes256CtrKey
__init__.pyi:77 ↗
Aes256CtrKey(
    secret: str | bytes,
    *,
    capabilities: Capability | None = None,
)
xorxor(text, nonce, offset=0) -> bytes
__init__.pyi:78 ↗
def xor(
    text: str | bytes,
    nonce: str | bytes,
    offset: int = 0,
) -> bytes

Exclusive-or text against the keystream, encrypting or decrypting with the same call.

Returns: bytes: As many bytes as text holds. Raises: ValueError: If the nonce is not exactly 12 bytes long.

Parameters

textstr | bytesThe bytes to transform.
noncestr | bytesExactly 12 nonce bytes, never repeated under one secret.
offsetintAbsolute byte position of text in the stream. Defaults to 0.

Examples

>>> key, nonce = sz.Aes256CtrKey(bytes(32)), bytes(12)
>>> whole = key.xor(bytes(64), nonce)
>>> key.xor(bytes(16), nonce, offset=32) == whole[32:48]
True
ClassAes256GcmKeyAES-256 in Galois/counter mode, abbreviated GCM: authenticated, and therefore not seekable.3
__init__.pyi:99 ↗
class Aes256GcmKey

The 32-byte secret is expanded once into a schedule and the Galois hash powers, also called GHASH, and is wiped on collection.

Raises: ValueError: If the secret is not exactly 32 bytes long. Note: Reusing a nonce here exposes the hash subkey, after which every message can be forged.

Examples

>>> key = sz.Aes256GcmKey(bytes(32))
>>> ciphertext, tag = key.encrypt(b'hello', bytes(12))
>>> key.decrypt(ciphertext, bytes(12), tag)
b'hello'
Aes256GcmKey
__init__.pyi:123 ↗
Aes256GcmKey(
    secret: str | bytes,
    *,
    capabilities: Capability | None = None,
)
encryptEncrypt text and authenticate both it and associated, returning ciphertext and tag.
__init__.pyi:124 ↗
def encrypt(
    text: str | bytes,
    nonce: str | bytes,
    associated: str | bytes | None = None,
) -> tuple[bytes, bytes]

Returns: tuple: The ciphertext, as long as text, and the 16-byte tag. Raises: ValueError: If the nonce is not exactly 12 bytes long.

Parameters

textstr | bytesThe plaintext.
noncestr | bytesExactly 12 nonce bytes, never repeated under one secret.
associatedstr | bytes | NoneBytes authenticated but not encrypted.

Examples

>>> ciphertext, tag = sz.Aes256GcmKey(bytes(32)).encrypt(b'hi', bytes(12), b'header')
>>> len(ciphertext), len(tag)
(2, 16)
decryptdecrypt(text, nonce, tag, associated=None) -> bytes
__init__.pyi:145 ↗
def decrypt(
    text: str | bytes,
    nonce: str | bytes,
    tag: str | bytes,
    associated: str | bytes | None = None,
) -> bytes

Check tag against the ciphertext and associated data, then return the plaintext.

Returns: bytes: The plaintext, as long as text. Raises: AuthenticationError: If the tag does not match, in which case nothing is returned.

Parameters

textstr | bytesThe ciphertext.
noncestr | bytesThe 12 nonce bytes used to encrypt.
tagstr | bytesThe 16-byte tag to check.
associatedstr | bytes | NoneThe same bytes passed to encrypt.

Examples

>>> key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12)
>>> ciphertext, tag = key.encrypt(b'hello', nonce)
>>> key.decrypt(ciphertext, nonce, tag)
b'hello'
ClassAes256GcmEncryptorThe same Galois/counter mode construction as Aes256GcmKey, sealing a message in chunks.4
__init__.pyi:169 ↗
class Aes256GcmEncryptor

Chunk boundaries are invisible to the result. Opening a message uses Aes256GcmDecryptor, a separate type because the tag absorbs ciphertext in both directions, which is the output when sealing and the input when opening. Copies the key, so it cannot outlive it.

Examples

>>> key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12)
>>> sealed = sz.Aes256GcmEncryptor(key, nonce)
>>> ciphertext, tag = key.encrypt(b'hello', nonce)
>>> sealed.encrypt(b'hel') + sealed.encrypt(b'lo') == ciphertext
True
Aes256GcmEncryptor
__init__.pyi:192 ↗
Aes256GcmEncryptor(
    key: Aes256GcmKey,
    nonce: str | bytes,
    *,
    capabilities: Capability | None = None,
)
associateassociate(data) -> Aes256GcmEncryptor
__init__.pyi:193 ↗
def associate(data: str | bytes, /) -> Aes256GcmEncryptor

Absorb bytes that are authenticated but not encrypted, and return self for chaining. All associated data must be absorbed before the first message chunk.

Returns: Aes256GcmEncryptor: The same object, enabling sealed.associate(a).associate(b). Example:

key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12) sealed = sz.Aes256GcmEncryptor(key, nonce).associate(b'head') ciphertext, tag = key.encrypt(b'body', nonce, b'head') sealed.encrypt(b'body') == ciphertext True

Parameters

datastr | bytesAssociated bytes, such as a routing header.
encryptencrypt(data) -> bytes
__init__.pyi:210 ↗
def encrypt(data: str | bytes, /) -> bytes

Encrypt one chunk and absorb its ciphertext into the running tag.

Returns: bytes: The ciphertext chunk, as long as data. Example:

sealed = sz.Aes256GcmEncryptor(sz.Aes256GcmKey(bytes(32)), bytes(12)) len(sealed.encrypt(b'chunk')) 5

Parameters

datastr | bytesThe plaintext chunk.
*× 2digest() -> bytes
__init__.pyi:224 ↗
def digest() -> bytes

Return the 16-byte tag over everything absorbed so far, leaving the seal appendable.

Example:

key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12) sealed = sz.Aes256GcmEncryptor(key, nonce) _ = sealed.encrypt(b'hello') ciphertext, tag = key.encrypt(b'hello', nonce) sealed.digest() == tag True

Return the current 32-byte SHA-256 digest without consuming state.

Example:

len(sz.Sha256().update(b'abc').digest()) 32

Returns

The 16-byte authentication tag.
ClassAes256GcmDecryptorThe same Galois/counter mode construction as Aes256GcmKey, opening a message in chunks and checking its tag at the end.4
__init__.pyi:240 ↗
class Aes256GcmDecryptor

Chunk boundaries are invisible to the result. Sealing a message uses Aes256GcmEncryptor. Copies the key, so it cannot outlive it.

Examples

>>> key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12)
>>> ciphertext, tag = key.encrypt(b'hello', nonce)
>>> sz.Aes256GcmDecryptor(key, nonce).decrypt_unverified(ciphertext)
b'hello'
Aes256GcmDecryptor
__init__.pyi:261 ↗
Aes256GcmDecryptor(
    key: Aes256GcmKey,
    nonce: str | bytes,
    *,
    capabilities: Capability | None = None,
)
associateassociate(data) -> Aes256GcmDecryptor
__init__.pyi:262 ↗
def associate(data: str | bytes, /) -> Aes256GcmDecryptor

Absorb bytes that are authenticated but not encrypted, and return self for chaining. All associated data must be absorbed before the first message chunk.

Returns: Aes256GcmDecryptor: The same object, enabling opened.associate(a).associate(b). Example:

key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12) ciphertext, tag = key.encrypt(b'body', nonce, b'head') opened = sz.Aes256GcmDecryptor(key, nonce).associate(b'head') opened.decrypt_unverified(ciphertext) b'body'

Parameters

datastr | bytesThe same associated bytes the sender authenticated.
decrypt_unverifieddecrypt_unverified(data) -> bytes
__init__.pyi:279 ↗
def decrypt_unverified(data: str | bytes, /) -> bytes

Decrypt one chunk and absorb its ciphertext into the running tag. The bytes returned are not yet authenticated: nothing has checked that this ciphertext is genuine, so buffer them until verify() succeeds rather than acting on them.

Returns: bytes: The unverified plaintext chunk, as long as data. Example:

key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12) ciphertext, tag = key.encrypt(b'hello', nonce) opened = sz.Aes256GcmDecryptor(key, nonce) opened.decrypt_unverified(ciphertext) b'hello'

Parameters

datastr | bytesThe ciphertext chunk.
verifyverify(tag) -> None
__init__.pyi:297 ↗
def verify(tag: str | bytes, /) -> None

Check tag against everything absorbed so far, in time independent of where it differs.

Raises: AuthenticationError: If the tag does not match. ValueError: If the tag is not exactly 16 bytes long. Example:

key, nonce = sz.Aes256GcmKey(bytes(32)), bytes(12) ciphertext, tag = key.encrypt(b'hello', nonce) opened = sz.Aes256GcmDecryptor(key, nonce) _ = opened.decrypt_unverified(ciphertext) opened.verify(tag) is None True

Parameters

tagstr | bytesThe 16-byte tag to check.
ClassHasherIncremental, AES-accelerated hasher producing the same 64-bit value as sz.hash.5
__init__.pyi:2037 ↗
class Hasher

Feed data in chunks with update(), read the digest, and reuse the object via reset().

Example:

h = sz.Hasher() _ = h.update(b'hello').update(b' world') h.digest() == sz.hash(b'hello world') True

Hasher
__init__.pyi:2055 ↗
Hasher(seed: int = 0, *, capabilities: Capability | None = None)
updateupdate(data) -> Hasher
__init__.pyi:2056 ↗
def update(data: str | bytes, /) -> Hasher

Absorb more bytes into the running hash and return self for chaining.

Returns: Hasher: The same object, enabling h.update(a).update(b). Example:

sz.Hasher().update(b'ab').update(b'c').digest() == sz.hash(b'abc') True

Parameters

datastr | bytesChunk to mix into the state.
digestdigest() -> int
__init__.pyi:2069 ↗
def digest() -> int

Return the current hash as an unsigned 64-bit integer without consuming state.

Example:

isinstance(sz.Hasher().update(b'x').digest(), int) True

Returns

Hash of all data absorbed so far.
*× 2hexdigest() -> str
__init__.pyi:2080 ↗
def hexdigest() -> str

Return the current hash as a 16-character lowercase hex string.

Example:

len(sz.Hasher().hexdigest()) 16

Return the current SHA-256 digest as a 64-character lowercase hex string.

Example:

import hashlib sz.Sha256().update(b'abc').hexdigest() == hashlib.sha256(b'abc').hexdigest() True

Returns

Zero-padded 16-digit hex of the 64-bit hash.
resetreset() -> Hasher
__init__.pyi:2091 ↗
def reset() -> Hasher

Reset the state to the initial seed and return self.

Example:

h = sz.Hasher().update(b'data').reset() h.digest() == sz.Hasher().digest() True

Returns

The same object, emptied and reusable.
ClassSha256Incremental SHA-256 hasher with a hashlib-compatible interface, hardware-accelerated where available.4
__init__.pyi:2104 ↗
class Sha256

Feed data with update(), then read digest() or hexdigest(). The capabilities it and its calls run default to the CPU's enabled ones.

Examples

>>> import hashlib
>>> sz.Sha256().update(b'abc').hexdigest() == hashlib.sha256(b'abc').hexdigest()
True
Sha256
__init__.pyi:2119 ↗
Sha256(*, capabilities: Capability | None = None)
updateupdate(data) -> Sha256
__init__.pyi:2120 ↗
def update(data: str | bytes, /) -> Sha256

Absorb more bytes into the running SHA-256 state and return self for chaining.

Returns: Sha256: The same object, enabling h.update(a).update(b). Example:

hexdigest = sz.Sha256().update(b'a').update(b'bc').hexdigest() hexdigest[:8] 'ba7816bf'

Parameters

datastr | bytesChunk to mix into the state.
*× 2reset() -> Sha256
__init__.pyi:2157 ↗
def reset() -> Sha256
Symbol
reset
copy

Reset the state to the initial SHA-256 constants and return self.

Example:

h = sz.Sha256().update(b'x').reset() h.hexdigest() == sz.Sha256().hexdigest() True

Return an independent copy of the hasher with identical internal state.

Example:

a = sz.Sha256().update(b'ab') a.copy().update(b'c').hexdigest() == sz.Sha256().update(b'abc').hexdigest() True

Returns

The same object, emptied and reusable.
digest_length
__init__.pyi:2118 ↗
digest_length: int
ClassSha256sIncremental SHA-256 over many independent messages at once, one message per lane.6
__init__.pyi:2182 ↗
class Sha256s

Hashing a single message is a serial dependency chain no instruction set can speed up, but independent messages compress in parallel lanes: sixteen at a time on AVX-512, eight on AVX2. Feed at least a few kilobytes per lane per call, as shorter chunks never reach the lane-parallel path.

Lanes are workers rather than messages: assigning lanes[i] = sz.Sha256() retires a finished lane and starts the next message in it, leaving every other lane streaming.

Example:

lanes = sz.Sha256s(2) _ = lanes.update([b'Hello, ', b'Goodbye, ']) _ = lanes.update([b'world!', b'world!']) hello, goodbye = lanes.digest() hello == sz.Sha256().update(b'Hello, world!').digest() True

Sha256s
__init__.pyi:2210 ↗
Sha256s(lanes: int, /, *, capabilities: Capability | None = None)
updateupdate(chunks) -> Sha256s
__init__.pyi:2214 ↗
def update(
    chunks: Strings | Sequence[str | bytes],
    /,
) -> Sha256s

Append the next chunk of every lane's message.

Returns: Sha256s: The same object, to allow chaining.

Parameters

chunksStrings | Sequence[str | bytes]Exactly one string-like chunk per lane; may be empty. A Strings is read in place, whatever its layout, so a tape never leaves its buffer.
digestdigest(out=None) -> list[bytes] | buffer
__init__.pyi:2225 ↗
def digest(out: _Buffer | None = None) -> list[bytes] | _Buffer

Return each lane's 32-byte digest, leaving every lane able to accept more data.

Returns: list[bytes]: One digest per lane in lane order, or out itself when a buffer is given. Example:

lanes = sz.Sha256s(2) digests = bytearray(len(lanes) * sz.Sha256.digest_length) _ = lanes.update([b'abc', b'abc']).digest(out=digests) bytes(digests[:sz.Sha256.digest_length]) == sz.Sha256().update(b'abc').digest() True

Parameters

out_Buffer | NoneWritable, C-contiguous buffer of bytes holding at least len(lanes) * 32 of them - a (len(lanes), 32) numpy.uint8 matrix receives one digest per row with zero allocation. Defaults to None.
hexdigesthexdigest() -> list[str]
__init__.pyi:2243 ↗
def hexdigest() -> list[str]

Return each lane's digest as a 64-character lowercase hex string, in lane order.

Returns

list[str]: One 64-digit hex string per lane.
*× 2reset() -> Sha256s
__init__.pyi:2251 ↗
def reset() -> Sha256s
Symbol
reset
copy

Return every lane to the initial state, reusing the allocation.

Return an independent batch whose lanes hold identical internal state.

Returns

The same object, to allow chaining.
digest_length
__init__.pyi:2209 ↗
digest_length: int

Functions

hashCompute the hash value of the string.
__init__.pyi:1005 ↗
def hash(
    text: _StringLike,
    seed: int = 0,
    /,
    *,
    capabilities: Capability | None = None,
) -> int

This function can be called as a method on a String object or as a standalone function. Args: text (String or str or bytes): The string to hash, positional-only when standalone. seed (int, optional): The seed value for hashing. Defaults to 0. Can be positional or keyword. capabilities (Capability, optional): Capabilities to run, defaulting to the CPU's enabled ones. Returns: int: The hash value as an unsigned 64-bit integer. This differs from Python's built-in hash() which returns a Py_hash_t and may be platform-dependent. Raises: TypeError: If the argument is not string-like or incorrect number of arguments is provided. Signature:

def hash(text, seed=0, /, *, capabilities=None) -> int: ...

hash_multiseedHash one string under many seeds at once.
__init__.pyi:1022 ↗
def hash_multiseed(
    text: _StringLike,
    seeds: _Buffer,
    /,
    out: _Buffer | None = None,
    *,
    capabilities: Capability | None = None,
) -> tuple[int, ...] | None

Equivalent to tuple(hash(text, s) for s in seeds), but normalizes the input into AES blocks once and replays the cheap per-seed rounds - markedly faster for short strings under many seeds (feature hashing, Count-Min sketches, Bloom/cuckoo filters, MinHash/LSH).

Returns: tuple[int, ...] | None: Tuple of 64-bit hashes, or None when writing into out.

Parameters

text_StringLikeThe string to hash, positional-only when standalone.
seeds_BufferContiguous 64-bit seeds, e.g. numpy.uint64 array or array('Q', ...). Plain int lists are not accepted - wrap them in array('Q', seeds) or numpy.
out_Buffer | NoneA contiguous buffer of at least len(seeds) elements. If given, it is filled in place and None returned; otherwise a tuple of ints is returned.
capabilitiesCapability | NoneCapabilities to run, defaulting to the CPU's enabled ones.

Examples

>>> def hash_multiseed(text, seeds, /, out=None, *, capabilities=None) -> tuple | None: ...
bytesumCompute the checksum of individual byte values in a string.
__init__.pyi:1073 ↗
def bytesum(
    text: _StringLike,
    *,
    capabilities: Capability | None = None,
) -> int

This function can be called as a method on a String object or as a standalone function. Args: text (String or str or bytes): The string to hash. capabilities (Capability, optional): Capabilities to run, defaulting to the CPU's enabled ones. Returns: int: The checksum of individual byte values in a string. Raises: TypeError: If the argument is not string-like or incorrect number of arguments is provided.

Examples

>>> sz.bytesum('abc')
294
sha256Compute SHA256 cryptographic hash of the input data.
__init__.pyi:1090 ↗
def sha256(
    text: _StringLike,
    *,
    capabilities: Capability | None = None,
) -> bytes

This function can be called as a method on a String object or as a standalone function. Args: text (String or str or bytes): The input data to hash. capabilities (Capability, optional): Capabilities to run, defaulting to the CPU's enabled ones. Returns: bytes: The 32-byte (256-bit) SHA256 digest. Raises: TypeError: If the argument is not string-like or incorrect number of arguments is provided.

Examples

>>> digest = sz.String('abc').sha256().hex()
>>> digest[:8]
'ba7816bf'
hmac_sha256hmac_sha256(key, message, out=None, *, capabilities=None) -> bytes | list[bytes] | buffer
__init__.pyi:1108 ↗
def hmac_sha256(
    key: str | bytes,
    message: str | bytes | Strings | Sequence[str | bytes],
    out: _Buffer | None = None,
    *,
    capabilities: Capability | None = None,
) -> bytes | list[bytes] | _Buffer

Compute the HMAC-SHA256 authentication code of one message, or of many under one key.

Authenticating one message is a serial dependency chain no instruction set can speed up, but independent messages compress in parallel lanes: sixteen at a time on AVX-512, eight on AVX2. Pass a Strings of messages - a batch of tokens or requests sharing a secret - and both the message pass and the outer wrap run lane-parallel.

Returns: bytes: The 32-byte tag, for a single string-like message. list[bytes]: One tag per message, in order, for a collection. buffer: out itself, when an output buffer is given. Example:

len(sz.hmac_sha256(b'key', b'message')) 32 sz.hmac_sha256(b'key', sz.Strings(['a', 'b'])) == [ ... sz.hmac_sha256(b'key', b'a'), sz.hmac_sha256(b'key', b'b')] True

Parameters

keystr | bytesThe secret key.
messagestr | bytes | Strings | Sequence[str | bytes]One message, or many to authenticate together.
out_Buffer | NoneWritable, C-contiguous buffer of bytes holding at least 32 per message - a (len(message), 32) numpy.uint8 matrix receives one tag per row with zero allocation. Defaults to None.
capabilitiesCapability | NoneCapabilities to run, defaulting to the CPU's enabled ones.