@@ -8,7 +8,7 @@ import { WarningIcon, Icon } from '../../icons';
8
8
import { trackEvent } from '../../metrics' ;
9
9
10
10
import { uploadFile } from '../../util/ui' ;
11
- import { asError } from '../../util/error' ;
11
+ import { UnreachableCheck , asError , unreachableCheck } from '../../util/error' ;
12
12
13
13
import { UpstreamProxyType , RulesStore } from '../../model/rules/rules-store' ;
14
14
import { ParsedCertificate , ValidationResult } from '../../model/crypto' ;
@@ -316,6 +316,9 @@ class ClientCertificateConfig extends React.Component<{ rulesStore: RulesStore }
316
316
@observable
317
317
clientCertState : undefined | 'encrypted' | 'processing' | 'error' | 'decrypted' ;
318
318
319
+ @observable
320
+ clientCertError : string | undefined ;
321
+
319
322
@action . bound
320
323
onClientCertSelected ( event : React . ChangeEvent < HTMLInputElement > ) {
321
324
const input = event . target ;
@@ -326,6 +329,7 @@ class ClientCertificateConfig extends React.Component<{ rulesStore: RulesStore }
326
329
fileReader . readAsArrayBuffer ( file ) ;
327
330
328
331
this . clientCertState = 'processing' ;
332
+ this . clientCertError = undefined ;
329
333
330
334
const thisConfig = this ; // fileReader events set 'this'
331
335
fileReader . addEventListener ( 'load' , flow ( function * ( ) {
@@ -338,29 +342,18 @@ class ClientCertificateConfig extends React.Component<{ rulesStore: RulesStore }
338
342
339
343
result = yield validatePKCS ( thisConfig . clientCertData . pfx , undefined ) ;
340
344
341
- if ( result === 'valid' ) {
342
- thisConfig . clientCertState = 'decrypted' ;
343
- thisConfig . clientCertData . passphrase = undefined ;
344
- return ;
345
- }
346
-
347
- if ( result === 'invalid-format' ) {
348
- thisConfig . clientCertState = 'error' ;
349
- return ;
350
- }
345
+ if ( result === 'invalid-passphrase' ) {
346
+ // If it fails, try again with an empty key, since that is sometimes used for 'no passphrase'
347
+ result = yield validatePKCS ( thisConfig . clientCertData . pfx , '' ) ;
351
348
352
- // If it fails, try again with an empty key, since that is sometimes used for 'no passphrase'
353
- result = yield validatePKCS ( thisConfig . clientCertData . pfx , '' ) ;
349
+ if ( result === 'valid' ) {
350
+ thisConfig . clientCertData . passphrase = '' ;
351
+ }
354
352
355
- if ( result === 'valid' ) {
356
- thisConfig . clientCertState = 'decrypted' ;
357
- thisConfig . clientCertData . passphrase = '' ;
358
- return ;
353
+ thisConfig . handleClientCertValidationResult ( result ) ;
354
+ } else {
355
+ thisConfig . handleClientCertValidationResult ( result ) ;
359
356
}
360
-
361
- // If that still hasn't worked, it's encrypted. Mark is as such, and wait for the user
362
- // to either cancel, or enter the correct passphrase.
363
- thisConfig . clientCertState = 'encrypted' ;
364
357
} ) ) ;
365
358
366
359
fileReader . addEventListener ( 'error' , ( ) => {
@@ -371,15 +364,32 @@ class ClientCertificateConfig extends React.Component<{ rulesStore: RulesStore }
371
364
readonly decryptClientCertData = flow ( function * ( this : ClientCertificateConfig ) {
372
365
const { pfx, passphrase } = this . clientCertData ! ;
373
366
374
- let result : ValidationResult ;
375
-
376
367
this . clientCertState = 'processing' ;
377
- result = yield validatePKCS ( pfx , passphrase ) ;
378
- this . clientCertState = result === 'valid'
379
- ? 'decrypted'
380
- : 'encrypted' ;
368
+ this . clientCertError = undefined ;
369
+
370
+ const result = yield validatePKCS ( pfx , passphrase ) ;
371
+ this . handleClientCertValidationResult ( result ) ;
381
372
} ) ;
382
373
374
+ handleClientCertValidationResult ( result : ValidationResult ) {
375
+ this . clientCertError = undefined ;
376
+
377
+ if ( result === 'valid' ) {
378
+ this . clientCertState = 'decrypted' ;
379
+ } else if ( result === 'invalid-passphrase' ) {
380
+ this . clientCertState = 'encrypted' ;
381
+ } else if ( result === 'invalid-format' ) {
382
+ this . clientCertState = 'error' ;
383
+ this . clientCertError = 'Parsing failed' ;
384
+ } else if ( result === 'missing-key' ) {
385
+ this . clientCertState = 'error' ;
386
+ this . clientCertError = 'No private key found' ;
387
+ } else if ( result === 'missing-cert' ) {
388
+ this . clientCertState = 'error' ;
389
+ this . clientCertError = 'No certificate found' ;
390
+ } else unreachableCheck ( result ) ;
391
+ }
392
+
383
393
@action . bound
384
394
dropClientCertData ( ) {
385
395
this . clientCertData = undefined ;
@@ -456,12 +466,14 @@ class ClientCertificateConfig extends React.Component<{ rulesStore: RulesStore }
456
466
< Icon icon = { [ 'fas' , 'undo' ] } title = 'Deselect this certificate' />
457
467
</ SettingsButton >
458
468
</ DecryptionInput >
459
- : < DecryptionInput >
460
- < p > < WarningIcon /> Invalid certificate</ p >
461
- < SettingsButton onClick = { this . dropClientCertData } >
462
- < Icon icon = { [ 'fas' , 'undo' ] } title = 'Deselect this certificate' />
463
- </ SettingsButton >
464
- </ DecryptionInput >
469
+ : this . clientCertState === 'error'
470
+ ? < DecryptionInput >
471
+ < p > < WarningIcon /> { this . clientCertError || 'Invalid certificate' } </ p >
472
+ < SettingsButton onClick = { this . dropClientCertData } >
473
+ < Icon icon = { [ 'fas' , 'undo' ] } title = 'Deselect this certificate' />
474
+ </ SettingsButton >
475
+ </ DecryptionInput >
476
+ : unreachableCheck ( this . clientCertState )
465
477
}
466
478
< SettingsButton
467
479
disabled = {
0 commit comments