|  |  | 1 |  | // Copyright (c) Microsoft Corporation. All rights reserved. | 
|  |  | 2 |  | // Licensed under the MIT License. See License.txt in the project root for | 
|  |  | 3 |  | // license information. | 
|  |  | 4 |  |  | 
|  |  | 5 |  | using System; | 
|  |  | 6 |  | using System.Collections.Generic; | 
|  |  | 7 |  | using System.Diagnostics; | 
|  |  | 8 |  | using System.Text; | 
|  |  | 9 |  |  | 
|  |  | 10 |  | namespace Microsoft.Azure.KeyVault.WebKey | 
|  |  | 11 |  | { | 
|  |  | 12 |  |     /// <summary> | 
|  |  | 13 |  |     /// A class that verifies instances of <see cref="JsonWebKey"/> according to key type. | 
|  |  | 14 |  |     /// </summary> | 
|  |  | 15 |  |     public abstract class JsonWebKeyVerifier | 
|  |  | 16 |  |     { | 
|  |  | 17 |  |         /// <summary> | 
|  |  | 18 |  |         /// Indicates which type of key this verifier applies to. | 
|  |  | 19 |  |         /// </summary> | 
|  |  | 20 |  |         /// This is typically a value of <see cref="JsonWebKeyType"/>, though other values are allowed if registered | 
|  |  | 21 |  |         /// with the <see cref="Register"/> method. | 
|  |  | 22 |  |         public string Kty { get; } | 
|  |  | 23 |  |  | 
|  |  | 24 |  |         /// <summary> | 
|  |  | 25 |  |         /// Initializes a new instance setting the specified value in the <see cref="Kty"/> property. | 
|  |  | 26 |  |         /// </summary> | 
|  |  | 27 |  |         /// <param name="kty">Indicates which type of key this verifier applies to.</param> | 
|  |  | 28 |  |         /// <exception cref="ArgumentNullException">If the specified value is <code>null</code>, empty or whitespace.</e | 
|  |  | 29 |  |         /// <exception cref="ArgumentException">If the specified value contains invalid characters.</exception> | 
|  |  | 30 |  |         protected JsonWebKeyVerifier( string kty ) | 
|  |  | 31 |  |         { | 
|  |  | 32 |  |             if ( string.IsNullOrWhiteSpace( kty ) ) | 
|  |  | 33 |  |                 throw new ArgumentNullException( nameof( kty ) ); | 
|  |  | 34 |  |  | 
|  |  | 35 |  |             foreach ( var ch in kty ) | 
|  |  | 36 |  |             { | 
|  |  | 37 |  |                 if ( ch >= '0' && ch <= '9' ) | 
|  |  | 38 |  |                     continue; | 
|  |  | 39 |  |                 if ( ch >= 'A' && ch <= 'Z' ) | 
|  |  | 40 |  |                     continue; | 
|  |  | 41 |  |                 if ( ch >= 'a' && ch <= 'z' ) | 
|  |  | 42 |  |                     continue; | 
|  |  | 43 |  |                 if ( ch == '-' || ch == '.' || ch == '_' ) | 
|  |  | 44 |  |                     continue; | 
|  |  | 45 |  |  | 
|  |  | 46 |  |                 throw new ArgumentException( "Value contains invalid characters.", nameof( kty ) ); | 
|  |  | 47 |  |             } | 
|  |  | 48 |  |  | 
|  |  | 49 |  |             Kty = kty; | 
|  |  | 50 |  |         } | 
|  |  | 51 |  |  | 
|  |  | 52 |  |         /// <summary> | 
|  |  | 53 |  |         /// Tells if the type of key verified by this object supports public key algorithms. | 
|  |  | 54 |  |         /// </summary> | 
|  |  | 55 |  |         /// Note to implementers: if this method returns <code>true</code>, the methods | 
|  |  | 56 |  |         /// <see cref="IsPublicKeyComplete"/>, <see cref="IsPublicKeyValid"/>, <see cref="IsPrivateKeyComplete"/> and | 
|  |  | 57 |  |         /// <see cref="IsPrivateKeyValid"/> must be overriden. | 
|  |  | 58 |  |         public abstract bool IsPublicKeyCrypto { get; } | 
|  |  | 59 |  |  | 
|  |  | 60 |  |         /// <summary> | 
|  |  | 61 |  |         /// Tells if the type of key verified by this object supports symmetric key algorithms. | 
|  |  | 62 |  |         /// </summary> | 
|  |  | 63 |  |         /// Note to implementers: if this method returns <code>true</code>, the methods | 
|  |  | 64 |  |         /// <see cref="IsSymmetricKeyComplete"/> and <see cref="IsSymmetricKeyValid"/> must be overriden. | 
|  |  | 65 |  |         public abstract bool IsSymmetricKeyCrypto { get; } | 
|  |  | 66 |  |  | 
|  |  | 67 |  |         /// <summary> | 
|  |  | 68 |  |         /// Tells if the type of key verified by this object contains a secret component, such as a hardware key token. | 
|  |  | 69 |  |         /// </summary> | 
|  |  | 70 |  |         /// Note to implementers: if this method returns <code>true</code>, the methods | 
|  |  | 71 |  |         /// <see cref="IsSecretKeyComplete"/> and <see cref="IsSecretKeyValid"/> must be overriden. | 
|  |  | 72 |  |         public abstract bool HasSecretKey { get; } | 
|  |  | 73 |  |  | 
|  |  | 74 |  |         /// <summary> | 
|  |  | 75 |  |         /// Determines if the specified <see cref="JsonWebKey"/> instance contains values at properties that represent t | 
|  |  | 76 |  |         /// </summary> | 
|  |  | 77 |  |         /// <para>If all required public key properties (for the key type) are specified in the instance, the method mus | 
|  |  | 78 |  |         /// <code>true</code> and not modify the <paramref name="missingProps"/> parameter.</para> | 
|  |  | 79 |  |         /// <para>If some public key property is missing, the method must return <code>false</code> and set <paramref na | 
|  |  | 80 |  |         /// with a value - typically a <see cref="List{T}"/> - containing all missing properties.</para> | 
|  |  | 81 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 82 |  |         /// <param name="missingProps">A reference to a variable that tells the list of missing properties. Callers must | 
|  |  | 83 |  |         /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>. | 
|  |  | 84 |  |         public virtual bool IsPublicKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 85 |  |         { | 
|  |  | 86 |  |             throw ThrowDefaultForPublicKeyCrypto( nameof( IsPublicKeyComplete ) ); | 
|  |  | 87 |  |         } | 
|  |  | 88 |  |  | 
|  |  | 89 |  |         /// <summary> | 
|  |  | 90 |  |         /// Determines if the specified <see cref="JsonWebKey"/> instance contains a possibly valid public key (see rema | 
|  |  | 91 |  |         /// </summary> | 
|  |  | 92 |  |         /// <para>Because fully validating a key may require unfeasable amount of resources, this method only has to che | 
|  |  | 93 |  |         /// for obvious issues. As a guideline, we say that the code only verifies obvious issues if it runs in constant | 
|  |  | 94 |  |         /// It's perfectly valid for implementors to do nothing and simply return <code>true</code>.</para> | 
|  |  | 95 |  |         /// <para>This method assumes that <see cref="IsPublicKeyComplete"/> was called and returned <code>true</code>.  | 
|  |  | 96 |  |         /// doesn't test again for the presence of required properties. It may throw <see cref="NullReferenceException"/ | 
|  |  | 97 |  |         /// if the caller doesn't see <see cref="IsPublicKeyComplete"/> returning <code>true</code> first.</para> | 
|  |  | 98 |  |         /// <para>If the valiation code finds no issue, this method must return <code>true</code> without modifying the  | 
|  |  | 99 |  |         /// <paramref name="errorMsg"/>.</para> | 
|  |  | 100 |  |         /// <para>If some issue is found, this method must return <code>false</code> and tell more details in the | 
|  |  | 101 |  |         /// <paramref name="errorMsg"/> parameter.</para> | 
|  |  | 102 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 103 |  |         /// <param name="errorMsg">A reference to a variable that will contain an error message. Callers must | 
|  |  | 104 |  |         /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>. | 
|  |  | 105 |  |         public virtual bool IsPublicKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 106 |  |         { | 
|  |  | 107 |  |             throw ThrowDefaultForPublicKeyCrypto( nameof( IsPublicKeyValid ) ); | 
|  |  | 108 |  |         } | 
|  |  | 109 |  |  | 
|  |  | 110 |  |         /// <summary> | 
|  |  | 111 |  |         /// Same as <see cref="IsPublicKeyComplete"/>, but for the private key. | 
|  |  | 112 |  |         /// </summary> | 
|  |  | 113 |  |         public virtual bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 114 |  |         { | 
|  |  | 115 |  |             throw ThrowDefaultForPublicKeyCrypto( nameof( IsPrivateKeyComplete ) ); | 
|  |  | 116 |  |         } | 
|  |  | 117 |  |  | 
|  |  | 118 |  |         /// <summary> | 
|  |  | 119 |  |         /// Same as <see cref="IsPublicKeyValid"/>, but for the private key. | 
|  |  | 120 |  |         /// </summary> | 
|  |  | 121 |  |         public virtual bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 122 |  |         { | 
|  |  | 123 |  |             throw ThrowDefaultForPublicKeyCrypto( nameof( IsPrivateKeyValid ) ); | 
|  |  | 124 |  |         } | 
|  |  | 125 |  |  | 
|  |  | 126 |  |         /// <summary> | 
|  |  | 127 |  |         /// Determines if the specified <see cref="JsonWebKey"/> instance contains values in one or more properties that | 
|  |  | 128 |  |         /// represent the private key. | 
|  |  | 129 |  |         /// </summary> | 
|  |  | 130 |  |         /// <para>This method is used to protect private key material from accidental leakage.</para> | 
|  |  | 131 |  |         /// <para>If no private key property (for the key type) is specified in the instance, the method must return | 
|  |  | 132 |  |         /// <code>false</code> and not modify the <paramref name="specifiedProps"/> parameter.</para> | 
|  |  | 133 |  |         /// <para>If one or more private key property is specified, the method must return <code>true</code> and optiona | 
|  |  | 134 |  |         /// set <paramref name="specifiedProps"/> with a value - typically a <see cref="List{T}"/> - containing the spec | 
|  |  | 135 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 136 |  |         /// <param name="specifiedProps">A reference to a variable that tells the list of specified properties. Callers  | 
|  |  | 137 |  |         /// set the variable to <code>null</code> and examine the value only if this method returns <code>true</code>.</ | 
|  |  | 138 |  |         /// <returns><code>true</code> if a value is found in at least one property that describe the private key; | 
|  |  | 139 |  |         /// <code>false</code> otherwise.</returns> | 
|  |  | 140 |  |         public virtual bool IsAnyPrivateKeyParamSpecified( JsonWebKey webKey, ref ICollection<string> specifiedProps ) | 
|  |  | 141 |  |         { | 
|  |  | 142 |  |             throw ThrowDefaultForPublicKeyCrypto( nameof( IsAnyPrivateKeyParamSpecified ) ); | 
|  |  | 143 |  |         } | 
|  |  | 144 |  |  | 
|  |  | 145 |  |         private Exception ThrowDefaultForPublicKeyCrypto( string methodName ) | 
|  |  | 146 |  |         { | 
|  |  | 147 |  |             if ( IsPublicKeyCrypto ) | 
|  |  | 148 |  |                 throw new NotImplementedException( $"Type {GetType().Name} is a bad implementation. If {nameof( IsPublic | 
|  |  | 149 |  |  | 
|  |  | 150 |  |             throw new InvalidOperationException( $"Type {GetType().Name} is not intended for public key cryptography." ) | 
|  |  | 151 |  |         } | 
|  |  | 152 |  |  | 
|  |  | 153 |  |         /// <summary> | 
|  |  | 154 |  |         /// Determines if the specified <see cref="JsonWebKey"/> instance contains values at properties that represent t | 
|  |  | 155 |  |         /// </summary> | 
|  |  | 156 |  |         /// <para>If all required symmetric key properties (for the key type) are specified in the instance, the method  | 
|  |  | 157 |  |         /// <code>true</code> and not modify the <paramref name="missingProps"/> parameter.</para> | 
|  |  | 158 |  |         /// <para>If some property is missing, the method must return <code>false</code> and set <paramref name="missing | 
|  |  | 159 |  |         /// to a value - typically a <see cref="List{T}"/> - containing all missing properties.</para> | 
|  |  | 160 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 161 |  |         /// <param name="missingProps">A reference to a variable that tells the list of missing properties. Callers must | 
|  |  | 162 |  |         /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>. | 
|  |  | 163 |  |         public virtual bool IsSymmetricKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 164 |  |         { | 
|  |  | 165 |  |             throw ThrowDefaultForSymmetricKeyCrypto( nameof( IsSymmetricKeyComplete ) ); | 
|  |  | 166 |  |         } | 
|  |  | 167 |  |  | 
|  |  | 168 |  |         /// <summary> | 
|  |  | 169 |  |         /// Determines if the specified <see cref="JsonWebKey"/> instance contains a possibly valid symmetric key (see r | 
|  |  | 170 |  |         /// </summary> | 
|  |  | 171 |  |         /// <para>Because fully validating a key may require unfeasable amount of resources, this method only has to che | 
|  |  | 172 |  |         /// for obvious issues. As a guideline, we say that the code only verifies obvious issues if it runs in constant | 
|  |  | 173 |  |         /// It's perfectly valid for implementors to do nothing and simply return <code>true</code>.</para> | 
|  |  | 174 |  |         /// <para>This method assumes that <see cref="IsSymmetricKeyComplete"/> was called and returned <code>true</code | 
|  |  | 175 |  |         /// doesn't test again for the presence of required properties. It may throw <see cref="NullReferenceException"/ | 
|  |  | 176 |  |         /// if the caller doesn't see <see cref="IsSymmetricKeyComplete"/> returning <code>true</code> first.</para> | 
|  |  | 177 |  |         /// <para>If the valiation code finds no issue, this method must return <code>true</code> without modifying the  | 
|  |  | 178 |  |         /// <paramref name="errorMsg"/>.</para> | 
|  |  | 179 |  |         /// <para>If some issue is found, this method must return <code>false</code> and tell more details in the | 
|  |  | 180 |  |         /// <paramref name="errorMsg"/> parameter.</para> | 
|  |  | 181 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 182 |  |         /// <param name="errorMsg">A reference to a variable that will contain an error message. Callers must | 
|  |  | 183 |  |         /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>. | 
|  |  | 184 |  |         public virtual bool IsSymmetricKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 185 |  |         { | 
|  |  | 186 |  |             throw ThrowDefaultForSymmetricKeyCrypto( nameof( IsSymmetricKeyValid ) ); | 
|  |  | 187 |  |         } | 
|  |  | 188 |  |  | 
|  |  | 189 |  |         private Exception ThrowDefaultForSymmetricKeyCrypto( string methodName ) | 
|  |  | 190 |  |         { | 
|  |  | 191 |  |             if ( IsSymmetricKeyCrypto ) | 
|  |  | 192 |  |                 throw new NotImplementedException( $"Type {GetType().Name} is a bad implementation. If {nameof( IsSymmet | 
|  |  | 193 |  |  | 
|  |  | 194 |  |             throw new InvalidOperationException( $"Type {GetType().Name} is not intended for symmetric key cryptography. | 
|  |  | 195 |  |         } | 
|  |  | 196 |  |  | 
|  |  | 197 |  |         /// <summary> | 
|  |  | 198 |  |         /// Determines if the specified <see cref="JsonWebKey"/> instance contains values at properties that represent t | 
|  |  | 199 |  |         /// </summary> | 
|  |  | 200 |  |         /// <para>If all required secret key properties (for the key type) are specified in the instance, the method mus | 
|  |  | 201 |  |         /// <code>true</code> and not modify the <paramref name="missingProps"/> parameter.</para> | 
|  |  | 202 |  |         /// <para>If some property is missing, the method must return <code>false</code> and set <paramref name="missing | 
|  |  | 203 |  |         /// to a value - typically a <see cref="List{T}"/> - containing all missing properties.</para> | 
|  |  | 204 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 205 |  |         /// <param name="missingProps">A reference to a variable that tells the list of missing properties. Callers must | 
|  |  | 206 |  |         /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>. | 
|  |  | 207 |  |         public virtual bool IsSecretKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 208 |  |         { | 
|  |  | 209 |  |             throw ThrowDefaultForSecretKeyCrypto( nameof( IsSecretKeyComplete ) ); | 
|  |  | 210 |  |         } | 
|  |  | 211 |  |  | 
|  |  | 212 |  |         /// <summary> | 
|  |  | 213 |  |         /// Determines if the specified <see cref="JsonWebKey"/> instance contains a possibly valid secret key (see rema | 
|  |  | 214 |  |         /// </summary> | 
|  |  | 215 |  |         /// <para>Because fully validating a key may require unfeasable amount of resources, this method only has to che | 
|  |  | 216 |  |         /// for obvious issues. As a guideline, we say that the code only verifies obvious issues if it runs in constant | 
|  |  | 217 |  |         /// It's perfectly valid for implementors to do nothing and simply return <code>true</code>.</para> | 
|  |  | 218 |  |         /// <para>This method assumes that <see cref="IsSecretKeyComplete"/> was called and returned <code>true</code>.  | 
|  |  | 219 |  |         /// doesn't test again for the presence of required properties. It may throw <see cref="NullReferenceException"/ | 
|  |  | 220 |  |         /// if the caller doesn't see <see cref="IsSecretKeyComplete"/> returning <code>true</code> first.</para> | 
|  |  | 221 |  |         /// <para>If the valiation code finds no issue, this method must return <code>true</code> without modifying the  | 
|  |  | 222 |  |         /// <paramref name="errorMsg"/>.</para> | 
|  |  | 223 |  |         /// <para>If some issue is found, this method must return <code>false</code> and tell more details in the | 
|  |  | 224 |  |         /// <paramref name="errorMsg"/> parameter.</para> | 
|  |  | 225 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 226 |  |         /// <param name="errorMsg">A reference to a variable that will contain an error message. Callers must | 
|  |  | 227 |  |         /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>. | 
|  |  | 228 |  |         public virtual bool IsSecretKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 229 |  |         { | 
|  |  | 230 |  |             throw ThrowDefaultForSecretKeyCrypto( nameof( IsSecretKeyValid ) ); | 
|  |  | 231 |  |         } | 
|  |  | 232 |  |  | 
|  |  | 233 |  |         private Exception ThrowDefaultForSecretKeyCrypto( string methodName ) | 
|  |  | 234 |  |         { | 
|  |  | 235 |  |             if ( HasSecretKey ) | 
|  |  | 236 |  |                 throw new NotImplementedException( $"Type {GetType().Name} is a bad implementation. If {nameof( HasSecre | 
|  |  | 237 |  |  | 
|  |  | 238 |  |             throw new InvalidOperationException( $"Type {GetType().Name} is not intended to keys that have a secret comp | 
|  |  | 239 |  |         } | 
|  |  | 240 |  |  | 
|  |  | 241 |  |         private static volatile SortedSet<string> _validOperations; | 
|  |  | 242 |  |  | 
|  |  | 243 |  |         public static bool IsOperationValid( string opName ) | 
|  |  | 244 |  |         { | 
|  |  | 245 |  |             if ( _validOperations == null ) | 
|  |  | 246 |  |             { | 
|  |  | 247 |  |                 // No lock here; we accept multiple threads initializing. | 
|  |  | 248 |  |                 var validOperations = new SortedSet<string>(); | 
|  |  | 249 |  |  | 
|  |  | 250 |  |                 foreach ( var op in JsonWebKeyOperation.AllOperations ) | 
|  |  | 251 |  |                     validOperations.Add( op ); | 
|  |  | 252 |  |  | 
|  |  | 253 |  |                 _validOperations = validOperations; | 
|  |  | 254 |  |             } | 
|  |  | 255 |  |  | 
|  |  | 256 |  |             // No lock here; collection is read-only at this time. | 
|  |  | 257 |  |             return _validOperations.Contains( opName ); | 
|  |  | 258 |  |         } | 
|  |  | 259 |  |  | 
|  |  | 260 |  |         private volatile SortedSet<string> _compatibleOperations; | 
|  |  | 261 |  |  | 
|  |  | 262 |  |         public bool IsOperationCompatible( string opName ) | 
|  |  | 263 |  |         { | 
|  |  | 264 |  |             if ( _compatibleOperations == null ) | 
|  |  | 265 |  |             { | 
|  |  | 266 |  |                 // No lock here; we accept multiple threads initializing. | 
|  |  | 267 |  |                 var compatibleOperations = new SortedSet<string>(); | 
|  |  | 268 |  |  | 
|  |  | 269 |  |                 AddCompatibleOperations( compatibleOperations ); | 
|  |  | 270 |  |  | 
|  |  | 271 |  |                 _compatibleOperations = compatibleOperations; | 
|  |  | 272 |  |             } | 
|  |  | 273 |  |  | 
|  |  | 274 |  |             // No lock here; collection is read-only at this time. | 
|  |  | 275 |  |             return _compatibleOperations.Contains( opName ); | 
|  |  | 276 |  |         } | 
|  |  | 277 |  |  | 
|  |  | 278 |  |         /// <summary> | 
|  |  | 279 |  |         /// Adds to the specified collection all operations that can be performed with keys whose type is handled by | 
|  |  | 280 |  |         /// this object. | 
|  |  | 281 |  |         /// </summary> | 
|  |  | 282 |  |         /// For instance, if keys can only be used for digital signatures, this method should add only | 
|  |  | 283 |  |         /// <see cref="JsonWebKeyOperation.Sign"/> and <see cref="JsonWebKeyOperation.Verify"/>. | 
|  |  | 284 |  |         protected abstract void AddCompatibleOperations( ICollection<string> compatibleOperations ); | 
|  |  | 285 |  |  | 
|  |  | 286 |  |         private HashSet<string> _usedProperties; | 
|  |  | 287 |  |  | 
|  |  | 288 |  |         public bool IsPropertyUsed( string propName ) | 
|  |  | 289 |  |         { | 
|  |  | 290 |  |             if ( _usedProperties == null ) | 
|  |  | 291 |  |             { | 
|  |  | 292 |  |                 // No lock here; we accept multiple threads initializing. | 
|  |  | 293 |  |                 var usedProperties = new HashSet<string>(); | 
|  |  | 294 |  |  | 
|  |  | 295 |  |                 AddUsedProperties( usedProperties ); | 
|  |  | 296 |  |  | 
|  |  | 297 |  |                 usedProperties.Add( JsonWebKey.Property_Kid ); | 
|  |  | 298 |  |                 usedProperties.Add( JsonWebKey.Property_Kty ); | 
|  |  | 299 |  |                 usedProperties.Add( JsonWebKey.Property_KeyOps ); | 
|  |  | 300 |  |  | 
|  |  | 301 |  |                 _usedProperties = usedProperties; | 
|  |  | 302 |  |             } | 
|  |  | 303 |  |  | 
|  |  | 304 |  |             // No lock here; collection is read-only at this point. | 
|  |  | 305 |  |             return _usedProperties.Contains( propName ); | 
|  |  | 306 |  |         } | 
|  |  | 307 |  |  | 
|  |  | 308 |  |         /// <summary> | 
|  |  | 309 |  |         /// Adds to the specified collection all JsonWebKey properties that are useful to keys whose type is handled by | 
|  |  | 310 |  |         /// this object. | 
|  |  | 311 |  |         /// </summary> | 
|  |  | 312 |  |         /// <para>This method must add JSON property names, such as <code>"crv"</code>, <code>"p"</code>, etc. It must n | 
|  |  | 313 |  |         /// C# property names.</para> | 
|  |  | 314 |  |         /// <para>This method doesn't have to add <code>"kid"</code>, <code>"kty"</code> and <code>"key_ops"</code>. | 
|  |  | 315 |  |         /// Thes properties are assumed to be useful to all keys.</para>. | 
|  |  | 316 |  |         protected abstract void AddUsedProperties( ICollection<string> usedProperties ); | 
|  |  | 317 |  |  | 
|  |  | 318 |  |         [Flags] | 
|  |  | 319 |  |         public enum Options | 
|  |  | 320 |  |         { | 
|  |  | 321 |  |             /// <summary> | 
|  |  | 322 |  |             /// Use this value if you don't want to specify any other. | 
|  |  | 323 |  |             /// </summary> | 
|  |  | 324 |  |             None = 0, | 
|  |  | 325 |  |  | 
|  |  | 326 |  |             /// <summary> | 
|  |  | 327 |  |             /// Fails if any private key material is present. Use this to defend against leakage. | 
|  |  | 328 |  |             /// </summary> | 
|  |  | 329 |  |             /// This value is only used for keys that support public key cryptography. It's ignored in other key types. | 
|  |  | 330 |  |             DenyPrivateKey /*********/ = 1 << 0, | 
|  |  | 331 |  |  | 
|  |  | 332 |  |             /// <summary> | 
|  |  | 333 |  |             /// Fails if private key material is not fully present. Use this before storing or importing a JsonWebKey | 
|  |  | 334 |  |             /// value into a subsystem that needs to keep the private key. | 
|  |  | 335 |  |             /// </summary> | 
|  |  | 336 |  |             /// This value is only used for keys that support public key cryptography. It's ignored in other key types. | 
|  |  | 337 |  |             RequirePrivateKey /******/ = 1 << 1, | 
|  |  | 338 |  |  | 
|  |  | 339 |  |             /// <summary> | 
|  |  | 340 |  |             /// Fails if there the <code>"key_ops"</code> value of the verified key contains an incompatible operation. | 
|  |  | 341 |  |             /// </summary> | 
|  |  | 342 |  |             DenyIncompatibleOperations = 1 << 2, | 
|  |  | 343 |  |  | 
|  |  | 344 |  |             /// <summary> | 
|  |  | 345 |  |             /// Fails if the JsonWebKey object describes values at properties that are not used by the corresponding key | 
|  |  | 346 |  |             /// Use this to defend against properties incorrectly set, and also some forms of leakage. | 
|  |  | 347 |  |             /// </summary> | 
|  |  | 348 |  |             DenyExtraneousFields /***/ = 1 << 3, | 
|  |  | 349 |  |  | 
|  |  | 350 |  |             /// <summary> | 
|  |  | 351 |  |             /// Reserved for future use. | 
|  |  | 352 |  |             /// </summary> | 
|  |  | 353 |  |             VerifyDecrypt /**********/ = 1 << 10, | 
|  |  | 354 |  |  | 
|  |  | 355 |  |             /// <summary> | 
|  |  | 356 |  |             /// Reserved for future use. | 
|  |  | 357 |  |             /// </summary> | 
|  |  | 358 |  |             VerifySign /*************/ = 1 << 11, | 
|  |  | 359 |  |  | 
|  |  | 360 |  |             /// <summary> | 
|  |  | 361 |  |             /// Do not return <code>false</code> if the verification fails; throws an exception instead. | 
|  |  | 362 |  |             /// </summary> | 
|  |  | 363 |  |             ThrowException /*********/ = 1 << 20 | 
|  |  | 364 |  |         } | 
|  |  | 365 |  |  | 
|  |  | 366 |  |         /// <summary> | 
|  |  | 367 |  |         /// Verifies the specified JsonWebKey instance. | 
|  |  | 368 |  |         /// </summary> | 
|  |  | 369 |  |         /// <para>Verification first examines the <see cref="JsonWebKey.Kty"/> property to select a verifier instance | 
|  |  | 370 |  |         /// (for more information, see the <see cref="Register"/> method). If a verifier is found, it's used to check | 
|  |  | 371 |  |         /// if the key conforms to the corresponding key type.</para> | 
|  |  | 372 |  |         /// <param name="webKey">The instance to verify.</param> | 
|  |  | 373 |  |         /// <param name="options">Tells how verification is to behave.</param> | 
|  |  | 374 |  |         /// <param name="error">A reference to a variable that will tell the error message, if verification fails. This | 
|  |  | 375 |  |         /// is only set if the method returns <code>false</code>. If the method returns <code>true</code> or throws | 
|  |  | 376 |  |         /// an exception, the <paramref name="error"/> will not me modified.</param> | 
|  |  | 377 |  |         /// <returns><code>true</code> if the JsonWebKey value is valid, <code>false otherwise</code>.</returns> | 
|  |  | 378 |  |         /// <exception cref="ArgumentNullException">If the <paramref name="webKey"/> parameter is null.</exception> | 
|  |  | 379 |  |         /// <exception cref="ArgumentException">If the <paramref name="options"/> parameter contains invalid options.</e | 
|  |  | 380 |  |         /// <exception cref="JsonWebKeyVerificationException">If the JsonWebKey object is invalid and the option | 
|  |  | 381 |  |         ///     <see cref="Options.ThrowException"/> was specified.</exception> | 
|  |  | 382 |  |         /// <seealso cref="Options"/> | 
|  |  | 383 |  |         public bool Verify( JsonWebKey webKey, Options options, ref string error ) | 
|  |  | 384 |  |         { | 
|  |  | 385 |  |             if ( webKey == null ) | 
|  |  | 386 |  |                 throw new ArgumentNullException( nameof( webKey ) ); | 
|  |  | 387 |  |  | 
|  |  | 388 |  |             if ( options.HasFlag( Options.DenyPrivateKey ) && options.HasFlag( Options.RequirePrivateKey ) ) | 
|  |  | 389 |  |                 throw new ArgumentException( $"Cannot use {Options.DenyPrivateKey} and {Options.RequirePrivateKey} at sa | 
|  |  | 390 |  |  | 
|  |  | 391 |  |             if ( options.HasFlag( Options.VerifyDecrypt ) || options.HasFlag( Options.VerifySign ) ) | 
|  |  | 392 |  |                 throw new NotImplementedException(); | 
|  |  | 393 |  |  | 
|  |  | 394 |  |             if ( !VerifyNotNullOrWhiteSpace( webKey.Kty, JsonWebKey.Property_Kty, options, ref error ) ) | 
|  |  | 395 |  |                 return false; | 
|  |  | 396 |  |  | 
|  |  | 397 |  |             if ( webKey.Kty != Kty ) | 
|  |  | 398 |  |                 return SetError( options, ref error, $"Expected {JsonWebKey.Property_Kty} to be \"{Kty}\", but found som | 
|  |  | 399 |  |  | 
|  |  | 400 |  |             if ( IsPublicKeyCrypto ) | 
|  |  | 401 |  |             { | 
|  |  | 402 |  |                 if ( !VerifyPublicKey( this, webKey, options, ref error ) ) | 
|  |  | 403 |  |                     return false; | 
|  |  | 404 |  |  | 
|  |  | 405 |  |                 if ( options.HasFlag( Options.DenyPrivateKey ) && !VerifyNoPrivateKey( this, webKey, options, ref error  | 
|  |  | 406 |  |                     return false; | 
|  |  | 407 |  |  | 
|  |  | 408 |  |                 if ( options.HasFlag( Options.RequirePrivateKey ) && !VerifyPrivateKey( this, webKey, options, ref error | 
|  |  | 409 |  |                     return false; | 
|  |  | 410 |  |             } | 
|  |  | 411 |  |  | 
|  |  | 412 |  |             if ( IsSymmetricKeyCrypto ) | 
|  |  | 413 |  |             { | 
|  |  | 414 |  |                 if ( !VerifySymmetricKey( this, webKey, options, ref error ) ) | 
|  |  | 415 |  |                     return false; | 
|  |  | 416 |  |             } | 
|  |  | 417 |  |  | 
|  |  | 418 |  |             if ( HasSecretKey && !VerifySecretKey( this, webKey, options, ref error ) ) | 
|  |  | 419 |  |                 return false; | 
|  |  | 420 |  |  | 
|  |  | 421 |  |             if ( options.HasFlag( Options.DenyIncompatibleOperations ) && !VerifyOperationsAreCompatible( this, webKey,  | 
|  |  | 422 |  |                 return false; | 
|  |  | 423 |  |  | 
|  |  | 424 |  |             if ( !options.HasFlag( Options.DenyIncompatibleOperations ) && !VerifyOperationsAreValid( webKey, options, r | 
|  |  | 425 |  |                 return false; | 
|  |  | 426 |  |  | 
|  |  | 427 |  |             if ( options.HasFlag( Options.DenyExtraneousFields ) && !VerifyNoExtraneousFields( this, webKey, options, re | 
|  |  | 428 |  |                 return false; | 
|  |  | 429 |  |  | 
|  |  | 430 |  |             return true; | 
|  |  | 431 |  |         } | 
|  |  | 432 |  |  | 
|  |  | 433 |  |         /// <summary> | 
|  |  | 434 |  |         /// Verifies the specified JsonWebKey instance according to <see cref="JsonWebKey.Kty"/>. | 
|  |  | 435 |  |         /// </summary> | 
|  |  | 436 |  |         /// This method selects a verifier based on the value of <see cref="JsonWebKey.Kty"/>, | 
|  |  | 437 |  |         /// then calls the verifier's <see cref="Verify"/> method. | 
|  |  | 438 |  |         /// <param name="webKey"></param> | 
|  |  | 439 |  |         /// <param name="options"></param> | 
|  |  | 440 |  |         /// <param name="error"></param> | 
|  |  | 441 |  |         /// <seealso cref="Verify"/> | 
|  |  | 442 |  |         public static bool VerifyByKeyType( JsonWebKey webKey, Options options, ref string error ) | 
|  |  | 443 |  |         { | 
|  |  | 444 |  |             if ( webKey == null ) | 
|  |  | 445 |  |                 throw new ArgumentNullException( nameof( webKey ) ); | 
|  |  | 446 |  |  | 
|  |  | 447 |  |             if ( !VerifyNotNullOrWhiteSpace( webKey.Kty, JsonWebKey.Property_Kty, options, ref error ) ) | 
|  |  | 448 |  |                 return false; | 
|  |  | 449 |  |  | 
|  |  | 450 |  |             JsonWebKeyVerifier verifier; | 
|  |  | 451 |  |  | 
|  |  | 452 |  |             lock ( _verifiersLock ) | 
|  |  | 453 |  |             { | 
|  |  | 454 |  |                 InitVerifiers(); | 
|  |  | 455 |  |                 if ( !VerifyEnum( webKey.Kty, JsonWebKey.Property_Kty, _verifiers, out verifier, options, ref error ) ) | 
|  |  | 456 |  |                     return false; | 
|  |  | 457 |  |             } | 
|  |  | 458 |  |  | 
|  |  | 459 |  |             return verifier.Verify( webKey, options, ref error ); | 
|  |  | 460 |  |         } | 
|  |  | 461 |  |  | 
|  |  | 462 |  |         private static readonly object _verifiersLock = new object(); | 
|  |  | 463 |  |  | 
|  |  | 464 |  |         private static volatile SortedDictionary<string, JsonWebKeyVerifier> _verifiers; | 
|  |  | 465 |  |  | 
|  |  | 466 |  |         private static void InitVerifiers() | 
|  |  | 467 |  |         { | 
|  |  | 468 |  |             if ( _verifiers != null ) | 
|  |  | 469 |  |                 return; | 
|  |  | 470 |  |  | 
|  |  | 471 |  |             var verifiers = new SortedDictionary<string, JsonWebKeyVerifier>(); | 
|  |  | 472 |  |  | 
|  |  | 473 |  |             RegisterAt( verifiers, OctetKeyVerifier.Instance ); | 
|  |  | 474 |  |             RegisterAt( verifiers, EllipticCurveKeyVerifier.Instance ); | 
|  |  | 475 |  |             RegisterAt( verifiers, EllipticCurveHsmKeyVerifier.Instance ); | 
|  |  | 476 |  |             RegisterAt( verifiers, RsaKeyVerifier.Instance ); | 
|  |  | 477 |  |             RegisterAt( verifiers, RsaHsmKeyVerifier.Instance ); | 
|  |  | 478 |  |  | 
|  |  | 479 |  |             _verifiers = verifiers; | 
|  |  | 480 |  |         } | 
|  |  | 481 |  |  | 
|  |  | 482 |  |         /// <summary> | 
|  |  | 483 |  |         /// Registers a verifier for a <see cref="Kty"/> value. | 
|  |  | 484 |  |         /// </summary> | 
|  |  | 485 |  |         /// Throws exception is a previous verifier for same <see cref="Kty"/> value is already registered. | 
|  |  | 486 |  |         /// There is no need to register verifiers for values described on <see cref="JsonWebKeyType"/>. | 
|  |  | 487 |  |         /// <param name="verifier">The verifier to register.</param> | 
|  |  | 488 |  |         /// <seealso cref="GetVerifier"/> | 
|  |  | 489 |  |         public static void Register( JsonWebKeyVerifier verifier ) | 
|  |  | 490 |  |         { | 
|  |  | 491 |  |             if ( verifier == null ) | 
|  |  | 492 |  |                 throw new ArgumentNullException( nameof( verifier ) ); | 
|  |  | 493 |  |  | 
|  |  | 494 |  |             lock ( _verifiersLock ) | 
|  |  | 495 |  |             { | 
|  |  | 496 |  |                 InitVerifiers(); | 
|  |  | 497 |  |                 RegisterAt( _verifiers, verifier ); | 
|  |  | 498 |  |             } | 
|  |  | 499 |  |         } | 
|  |  | 500 |  |  | 
|  |  | 501 |  |         private static void RegisterAt( IDictionary<string, JsonWebKeyVerifier> verifiers, JsonWebKeyVerifier verifier ) | 
|  |  | 502 |  |         { | 
|  |  | 503 |  |             Debug.Assert( verifiers != null ); | 
|  |  | 504 |  |  | 
|  |  | 505 |  |             if ( verifiers.TryGetValue( verifier.Kty, out JsonWebKeyVerifier _ ) ) | 
|  |  | 506 |  |                 throw new InvalidOperationException( $"Value already registered for \"{verifier.Kty}\"." ); | 
|  |  | 507 |  |  | 
|  |  | 508 |  |             verifiers[verifier.Kty] = verifier; | 
|  |  | 509 |  |         } | 
|  |  | 510 |  |  | 
|  |  | 511 |  |         /// <summary> | 
|  |  | 512 |  |         /// Returns the verifier registered for the specified kty value, or null if the kty value was not registered. | 
|  |  | 513 |  |         /// </summary> | 
|  |  | 514 |  |         /// This method never returns null for values described on <see cref="JsonWebKeyType"/>. | 
|  |  | 515 |  |         /// <seealso cref="Register"/> | 
|  |  | 516 |  |         public static JsonWebKeyVerifier GetVerifier( string kty ) | 
|  |  | 517 |  |         { | 
|  |  | 518 |  |             if ( string.IsNullOrWhiteSpace( kty ) ) | 
|  |  | 519 |  |                 throw new ArgumentNullException( nameof( kty ) ); | 
|  |  | 520 |  |  | 
|  |  | 521 |  |             lock ( _verifiersLock ) | 
|  |  | 522 |  |             { | 
|  |  | 523 |  |                 InitVerifiers(); | 
|  |  | 524 |  |                 _verifiers.TryGetValue( kty, out JsonWebKeyVerifier result ); | 
|  |  | 525 |  |                 return result; | 
|  |  | 526 |  |             } | 
|  |  | 527 |  |         } | 
|  |  | 528 |  |  | 
|  |  | 529 |  |         private static bool VerifyPublicKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref string | 
|  |  | 530 |  |         { | 
|  |  | 531 |  |             return VerifyKeyParameters( | 
|  |  | 532 |  |                 "public", | 
|  |  | 533 |  |                 verifier.IsPublicKeyComplete, | 
|  |  | 534 |  |                 verifier.IsPublicKeyValid, | 
|  |  | 535 |  |                 webKey, | 
|  |  | 536 |  |                 options, | 
|  |  | 537 |  |                 ref error ); | 
|  |  | 538 |  |         } | 
|  |  | 539 |  |  | 
|  |  | 540 |  |         private static bool VerifyNoPrivateKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref str | 
|  |  | 541 |  |         { | 
|  |  | 542 |  |             ICollection<string> specifiedProps = null; | 
|  |  | 543 |  |             if ( !verifier.IsAnyPrivateKeyParamSpecified( webKey, ref specifiedProps ) ) | 
|  |  | 544 |  |                 return true; | 
|  |  | 545 |  |  | 
|  |  | 546 |  |             if ( specifiedProps == null || specifiedProps.Count == 0 ) | 
|  |  | 547 |  |                 return SetError( options, ref error, "Private key parameters must not be specified." ); | 
|  |  | 548 |  |  | 
|  |  | 549 |  |             return SetError( options, ref error, $"Private key parameters must not be specified: {SurroundWithQuotes( sp | 
|  |  | 550 |  |         } | 
|  |  | 551 |  |  | 
|  |  | 552 |  |         private static bool VerifyPrivateKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref strin | 
|  |  | 553 |  |         { | 
|  |  | 554 |  |             return VerifyKeyParameters( | 
|  |  | 555 |  |                 "private", | 
|  |  | 556 |  |                 verifier.IsPrivateKeyComplete, | 
|  |  | 557 |  |                 verifier.IsPrivateKeyValid, | 
|  |  | 558 |  |                 webKey, | 
|  |  | 559 |  |                 options, | 
|  |  | 560 |  |                 ref error ); | 
|  |  | 561 |  |         } | 
|  |  | 562 |  |  | 
|  |  | 563 |  |         private static bool VerifySymmetricKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref str | 
|  |  | 564 |  |         { | 
|  |  | 565 |  |             return VerifyKeyParameters( | 
|  |  | 566 |  |                 "symmetric", | 
|  |  | 567 |  |                 verifier.IsSymmetricKeyComplete, | 
|  |  | 568 |  |                 verifier.IsSymmetricKeyValid, | 
|  |  | 569 |  |                 webKey, | 
|  |  | 570 |  |                 options, | 
|  |  | 571 |  |                 ref error ); | 
|  |  | 572 |  |         } | 
|  |  | 573 |  |  | 
|  |  | 574 |  |         private static bool VerifySecretKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref string | 
|  |  | 575 |  |         { | 
|  |  | 576 |  |             return VerifyKeyParameters( | 
|  |  | 577 |  |                 "secret", | 
|  |  | 578 |  |                 verifier.IsSecretKeyComplete, | 
|  |  | 579 |  |                 verifier.IsSecretKeyValid, | 
|  |  | 580 |  |                 webKey, | 
|  |  | 581 |  |                 options, | 
|  |  | 582 |  |                 ref error ); | 
|  |  | 583 |  |         } | 
|  |  | 584 |  |  | 
|  |  | 585 |  |         private delegate bool IsCompleteProc( JsonWebKey webKey, ref ICollection<string> missingProps ); | 
|  |  | 586 |  |  | 
|  |  | 587 |  |         private delegate bool IsValidProc( JsonWebKey webKey, ref string errorMsg ); | 
|  |  | 588 |  |  | 
|  |  | 589 |  |         private static bool VerifyKeyParameters( string name, IsCompleteProc isCompleteProc, IsValidProc isValidProc, Js | 
|  |  | 590 |  |         { | 
|  |  | 591 |  |             ICollection<string> missingParams = null; | 
|  |  | 592 |  |             if ( !isCompleteProc( webKey, ref missingParams ) ) | 
|  |  | 593 |  |             { | 
|  |  | 594 |  |                 if ( missingParams == null || missingParams.Count == 0 ) | 
|  |  | 595 |  |                     return SetError( options, ref error, $"Missing {name} key parameters." ); | 
|  |  | 596 |  |  | 
|  |  | 597 |  |                 return SetError( options, ref error, $"Missing {name} key parameters: {SurroundWithQuotes( missingParams | 
|  |  | 598 |  |             } | 
|  |  | 599 |  |  | 
|  |  | 600 |  |             string errorMsg = null; | 
|  |  | 601 |  |             if ( !isValidProc( webKey, ref errorMsg ) ) | 
|  |  | 602 |  |             { | 
|  |  | 603 |  |                 if ( errorMsg != null ) | 
|  |  | 604 |  |                     errorMsg = $"Invalid {name} key parameters: {errorMsg}"; | 
|  |  | 605 |  |                 else | 
|  |  | 606 |  |                     errorMsg = $"Invalid {name} key parameters."; | 
|  |  | 607 |  |  | 
|  |  | 608 |  |                 return SetError( options, ref error, errorMsg ); | 
|  |  | 609 |  |             } | 
|  |  | 610 |  |  | 
|  |  | 611 |  |             return true; | 
|  |  | 612 |  |         } | 
|  |  | 613 |  |  | 
|  |  | 614 |  |         private static bool VerifyOperationsAreValid( JsonWebKey webKey, Options options, ref string error ) | 
|  |  | 615 |  |         { | 
|  |  | 616 |  |             if ( webKey.KeyOps == null ) | 
|  |  | 617 |  |                 return true; | 
|  |  | 618 |  |  | 
|  |  | 619 |  |             StringBuilder invalid = null; | 
|  |  | 620 |  |  | 
|  |  | 621 |  |             foreach ( var keyOp in webKey.KeyOps ) | 
|  |  | 622 |  |                 if ( !IsOperationValid( keyOp ) ) | 
|  |  | 623 |  |                 { | 
|  |  | 624 |  |                     if ( invalid == null ) | 
|  |  | 625 |  |                         invalid = new StringBuilder(); | 
|  |  | 626 |  |                     else | 
|  |  | 627 |  |                         invalid.Append( ", " ); | 
|  |  | 628 |  |  | 
|  |  | 629 |  |                     invalid.Append( '"' ).Append( keyOp ).Append( '"' ); | 
|  |  | 630 |  |                 } | 
|  |  | 631 |  |  | 
|  |  | 632 |  |             if ( invalid != null ) | 
|  |  | 633 |  |             { | 
|  |  | 634 |  |                 var validOps = SurroundWithQuotes( _validOperations ); | 
|  |  | 635 |  |                 return SetError( options, ref error, $"Found invalid operations: {invalid}. Valid operations are: {valid | 
|  |  | 636 |  |             } | 
|  |  | 637 |  |  | 
|  |  | 638 |  |             return true; | 
|  |  | 639 |  |         } | 
|  |  | 640 |  |  | 
|  |  | 641 |  |         private static bool VerifyOperationsAreCompatible( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options optio | 
|  |  | 642 |  |         { | 
|  |  | 643 |  |             if ( webKey.KeyOps == null ) | 
|  |  | 644 |  |                 return true; | 
|  |  | 645 |  |  | 
|  |  | 646 |  |             StringBuilder incompatible = null; | 
|  |  | 647 |  |  | 
|  |  | 648 |  |             foreach ( var keyOp in webKey.KeyOps ) | 
|  |  | 649 |  |                 if ( !verifier.IsOperationCompatible( keyOp ) ) | 
|  |  | 650 |  |                 { | 
|  |  | 651 |  |                     if ( incompatible == null ) | 
|  |  | 652 |  |                         incompatible = new StringBuilder(); | 
|  |  | 653 |  |                     else | 
|  |  | 654 |  |                         incompatible.Append( ", " ); | 
|  |  | 655 |  |  | 
|  |  | 656 |  |                     incompatible.Append( '"' ).Append( keyOp ).Append( '"' ); | 
|  |  | 657 |  |                 } | 
|  |  | 658 |  |  | 
|  |  | 659 |  |             if ( incompatible != null ) | 
|  |  | 660 |  |             { | 
|  |  | 661 |  |                 var compatibleOps = SurroundWithQuotes( verifier._compatibleOperations ); | 
|  |  | 662 |  |                 return SetError( options, ref error, $"Found invalid or incompatible operations: {incompatible}. Valid a | 
|  |  | 663 |  |             } | 
|  |  | 664 |  |  | 
|  |  | 665 |  |             return true; | 
|  |  | 666 |  |         } | 
|  |  | 667 |  |  | 
|  |  | 668 |  |         private static bool VerifyNoExtraneousFields( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, r | 
|  |  | 669 |  |         { | 
|  |  | 670 |  |             StringBuilder extraneous = null; | 
|  |  | 671 |  |  | 
|  |  | 672 |  |             void VerifyProperty( string name, object value ) | 
|  |  | 673 |  |             { | 
|  |  | 674 |  |                 if ( value == null || verifier.IsPropertyUsed( name ) ) | 
|  |  | 675 |  |                     return; | 
|  |  | 676 |  |  | 
|  |  | 677 |  |                 if ( extraneous == null ) | 
|  |  | 678 |  |                     extraneous = new StringBuilder(); | 
|  |  | 679 |  |                 else | 
|  |  | 680 |  |                     extraneous.Append( ", " ); | 
|  |  | 681 |  |  | 
|  |  | 682 |  |                 extraneous.Append( '"' ).Append( name ).Append( '"' ); | 
|  |  | 683 |  |             } | 
|  |  | 684 |  |  | 
|  |  | 685 |  |             webKey.VisitProperties( VerifyProperty ); | 
|  |  | 686 |  |  | 
|  |  | 687 |  |             if ( extraneous != null ) | 
|  |  | 688 |  |                 return SetError( options, ref error, $"Extraneous properties: {extraneous}" ); | 
|  |  | 689 |  |  | 
|  |  | 690 |  |             return true; | 
|  |  | 691 |  |         } | 
|  |  | 692 |  |  | 
|  |  | 693 |  |         private static bool VerifyEnum<T>( string value, string name, IDictionary<string, T> dictionary, out T item, Opt | 
|  |  | 694 |  |         { | 
|  |  | 695 |  |             if ( !dictionary.TryGetValue( value, out item ) ) | 
|  |  | 696 |  |                 return SetError( options, ref error, $"Expected {name} to be one of {SurroundWithQuotes( dictionary.Keys | 
|  |  | 697 |  |  | 
|  |  | 698 |  |             if ( item == null ) | 
|  |  | 699 |  |                 throw new InvalidOperationException( $"Dictionary for {name} is returning null for \"{value}\"." ); | 
|  |  | 700 |  |  | 
|  |  | 701 |  |             return true; | 
|  |  | 702 |  |         } | 
|  |  | 703 |  |  | 
|  |  | 704 |  |         private static bool VerifyNotNullOrWhiteSpace( string value, string name, Options options, ref string error ) | 
|  |  | 705 |  |         { | 
|  |  | 706 |  |             if ( string.IsNullOrWhiteSpace( value ) ) | 
|  |  | 707 |  |                 return SetError( options, ref error, $"A value for {name} is mandatory." ); | 
|  |  | 708 |  |  | 
|  |  | 709 |  |             return true; | 
|  |  | 710 |  |         } | 
|  |  | 711 |  |  | 
|  |  | 712 |  |         private static bool SetError( Options options, ref string error, string message ) | 
|  |  | 713 |  |         { | 
|  |  | 714 |  |             if ( options.HasFlag( Options.ThrowException ) ) | 
|  |  | 715 |  |                 throw new JsonWebKeyVerificationException( message ); | 
|  |  | 716 |  |             error = message; | 
|  |  | 717 |  |             return false; | 
|  |  | 718 |  |         } | 
|  |  | 719 |  |  | 
|  |  | 720 |  |         /// <summary> | 
|  |  | 721 |  |         /// Helper method that surrounds string values with double-quotes. | 
|  |  | 722 |  |         /// </summary> | 
|  |  | 723 |  |         /// For instance, the strings Foo, Bar cause this method to return <code>"Foo", "Bar"</code>. | 
|  |  | 724 |  |         protected static string SurroundWithQuotes( ICollection<string> items ) | 
|  |  | 725 |  |         { | 
|  |  | 726 |  |             return "\"" + string.Join( "\", \"", items ) + "\""; | 
|  |  | 727 |  |         } | 
|  |  | 728 |  |  | 
|  |  | 729 |  |         /// <summary> | 
|  |  | 730 |  |         /// Helper method that joins the operation of creating a collection (if required) and adding an item to it. | 
|  |  | 731 |  |         /// </summary> | 
|  |  | 732 |  |         /// If the collection is null, this method creates one of type <see cref="List{T}"/>. Then it adds the specified | 
|  |  | 733 |  |         protected static void AddItem<T>( ref ICollection<T> items, T newItem ) | 
|  |  | 734 |  |         { | 
|  |  | 735 |  |             if ( items == null ) | 
|  |  | 736 |  |                 items = new List<T>(); | 
|  |  | 737 |  |             items.Add( newItem ); | 
|  |  | 738 |  |         } | 
|  |  | 739 |  |  | 
|  |  | 740 |  |         /// <summary> | 
|  |  | 741 |  |         /// Helper method that validates the size of a byte array. | 
|  |  | 742 |  |         /// </summary> | 
|  |  | 743 |  |         /// <para>A valid array meets the following criteria:</para> | 
|  |  | 744 |  |         /// <list type="bullet"> | 
|  |  | 745 |  |         /// <item><description>is not <code>null</code>;</description></item> | 
|  |  | 746 |  |         /// <item><description>the length is at least <paramref name="requiredSize"/>; and</description></item> | 
|  |  | 747 |  |         /// <item><description>excess leading bytes are all zeros.</description></item> | 
|  |  | 748 |  |         /// </list> | 
|  |  | 749 |  |         /// <param name="value">The array to validate.</param> | 
|  |  | 750 |  |         /// <param name="name">The array name, which may be used to build error messages.</param> | 
|  |  | 751 |  |         /// <param name="requiredSize">The required size, in bytes.</param> | 
|  |  | 752 |  |         /// <param name="errorMsg">A reference to a variable that will contain the error message. This is only set | 
|  |  | 753 |  |         /// if the method returns <code>false</code>.</param> | 
|  |  | 754 |  |         /// <returns><code>true</code> if the array has a valid size; <code>false otherwise</code>.</returns> | 
|  |  | 755 |  |         protected static bool ValidateKeyParameterSize( byte[] value, string name, int requiredSize, ref string errorMsg | 
|  |  | 756 |  |         { | 
|  |  | 757 |  |             if ( value == null || value.Length < requiredSize ) | 
|  |  | 758 |  |             { | 
|  |  | 759 |  |                 var sizeDesc = value == null ? "null" : value.Length.ToString(); | 
|  |  | 760 |  |                 errorMsg = $"Expected {name} to have at least {requiredSize} bytes, but found {sizeDesc}."; | 
|  |  | 761 |  |                 return false; | 
|  |  | 762 |  |             } | 
|  |  | 763 |  |  | 
|  |  | 764 |  |             var excess = value.Length - requiredSize; | 
|  |  | 765 |  |             for ( var i = 0; i < excess; ++i ) | 
|  |  | 766 |  |             { | 
|  |  | 767 |  |                 if ( value[i] != 0 ) | 
|  |  | 768 |  |                     errorMsg = $"Expected {name} to have at most {requiredSize} bytes, but found {requiredSize + excess} | 
|  |  | 769 |  |                 --excess; | 
|  |  | 770 |  |             } | 
|  |  | 771 |  |  | 
|  |  | 772 |  |             return true; | 
|  |  | 773 |  |         } | 
|  |  | 774 |  |     } | 
|  |  | 775 |  |  | 
|  |  | 776 |  |     internal sealed class OctetKeyVerifier : JsonWebKeyVerifier | 
|  |  | 777 |  |     { | 
|  |  | 778 |  |         public static JsonWebKeyVerifier Instance { get; } = new OctetKeyVerifier(); | 
|  |  | 779 |  |  | 
|  |  | 780 |  |         private OctetKeyVerifier() : base( JsonWebKeyType.Octet ) | 
|  |  | 781 |  |         { | 
|  |  | 782 |  |         } | 
|  |  | 783 |  |  | 
|  |  | 784 |  |         public override bool IsPublicKeyCrypto => false; | 
|  |  | 785 |  |         public override bool IsSymmetricKeyCrypto => true; | 
|  |  | 786 |  |         public override bool HasSecretKey => false; | 
|  |  | 787 |  |  | 
|  |  | 788 |  |         protected override void AddCompatibleOperations( ICollection<string> compatibleOperations ) | 
|  |  | 789 |  |         { | 
|  |  | 790 |  |             compatibleOperations.Add( JsonWebKeyOperation.Encrypt ); | 
|  |  | 791 |  |             compatibleOperations.Add( JsonWebKeyOperation.Decrypt ); | 
|  |  | 792 |  |             compatibleOperations.Add( JsonWebKeyOperation.Wrap ); | 
|  |  | 793 |  |             compatibleOperations.Add( JsonWebKeyOperation.Unwrap ); | 
|  |  | 794 |  |         } | 
|  |  | 795 |  |  | 
|  |  | 796 |  |         protected override void AddUsedProperties( ICollection<string> usedProperties ) | 
|  |  | 797 |  |         { | 
|  |  | 798 |  |             usedProperties.Add( JsonWebKey.Property_K ); | 
|  |  | 799 |  |         } | 
|  |  | 800 |  |  | 
|  |  | 801 |  |         public override bool IsSymmetricKeyComplete( JsonWebKey arg, ref ICollection<string> missingProps ) | 
|  |  | 802 |  |         { | 
|  |  | 803 |  |             if ( arg.K == null || arg.K.Length == 0 ) | 
|  |  | 804 |  |             { | 
|  |  | 805 |  |                 AddItem( ref missingProps, JsonWebKey.Property_K ); | 
|  |  | 806 |  |                 return false; | 
|  |  | 807 |  |             } | 
|  |  | 808 |  |  | 
|  |  | 809 |  |             return true; | 
|  |  | 810 |  |         } | 
|  |  | 811 |  |  | 
|  |  | 812 |  |         public override bool IsSymmetricKeyValid( JsonWebKey arg, ref string errorMsg ) | 
|  |  | 813 |  |         { | 
|  |  | 814 |  |             return true; | 
|  |  | 815 |  |         } | 
|  |  | 816 |  |     } | 
|  |  | 817 |  |  | 
|  |  | 818 |  |     internal abstract class EllipticCurveKeyVerifierBase : JsonWebKeyVerifier | 
|  |  | 819 |  |     { | 
|  |  | 820 |  |         protected EllipticCurveKeyVerifierBase( string kty ) : base( kty ) | 
|  |  | 821 |  |         { | 
|  |  | 822 |  |         } | 
|  |  | 823 |  |  | 
|  |  | 824 |  |         public override bool IsPublicKeyCrypto => true; | 
|  |  | 825 |  |         public override bool IsSymmetricKeyCrypto => false; | 
|  |  | 826 |  |  | 
|  |  | 827 |  |         protected override void AddCompatibleOperations( ICollection<string> compatibleOperations ) | 
|  |  | 828 |  |         { | 
|  |  | 829 |  |             compatibleOperations.Add( JsonWebKeyOperation.Sign ); | 
|  |  | 830 |  |             compatibleOperations.Add( JsonWebKeyOperation.Verify ); | 
|  |  | 831 |  |         } | 
|  |  | 832 |  |  | 
|  |  | 833 |  |         protected override void AddUsedProperties( ICollection<string> usedProperties ) | 
|  |  | 834 |  |         { | 
|  |  | 835 |  |             usedProperties.Add( JsonWebKey.Property_Crv ); | 
|  |  | 836 |  |             usedProperties.Add( JsonWebKey.Property_X ); | 
|  |  | 837 |  |             usedProperties.Add( JsonWebKey.Property_Y ); | 
|  |  | 838 |  |         } | 
|  |  | 839 |  |  | 
|  |  | 840 |  |         public override bool IsPublicKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 841 |  |         { | 
|  |  | 842 |  |             var result = true; | 
|  |  | 843 |  |  | 
|  |  | 844 |  |             if ( string.IsNullOrWhiteSpace( webKey.CurveName ) ) | 
|  |  | 845 |  |             { | 
|  |  | 846 |  |                 AddItem( ref missingProps, JsonWebKey.Property_Crv ); | 
|  |  | 847 |  |                 result = false; | 
|  |  | 848 |  |             } | 
|  |  | 849 |  |  | 
|  |  | 850 |  |             if ( webKey.X == null || webKey.X.Length == 0 ) | 
|  |  | 851 |  |             { | 
|  |  | 852 |  |                 AddItem( ref missingProps, JsonWebKey.Property_X ); | 
|  |  | 853 |  |                 result = false; | 
|  |  | 854 |  |             } | 
|  |  | 855 |  |  | 
|  |  | 856 |  |             if ( webKey.Y == null || webKey.Y.Length == 0 ) | 
|  |  | 857 |  |             { | 
|  |  | 858 |  |                 AddItem( ref missingProps, JsonWebKey.Property_Y ); | 
|  |  | 859 |  |                 result = false; | 
|  |  | 860 |  |             } | 
|  |  | 861 |  |  | 
|  |  | 862 |  |             return result; | 
|  |  | 863 |  |         } | 
|  |  | 864 |  |  | 
|  |  | 865 |  |         public override bool IsPublicKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 866 |  |         { | 
|  |  | 867 |  |             var requiredSize = JsonWebKeyCurveName.GetKeyParameterSize( webKey.CurveName ); | 
|  |  | 868 |  |  | 
|  |  | 869 |  |             if ( requiredSize < 0 ) | 
|  |  | 870 |  |             { | 
|  |  | 871 |  |                 errorMsg = $"Unsupported curve: \"{webKey.CurveName}\". Supported curves are: {SurroundWithQuotes( JsonW | 
|  |  | 872 |  |                 return false; | 
|  |  | 873 |  |             } | 
|  |  | 874 |  |  | 
|  |  | 875 |  |             if ( !ValidateKeyParameterSize( webKey.X, nameof( webKey.X ), requiredSize, ref errorMsg ) ) | 
|  |  | 876 |  |                 return false; | 
|  |  | 877 |  |  | 
|  |  | 878 |  |             if ( !ValidateKeyParameterSize( webKey.Y, nameof( webKey.Y ), requiredSize, ref errorMsg ) ) | 
|  |  | 879 |  |                 return false; | 
|  |  | 880 |  |  | 
|  |  | 881 |  |             return true; | 
|  |  | 882 |  |         } | 
|  |  | 883 |  |     } | 
|  |  | 884 |  |  | 
|  |  | 885 |  |     internal sealed class EllipticCurveKeyVerifier : EllipticCurveKeyVerifierBase | 
|  |  | 886 |  |     { | 
|  |  | 887 |  |         public static JsonWebKeyVerifier Instance { get; } = new EllipticCurveKeyVerifier(); | 
|  |  | 888 |  |  | 
|  |  | 889 |  |         private EllipticCurveKeyVerifier() : base( JsonWebKeyType.EllipticCurve ) | 
|  |  | 890 |  |         { | 
|  |  | 891 |  |         } | 
|  |  | 892 |  |  | 
|  |  | 893 |  |         public override bool HasSecretKey => false; | 
|  |  | 894 |  |  | 
|  |  | 895 |  |         protected override void AddUsedProperties( ICollection<string> usedProperties ) | 
|  |  | 896 |  |         { | 
|  |  | 897 |  |             base.AddUsedProperties( usedProperties ); | 
|  |  | 898 |  |             usedProperties.Add( JsonWebKey.Property_D ); | 
|  |  | 899 |  |         } | 
|  |  | 900 |  |  | 
|  |  | 901 |  |         public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 902 |  |         { | 
|  |  | 903 |  |             if ( webKey.D == null || webKey.D.Length == 0 ) | 
|  |  | 904 |  |             { | 
|  |  | 905 |  |                 AddItem( ref missingProps, JsonWebKey.Property_D ); | 
|  |  | 906 |  |                 return false; | 
|  |  | 907 |  |             } | 
|  |  | 908 |  |  | 
|  |  | 909 |  |             return true; | 
|  |  | 910 |  |         } | 
|  |  | 911 |  |  | 
|  |  | 912 |  |         public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 913 |  |         { | 
|  |  | 914 |  |             var requiredSize = JsonWebKeyCurveName.GetKeyParameterSize( webKey.CurveName ); | 
|  |  | 915 |  |  | 
|  |  | 916 |  |             if ( requiredSize < 0 ) | 
|  |  | 917 |  |             { | 
|  |  | 918 |  |                 errorMsg = $"Unsupported curve: \"{webKey.CurveName}\". Supported curves are: {SurroundWithQuotes( JsonW | 
|  |  | 919 |  |                 return false; | 
|  |  | 920 |  |             } | 
|  |  | 921 |  |  | 
|  |  | 922 |  |             if ( !ValidateKeyParameterSize( webKey.D, nameof( webKey.D ), requiredSize, ref errorMsg ) ) | 
|  |  | 923 |  |                 return false; | 
|  |  | 924 |  |  | 
|  |  | 925 |  |             return true; | 
|  |  | 926 |  |         } | 
|  |  | 927 |  |     } | 
|  |  | 928 |  |  | 
|  |  | 929 |  |     internal sealed class EllipticCurveHsmKeyVerifier : EllipticCurveKeyVerifierBase | 
|  |  | 930 |  |     { | 
|  |  | 931 |  |         public static JsonWebKeyVerifier Instance { get; } = new EllipticCurveHsmKeyVerifier(); | 
|  |  | 932 |  |  | 
|  |  | 933 |  |         private EllipticCurveHsmKeyVerifier() : base( JsonWebKeyType.EllipticCurveHsm ) | 
|  |  | 934 |  |         { | 
|  |  | 935 |  |         } | 
|  |  | 936 |  |  | 
|  |  | 937 |  |         public override bool HasSecretKey => true; | 
|  |  | 938 |  |  | 
|  |  | 939 |  |         protected override void AddUsedProperties( ICollection<string> usedProperties ) | 
|  |  | 940 |  |         { | 
|  |  | 941 |  |             base.AddUsedProperties( usedProperties ); | 
|  |  | 942 |  |             usedProperties.Add( JsonWebKey.Property_T ); | 
|  |  | 943 |  |         } | 
|  |  | 944 |  |  | 
|  |  | 945 |  |         public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 946 |  |         { | 
|  |  | 947 |  |             return true; | 
|  |  | 948 |  |         } | 
|  |  | 949 |  |  | 
|  |  | 950 |  |         public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 951 |  |         { | 
|  |  | 952 |  |             return true; | 
|  |  | 953 |  |         } | 
|  |  | 954 |  |  | 
|  |  | 955 |  |         public override bool IsSecretKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 956 |  |         { | 
|  |  | 957 |  |             if ( webKey.T == null || webKey.T.Length == 0 ) | 
|  |  | 958 |  |             { | 
|  |  | 959 |  |                 AddItem( ref missingProps, JsonWebKey.Property_T ); | 
|  |  | 960 |  |                 return false; | 
|  |  | 961 |  |             } | 
|  |  | 962 |  |  | 
|  |  | 963 |  |             return true; | 
|  |  | 964 |  |         } | 
|  |  | 965 |  |  | 
|  |  | 966 |  |         public override bool IsSecretKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 967 |  |         { | 
|  |  | 968 |  |             return true; | 
|  |  | 969 |  |         } | 
|  |  | 970 |  |     } | 
|  |  | 971 |  |  | 
|  |  | 972 |  |     internal abstract class RsaKeyVerifierBase : JsonWebKeyVerifier | 
|  |  | 973 |  |     { | 
|  |  | 974 |  |         protected RsaKeyVerifierBase( string kty ) : base( kty ) | 
|  |  | 975 |  |         { | 
|  |  | 976 |  |         } | 
|  |  | 977 |  |  | 
|  |  | 978 |  |         public override bool IsPublicKeyCrypto => true; | 
|  |  | 979 |  |         public override bool IsSymmetricKeyCrypto => false; | 
|  |  | 980 |  |  | 
|  |  | 981 |  |         protected override void AddCompatibleOperations( ICollection<string> compatibleOperations ) | 
|  |  | 982 |  |         { | 
|  |  | 983 |  |             compatibleOperations.Add( JsonWebKeyOperation.Encrypt ); | 
|  |  | 984 |  |             compatibleOperations.Add( JsonWebKeyOperation.Decrypt ); | 
|  |  | 985 |  |             compatibleOperations.Add( JsonWebKeyOperation.Wrap ); | 
|  |  | 986 |  |             compatibleOperations.Add( JsonWebKeyOperation.Unwrap ); | 
|  |  | 987 |  |             compatibleOperations.Add( JsonWebKeyOperation.Sign ); | 
|  |  | 988 |  |             compatibleOperations.Add( JsonWebKeyOperation.Verify ); | 
|  |  | 989 |  |         } | 
|  |  | 990 |  |  | 
|  |  | 991 |  |         protected override void AddUsedProperties( ICollection<string> usedProperties ) | 
|  |  | 992 |  |         { | 
|  |  | 993 |  |             usedProperties.Add( JsonWebKey.Property_E ); | 
|  |  | 994 |  |             usedProperties.Add( JsonWebKey.Property_N ); | 
|  |  | 995 |  |         } | 
|  |  | 996 |  |  | 
|  |  | 997 |  |         public override bool IsPublicKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 998 |  |         { | 
|  |  | 999 |  |             var result = true; | 
|  |  | 1000 |  |  | 
|  |  | 1001 |  |             if ( webKey.E == null || webKey.E.Length == 0 ) | 
|  |  | 1002 |  |             { | 
|  |  | 1003 |  |                 AddItem( ref missingProps, JsonWebKey.Property_E ); | 
|  |  | 1004 |  |                 result = false; | 
|  |  | 1005 |  |             } | 
|  |  | 1006 |  |  | 
|  |  | 1007 |  |             if ( webKey.N == null || webKey.N.Length == 0 ) | 
|  |  | 1008 |  |             { | 
|  |  | 1009 |  |                 AddItem( ref missingProps, JsonWebKey.Property_N ); | 
|  |  | 1010 |  |                 result = false; | 
|  |  | 1011 |  |             } | 
|  |  | 1012 |  |  | 
|  |  | 1013 |  |             return result; | 
|  |  | 1014 |  |         } | 
|  |  | 1015 |  |  | 
|  |  | 1016 |  |         public override bool IsPublicKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 1017 |  |         { | 
|  |  | 1018 |  |             return true; | 
|  |  | 1019 |  |         } | 
|  |  | 1020 |  |     } | 
|  |  | 1021 |  |  | 
|  |  | 1022 |  |     internal sealed class RsaKeyVerifier : RsaKeyVerifierBase | 
|  |  | 1023 |  |     { | 
|  | 4 | 1024 |  |         public static JsonWebKeyVerifier Instance { get; } = new RsaKeyVerifier(); | 
|  |  | 1025 |  |  | 
|  | 2 | 1026 |  |         private RsaKeyVerifier() : base( JsonWebKeyType.Rsa ) | 
|  |  | 1027 |  |         { | 
|  | 2 | 1028 |  |         } | 
|  |  | 1029 |  |  | 
|  | 0 | 1030 |  |         public override bool HasSecretKey => false; | 
|  |  | 1031 |  |  | 
|  |  | 1032 |  |         protected override void AddUsedProperties( ICollection<string> usedProperties ) | 
|  |  | 1033 |  |         { | 
|  | 0 | 1034 |  |             base.AddUsedProperties( usedProperties ); | 
|  | 0 | 1035 |  |             usedProperties.Add( JsonWebKey.Property_D ); | 
|  | 0 | 1036 |  |             usedProperties.Add( JsonWebKey.Property_DP ); | 
|  | 0 | 1037 |  |             usedProperties.Add( JsonWebKey.Property_DQ ); | 
|  | 0 | 1038 |  |             usedProperties.Add( JsonWebKey.Property_P ); | 
|  | 0 | 1039 |  |             usedProperties.Add( JsonWebKey.Property_Q ); | 
|  | 0 | 1040 |  |             usedProperties.Add( JsonWebKey.Property_QI ); | 
|  | 0 | 1041 |  |         } | 
|  |  | 1042 |  |  | 
|  |  | 1043 |  |         public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 1044 |  |         { | 
|  | 0 | 1045 |  |             var result = true; | 
|  |  | 1046 |  |  | 
|  | 0 | 1047 |  |             if ( webKey.D == null || webKey.D.Length == 0 ) | 
|  |  | 1048 |  |             { | 
|  | 0 | 1049 |  |                 AddItem( ref missingProps, JsonWebKey.Property_D ); | 
|  | 0 | 1050 |  |                 result = false; | 
|  |  | 1051 |  |             } | 
|  |  | 1052 |  |  | 
|  | 0 | 1053 |  |             if ( webKey.DP == null || webKey.DP.Length == 0 ) | 
|  |  | 1054 |  |             { | 
|  | 0 | 1055 |  |                 AddItem( ref missingProps, JsonWebKey.Property_DP ); | 
|  | 0 | 1056 |  |                 result = false; | 
|  |  | 1057 |  |             } | 
|  |  | 1058 |  |  | 
|  | 0 | 1059 |  |             if ( webKey.DQ == null || webKey.DQ.Length == 0 ) | 
|  |  | 1060 |  |             { | 
|  | 0 | 1061 |  |                 AddItem( ref missingProps, JsonWebKey.Property_DQ ); | 
|  | 0 | 1062 |  |                 result = false; | 
|  |  | 1063 |  |             } | 
|  |  | 1064 |  |  | 
|  | 0 | 1065 |  |             if ( webKey.P == null || webKey.P.Length == 0 ) | 
|  |  | 1066 |  |             { | 
|  | 0 | 1067 |  |                 AddItem( ref missingProps, JsonWebKey.Property_P ); | 
|  | 0 | 1068 |  |                 result = false; | 
|  |  | 1069 |  |             } | 
|  |  | 1070 |  |  | 
|  | 0 | 1071 |  |             if ( webKey.Q == null || webKey.Q.Length == 0 ) | 
|  |  | 1072 |  |             { | 
|  | 0 | 1073 |  |                 AddItem( ref missingProps, JsonWebKey.Property_Q ); | 
|  | 0 | 1074 |  |                 result = false; | 
|  |  | 1075 |  |             } | 
|  |  | 1076 |  |  | 
|  | 0 | 1077 |  |             if ( webKey.QI == null || webKey.QI.Length == 0 ) | 
|  |  | 1078 |  |             { | 
|  | 0 | 1079 |  |                 AddItem( ref missingProps, JsonWebKey.Property_QI ); | 
|  | 0 | 1080 |  |                 result = false; | 
|  |  | 1081 |  |             } | 
|  |  | 1082 |  |  | 
|  | 0 | 1083 |  |             return result; | 
|  |  | 1084 |  |         } | 
|  |  | 1085 |  |  | 
|  |  | 1086 |  |         public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 1087 |  |         { | 
|  | 0 | 1088 |  |             return true; | 
|  |  | 1089 |  |         } | 
|  |  | 1090 |  |     } | 
|  |  | 1091 |  |  | 
|  |  | 1092 |  |     internal sealed class RsaHsmKeyVerifier : RsaKeyVerifierBase | 
|  |  | 1093 |  |     { | 
|  |  | 1094 |  |         public static JsonWebKeyVerifier Instance { get; } = new RsaHsmKeyVerifier(); | 
|  |  | 1095 |  |  | 
|  |  | 1096 |  |         private RsaHsmKeyVerifier() : base( JsonWebKeyType.RsaHsm ) | 
|  |  | 1097 |  |         { | 
|  |  | 1098 |  |         } | 
|  |  | 1099 |  |  | 
|  |  | 1100 |  |         public override bool HasSecretKey => true; | 
|  |  | 1101 |  |  | 
|  |  | 1102 |  |         protected override void AddUsedProperties( ICollection<string> usedProperties ) | 
|  |  | 1103 |  |         { | 
|  |  | 1104 |  |             base.AddUsedProperties( usedProperties ); | 
|  |  | 1105 |  |             usedProperties.Add( JsonWebKey.Property_T ); | 
|  |  | 1106 |  |         } | 
|  |  | 1107 |  |  | 
|  |  | 1108 |  |         public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 1109 |  |         { | 
|  |  | 1110 |  |             return true; | 
|  |  | 1111 |  |         } | 
|  |  | 1112 |  |  | 
|  |  | 1113 |  |         public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 1114 |  |         { | 
|  |  | 1115 |  |             return true; | 
|  |  | 1116 |  |         } | 
|  |  | 1117 |  |  | 
|  |  | 1118 |  |         public override bool IsSecretKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps ) | 
|  |  | 1119 |  |         { | 
|  |  | 1120 |  |             if ( webKey.T == null || webKey.T.Length == 0 ) | 
|  |  | 1121 |  |             { | 
|  |  | 1122 |  |                 AddItem( ref missingProps, JsonWebKey.Property_T ); | 
|  |  | 1123 |  |                 return false; | 
|  |  | 1124 |  |             } | 
|  |  | 1125 |  |  | 
|  |  | 1126 |  |             return true; | 
|  |  | 1127 |  |         } | 
|  |  | 1128 |  |  | 
|  |  | 1129 |  |         public override bool IsSecretKeyValid( JsonWebKey webKey, ref string errorMsg ) | 
|  |  | 1130 |  |         { | 
|  |  | 1131 |  |             return true; | 
|  |  | 1132 |  |         } | 
|  |  | 1133 |  |     } | 
|  |  | 1134 |  | } |