fscrypt/
direntry.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use siphasher::sip::SipHasher;
5use std::hash::Hasher;
6
7// We have to match the hash_code implementation used by f2fs to migrate direntry without
8// decrypting them.
9
10// See: https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
11fn tea(input: &[u32; 4], buf: &mut [u32; 2]) {
12    const DELTA: u32 = 0x9e3779b9;
13    let mut sum = 0u32;
14    let mut v = buf.clone();
15    for _ in 0..16 {
16        sum = sum.wrapping_add(DELTA);
17        v[0] = v[0].wrapping_add(
18            (v[1] << 4).wrapping_add(input[0])
19                ^ v[1].wrapping_add(sum)
20                ^ (v[1] >> 5).wrapping_add(input[1]),
21        );
22        v[1] = v[1].wrapping_add(
23            (v[0] << 4).wrapping_add(input[2])
24                ^ v[0].wrapping_add(sum)
25                ^ (v[0] >> 5).wrapping_add(input[3]),
26        );
27    }
28    buf[0] = buf[0].wrapping_add(v[0]);
29    buf[1] = buf[1].wrapping_add(v[1]);
30}
31
32fn str_hash(input: &[u8], padding: u32, out: &mut [u32; 4]) {
33    debug_assert!(input.len() <= out.len() * 4);
34    *out = [padding; 4];
35    let mut out_ix = 0;
36    let mut v = padding;
37    for i in 0..input.len() {
38        v = (input[i] as u32).wrapping_add(v << 8);
39        if i % 4 == 3 {
40            out[out_ix] = v;
41            out_ix += 1;
42            v = padding;
43        }
44    }
45    if out_ix < 4 {
46        out[out_ix] = v;
47    }
48}
49
50/// This is the function used unless both casefolding + encryption are enabled.
51pub fn tea_hash_filename(name: &[u8]) -> u32 {
52    let mut buf = [0x67452301, 0xefcdab89];
53    let mut len = name.len() as u32;
54    name.chunks(16).for_each(|chunk| {
55        let mut k = [0; 4];
56        let padding = len | (len << 8) | (len << 16) | (len << 24);
57        len -= 16;
58        str_hash(chunk, padding, &mut k);
59        tea(&k, &mut buf);
60    });
61    buf[0]
62}
63
64// A stronger hash function is used if casefold + FBE are used together.
65// Nb: If encryption is used without casefolding, the hash_code is based on the encrypted filename.
66pub fn casefold_encrypt_hash_filename(name: &[u8], dirhash_key: &[u8; 16]) -> u32 {
67    let mut hasher = SipHasher::new_with_key(dirhash_key);
68    hasher.write(name);
69    hasher.finish() as u32
70}