Skip to content

Instantly share code, notes, and snippets.

@NF1198
Last active September 6, 2025 07:15
Show Gist options
  • Save NF1198/1780be26831ba3a79b1c9bd6f209a596 to your computer and use it in GitHub Desktop.
Save NF1198/1780be26831ba3a79b1c9bd6f209a596 to your computer and use it in GitHub Desktop.
Hermes32 Hashing Function for Excel

Hermes32

Hermes32 is a hashing function written in Excel using LAMBDA. It takes text or numbers, plus an optional key, and produces a fixed-length 32-character string (approximately 192 bits). It is highly recommended to use a global key, such as hermes_key, as a named value in your spreadsheet.

Hermes32 is meant for everyday workbook use. You define it once in the Name Manager and then call it like any other Excel function.

Download

Hermes32.xlsx (Rename file to Hermes32.xlsx when downloading; GitHub gist doesn't support uploading XLSX)

Examples

=Hermes32K("hello")           // uses workbook scoped named key hermes_key
=Hermes32("hello","venus")    // uses "venus" as the key
=Hermes32(A2, my_key)         // hashes the value in cell A2 using named value "my_key"

Sample output:

3g/Zpd+zC0ZqEmMHi32a5PqQItQbQTRn

What it’s good for

  • Creating unique row identifiers.
  • Comparing large text or numeric fields by hash.
  • Adding lightweight fingerprints to tables.
  • Experimenting with hashing structures in Excel without VBA or add-ins.

How it works

Hermes32 maintains six 32-bit words of state. Four are initialized with constants; two more are derived from mixing. Each input character updates the state using add-rotate-xor operations. After all characters are processed, the state is packed into 24 bytes, Base64-encoded, and returned as a 32-character string.

The name Hermes32 comes from Hermes (the Greek counterpart of Mercury) and the 32-character output.

Notes

Hermes32 is not a replacement for standard cryptographic functions like SHA-256 or BLAKE2. It hasn’t been vetted for cryptographic security. It’s a practical hash for use in spreadsheets, not a secure hash for authentication or signatures.

MIT License
Copyright (c) 2025 Nicholas Folse
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=LAMBDA(input,key,
LET(
modu,2147483648,
mask,2147483647,
safe,LAMBDA(v,MOD(v,modu)),
addm,LAMBDA(a,b,MOD(MOD(a,modu)+MOD(b,modu),modu)),
xorb,LAMBDA(a,b,MOD(BITXOR(MOD(a,modu),MOD(b,modu)),modu)),
rotm,LAMBDA(v,s,
LET(k,MOD(s,31),
x,MOD(v,modu),
IF(k=0,
x,
MOD(MOD(x*POWER(2,k),modu)+QUOTIENT(x,POWER(2,31-k)),modu)
)
)
),
packword,LAMBDA(n,
HSTACK(
BITAND(BITRSHIFT(safe(n),24),255),
BITAND(BITRSHIFT(safe(n),16),255),
BITAND(BITRSHIFT(safe(n),8),255),
BITAND(safe(n),255)
)
),
repbyte,LAMBDA(b,cnt,
MAKEARRAY(1,cnt,LAMBDA(r,c,b))
),
xorrow,LAMBDA(a,b,
LET(m,COLUMNS(a),
idx,SEQUENCE(1,m),
MAP(idx,LAMBDA(i, xorb(INDEX(a,1,i), INDEX(b,1,i))))
)
),
utfbytes,LAMBDA(s,
LET(
n,LEN(s),
idx,SEQUENCE(1,n),
DROP(
REDUCE(HSTACK(0),idx,
LAMBDA(acc,i,
LET(cp,UNICODE(MID(s,i,1)),
seg,IF(cp<128,
HSTACK(cp),
IF(cp<2048,
HSTACK(
BITOR(192,BITRSHIFT(cp,6)),
BITOR(128,BITAND(cp,63))
),
IF(cp<65536,
HSTACK(
BITOR(224,BITRSHIFT(cp,12)),
BITOR(128,BITAND(BITRSHIFT(cp,6),63)),
BITOR(128,BITAND(cp,63))
),
HSTACK(
BITOR(240,BITRSHIFT(cp,18)),
BITOR(128,BITAND(BITRSHIFT(cp,12),63)),
BITOR(128,BITAND(BITRSHIFT(cp,6),63)),
BITOR(128,BITAND(cp,63))
)
)
)
),
HSTACK(acc,seg)
)
)
),
,1)
)
),
mixword,LAMBDA(v,
LET(
x,safe(v),
a,xorb(x,rotm(x,7)),
b,addm(a,2654435769),
c,xorb(b,rotm(b,11)),
xorb(c,rotm(c,13))
)
),
stepword,LAMBDA(state,byte,
LET(
aval,safe(INDEX(state,1,1)),
bval,safe(INDEX(state,2,1)),
cval,safe(INDEX(state,3,1)),
dval,safe(INDEX(state,4,1)),
ctr,INDEX(state,5,1),
rotone,5+MOD(ctr,27),
rottwo,9+MOD(ctr,23),
rotthr,13+MOD(ctr,19),
rotfor,17+MOD(ctr,15),
anew,addm(rotm(xorb(aval,byte),rotone),bval),
bnew,addm(rotm(xorb(bval,byte),rottwo),cval),
cnew,addm(rotm(xorb(cval,byte),rotthr),dval),
dnew,addm(rotm(xorb(dval,byte),rotfor),aval),
VSTACK(
safe(anew),
safe(bnew),
safe(cnew),
safe(dnew),
MOD(ctr+1,modu)
)
)
),
hashbytes,LAMBDA(bytes,domain,
LET(
header,HSTACK(72,51,50,domain),
seed,VSTACK(safe(2166136261),safe(36776121),safe(144066263),safe(374761393),0),
st,REDUCE(seed,HSTACK(header,bytes),stepword),
aval,INDEX(st,1,1),
bval,INDEX(st,2,1),
cval,INDEX(st,3,1),
dval,INDEX(st,4,1),
ha,mixword(xorb(aval,bval)),
hb,mixword(xorb(bval,cval)),
hc,mixword(xorb(cval,dval)),
hd,mixword(xorb(dval,aval)),
he,mixword(xorb(ha,hc)),
hf,mixword(xorb(hb,hd)),
HSTACK(
BITAND(BITRSHIFT(ha,24),255),BITAND(BITRSHIFT(ha,16),255),BITAND(BITRSHIFT(ha,8),255),BITAND(ha,255),
BITAND(BITRSHIFT(hb,24),255),BITAND(BITRSHIFT(hb,16),255),BITAND(BITRSHIFT(hb,8),255),BITAND(hb,255),
BITAND(BITRSHIFT(hc,24),255),BITAND(BITRSHIFT(hc,16),255),BITAND(BITRSHIFT(hc,8),255),BITAND(hc,255),
BITAND(BITRSHIFT(hd,24),255),BITAND(BITRSHIFT(hd,16),255),BITAND(BITRSHIFT(hd,8),255),BITAND(hd,255),
BITAND(BITRSHIFT(he,24),255),BITAND(BITRSHIFT(he,16),255),BITAND(BITRSHIFT(he,8),255),BITAND(he,255),
BITAND(BITRSHIFT(hf,24),255),BITAND(BITRSHIFT(hf,16),255),BITAND(BITRSHIFT(hf,8),255),BITAND(hf,255)
)
)
),
basecode,LAMBDA(bytes,
LET(
alpha,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
idx,SEQUENCE(1,32),
TEXTJOIN("",TRUE,
MAP(idx,
LAMBDA(i,
LET(
base,QUOTIENT(i-1,4)*3,
pos,MOD(i-1,4),
ba,IF(base+1<=24,INDEX(bytes,1,base+1),0),
bb,IF(base+2<=24,INDEX(bytes,1,base+2),0),
bc,IF(base+3<=24,INDEX(bytes,1,base+3),0),
blk,ba*65536+bb*256+bc,
val,CHOOSE(
pos+1,
BITRSHIFT(blk,18),
BITAND(BITRSHIFT(blk,12),63),
BITAND(BITRSHIFT(blk,6),63),
BITAND(blk,63)
),
MID(alpha,val+1,1)
)
)
)
)
)
),
msgbytes,utfbytes(input),
msglen,packword(COLUMNS(msgbytes)),
keybytes,IF(LEN(key)=0,repbyte(0,1),utfbytes(key)),
klen,COLUMNS(keybytes),
keyblock,IF(
klen=0,
repbyte(0,64),
IF(
klen>64,
HSTACK(hashbytes(keybytes,161),repbyte(0,64-24)),
HSTACK(keybytes,repbyte(0,64-klen))
)
),
ipad,repbyte(54,64),
opad,repbyte(92,64),
kip,xorrow(keyblock,ipad),
kop,xorrow(keyblock,opad),
inner,hashbytes(HSTACK(kip,msglen,msgbytes),17),
outer,hashbytes(HSTACK(kop,packword(24),inner),34),
basecode(outer)
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment