@@ -119,13 +119,13 @@ public void SecureDelete_Plaintext_ShouldRemoveDataFile()
119119 }
120120
121121 [ TestMethod ]
122- public void GetAllowedWindowsSidHashes_ShouldReturnHmacSha256Hashes_NotRawSids ( )
122+ public void GetAllowedPrincipalHashes_ShouldReturnHmacSha256Hashes_NotRawPrincipalIds ( )
123123 {
124124 using TestStoreScope scope = new ( ) ;
125125
126126 scope . Store . Save ( "person-5" , new TestPerson ( "Emmy" , "Noether" ) ) ;
127127
128- IReadOnlyCollection < string > lstHashes = scope . Store . GetAllowedWindowsSidHashes ( "person-5" ) ;
128+ IReadOnlyCollection < string > lstHashes = scope . Store . GetAllowedPrincipalHashes ( "person-5" ) ;
129129
130130 Assert . AreEqual ( 1 , lstHashes . Count ) ;
131131
@@ -134,28 +134,27 @@ public void GetAllowedWindowsSidHashes_ShouldReturnHmacSha256Hashes_NotRawSids()
134134 // An HMAC-SHA256 output is 32 bytes = exactly 64 lowercase hex characters
135135 Assert . AreEqual ( 64 , sSidHash . Length ) ;
136136 Assert . IsTrue ( sSidHash . All ( c => ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' ) ) ,
137- "The returned value must be a lowercase hex string, not a raw Windows SID ." ) ;
137+ "The returned value must be a lowercase hex string, not a raw principal identifier ." ) ;
138138
139- // Must not look like a Windows SID
140- Assert . IsFalse ( sSidHash . StartsWith ( "S-" , StringComparison . OrdinalIgnoreCase ) ) ;
139+ // Must not be the raw principal identifier
140+ Assert . IsFalse ( string . Equals ( scope . PrincipalProvider . CurrentPrincipalId , sSidHash , StringComparison . OrdinalIgnoreCase ) ) ;
141141
142- // Must not be the plain SHA-256 of the SID — proves the HMAC pepper is in use
143- string sCurrentSid = System . Security . Principal . WindowsIdentity . GetCurrent ( ) . User ? . Value ?? string . Empty ;
144- string sPlainSha256 = Convert . ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( sCurrentSid ) ) ) . ToLowerInvariant ( ) ;
142+ // Must not be the plain SHA-256 of the principal ID — proves the HMAC pepper is in use
143+ string sPlainSha256 = Convert . ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( scope . PrincipalProvider . CurrentPrincipalId ) ) ) . ToLowerInvariant ( ) ;
145144 Assert . AreNotEqual ( sPlainSha256 , sSidHash ,
146- "The stored hash must be HMAC-SHA256(SID , pepper), not plain SHA-256(SID )." ) ;
145+ "The stored hash must be HMAC-SHA256(principalId , pepper), not plain SHA-256(principalId )." ) ;
147146 }
148147
149148 [ TestMethod ]
150149 public void GrantAccess_ShouldStoreHashAndBeVerifiable ( )
151150 {
152151 using TestStoreScope scope = new ( ) ;
153152
154- const string sFakeSid = "S-1-5-21-0000000000-1111111111-2222222222-500 " ;
153+ const string sFakePrincipalId = "user:granted-user " ;
155154 scope . Store . Save ( "person-6" , new TestPerson ( "Lise" , "Meitner" ) ) ;
156- scope . Store . GrantAccess ( "person-6" , sFakeSid ) ;
155+ scope . Store . GrantAccess ( "person-6" , sFakePrincipalId ) ;
157156
158- IReadOnlyCollection < string > lstHashes = scope . Store . GetAllowedWindowsSidHashes ( "person-6" ) ;
157+ IReadOnlyCollection < string > lstHashes = scope . Store . GetAllowedPrincipalHashes ( "person-6" ) ;
159158
160159 // Owner hash (current user) + granted hash = 2 entries
161160 Assert . AreEqual ( 2 , lstHashes . Count ) ;
@@ -165,17 +164,17 @@ public void GrantAccess_ShouldStoreHashAndBeVerifiable()
165164 {
166165 Assert . AreEqual ( 64 , sHash . Length ) ;
167166 Assert . IsTrue ( sHash . All ( c => ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' ) ) ,
168- "Every stored hash must be a lowercase hex string, not a raw SID ." ) ;
167+ "Every stored hash must be a lowercase hex string, not a raw principal identifier ." ) ;
169168 }
170169
171- // Raw SID must not appear in the list
172- Assert . IsFalse ( lstHashes . Contains ( sFakeSid , StringComparer . OrdinalIgnoreCase ) ,
173- "The raw Windows SID must never be stored." ) ;
170+ // Raw principal identifier must not appear in the list
171+ Assert . IsFalse ( lstHashes . Contains ( sFakePrincipalId , StringComparer . OrdinalIgnoreCase ) ,
172+ "The raw principal identifier must never be stored." ) ;
174173
175- // Plain SHA-256(SID ) must not appear — proves the pepper (HMAC) is in use
176- string sPlainSha256 = Convert . ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( sFakeSid ) ) ) . ToLowerInvariant ( ) ;
174+ // Plain SHA-256(principalId ) must not appear — proves the pepper (HMAC) is in use
175+ string sPlainSha256 = Convert . ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( sFakePrincipalId ) ) ) . ToLowerInvariant ( ) ;
177176 Assert . IsFalse ( lstHashes . Contains ( sPlainSha256 , StringComparer . OrdinalIgnoreCase ) ,
178- "The stored hash must be HMAC-SHA256(SID , pepper), not plain SHA-256(SID )." ) ;
177+ "The stored hash must be HMAC-SHA256(principalId , pepper), not plain SHA-256(principalId )." ) ;
179178 }
180179
181180 [ TestMethod ]
@@ -205,12 +204,18 @@ public TestStoreScope()
205204 {
206205 RootDirectory = sRootDirectory ,
207206 } ;
208- BackupService = new MasterKeyBackupService ( Options ) ;
209- Store = new PersonSecureStore ( BackupService , Options ) ;
207+ LocalKeyProtector = new PassThroughLocalKeyProtector ( ) ;
208+ PrincipalProvider = new FixedPrincipalProvider ( ) ;
209+ BackupService = new MasterKeyBackupService ( Options , LocalKeyProtector ) ;
210+ Store = new PersonSecureStore ( BackupService , Options , PrincipalProvider ) ;
210211 }
211212
212213 public GenSecureStoreOptions Options { get ; }
213214
215+ public PassThroughLocalKeyProtector LocalKeyProtector { get ; }
216+
217+ public FixedPrincipalProvider PrincipalProvider { get ; }
218+
214219 public MasterKeyBackupService BackupService { get ; }
215220
216221 public PersonSecureStore Store { get ; }
@@ -245,6 +250,28 @@ public void Dispose()
245250
246251 }
247252
253+ private sealed class PassThroughLocalKeyProtector : ILocalKeyProtector
254+ {
255+ public byte [ ] Protect ( byte [ ] arrPlaintext )
256+ {
257+ ArgumentNullException . ThrowIfNull ( arrPlaintext ) ;
258+ return arrPlaintext . ToArray ( ) ;
259+ }
260+
261+ public byte [ ] Unprotect ( byte [ ] arrProtectedData )
262+ {
263+ ArgumentNullException . ThrowIfNull ( arrProtectedData ) ;
264+ return arrProtectedData . ToArray ( ) ;
265+ }
266+ }
267+
268+ private sealed class FixedPrincipalProvider : ICurrentPrincipalProvider
269+ {
270+ public string CurrentPrincipalId { get ; } = "user:test-user" ;
271+
272+ public string GetCurrentPrincipalId ( ) => CurrentPrincipalId ;
273+ }
274+
248275 private static string [ ] GetRelativeSegments ( string sRootPath , string sFilePath )
249276 {
250277 string sRelativePath = Path . GetRelativePath ( sRootPath , sFilePath ) ;
0 commit comments