The Office password tables are a CRC
Microsoft Office’s old password code carries two tables of unexplained numbers, copied into LibreOffice, Apache POI and a dozen other projects. They are a standard 16-bit checksum, worked out in advance. A third table, inside Windows, is a 1990 journal table read in the wrong direction.
Type a password. Each letter switches on the entries of Microsoft’s table where its bits are 1, and the lit entries are XORed into a starting value from a second table. A textbook CRC-16 of the same letters gives the same 16-bit key.
Summary
- Question
- Office’s oldest password protection, documented by Microsoft as “XOR obfuscation”, turns a password into a 16-bit key with two hard-coded tables of 105 and 15 numbers. The same numbers give half of Word’s legacy password hash in the Office Open XML standard. Neither document says where they come from.
- Answer
- Every XorMatrix entry is a power of x modulo the CRC-16-CCITT polynomial, 0x1021, and every InitialCode entry is 0xFFFF times a power of x. The key is the CRC-16/CCITT-FALSE checksum (also catalogued as CRC-16/IBM-3740) of the password, with the top bit of each byte cleared. This rebuilds both tables in full and matches every published example.
- Second finding
- The 256-byte table in Windows’ HashData function, as reimplemented in Wine, is Table I of Peter Pearson’s 1990 paper on hashing, with rows and columns swapped. The table itself, and its link to HashData, were already public.
- How sure
- Both results are exact computations. Why Microsoft shipped the CRC as a table is not documented. We found no earlier explanation, and neither result affects security.
1The question
Word and Excel files from the 1990s could be protected with a password in a way Microsoft’s own documentation calls “XOR obfuscation”. The password is turned into a 16-bit key, the key and password are mixed into a 16-byte array, and that array is XORed over the file. When Microsoft published its file-format specifications in 2008, the procedure appeared as pseudocode in [MS-OFFCRYPTO] §2.3.7.1 At its centre are two tables of 16-bit numbers:
// 15 values InitialCode ← (0xE1F0, 0x1D0F, 0xCC9C, …) // 105 values XorMatrix ← (0xAEFC, 0x4DD9, 0x9BB2, …) XorKey ← InitialCode[length − 1] Element ← 0x68 for each character, last to first: repeat 7 times: if character AND 0x40: XorKey ← XorKey XOR XorMatrix[Element] character ← character × 2 Element ← Element − 1
The same 120 numbers reappear in the Office Open XML standard, ECMA-376 Part 4 §14.8.1, as the “initial code array” and “encryption matrix” of the legacy password hash Word uses for document protection.2 Neither document explains them. They have since been copied into LibreOffice, Apache OpenOffice, Apache POI, NPOI, docx4j, PHPWord and others: 42 files in 36 public repositories, about 15 independent projects once vendored copies are merged. Many cite the specification as their source. None explains how the values were made.3
Microsoft did not defend it either. Introducing the specification in 2008, Microsoft engineer David LeBlanc called it “the worst way to protect a document” and wrote that its designers “did all sorts of silly things to make this hard to figure out”.4
The tables sit in an international standard and in the code of LibreOffice, OpenOffice and Apache POI, and for 18 years after the specification was published no one recorded what they are. They are a CRC-16 built on the same polynomial as the checksums in modems and SD cards, with that polynomial printed in the table itself.
2The answer
Read XorMatrix seven entries at a time, as MS-OFFCRYPTO prints it. Within each group, every entry is the one before it doubled, and whenever doubling overflows 16 bits the result is XORed with 0x1021:
| Entry | Previous × 2 | Overflow? | Published |
|---|---|---|---|
| 0 | — | — | 0xAEFC |
| 1 | 0x15DF8 | yes: drop bit 16, XOR 0x1021 | 0x4DD9 |
| 2 | 0x9BB2 | no | 0x9BB2 |
| 3 | 0x13764 | yes | 0x2745 |
| 4 | 0x4E8A | no | 0x4E8A |
| 5 | 0x9D14 | no | 0x9D14 |
| 6 | 0x13A28 | yes | 0x2A09 |
0x1021 is the CRC-16-CCITT polynomial, x16 + x12 + x5 + 1, used in X.25, Bluetooth, XMODEM and SD cards. Doubling with that reduction is multiplication by x in the arithmetic CRCs are built on, and in that arithmetic every entry of both tables has a short closed form:
P(x) = x16 + x12 + x5 + 1 // 0x1021 // r = 0…14, c = 0…6 XorMatrix[7r + c] = x128 − 8r + c mod P(x) // L = 1…15 InitialCode[L − 1] = 0xFFFF · x8L mod P(x)
Microsoft’s table even prints the polynomial: the last group of XorMatrix, the one every password’s final letter uses, begins with 0x1021 itself, which is x16 reduced once. Each earlier group starts eight powers higher, one byte’s worth of shifting, and holds seven entries because only the low seven bits of each character are used. InitialCode is the state of a CRC that starts at 0xFFFF after one zero byte per character.
So the procedure in Section 1 is a standard checksum. The XOR key is CRC-16/CCITT-FALSE of the password: polynomial 0x1021, initial value 0xFFFF, no bit reflection and no final XOR, run over the characters from first to last with the top bit of each cleared. The tables are that CRC unrolled into a lookup, with the characters taken last to first and the bits from the top. CRC catalogues list this variant as CRC-16/IBM-3740 as well as CRC-16/CCITT-FALSE.
3Verification
Algorithm 1 rebuilds all 105 entries of XorMatrix and all 15 of InitialCode exactly, from the polynomial and the start value 0xFFFF. The specification’s procedure and a textbook bit-by-bit CRC-16/CCITT-FALSE give the same key on 10,000 random passwords of printable characters and 10,000 of arbitrary bytes, from 1 to 15 long.
A 2008 post on Microsoft’s developer blogs by Eric White printed five sample outputs of code written from the Office Open XML specification, whose hash uses these tables for its high 16 bits; one example, “Example”, is also the worked example in ECMA-376 Part 4 §14.8.1.5 The standard first cuts the password to 15 characters and turns each into one byte: its low byte, or its high byte if the low byte is zero. A CRC of those bytes gives the published value in all five, including passwords in Cyrillic and Japanese:
| Password | Published | CRC |
|---|---|---|
| Example | 64CE | 64CE |
| фис | D928 | D928 |
| サインイン | C684 | C684 |
| Ābc | CA21 | CA21 |
| VeryVeryLongPassword (cut to 15) | B5CC | B5CC |
For a given password length, both procedures are affine over GF(2): a start value XORed with one fixed contribution per set bit. So matching the table entries and start values already makes them the same function, and the random tests are a sanity check. A second implementation, written independently from the published tables and a one-paragraph statement of the claim, confirmed both closed forms and agreed on every one of more than 10,000 test passwords.a The script that accompanies this note checks everything above in under a second.
4A second table: Windows HashData
Windows exports a small function, HashData, that hashes a buffer to an output of up to 256 bytes.6 Wine’s reimplementation shows that it is a form of Pearson hashing, the method Peter Pearson of Lawrence Livermore National Laboratory published in Communications of the ACM in 1990: each byte is XORed into the hash and looked up in a fixed permutation of 0 to 255.7,8 That much was noticed before. A 2021 Stack Overflow answer called HashData “apparently a variant” of a Pearson-hash routine in a 1995 library archived at Rensselaer Polytechnic Institute (RPI).9
The table itself is Pearson’s, at least as Wine reproduces it; Wine’s test suite checks HashData’s output against Windows. His paper prints the permutation used in its tests as a 16 × 16 grid, Table I, “Pseudorandom Permutation of the Integers 0 through 255”. The HashData table is that grid read down the columns instead of across the rows, as Figure 3 shows.
This figure requires JavaScript. The reproduction script performs the same comparison.
The paper means its grids to be read across the rows. Its Table II, a second permutation, is said to hash 31 common English words to 1 through 31 in alphabetical order. Read row by row it does, for all 31; read down the columns it gets none of them right.
Microsoft was not alone in the swap. The RPI routine the Stack Overflow answer pointed to, SymT_hash.c (revision dated 15 September 1995), carries the same transposed table, all 256 entries, under a comment saying it “uses the permutation printed with this article”.10 So the table and its link to HashData were already public. What had not been written down, as far as we found, is that it is Pearson’s Table I transposed. Whether Microsoft and the RPI author misread the paper independently or copied a common source is not known. How Pearson made his own permutation is not stated either; the paper says only that he experimented with randomly generated ones.
5The tables over the years
- 1990Peter Pearson publishes his hashing method in Communications of the ACM, with a 16 × 16 permutation as Table I.7
- 1995A library at RPI carries Pearson’s table, read down the columns, “the permutation printed with this article”.10
- Late 1990sWord and Excel protect documents with XOR obfuscation, its key built from XorMatrix and InitialCode.
- by 2000Windows ships HashData with the transposed Pearson table.6
- 2008
- 2008–26LibreOffice, Apache POI and about a dozen other projects reproduce the 120 numbers verbatim, without explanation.3
- 2021A Stack Overflow answer links HashData to the 1995 RPI routine.9
6Discussion
Established by computation: the two closed forms in Algorithm 1; that the specification’s key procedure equals CRC-16/CCITT-FALSE over the 7-bit password bytes; agreement with Microsoft’s published examples; the transposition relating HashData’s table to Pearson’s Table I; that row by row is the paper’s intended reading; and that the 1995 RPI routine carries the same transposed table.
Not established: why the CRC was shipped as a table rather than computed. LeBlanc’s remark about making the scheme hard to figure out suggests it was deliberate, but that is inference. A table of per-bit masks is also a fast way to compute a CRC, and the specification may just transcribe the code that shipped. Also not established: how Pearson generated his table, and whether Microsoft’s transposed copy and RPI’s share a source.
There is an irony in LeBlanc’s post. Of the other half of the scheme, the 16-bit password verifier, he wrote: “We’d have been much better off just taking a CRC16 of the password.”4 The key derivation already was a CRC-16.
Neither result affects security. XOR obfuscation has been documented as insecure since the specification was published, and HashData is not a security function.
The search for an earlier explanation covered the [MS-OFFCRYPTO] and ECMA-376 texts, the OpenOffice.org Excel file-format documentation, 108 messages on the Apache POI mailing lists, the commit histories of LibreOffice, Apache OpenOffice, Apache POI and Gnumeric, every public copy of the tables we found, Stack Overflow, Cryptography Stack Exchange, password-recovery vendors’ documentation and general web search. It included a 2007 Aspose blog post that Apache POI’s code cites next to these functions; the post is about Word 2007’s newer hash and mentions CRCs only as a general analogy.11 A separate, independent search in October 2026 also found none. A current open-source toolkit documents the scheme in detail and prints the tables, including the 0x1021 entry, without connecting them to a CRC.12 Not checked: the LibreOffice developer mailing-list archive, which cannot be searched directly. Corrections are welcome and will be credited here.
7A note on method
These results came from the same experiment as the earlier note on RC2’s π table: AI agents working through lookup tables that ship without an explanation, where a proposed answer can be checked exactly.13 The agent, Claude, pulled hard-coded tables from Wine and other open-source reimplementations of Microsoft software and tested each against known constructions. The CRC turned up in a test for entries that double under XOR; the HashData match came from comparing every collected table with every other, transposed and inverted. OpenAI’s Codex wrote the independent check in Section 3.
Notes
- a.The second implementation used its own CRC routine and confirmed the standard CRC-16/CCITT-FALSE check value, 0x29B1 for the string “123456789”.
- b.A smaller curiosity: the 15-byte padding array in the same section of [MS-OFFCRYPTO],
BB FF FF BA FF FF B9 80 00 BE 0F 00 BF 0F 00, decodes as five 16-bit x86 instructions (mov bx,0FFFFh; mov dx,0FFFFh; mov cx,80h; mov si,0Fh; mov di,0Fh). The decoding is exact; that the bytes were lifted from code is a guess.
References
- [1]Microsoft. [MS-OFFCRYPTO]: Office Document Cryptography Structure, §2.3.7.1–2.3.7.4 (XOR obfuscation; CreateXorKey_Method1), version of 17 February 2026.
- [2]Ecma International. ECMA-376 Office Open XML File Formats, Part 4: Transitional Migration Features, 5th edition, December 2016, §14.8.1 “Legacy Password Hash Algorithm”.
- [3]Public copies of the tables located by searching for their first values: e.g. LibreOffice
comphelper/source/misc/docpasswordhelper.cxx, Apache POICryptoFunctions.java, NPOI, docx4j, PHPOffice, Xceed DocX, ExcelDataReader (42 files in 36 repositories, 18 October 2026); also msoffcrypto-tool. - [4]D. LeBlanc. Office Crypto Follies. Microsoft (MSDN blogs archive), 3 July 2008.
- [5]E. White (code by B. McClellan). The Legacy Hashing Algorithm in Open XML. Microsoft (MSDN blogs archive), 22 February 2008.
- [6]Microsoft. HashData function (shlwapi.h); UrlHashA function.
- [7]P. K. Pearson. Fast Hashing of Variable-Length Text Strings. Communications of the ACM 33(6):677–680, June 1990. doi:10.1145/78973.78978.
- [8]Wine.
dlls/kernelbase/path.c,hashdata_lookupand HashData (accessed 18 October 2026). - [9]jimhark. Answer to “What hashing algorithm is HashData in Shlwapi.dll based on?”. Stack Overflow, 22 August 2021.
- [10]RPI rlt-portlib,
lib/SymT/SymT_hash.c(RCS revision 1.1, 15 September 1995), as packed in the library’s shell archive Part02 (Internet Archive copy of 5 November 2004). The file’s own URL, cited in [9], was not archived. - [11]V. Averkin. Funny: How the new powerful cryptography implemented in Word 2007 turns it into a perfect tool for document password removal. Aspose blogs, 20 August 2007 (Internet Archive copy of 17 July 2014).
- [12]StrongWind1. Office Password Toolkit: XOR obfuscation. strongwind.dev (2025–26; accessed 19 October 2026).
- [13]K. Hamadeh. How RC2’s π table was made. 18 October 2026. khalidhamadeh.com/writing/rc2-pi-table
Citing this note
Hamadeh, K. (2026). The Office password tables are a CRC. https://khalidhamadeh.com/writing/office-password-crc