/// <summary> /// Gets a validation function that returns the output of a configured verification method. /// Input: <c>tag || salt || AD || message</c> /// </summary> /// <returns>Callable validation function.</returns> /// <param name="keyConfirmation">Key confirmation configuration defining validation method to be employed.</param> /// <param name="tag"></param> /// <param name="message"></param> /// <param name="outputSizeBytes">Expected length of output of verification function in bytes.</param> /// <exception cref="ConfigurationInvalidException"> /// Some aspect of configuration invalid - detailed inside exception message. /// </exception> internal static Func <byte[], byte[]> GetValidator(IAuthenticationConfiguration keyConfirmation, byte[] tag, byte[] message, int?outputSizeBytes = null) { AuthenticationFunctionType functionType = keyConfirmation.FunctionType; if (functionType == AuthenticationFunctionType.None) { throw new ConfigurationInvalidException("Authentication function type cannot be None."); } if (String.IsNullOrEmpty(keyConfirmation.FunctionName)) { throw new ConfigurationInvalidException("Authentication function name cannot be null or empty."); } const string lengthIncompatibleString = "Expected length incompatible with function specified."; Func <byte[], byte[]> validator; // Used as an adaptor between different validation methods switch (functionType) { case AuthenticationFunctionType.Kdf: { if (outputSizeBytes == null) { throw new ArgumentNullException("outputSizeBytes", "Cannot be null if KDF is being used."); } KeyDerivationFunction kdfEnum; try { kdfEnum = keyConfirmation.FunctionName.ToEnum <KeyDerivationFunction>(); } catch (EnumerationParsingException ex) { throw new ConfigurationInvalidException("Key derivation function is unsupported/unknown.", ex); } validator = key => { int superSaltSize = keyConfirmation.Salt.Length + (keyConfirmation.AdditionalData != null ? keyConfirmation.AdditionalData.Length : 0) + (tag != null ? tag.Length : 0) + (message != null ? message.Length : 0); var superSalt = new byte[superSaltSize]; tag.DeepCopy_NoChecks(0, superSalt, 0, tag.Length); int index = tag.Length; // Compose the rest of the input to the KDF (as a super-salt) if (keyConfirmation.Salt.IsNullOrZeroLength() == false) { keyConfirmation.Salt.DeepCopy_NoChecks(0, superSalt, index, keyConfirmation.Salt.Length); index += keyConfirmation.Salt.Length; } if (keyConfirmation.AdditionalData.IsNullOrZeroLength() == false) { keyConfirmation.AdditionalData.DeepCopy_NoChecks(0, superSalt, index, keyConfirmation.AdditionalData.Length); index += keyConfirmation.AdditionalData.Length; } if (message.IsNullOrZeroLength() == false) { message.DeepCopy_NoChecks(0, superSalt, index, message.Length); } return(KdfFactory.DeriveKeyWithKdf(kdfEnum, key, superSalt, outputSizeBytes.Value, keyConfirmation.FunctionConfiguration)); }; break; } case AuthenticationFunctionType.Mac: MacFunction macFEnum; try { macFEnum = keyConfirmation.FunctionName.ToEnum <MacFunction>(); } catch (EnumerationParsingException ex) { throw new ConfigurationInvalidException("MAC function is unsupported/unknown.", ex); } validator = key => { IMac macF = AuthenticatorFactory.CreateMacPrimitive(macFEnum, key, tag, keyConfirmation.FunctionConfiguration, keyConfirmation.Nonce); if (outputSizeBytes != null && outputSizeBytes != macF.OutputSize) { throw new ArgumentException(lengthIncompatibleString, "outputSizeBytes"); } if (keyConfirmation.Salt.IsNullOrZeroLength() == false) { macF.BlockUpdate(keyConfirmation.Salt, 0, keyConfirmation.Salt.Length); } if (keyConfirmation.AdditionalData.IsNullOrZeroLength() == false) { macF.BlockUpdate(keyConfirmation.AdditionalData, 0, keyConfirmation.AdditionalData.Length); } if (message.IsNullOrZeroLength() == false) { macF.BlockUpdate(message, 0, message.Length); } var output = new byte[macF.OutputSize]; macF.DoFinal(output, 0); return(output); }; break; default: throw new NotSupportedException("Function type not supported for key confirmation."); } return(validator); }