Outlook PST Password (CRC-32 Verifier)¶
⚪ Advisory Only — UI-level access gate with no cryptographic protection; the CRC-32 verifier encrypts nothing and is trivially bypassed
Overview¶
- Optional password gate on Outlook Personal Folders files (
.pst) — Outlook prompts for a password before mounting the store, but the data remains fully readable without it - Stores a single CRC-32 checksum of the password text as
PidTagPstPassword(0x67FF0003) on the message store node; the password is never used as cipher key material - No salt, no iteration, no pre-image resistance — the 32-bit CRC is prone to collisions and trivially brute-forced per the spec's own security assessment ([MS-PST] section 4.2)
- Identical algorithm across both PST variants: ANSI (
wVer14-15, Outlook 97-2002) and Unicode (wVer>= 23, Outlook 2003+) - Used by: Outlook PST
- Spec: [MS-PST] sections 2.4.3.3, 4.2, 5.3
KDF Pipeline¶
flowchart LR
A["Password Text\n(encoding unspecified)"] --> B["CRC-32\nseed = 0x00000000\npolynomial 0xEDB88320\nno pre/post XOR"]
B --> C["32-bit Verifier\nstored as PtypInteger32"]
No key is derived. No cipher key is produced. The CRC-32 output is stored directly as PidTagPstPassword and used only for a UI-level equality check.
Verification Flow¶
flowchart TD
A["Read PidTagPstPassword\nfrom NID_MESSAGE_STORE PC"] --> B{"Value == 0\nor absent?"}
B -->|"Yes"| C["No password set\nGrant access"]
B -->|"No"| D["Prompt user\nfor password"]
D --> E["CRC-32(candidate_bytes)\nseed = 0"]
E --> F{"candidate_crc\n==\nstored value?"}
F -->|"Yes"| G["Password accepted"]
F -->|"No"| H["Password rejected"]
On-Disk Structure¶
flowchart LR
subgraph PST["PST File"]
direction TB
HDR["HEADER (offset 0)\ndwMagic = !BDN\nwVer, bCryptMethod\nROOT (contains bRefNBT)"]
NBT["Node BTree\n(root page at bRefNBT)"]
HDR --> NBT
end
subgraph MS["NID_MESSAGE_STORE (0x21)"]
direction TB
PC["Property Context\n(Heap-on-Node + BTH)"]
PROPS["Properties:\nPidTagRecordKey\nPidTagDisplayName\n..."]
PWD["PidTagPstPassword\nTag: 0x67FF0003\nType: PtypInteger32\nValue: CRC-32 or 0"]
PC --> PROPS
PC --> PWD
end
NBT --> PC
Quick Reference¶
| Parameter | Value | Notes |
|---|---|---|
| Algorithm | CRC-32 (IEEE 802.3 reflected) | Polynomial 0xEDB88320 |
| Seed | 0x00000000 |
No pre-inversion |
| Final XOR | None | No post-inversion |
| Salt | None | — |
| Iterations | 1 | Single CRC pass |
| Output | 32 bits | PtypInteger32 DWORD |
| Key derivation | None | No cipher key produced |
| Property tag | 0x67FF0003 |
PidTagPstPassword |
| Store node | NID_MESSAGE_STORE (0x21) |
Property Context lookup |
| Encoding | Unspecified by spec | See section 5.4 below |
Extraction Target¶
Fields to extract from the PST file:
| Field | Source Location | Type | Example Value |
|---|---|---|---|
| PST version | HEADER.wVer |
WORD | 23 (Unicode PST) |
| Crypt method | HEADER.bCryptMethod |
BYTE | 0x01 (Permutative) |
| Password verifier | PidTagPstPassword in NID_MESSAGE_STORE PC |
DWORD (LE) | 0x3D7512A9 |
| No-password sentinel | Same property, value 0 or absent |
DWORD | 0x00000000 |
Python function signatures:
def compute_pst_crc32(password_bytes: bytes) -> int:
"""Compute CRC-32 verifier per [MS-PST] section 5.3.
Uses seed=0, polynomial 0xEDB88320, no pre/post XOR.
NOT equivalent to zlib.crc32() which applies 0xFFFFFFFF pre/post.
"""
...
def check_pst_password(password_bytes: bytes, stored_value: int) -> bool:
"""Check candidate password against stored PidTagPstPassword.
Returns True if stored_value is 0 (no password) or if
compute_pst_crc32(password_bytes) == stored_value.
Per [MS-PST] section 2.4.3.3.
"""
...
1. What It Is and Where It Is Used¶
- The PST password is an optional advisory gate on Microsoft Outlook Personal Folders files (
.pst); when present, Outlook prompts the user for a password before mounting the store - The spec is unambiguous that this provides zero data protection: per [MS-PST] section 2.4.3.3, "the PST password is just implemented at the UI level, meaning that the password is only required to gain access of the PST through the UI. The password itself is not used to secure the PST data in any way."
- The mechanism stores a single CRC-32 hash of the password text as a 32-bit integer property (
PidTagPstPassword, property tag0x67FF0003) on the message store node (NID_MESSAGE_STORE, NID0x21) - The password itself is never stored, and the CRC-32 value is never used as a cipher key or key-derivation input
- This verifier applies to both PST container variants: ANSI (Outlook 97-2002,
wVer14-15) and Unicode (Outlook 2003+,wVer>= 23); both use the identical CRC-32 algorithm from [MS-PST] section 5.3 with seed zero - The ANSI/Unicode distinction affects only container-level field widths (BIDs, IBs, ROOT structure size), not the password verifier computation
- PST content-level obfuscation (the body cipher controlled by
HEADER.bCryptMethod) is a completely independent mechanism that does not take any password input; it is documented separately in ../non-password/pst-body-cipher.md - File-format context is in ../../formats/outlook.md
Spec sections: [MS-PST] sections 2.4.3.3 (PST Password Security), 4.2 (Strength of PST Password), 5.3 (CRC Calculation), 2.4.3 and 2.4.3.1 (Message Store), 2.2.2.6 (HEADER structure).
2. Profiles / Parameters¶
There is a single algorithm profile. The ANSI and Unicode PST variants differ only in their container-level structures, not in the verifier computation or interpretation.
| Parameter | Value | Spec Reference |
|---|---|---|
| Algorithm | CRC-32 (IEEE 802.3 reflected polynomial 0xEDB88320) |
[MS-PST] section 5.3 |
| Seed (initial CRC value) | 0x00000000 |
[MS-PST] section 5.3 ("MUST be zero in the context of this document") |
| Pre-inversion | None (seed is 0, not 0xFFFFFFFF) |
[MS-PST] section 5.3 (ComputeCRC code) |
| Post-inversion (final XOR) | None | [MS-PST] section 5.3 (ComputeCRC code) |
| Salt | None | [MS-PST] section 2.4.3.3 |
| Iterations | 1 (single CRC pass) | [MS-PST] section 2.4.3.3 |
| Output size | 32 bits (DWORD, stored as PtypInteger32) |
[MS-PST] section 2.1.2, [MS-OXPROPS] |
| Key derivation | None; no cipher key is derived from the password | [MS-PST] section 2.4.3.3, section 4.2 |
| Property tag | 0x67FF0003 (PidTagPstPassword, PtypInteger32) |
[MS-PST] section 2.1.2 |
| Store location | PC of NID_MESSAGE_STORE (0x21) |
[MS-PST] section 2.4.3.3, section 2.4.1 |
ANSI PST (wVer 14-15) |
Same CRC-32 computation | [MS-PST] section 2.2.2.6 |
Unicode PST (wVer >= 23) |
Same CRC-32 computation | [MS-PST] section 2.2.2.6 |
Important: The CRC-32 as implemented by [MS-PST] section 5.3 does NOT apply the standard pre/post XOR with 0xFFFFFFFF that is conventional in zlib/gzip CRC-32. The seed is zero and the return value is the raw CRC accumulator with no final complement. This means the output differs from what zlib.crc32() or similar standard library functions produce for the same input.
3. Byte Layout / On-Disk Structure¶
3.1 PidTagPstPassword Property¶
The password verifier is a single tagged property on the message store node. It is stored in the Property Context (PC) of NID_MESSAGE_STORE, not in an OLE stream or HEADER field.
| Field | Value | Type | Size | Meaning |
|---|---|---|---|---|
| Property ID | 0x67FF |
WORD | 2 bytes | PidTagPstPassword identifier |
| Property Type | 0x0003 |
WORD | 2 bytes | PtypInteger32 |
| Full property tag | 0x67FF0003 |
DWORD | 4 bytes | Combined tag for BTH key lookup |
| Property value | CRC-32(password_bytes) or 0x00000000 |
DWORD (little-endian) | 4 bytes | The stored verifier; 0 means no password |
Per [MS-PST] section 2.4.3.3: "if the property exists and is nonzero, implementations SHOULD prompt the end user for a password, compute the CRC-32 hash of the user password, and verify it against the value stored in PidTagPstPassword. Implementations MUST enforce the PST Password check if a nonzero value for PidTagPstPassword is set in the message store."
A value of 0x00000000 means no password is set. The property may also be absent entirely, which is equivalent to "no password."
3.2 Message Store Location¶
The message store PC is reached via the NDB layer. Its location chain is:
- HEADER (file offset 0) — contains the ROOT structure ([MS-PST] section 2.2.2.6)
- ROOT — contains
bRefNBT(BREF pointing to the root page of the Node BTree) ([MS-PST] section 2.2.2.5) - NBT (Node BTree) — locate the entry with NID =
NID_MESSAGE_STORE(0x21) ([MS-PST] section 2.4.1) - NBTENTRY for
0x21— containsbidData(BID of the data block) andbidSub(BID of subnode BTree) ([MS-PST] section 2.2.2.7.7.4) - Data block — contains the HN (Heap-on-Node) with the PC's BTHHEADER, which indexes all properties including
0x67FF0003([MS-PST] section 2.3.3)
3.3 HEADER Structure Context (bCryptMethod)¶
The HEADER field bCryptMethod ([MS-PST] section 2.2.2.6) controls the body cipher for NDB data blocks, which is entirely independent of the PST password.
bCryptMethod Value |
Name | Meaning | Relevance to Password |
|---|---|---|---|
0x00 |
NDB_CRYPT_NONE |
Data blocks are not encoded | None; password CRC is unaffected |
0x01 |
NDB_CRYPT_PERMUTE |
Encoded with the Permutative algorithm ([MS-PST] section 5.1) | None; password CRC is unaffected |
0x02 |
NDB_CRYPT_CYCLIC |
Encoded with the Cyclic algorithm ([MS-PST] section 5.2) | None; password CRC is unaffected |
0x10 |
NDB_CRYPT_EDPCRYPTED |
Encrypted with Windows Information Protection | None; password CRC is unaffected |
The body cipher is documented in ../non-password/pst-body-cipher.md.
3.4 Message Store Required Properties¶
Per [MS-PST] section 2.4.3.1, the following properties MUST be present in any valid message store PC. PidTagPstPassword is NOT in this required set; it is optional.
| Property ID | Property Type | Friendly Name | Description |
|---|---|---|---|
0x0FF9 |
PtypBinary |
PidTagRecordKey |
Record key (Provider UID of this PST) |
0x3001 |
PtypString |
PidTagDisplayName |
Display name of PST |
0x35E0 |
PtypBinary |
PidTagIpmSubTreeEntryId |
EntryID of the Root Mailbox Folder object |
0x35E3 |
PtypBinary |
PidTagIpmWastebasketEntryId |
EntryID of the Deleted Items Folder object |
0x35E7 |
PtypBinary |
PidTagFinderEntryId |
EntryID of the search Folder object |
The full property tag table including PidTagPstPassword (0x67FF, PtypInteger32) is in [MS-PST] section 2.1.2.
4. Key Derivation¶
There is no key derivation. No cipher key is produced, no salt is used, and no iteration occurs. The CRC-32 is a one-pass checksum of the password text, stored as a 32-bit integer. The password plays no role in any encryption or decryption operation within the PST file.
5. CRC-32 Computation — Ordered Steps with Pseudocode¶
The CRC-32 algorithm used for the password verifier is the same ComputeCRC function defined in [MS-PST] section 5.3, which is also used for all other CRC checksums in the PST format (page trailer CRCs in PAGETRAILER.dwCRC, block trailer CRCs in BLOCKTRAILER.dwCRC, and HEADER CRCs dwCRCPartial / dwCRCFull).
5.1 Algorithm Steps¶
- Accept the password text as a byte sequence
pvof lengthcbLength. (See section 5.3 below for encoding considerations.) - Initialize the CRC accumulator
dwCRCto0(the seed value, which [MS-PST] section 5.3 states "MUST be zero in the context of this document"). - For each byte in the input, update the CRC using the reflected table-driven algorithm.
- The final value of
dwCRCis the verifier. Store it (or compare it) as aPtypInteger32inPidTagPstPassword.
5.2 Pseudocode — Simplified Byte-at-a-Time Form¶
The following is the essential algorithm extracted from [MS-PST] section 5.3. The full spec implementation includes an 8-byte-at-a-time slicing optimization using eight lookup tables (CrcTableOffset32 through CrcTableOffset88), but for password-length inputs the byte-at-a-time loop is functionally equivalent and sufficient.
FUNCTION ComputeCRC(dwCRC, pv, cbLength):
// [MS-PST] §5.3
// dwCRC: initial seed, MUST be 0 for all uses in this document
// pv: pointer to the data buffer
// cbLength: number of bytes to process
FOR i = 0 TO cbLength - 1:
dwCRC = CrcTableOffset32[(dwCRC XOR pv[i]) AND 0x000000FF] XOR (dwCRC >> 8)
RETURN dwCRC
5.3 Pseudocode — Full Spec Implementation with Slicing-by-8 Optimization¶
The complete ComputeCRC function from [MS-PST] section 5.3 processes 8 aligned bytes per iteration using eight pre-computed lookup tables (CrcTableOffset32 through CrcTableOffset88). This is the canonical form that implementations SHOULD use for performance on bulk data (page CRCs, block CRCs). For password verification, inputs are short enough that only the byte-at-a-time paths execute.
FUNCTION ComputeCRC(dwCRC, pv, cbLength):
// [MS-PST] §5.3 -- full slicing-by-8 implementation
pbBuffer = pv
// Compute alignment parameters
cbAlignedOffset = (cbLength < 4) ? 0 : ((DWORD_PTR)pv MOD 4)
cbInitialUnalignedBytes = (cbAlignedOffset == 0) ? 0 : (4 - cbAlignedOffset)
cbRunningLength = (cbLength < 4) ? 0 : (((cbLength - cbInitialUnalignedBytes) / 8) * 8)
cbEndUnalignedBytes = cbLength - cbInitialUnalignedBytes - cbRunningLength
// Process initial unaligned bytes (byte-at-a-time)
FOR i = 0 TO cbInitialUnalignedBytes - 1:
dwCRC = CrcTableOffset32[(dwCRC XOR pbBuffer[0]) AND 0xFF] XOR (dwCRC >> 8)
pbBuffer = pbBuffer + 1
// Process aligned 8-byte chunks (slicing-by-8)
FOR i = 0 TO (cbRunningLength / 8) - 1:
dwCRC = dwCRC XOR *(DWORD*)pbBuffer
dwCRC = CrcTableOffset88[ dwCRC AND 0xFF] XOR
CrcTableOffset80[(dwCRC >> 8) AND 0xFF] XOR
CrcTableOffset72[(dwCRC >> 16) AND 0xFF] XOR
CrcTableOffset64[(dwCRC >> 24) AND 0xFF]
pbBuffer = pbBuffer + 4
dw2nd32 = *(DWORD*)pbBuffer
dwCRC = dwCRC XOR
CrcTableOffset56[ dw2nd32 AND 0xFF] XOR
CrcTableOffset48[(dw2nd32 >> 8) AND 0xFF] XOR
CrcTableOffset40[(dw2nd32 >> 16) AND 0xFF] XOR
CrcTableOffset32[(dw2nd32 >> 24) AND 0xFF]
pbBuffer = pbBuffer + 4
// Process remaining unaligned bytes (byte-at-a-time)
FOR i = 0 TO cbEndUnalignedBytes - 1:
dwCRC = CrcTableOffset32[(dwCRC XOR pbBuffer[0]) AND 0xFF] XOR (dwCRC >> 8)
pbBuffer = pbBuffer + 1
RETURN dwCRC
5.4 Password Encoding¶
UNCONFIRMED by the spec. [MS-PST] section 2.4.3.3 refers only to "the password text" and "the CRC-32 hash of the password text" without specifying the character encoding used to convert the password string to bytes before CRC computation. The spec does not state whether the password is encoded as ANSI/MBCS (the system's active code page), UTF-8, UTF-16LE, or some other encoding, nor whether a null terminator is included in the CRC input.
6. Verification Procedure — Pseudocode¶
The verification check is a direct 32-bit integer comparison. No decryption occurs.
FUNCTION VerifyPstPassword(candidate_password_bytes, cbLength, stored_value):
// [MS-PST] §2.4.3.3
// stored_value: PidTagPstPassword from the message store PC (NID 0x21)
IF stored_value == 0x00000000:
// No password set; grant access without prompting
RETURN ACCESS_GRANTED
candidate_crc = ComputeCRC(0, candidate_password_bytes, cbLength)
IF candidate_crc == stored_value:
RETURN ACCESS_GRANTED
ELSE:
RETURN ACCESS_DENIED
Per [MS-PST] section 2.4.3.3:
- If PidTagPstPassword does not exist or is zero, no password check is performed.
- If PidTagPstPassword exists and is nonzero, implementations SHOULD prompt for a password and MUST enforce the check.
7. Reference Constants¶
7.1 Property and Node Constants¶
| Constant | Value | Spec Reference |
|---|---|---|
PidTagPstPassword property ID |
0x67FF |
[MS-PST] section 2.1.2 |
PtypInteger32 property type |
0x0003 |
[MS-PST] section 2.1.1; [MS-OXCDATA] |
| Full property tag | 0x67FF0003 |
Combined |
NID_MESSAGE_STORE |
0x21 |
[MS-PST] section 2.4.1 |
| CRC-32 seed | 0x00000000 |
[MS-PST] section 5.3 |
| "No password" sentinel | 0x00000000 |
[MS-PST] section 2.4.3.3 |
7.2 CrcTableOffset32[256] — Complete Table¶
The primary CRC-32 lookup table from [MS-PST] section 5.3. This is the standard IEEE 802.3 reflected CRC-32 table generated from polynomial 0xEDB88320 (the bit-reversal of 0x04C11DB7). Reproduced verbatim from the spec.
CrcTableOffset32[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
Confirmation that this is the standard IEEE 802.3 reflected CRC-32 table: entry [0] = 0x00000000, entry [1] = 0x77073096, and the generator polynomial 0xEDB88320 appears at index 128 (CrcTableOffset32[128] = 0xEDB88320). The polynomial 0xEDB88320 is the bit-reversal of the IEEE 802.3 generator 0x04C11DB7.
7.3 Slicing-by-8 Acceleration Tables¶
The full ComputeCRC implementation in [MS-PST] section 5.3 defines seven additional 256-entry lookup tables for its 8-byte-at-a-time slicing optimization:
CrcTableOffset40[256]CrcTableOffset48[256]CrcTableOffset56[256]CrcTableOffset64[256]CrcTableOffset72[256]CrcTableOffset80[256]CrcTableOffset88[256]
These tables are derived deterministically from CrcTableOffset32 and the reflected polynomial 0xEDB88320. They are used together to process 8 bytes per loop iteration (see section 5.3 above). For password verification with short inputs, these tables are not exercised; only CrcTableOffset32 is used in the byte-at-a-time fallback path. The complete table values are given verbatim in [MS-PST] section 5.3 (pages 175-183 of the specification).
First entry of each table for cross-validation (all tables have [0] = 0x00000000):
| Table | Entry [1] | Spec Reference |
|---|---|---|
CrcTableOffset32 |
0x77073096 |
[MS-PST] section 5.3 |
CrcTableOffset40 |
0x191B3141 |
[MS-PST] section 5.3 |
CrcTableOffset48 |
0x01C26A37 |
[MS-PST] section 5.3 |
CrcTableOffset56 |
0xB8BC6765 |
[MS-PST] section 5.3 |
CrcTableOffset64 |
0x3D6029B0 |
[MS-PST] section 5.3 |
CrcTableOffset72 |
0xCB5CD3A5 |
[MS-PST] section 5.3 |
CrcTableOffset80 |
0xA6770BB4 |
[MS-PST] section 5.3 |
CrcTableOffset88 |
0xCCAA009E |
[MS-PST] section 5.3 |
7.4 HEADER Magic Values¶
For context when parsing PST files to locate the message store:
| Constant | Value | Spec Reference |
|---|---|---|
dwMagic |
{ 0x21, 0x42, 0x44, 0x4E } ("!BDN") |
[MS-PST] section 2.2.2.6 |
wMagicClient |
{ 0x53, 0x4D } ("SM") |
[MS-PST] section 2.2.2.6 |
bSentinel |
0x80 |
[MS-PST] section 2.2.2.6 |
wVer (ANSI) |
14 or 15 | [MS-PST] section 2.2.2.6 |
wVer (Unicode) |
>= 23 | [MS-PST] section 2.2.2.6 |
wVer (WIP-capable) |
37 | [MS-PST] section 2.2.2.6 |
bPlatformCreate |
0x01 |
[MS-PST] section 2.2.2.6 |
bPlatformAccess |
0x01 |
[MS-PST] section 2.2.2.6 |
7.5 Special NID Values¶
| Value | Name | Description | Spec Reference |
|---|---|---|---|
0x21 |
NID_MESSAGE_STORE |
Message store node | [MS-PST] section 2.4.1 |
0x61 |
NID_NAME_TO_ID_MAP |
Named Properties Map | [MS-PST] section 2.4.1 |
0x122 |
NID_ROOT_FOLDER |
Root Mailbox Folder object | [MS-PST] section 2.4.1 |
8. Security Considerations¶
The following security assessments are stated directly by the specification itself.
[MS-PST] section 4.2, "Strength of PST Password": "The PST Password, which is stored as a property value in the message store, is a superficial mechanism that requires the client implementation to enforce the stored password. Because the password itself is not used as a key to the encoding and decoding cipher algorithms, it does not provide any security benefit to preventing the PST data to be read by unauthorized parties."
[MS-PST] section 4.2 (continued): "Moreover, the password is stored as a CRC-32 hash of the original password string, which is prone to collisions and is relatively weak against a brute-force approach."
[MS-PST] section 2.4.3.3: "In practice, the PST password is just implemented at the UI level, meaning that the password is only required to gain access of the PST through the UI. The password itself is not used to secure the PST data in any way."
[MS-PST] section 4.1, "Strength of Encoded PST Data Blocks" (regarding the separate body cipher, not the password): "This file format specification uses two keyless cipher algorithms to encode the data blocks in the PST. These algorithms only provide data obfuscation and can be conveniently decoded once the exact encoding algorithm is understood." And: "the strength of the encoded PST data blocks provides no additional security beyond data obfuscation."
In summary:
- The PST password provides no data confidentiality
- The CRC-32 verifier is a 32-bit value with no salt, no iteration, and no pre-image resistance
- The body cipher (bCryptMethod) is a separate keyless obfuscation that also provides no meaningful security
- Neither mechanism uses the password as cryptographic key material
9. Spec References¶
| Section | Title | Content Relevant Here |
|---|---|---|
| [MS-PST] section 2.1.2 | Properties | Property tag table including PidTagPstPassword (0x67FF, PtypInteger32) |
| [MS-PST] section 2.2.2.5 | ROOT | ROOT structure containing NBT/BBT BREFs used to locate the message store |
| [MS-PST] section 2.2.2.6 | HEADER | PST file header with bCryptMethod, wVer, magic values, ROOT embed |
| [MS-PST] section 2.4.1 | Special Internal NIDs | NID_MESSAGE_STORE = 0x21 and other reserved NIDs |
| [MS-PST] section 2.4.3 | Message Store | Message store as a PC on NID_MESSAGE_STORE; "any valid PST MUST have exactly one message store node" |
| [MS-PST] section 2.4.3.1 | Minimum Set of Required Properties | Five required properties of a message store PC (PidTagPstPassword is not among them) |
| [MS-PST] section 2.4.3.3 | PST Password Security | UI-only password gate; CRC-32 of password text stored in PidTagPstPassword; enforcement requirements |
| [MS-PST] section 4.1 | Strength of Encoded PST Data Blocks | Body cipher provides only data obfuscation |
| [MS-PST] section 4.2 | Strength of PST Password | Password is "superficial"; CRC-32 is "prone to collisions and is relatively weak against a brute-force approach" |
| [MS-PST] section 5.3 | CRC Calculation | Full ComputeCRC algorithm with CrcTableOffset32 through CrcTableOffset88; seed MUST be zero |
Related documents:
- ../non-password/pst-body-cipher.md — PST NDB block cipher (
NDB_CRYPT_PERMUTE/NDB_CRYPT_CYCLIC), the independent keyless obfuscation layer - ../../formats/outlook.md — Outlook PST file format overview and container structure