@@ -12,9 +12,11 @@ use crate::store_fs::require_private_regular_file;
1212pub ( crate ) const MAX_INTEGRITY_KEY_BYTES : u64 = 4096 ;
1313const MIN_INTEGRITY_KEY_BYTES : usize = 32 ;
1414const SNAPSHOT_MAC_LABEL : & [ u8 ] = b"fluxheim-snapshot-v1\0 " ;
15+ const GENERATION_WITNESS_MAC_LABEL : & [ u8 ] = b"fluxheim-snapshot-generation-witness-v1\0 " ;
1516const RECOVERY_MAC_LABEL : & [ u8 ] = b"fluxheim-snapshot-recovery-v1\0 " ;
1617pub ( crate ) const GENERATION_MAC_LABEL : & [ u8 ] = b"fluxheim-snapshot-generation-v1\0 " ;
1718pub ( crate ) const PRUNE_BOUNDARY_MAC_LABEL : & [ u8 ] = b"fluxheim-snapshot-prune-boundary-v1\0 " ;
19+ pub ( crate ) const MAX_INTEGRITY_MANIFEST_BYTES : u64 = 4096 ;
1820
1921pub trait SnapshotCryptoProvider : std:: fmt:: Debug + Send + Sync {
2022 fn label ( & self ) -> & ' static str ;
@@ -38,6 +40,8 @@ pub(crate) struct SnapshotIntegrityManifest {
3840 pub key_id : String ,
3941 pub config_sha256 : String ,
4042 pub metadata_hmac_sha256 : String ,
43+ pub generation : u64 ,
44+ pub generation_hmac_sha256 : String ,
4145}
4246
4347impl SnapshotIntegrityKey {
@@ -89,17 +93,21 @@ impl SnapshotIntegrityKey {
8993 id : & str ,
9094 config : & [ u8 ] ,
9195 metadata : & [ u8 ] ,
96+ generation : u64 ,
9297 ) -> Result < SnapshotIntegrityManifest , SnapshotError > {
9398 let config_digest = self
9499 . provider
95100 . sha256 ( & [ config] )
96101 . map_err ( SnapshotError :: CryptoProvider ) ?;
97102 let signature = self . sign_result ( id, config, metadata) ?;
103+ let generation_signature = self . sign_generation_witness ( id, generation) ?;
98104 Ok ( SnapshotIntegrityManifest {
99105 algorithm : "hmac-sha256" . to_owned ( ) ,
100106 key_id : self . key_id . clone ( ) ,
101107 config_sha256 : hex ( & config_digest) ,
102108 metadata_hmac_sha256 : hex ( & signature) ,
109+ generation,
110+ generation_hmac_sha256 : hex ( & generation_signature) ,
103111 } )
104112 }
105113
@@ -153,6 +161,7 @@ impl SnapshotIntegrityKey {
153161 id : & str ,
154162 config : & [ u8 ] ,
155163 metadata : & [ u8 ] ,
164+ generation : u64 ,
156165 manifest : & SnapshotIntegrityManifest ,
157166 ) -> Result < ( ) , SnapshotError > {
158167 if manifest. algorithm != "hmac-sha256" || manifest. key_id != self . key_id {
@@ -166,6 +175,10 @@ impl SnapshotIntegrityKey {
166175 if config_digest != manifest. config_sha256 {
167176 return Err ( SnapshotError :: IntegrityVerificationFailed { id : id. to_owned ( ) } ) ;
168177 }
178+ if manifest. generation != generation {
179+ return Err ( SnapshotError :: IntegrityVerificationFailed { id : id. to_owned ( ) } ) ;
180+ }
181+ self . verify_generation_witness ( id, manifest) ?;
169182 let expected = decode_hex_32 ( & manifest. metadata_hmac_sha256 )
170183 . ok_or_else ( || SnapshotError :: IntegrityVerificationFailed { id : id. to_owned ( ) } ) ?;
171184 if self
@@ -179,6 +192,49 @@ impl SnapshotIntegrityKey {
179192 }
180193 }
181194
195+ pub ( crate ) fn verify_generation_witness (
196+ & self ,
197+ id : & str ,
198+ manifest : & SnapshotIntegrityManifest ,
199+ ) -> Result < u64 , SnapshotError > {
200+ crate :: metadata:: validate_snapshot_id ( id) ?;
201+ if manifest. algorithm != "hmac-sha256" || manifest. key_id != self . key_id {
202+ return Err ( SnapshotError :: IntegrityVerificationFailed { id : id. to_owned ( ) } ) ;
203+ }
204+ let expected = decode_hex_32 ( & manifest. generation_hmac_sha256 )
205+ . ok_or_else ( || SnapshotError :: IntegrityVerificationFailed { id : id. to_owned ( ) } ) ?;
206+ if self
207+ . sign_generation_witness ( id, manifest. generation ) ?
208+ . ct_eq ( & expected)
209+ . declassify ( "snapshot generation witness result is public" )
210+ {
211+ Ok ( manifest. generation )
212+ } else {
213+ Err ( SnapshotError :: IntegrityVerificationFailed { id : id. to_owned ( ) } )
214+ }
215+ }
216+
217+ fn sign_generation_witness (
218+ & self ,
219+ id : & str ,
220+ generation : u64 ,
221+ ) -> Result < [ u8 ; 32 ] , SnapshotError > {
222+ let id_length = encoded_length ( id. len ( ) ) ?;
223+ self . secret
224+ . with_secret ( |secret| {
225+ self . provider . hmac_sha256 (
226+ secret,
227+ & [
228+ GENERATION_WITNESS_MAC_LABEL ,
229+ & id_length,
230+ id. as_bytes ( ) ,
231+ & generation. to_be_bytes ( ) ,
232+ ] ,
233+ )
234+ } )
235+ . map_err ( SnapshotError :: CryptoProvider )
236+ }
237+
182238 fn sign_result (
183239 & self ,
184240 id : & str ,
0 commit comments