16
16
using Microsoft . Extensions . Logging ;
17
17
using Yubico . Core . Iso7816 ;
18
18
using Yubico . Core . Logging ;
19
+ using Yubico . YubiKey . Otp ;
19
20
using Yubico . YubiKey . Otp . Commands ;
20
21
21
22
namespace Yubico . YubiKey . Pipelines
@@ -37,45 +38,70 @@ public ResponseApdu Invoke(CommandApdu command, Type commandType, Type responseT
37
38
{
38
39
// If this is just a regular ReadStatusCommand, or it's a command that doesn't ask for a status response
39
40
// in return, invoke the pipeline as usual.
40
- if ( commandType == typeof ( ReadStatusCommand )
41
- || responseType != typeof ( ReadStatusResponse ) )
41
+ if ( commandType == typeof ( ReadStatusCommand ) ||
42
+ responseType != typeof ( ReadStatusResponse ) )
42
43
{
43
44
return _nextTransform . Invoke ( command , commandType , responseType ) ;
44
45
}
45
46
46
47
// Otherwise we assume this to be a command that applies a config (and therefore looks for a status response).
47
48
// In order to detect failures, we grab the status structure before applying said command so that we have a
48
49
// sequence number to compare to.
49
- int beforeSequence = new ReadStatusResponse (
50
- _nextTransform . Invoke (
51
- new ReadStatusCommand ( ) . CreateCommandApdu ( ) ,
52
- typeof ( ReadStatusCommand ) ,
53
- typeof ( ReadStatusResponse ) ) )
54
- . GetData ( )
55
- . SequenceNumber ;
50
+ var beforeStatus = GetCurrentStatus ( ) ;
51
+ var responseApdu = _nextTransform . Invoke ( command , commandType , responseType ) ;
52
+ var afterStatus = ReadStatus ( responseApdu ) ;
56
53
57
54
try
58
55
{
59
- var responseApdu = _nextTransform . Invoke ( command , commandType , responseType ) ;
60
- int afterSequence = new ReadStatusResponse ( responseApdu ) . GetData ( ) . SequenceNumber ;
61
- int expectedSequence = ( beforeSequence + 1 ) % 0x100 ;
62
-
63
- // If we see the sequence number change, we can assume that the configuration was applied successfully. Otherwise
64
- // we just invent an error in the response.
65
- return afterSequence != expectedSequence
66
- ? new ResponseApdu ( responseApdu . Data . ToArray ( ) , SWConstants . WarningNvmUnchanged )
67
- : responseApdu ;
56
+ // If we see the sequence number change, we can assume that the configuration was applied successfully.
57
+ // Otherwise we just invent an error in the response.
58
+ return IsValidSequenceProgression ( beforeStatus , afterStatus )
59
+ ? responseApdu
60
+ : CreateFailedApdu ( responseApdu . Data . ToArray ( ) ) ;
68
61
}
69
62
catch ( KeyboardConnectionException e )
70
63
{
71
64
_logger . LogWarning ( e , "Handling keyboard connection exception. Translating to APDU response." ) ;
72
65
73
- return new ResponseApdu ( Array . Empty < byte > ( ) , SWConstants . WarningNvmUnchanged ) ;
66
+ return CreateFailedApdu ( ) ;
74
67
}
75
68
}
76
69
77
70
public void Setup ( ) => _nextTransform . Setup ( ) ;
78
71
79
72
public void Cleanup ( ) => _nextTransform . Cleanup ( ) ;
73
+
74
+ // Internal for testing
75
+ internal static bool IsValidSequenceProgression ( OtpStatus beforeStatus , OtpStatus afterStatus )
76
+ {
77
+ byte before = beforeStatus . SequenceNumber ;
78
+ byte after = afterStatus . SequenceNumber ;
79
+
80
+ bool normalIncrement = after == before + 1 ;
81
+ bool validReset = before > 0 && after == 0 &&
82
+ afterStatus is { LongPressConfigured : false , ShortPressConfigured : false } ;
83
+
84
+ return normalIncrement || validReset ;
85
+ }
86
+
87
+ private OtpStatus GetCurrentStatus ( )
88
+ {
89
+ var command = new ReadStatusCommand ( ) ;
90
+ var responseApdu = _nextTransform . Invoke (
91
+ command . CreateCommandApdu ( ) ,
92
+ typeof ( ReadStatusCommand ) ,
93
+ typeof ( ReadStatusResponse ) ) ;
94
+
95
+ return ReadStatus ( responseApdu ) ;
96
+ }
97
+
98
+ private static OtpStatus ReadStatus ( ResponseApdu responseApdu )
99
+ {
100
+ var readStatusResponse = new ReadStatusResponse ( responseApdu ) ;
101
+ var afterStatus = readStatusResponse . GetData ( ) ;
102
+ return afterStatus ;
103
+ }
104
+
105
+ private static ResponseApdu CreateFailedApdu ( byte [ ] ? data = null ) => new ( data ?? [ ] , SWConstants . WarningNvmUnchanged ) ;
80
106
}
81
107
}
0 commit comments