File size: 6,033 Bytes
6900478
 
42d913f
6900478
42d913f
 
 
 
 
 
 
 
 
 
 
 
 
 
6900478
 
 
 
 
42d913f
 
6900478
42d913f
6900478
 
 
42d913f
6900478
42d913f
6900478
42d913f
6900478
 
 
 
 
 
 
 
 
 
 
 
 
 
42d913f
 
 
6900478
 
 
 
42d913f
6900478
42d913f
 
6900478
42d913f
6900478
42d913f
 
6900478
 
 
 
 
 
42d913f
6900478
 
 
 
 
 
 
 
 
 
42d913f
 
 
 
6900478
 
42d913f
 
6900478
 
 
 
42d913f
6900478
 
 
 
 
 
 
42d913f
6900478
 
 
 
 
 
42d913f
 
6900478
 
 
 
 
42d913f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6900478
42d913f
 
 
 
6900478
 
 
 
42d913f
 
6900478
 
 
 
42d913f
6900478
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#![no_std]
#![forbid(unsafe_code)]
//! BEHCS-1024 60-glyph hash-derived coordinate: Federation-1024's own `project_60d` and `sha256`.
//!
//! The two upstream functions are included byte-identical, so every tuple maps exactly as upstream.
//! A coordinate is an address, not source recovery.

/// The upstream source, byte-identical (federation-1024@dc1da1a9, `kernel/boot/src/hwinv.rs`).
mod hwinv {
    include!("upstream/hwinv-tuple-cap.rs");
    include!("upstream/hwinv-sha256-project-60d.rs");

    pub(crate) const UPSTREAM_TUPLE_CAP: usize = TUPLE_CAP;

    pub(crate) fn project(tuple: &[u8]) -> [u16; 60] {
        project_60d(tuple)
    }
}

/// Number of 10-bit glyph coordinates.
pub const DIMENSIONS: usize = 60;
/// Symbol indices range from 0 through 1023 inclusive.
pub const ALPHABET_SIZE: usize = 1024;
/// Upstream's `TUPLE_CAP`: "Input is bounded by `TUPLE_CAP`" (hwinv.rs:148).
pub const TUPLE_CAP: usize = hwinv::UPSTREAM_TUPLE_CAP;

/// 60 dimensions plus the digests upstream derives from them, stored without heap allocation.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Projection {
    pub glyphs: [u16; DIMENSIONS],
    /// Upstream SHA-256 of the 120 coordinate bytes (`device_pid_full` upstream).
    pub coordinate_sha256: [u8; 32],
    /// First eight bytes of `coordinate_sha256` (`device_pid` upstream); a short handle, not a signer.
    pub host8: [u8; 8],
    /// Upstream `sha256` of the tuple (`hw_tuple_pid_full` upstream).
    pub source_sha256: [u8; 32],
}

impl Projection {
    /// Canonical 60 * 2 bytes in big-endian glyph order.
    pub fn coordinate_bytes(&self) -> [u8; 120] {
        let mut bytes = [0u8; 120];
        for (i, glyph) in self.glyphs.iter().enumerate() {
            bytes[i * 2..i * 2 + 2].copy_from_slice(&glyph.to_be_bytes());
        }
        bytes
    }
}

/// Upstream's projection plus the digests upstream's `emit_device_pid` derives from it.
pub fn project_60d(tuple: &[u8]) -> Projection {
    let glyphs = hwinv::project(tuple);
    let mut coord_bytes = [0u8; 120];
    for (index, glyph) in glyphs.iter().enumerate() {
        coord_bytes[index * 2..index * 2 + 2].copy_from_slice(&glyph.to_be_bytes());
    }
    let coordinate_sha256 = hwinv::sha256(&coord_bytes);
    let mut host8 = [0u8; 8];
    host8.copy_from_slice(&coordinate_sha256[..8]);
    Projection {
        glyphs,
        coordinate_sha256,
        host8,
        source_sha256: hwinv::sha256(tuple),
    }
}

#[cfg(test)]
mod tests {
    extern crate std;
    use self::std::fmt::Write as _;
    use self::std::vec::Vec;
    use super::*;

