@@ -66,7 +66,7 @@ public string Protect(string value, string configurationName)
6666 "ff1" or "ff3" or "ff31" => ProtectFpe ( value , configuration ) ,
6767 "mask" => ProtectMask ( value , configuration ) ,
6868 "hash" => ProtectHash ( value , configuration ) ,
69- _ => throw new ArgumentException ( $ "Unknown engine: { configuration . Engine } ")
69+ _ => throw new ArgumentException ( $ "unknown engine: { configuration . Engine } ")
7070 } ;
7171 }
7272
@@ -80,14 +80,15 @@ public string Access(string protectedValue)
8080 {
8181 if ( protectedValue . Length > header . Length && protectedValue . StartsWith ( header ) )
8282 {
83- var configuration = GetConfiguration ( _headerIndex [ header ] ) ;
83+ var name = _headerIndex [ header ] ;
84+ var configuration = GetConfiguration ( name ) ;
8485 // Strip the header here so AccessFpe always receives raw headerless ciphertext.
8586 var stripped = protectedValue [ header . Length ..] ;
86- return AccessFpe ( stripped , configuration ) ;
87+ return AccessWithConfiguration ( stripped , configuration , name ) ;
8788 }
8889 }
8990
90- throw new ArgumentException ( "No matching header found" ) ;
91+ throw new ArgumentException ( "no matching header found" ) ;
9192 }
9293
9394 // Escape-hatch access for unique situations where the protected value
@@ -102,6 +103,14 @@ public string Access(string protectedValue, string configurationName)
102103 if ( configurationName == null )
103104 throw new ArgumentException ( "Access(value, configurationName) requires a configuration name." ) ;
104105 var configuration = GetConfiguration ( configurationName ) ;
106+ return AccessWithConfiguration ( protectedValue , configuration , configurationName ) ;
107+ }
108+
109+ private string AccessWithConfiguration ( string protectedValue , Configuration configuration , string configurationName )
110+ {
111+ if ( configuration . Engine == "mask" || configuration . Engine == "hash" )
112+ throw new ArgumentException (
113+ $ "cannot reverse '{ configurationName } ' — { configuration . Engine } is irreversible") ;
105114 return AccessFpe ( protectedValue , configuration ) ;
106115 }
107116
@@ -131,7 +140,7 @@ private string ProtectFpe(string value, Configuration configuration)
131140
132141 var ( encryptable , positions , chars ) = ExtractPassthroughs ( value , alphabet ) ;
133142 if ( encryptable . Length == 0 )
134- throw new ArgumentException ( "No encryptable characters in input" ) ;
143+ throw new ArgumentException ( "no encryptable characters in input" ) ;
135144
136145 string encrypted ;
137146 if ( configuration . Engine == "ff3" )
@@ -164,8 +173,10 @@ private string ProtectFpe(string value, Configuration configuration)
164173 // a headerless value.
165174 private string AccessFpe ( string protectedValue , Configuration configuration )
166175 {
176+ // Callers (AccessWithConfiguration) have already filtered mask/hash;
177+ // anything else here that isn't an FPE engine is an internal misuse.
167178 if ( configuration . Engine != "ff1" && configuration . Engine != "ff3" && configuration . Engine != "ff31" )
168- throw new ArgumentException ( $ "Cannot reverse ' { configuration . Engine } ' — not reversible ") ;
179+ throw new ArgumentException ( $ "unknown engine: { configuration . Engine } ") ;
169180
170181 var key = ResolveKey ( configuration . KeyRef ) ;
171182 var alphabet = configuration . Alphabet ;
@@ -198,7 +209,7 @@ private string AccessFpe(string protectedValue, Configuration configuration)
198209 private static string ProtectMask ( string value , Configuration configuration )
199210 {
200211 if ( string . IsNullOrEmpty ( configuration . Pattern ) )
201- throw new ArgumentException ( "Mask configuration requires ' pattern' " ) ;
212+ throw new ArgumentException ( "mask pattern required " ) ;
202213
203214 int len = value . Length ;
204215 return configuration . Pattern switch
@@ -248,16 +259,16 @@ private string ProtectHash(string value, Configuration configuration)
248259 private Configuration GetConfiguration ( string name )
249260 {
250261 if ( ! _configurations . TryGetValue ( name , out var configuration ) )
251- throw new ArgumentException ( $ "Unknown configuration: { name } ") ;
262+ throw new ArgumentException ( $ "configuration not found : { name } ") ;
252263 return configuration ;
253264 }
254265
255266 private byte [ ] ResolveKey ( string ? keyRef )
256267 {
257268 if ( string . IsNullOrEmpty ( keyRef ) )
258- throw new ArgumentException ( "No key_ref in configuration" ) ;
269+ throw new ArgumentException ( "key error: no key_ref in configuration" ) ;
259270 if ( ! _keys . TryGetValue ( keyRef , out var key ) )
260- throw new ArgumentException ( $ "Unknown key: { keyRef } ") ;
271+ throw new ArgumentException ( $ "key error: unknown key ' { keyRef } ' ") ;
261272 return key ;
262273 }
263274
@@ -321,7 +332,7 @@ private void LoadKeys(JsonElement root)
321332 }
322333 else
323334 {
324- throw new ArgumentException ( $ "Key '{ name } ' must have either 'material' or 'source'") ;
335+ throw new ArgumentException ( $ "key error: key '{ name } ' must have either 'material' or 'source'") ;
325336 }
326337 }
327338 }
@@ -372,12 +383,12 @@ private void LoadConfigurations(JsonElement root)
372383 string ? header = p . TryGetProperty ( "header" , out var hv ) ? hv . GetString ( ) : null ;
373384
374385 if ( headerEnabled && string . IsNullOrEmpty ( header ) )
375- throw new ArgumentException ( $ "Configuration ' { kv . Name } ' has header_enabled=true but no header specified") ;
386+ throw new ArgumentException ( "configuration error: header must be specified") ;
376387
377388 if ( headerEnabled && header != null )
378389 {
379390 if ( _headerIndex . ContainsKey ( header ) )
380- throw new ArgumentException ( $ "Header collision: ' { header } ' used by both ' { _headerIndex [ header ] } ' and ' { kv . Name } ' ") ;
391+ throw new ArgumentException ( "configuration error: header collision ") ;
381392 _headerIndex [ header ] = kv . Name ;
382393 }
383394
0 commit comments