Add a function to fingerprint flag/offset info.
Uses SipHasher implementation added to the project. Ideally the output
would remain consistent between builds (though the worst case of an
updated algorithm would be a string lookup for cross-container reads
only).
Sort the flags first so everything is always added to the hasher in the
same order - note this code is only run at build time, not runtime.
Next step is to write this fingerprint into the package.map file and
introduce a flag to guard this change.
Bug: 316357686
Test: atest aconfig.test
Change-Id: Ie5f34541d982dfa120ffb05fc0790603689d47c0
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 59f0662..1a14f64 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -17,7 +17,8 @@
use anyhow::{bail, ensure, Context, Result};
use itertools::Itertools;
use protobuf::Message;
-use std::collections::HashMap;
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hasher;
use std::io::Read;
use std::path::PathBuf;
@@ -31,6 +32,7 @@
ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
ProtoParsedFlags, ProtoTracepoint,
};
+use aconfig_storage_file::sip_hasher13::SipHasher13;
use aconfig_storage_file::StorageFileType;
pub struct Input {
@@ -410,12 +412,43 @@
Ok(flag_ids)
}
+#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to
+ // protect hardcoded offset reads.
+pub fn compute_flag_offsets_fingerprint(flags_map: &HashMap<String, u16>) -> Result<u64> {
+ let mut hasher = SipHasher13::new();
+
+ // Need to sort to ensure the data is added to the hasher in the same order
+ // each run.
+ let sorted_map: BTreeMap<&String, &u16> = flags_map.iter().collect();
+
+ for (flag, offset) in sorted_map {
+ // See https://docs.rs/siphasher/latest/siphasher/#note for use of write
+ // over write_i16. Similarly, use to_be_bytes rather than to_ne_bytes to
+ // ensure consistency.
+ hasher.write(flag.as_bytes());
+ hasher.write(&offset.to_be_bytes());
+ }
+ Ok(hasher.finish())
+}
+
#[cfg(test)]
mod tests {
use super::*;
use aconfig_protos::ProtoFlagPurpose;
#[test]
+ fn test_offset_fingerprint() {
+ let parsed_flags = crate::test::parse_test_flags();
+ let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string();
+ let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap();
+ let expected_fingerprint = 10709892481002252132u64;
+
+ let hash_result = compute_flag_offsets_fingerprint(&flag_ids);
+
+ assert_eq!(hash_result.unwrap(), expected_fingerprint);
+ }
+
+ #[test]
fn test_parse_flags() {
let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags
aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap();