@@ -21,111 +21,99 @@ namespace Yubico.YubiKey.Piv.Converters;
21
21
22
22
internal static class PivEncodingReader
23
23
{
24
+ // Works with both Piv Encoded and GetMetaData encoded keys (with or without leading Public Key Tag)
25
+ // public static (ReadOnlyMemory<byte> Modulus, ReadOnlyMemory<byte> Exponent) GetPublicRSAValues(
26
+ // ReadOnlyMemory<byte> encodedPublicKey)
27
+ // {
28
+ // var tlvReader = new TlvReader(encodedPublicKey);
29
+ // int tag = tlvReader.PeekTag(2);
30
+ // var rsaKeyEncoding = tag == PivConstants.PublicKeyTag
31
+ // ? tlvReader.ReadValue(PivConstants.PublicKeyTag)
32
+ // : encodedPublicKey;
33
+ //
34
+ // var publicKeyValues = TlvObjects.DecodeDictionary(rsaKeyEncoding.Span);
35
+ // bool hasModulus = publicKeyValues.TryGetValue(PivConstants.PublicRSAModulusTag, out var modulus);
36
+ // bool hasExponent = publicKeyValues.TryGetValue(PivConstants.PublicRSAExponentTag, out var exponent);
37
+ // if (!hasModulus || !hasExponent)
38
+ // {
39
+ // throw new ArgumentException(
40
+ // string.Format(
41
+ // CultureInfo.CurrentCulture,
42
+ // ExceptionMessages.InvalidPublicKeyData
43
+ // ));
44
+ // }
45
+ // return (modulus, exponent);
46
+ // }
47
+
24
48
public static ( ReadOnlyMemory < byte > Modulus , ReadOnlyMemory < byte > Exponent ) GetPublicRSAValues (
25
49
ReadOnlyMemory < byte > encodedPublicKey )
26
50
{
27
- var tlvReader = new TlvReader ( encodedPublicKey ) ;
28
- int tag = tlvReader . PeekTag ( 2 ) ;
29
- if ( tag == PivConstants . PublicKeyTag )
51
+ var tlvObject = TlvObject . Parse ( encodedPublicKey . Span ) ;
52
+ var rsaKeyEncoding = tlvObject . Tag == PivConstants . PublicKeyTag
53
+ ? tlvObject . Value
54
+ : encodedPublicKey ;
55
+
56
+ var publicKeyValues = TlvObjects . DecodeDictionary ( rsaKeyEncoding . Span ) ;
57
+ bool hasModulus = publicKeyValues . TryGetValue ( PivConstants . PublicRSAModulusTag , out var modulus ) ;
58
+ bool hasExponent = publicKeyValues . TryGetValue ( PivConstants . PublicRSAExponentTag , out var exponent ) ;
59
+ if ( ! hasModulus || ! hasExponent )
30
60
{
31
- tlvReader = tlvReader . ReadNestedTlv ( tag ) ;
32
- }
33
-
34
- var valueArray = new ReadOnlyMemory < byte > [ 2 ] ;
35
-
36
- while ( tlvReader . HasData )
37
- {
38
- tag = tlvReader . PeekTag ( ) ;
39
- int valueIndex = tag switch
40
- {
41
- PivConstants . PublicRSAModulusTag => 0 ,
42
- PivConstants . PublicRSAExponentTag => 1 ,
43
- _ => throw new ArgumentException (
44
- string . Format ( CultureInfo . CurrentCulture , ExceptionMessages . InvalidPublicKeyData ) )
45
- } ;
46
-
47
- if ( ! valueArray [ valueIndex ] . IsEmpty )
48
- {
49
- throw new ArgumentException (
50
- string . Format (
51
- CultureInfo . CurrentCulture ,
52
- ExceptionMessages . InvalidPublicKeyData )
53
- ) ;
54
- }
55
-
56
- valueArray [ valueIndex ] = tlvReader . ReadValue ( tag ) ;
61
+ throw new ArgumentException (
62
+ string . Format (
63
+ CultureInfo . CurrentCulture ,
64
+ ExceptionMessages . InvalidPublicKeyData
65
+ ) ) ;
57
66
}
58
-
59
- return ( valueArray [ 0 ] , valueArray [ 1 ] ) ;
67
+ return ( modulus , exponent ) ;
60
68
}
61
69
62
70
// Will read PIV public key data and return the public point
63
71
public static ReadOnlyMemory < byte > GetECPublicPointValues ( ReadOnlyMemory < byte > encodedPublicKey )
64
72
{
65
- var tlvReader = new TlvReader ( encodedPublicKey ) ;
66
-
67
- int tag = tlvReader . PeekTag ( 2 ) ;
68
- if ( tag == PivConstants . PublicKeyTag )
69
- {
70
- tlvReader = tlvReader . ReadNestedTlv ( tag ) ;
71
- }
72
-
73
- tag = tlvReader . PeekTag ( ) ;
74
- if ( tag != PivConstants . PublicECTag )
73
+ var tlvObject = TlvObject . Parse ( encodedPublicKey . Span ) ;
74
+ var keyEncoding = tlvObject . Tag == PivConstants . PublicKeyTag
75
+ ? tlvObject . Value
76
+ : encodedPublicKey ;
77
+
78
+ var publicKeyValues = TlvObjects . DecodeDictionary ( keyEncoding . Span ) ;
79
+ bool hasPublicPoint = publicKeyValues . TryGetValue ( PivConstants . PublicECTag , out var publicPoint ) ;
80
+ if ( ! hasPublicPoint )
75
81
{
76
82
throw new ArgumentException (
77
83
string . Format (
78
84
CultureInfo . CurrentCulture ,
79
- ExceptionMessages . InvalidPublicKeyData )
80
- ) ;
85
+ ExceptionMessages . InvalidPublicKeyData
86
+ ) ) ;
81
87
}
82
-
83
- var publicPoint = tlvReader . ReadValue ( PivConstants . PublicECTag ) ;
84
88
return publicPoint ;
85
89
}
86
90
87
91
public static RSAParameters GetRSAParameters ( ReadOnlyMemory < byte > pivEncodedKey )
88
92
{
89
- const int CrtComponentCount = 5 ;
90
-
91
- var tlvReader = new TlvReader ( pivEncodedKey ) ;
92
- var valueArray = new ReadOnlyMemory < byte > [ CrtComponentCount ] ;
93
-
94
- int index = 0 ;
95
- for ( ; index < CrtComponentCount ; index ++ )
93
+ var tlvObject = TlvObject . Parse ( pivEncodedKey . Span ) ;
94
+ var keyEncoding = tlvObject . Tag == PivConstants . PublicKeyTag
95
+ ? tlvObject . Value
96
+ : pivEncodedKey ;
97
+
98
+ var rsaTlvValues = TlvObjects . DecodeDictionary ( keyEncoding . Span ) ;
99
+ if ( ! rsaTlvValues . ContainsKey ( PivConstants . PrivateRSAPrimePTag ) ||
100
+ ! rsaTlvValues . ContainsKey ( PivConstants . PrivateRSAPrimeQTag ) ||
101
+ ! rsaTlvValues . ContainsKey ( PivConstants . PrivateRSAExponentPTag ) ||
102
+ ! rsaTlvValues . ContainsKey ( PivConstants . PrivateRSAExponentQTag ) ||
103
+ ! rsaTlvValues . ContainsKey ( PivConstants . PrivateRSACoefficientTag ) )
96
104
{
97
- valueArray [ index ] = ReadOnlyMemory < byte > . Empty ;
98
- }
99
-
100
- index = 0 ;
101
- while ( index < CrtComponentCount )
102
- {
103
- if ( tlvReader . HasData == false )
104
- {
105
- break ;
106
- }
107
-
108
- int tag = tlvReader . PeekTag ( ) ;
109
- var temp = tlvReader . ReadValue ( tag ) ;
110
- if ( tag <= 0 || tag > CrtComponentCount )
111
- {
112
- continue ;
113
- }
114
-
115
- if ( valueArray [ tag - 1 ] . IsEmpty == false )
116
- {
117
- continue ;
118
- }
119
-
120
- index ++ ;
121
- valueArray [ tag - 1 ] = temp ;
105
+ throw new ArgumentException (
106
+ string . Format (
107
+ CultureInfo . CurrentCulture ,
108
+ ExceptionMessages . InvalidPrivateKeyData
109
+ ) ) ;
122
110
}
123
-
124
- var primeP = valueArray [ PivConstants . PrivateRSAPrimePTag - 1 ] . Span ;
125
- var primeQ = valueArray [ PivConstants . PrivateRSAPrimeQTag - 1 ] . Span ;
126
- var exponentP = valueArray [ PivConstants . PrivateRSAExponentPTag - 1 ] . Span ;
127
- var exponentQ = valueArray [ PivConstants . PrivateRSAExponentQTag - 1 ] . Span ;
128
- var coefficient = valueArray [ PivConstants . PrivateRSACoefficientTag - 1 ] . Span ;
111
+
112
+ var primeP = rsaTlvValues [ PivConstants . PrivateRSAPrimePTag ] . Span ;
113
+ var primeQ = rsaTlvValues [ PivConstants . PrivateRSAPrimeQTag ] . Span ;
114
+ var exponentP = rsaTlvValues [ PivConstants . PrivateRSAExponentPTag ] . Span ;
115
+ var exponentQ = rsaTlvValues [ PivConstants . PrivateRSAExponentQTag ] . Span ;
116
+ var coefficient = rsaTlvValues [ PivConstants . PrivateRSACoefficientTag ] . Span ;
129
117
130
118
return new RSAParameters
131
119
{
@@ -134,6 +122,7 @@ public static RSAParameters GetRSAParameters(ReadOnlyMemory<byte> pivEncodedKey)
134
122
DP = exponentP . ToArray ( ) ,
135
123
DQ = exponentQ . ToArray ( ) ,
136
124
InverseQ = coefficient . ToArray ( ) ,
125
+
137
126
// The YubiKey only works with the CRT components of the private RSA key,
138
127
// that's why we set these values as empty.
139
128
D = Array . Empty < byte > ( ) ,
0 commit comments