    fn hex(bytes: &[u8]) -> std::string::String {
        let mut out = std::string::String::with_capacity(bytes.len() * 2);
        for b in bytes {
            write!(&mut out, "{b:02x}").expect("in-memory write");
        }
        out
    }

    fn pattern(len: usize) -> Vec<u8> {
        (0..len).map(|i| (i % 256) as u8).collect()
    }

    #[test]
    fn sha256_standard_vectors() {
        assert_eq!(hex(&hwinv::sha256(b"")), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
        assert_eq!(hex(&hwinv::sha256(b"abc")), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
    }

    #[test]
    fn independent_empty_reference_vector() {
        let p = project_60d(b"");
        assert_eq!(&p.glyphs[..10], &[831, 408, 169, 987, 87, 301, 61, 840, 631, 732]);
        assert_eq!(hex(&p.coordinate_sha256), "a93505a95d0aace1fb6edc8b2e8d94581fdb49cdb0f961097a6d9ac9cc0102e7");
        assert_eq!(&p.host8, &p.coordinate_sha256[..8]);
    }

    #[test]
    fn independent_abc_reference_vector() {
        let p = project_60d(b"abc");
        assert_eq!(&p.glyphs[..10], &[813, 428, 103, 701, 206, 505, 788, 597, 191, 714]);
        assert_eq!(hex(&p.coordinate_sha256), "ff504d90899902cd2ec8e0c075553f65c3116a8f6c1ad3fee6907f00edaff4d6");
        assert_eq!(hex(&p.source_sha256), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
    }

    #[test]
    fn upstream_vector_508() {
        let p = project_60d(&pattern(508));
        assert_eq!(&p.glyphs[..10], &[902, 3, 689, 384, 304, 662, 46, 568, 144, 1004]);
        assert_eq!(hex(&p.coordinate_sha256), "d9c002d12a4b667b1f4f8e351e38f35d4c731e566ebd51a09e367e23470dd943");
    }

    #[test]
    fn upstream_vectors_509_to_512() {
        // Vectors from independent_referee.py's model of upstream.
        let expected = [
            (509, [189, 526, 380, 507], "c8e0925819849fc411afe4c452e5da2c615949beaff0387114f93991604bb344"),
            (510, [539, 52, 514, 404], "79a25f0c2fac953137c230cc5ee89d46207931efeaa62639906cb468ea71df1a"),
            (511, [876, 847, 583, 217], "9bb6c8d92f63e1121fce300d6af9c9b7016bad1e4ac2f81d30e98e2be131290e"),
            (512, [256, 476, 545, 523], "f0e02e6b285e3ad7d6d2414da9153f1f6a7f3668f2406dd7bcf59f1bb8e5a333"),
        ];
        for (len, head, coordinate) in expected {
            let tuple = pattern(len);
            let p = project_60d(&tuple);
            assert_eq!(&p.glyphs[..4], &head, "len {len}");
            assert_eq!(hex(&p.coordinate_sha256), coordinate, "len {len}");
        }
    }

    #[test]
    fn upstream_vectors_beyond_tuple_cap() {
        let at_cap = project_60d(&pattern(TUPLE_CAP));
        for len in [513, 600, 1024] {
            assert_eq!(project_60d(&pattern(len)), at_cap, "len {len}");
        }
        assert_eq!(
            hex(&at_cap.source_sha256),
            "110009dcee21620b166f3abfecb5eff7a873be729d1c2d53822e7acc5f34eb9b"
        );
    }

    #[test]
    fn deterministic_and_bounded_coordinates() {
        let first = project_60d(b"bounded-test");
        let second = project_60d(b"bounded-test");
        assert_eq!(first, second);
        assert!(first.glyphs.iter().all(|g| usize::from(*g) < ALPHABET_SIZE));
        assert_eq!(first.coordinate_bytes().len(), DIMENSIONS * 2);
        assert_ne!(first.source_sha256, first.coordinate_sha256);
        assert_ne!(first, project_60d(b"bounded-tesu"));
    }
}