using System;
using System.Collections.Generic;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Iana;
using Org.BouncyCastle.Asn1.Misc;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Oiw;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.Rosstandart;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Collections;
namespace Org.BouncyCastle.Security
{
///
/// Utility class for creating HMac object from their names/Oids
///
public static class MacUtilities
{
private static readonly Dictionary AlgorithmMap =
new Dictionary(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary AlgorithmOidMap =
new Dictionary();
static MacUtilities()
{
AlgorithmOidMap[IanaObjectIdentifiers.HmacMD5] = "HMAC-MD5";
AlgorithmOidMap[IanaObjectIdentifiers.HmacRipeMD160] = "HMAC-RIPEMD160";
AlgorithmOidMap[IanaObjectIdentifiers.HmacSha1] = "HMAC-SHA1";
AlgorithmOidMap[IanaObjectIdentifiers.HmacTiger] = "HMAC-TIGER";
AlgorithmOidMap[PkcsObjectIdentifiers.IdHmacWithSha1] = "HMAC-SHA1";
AlgorithmOidMap[MiscObjectIdentifiers.HMAC_SHA1] = "HMAC-SHA1";
AlgorithmOidMap[PkcsObjectIdentifiers.IdHmacWithSha224] = "HMAC-SHA224";
AlgorithmOidMap[PkcsObjectIdentifiers.IdHmacWithSha256] = "HMAC-SHA256";
AlgorithmOidMap[PkcsObjectIdentifiers.IdHmacWithSha384] = "HMAC-SHA384";
AlgorithmOidMap[PkcsObjectIdentifiers.IdHmacWithSha512] = "HMAC-SHA512";
AlgorithmOidMap[NistObjectIdentifiers.IdHMacWithSha3_224] = "HMAC-SHA3-224";
AlgorithmOidMap[NistObjectIdentifiers.IdHMacWithSha3_256] = "HMAC-SHA3-256";
AlgorithmOidMap[NistObjectIdentifiers.IdHMacWithSha3_384] = "HMAC-SHA3-384";
AlgorithmOidMap[NistObjectIdentifiers.IdHMacWithSha3_512] = "HMAC-SHA3-512";
AlgorithmOidMap[RosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_256] = "HMAC-GOST3411-2012-256";
AlgorithmOidMap[RosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_512] = "HMAC-GOST3411-2012-512";
// TODO AESMAC?
AlgorithmMap["DES"] = "DESMAC";
AlgorithmMap["DES/CFB8"] = "DESMAC/CFB8";
AlgorithmMap["DES64"] = "DESMAC64";
AlgorithmMap["DESEDE"] = "DESEDEMAC";
AlgorithmOidMap[PkcsObjectIdentifiers.DesEde3Cbc] = "DESEDEMAC";
AlgorithmMap["DESEDE/CFB8"] = "DESEDEMAC/CFB8";
AlgorithmMap["DESISO9797MAC"] = "DESWITHISO9797";
AlgorithmMap["DESEDE64"] = "DESEDEMAC64";
AlgorithmMap["DESEDE64WITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING";
AlgorithmMap["DESEDEISO9797ALG1MACWITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING";
AlgorithmMap["DESEDEISO9797ALG1WITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING";
AlgorithmMap["ISO9797ALG3"] = "ISO9797ALG3MAC";
AlgorithmMap["ISO9797ALG3MACWITHISO7816-4PADDING"] = "ISO9797ALG3WITHISO7816-4PADDING";
AlgorithmMap["SKIPJACK"] = "SKIPJACKMAC";
AlgorithmMap["SKIPJACK/CFB8"] = "SKIPJACKMAC/CFB8";
AlgorithmMap["IDEA"] = "IDEAMAC";
AlgorithmMap["IDEA/CFB8"] = "IDEAMAC/CFB8";
AlgorithmMap["RC2"] = "RC2MAC";
AlgorithmMap["RC2/CFB8"] = "RC2MAC/CFB8";
AlgorithmMap["RC5"] = "RC5MAC";
AlgorithmMap["RC5/CFB8"] = "RC5MAC/CFB8";
AlgorithmMap["GOST28147"] = "GOST28147MAC";
AlgorithmMap["VMPC"] = "VMPCMAC";
AlgorithmMap["VMPC-MAC"] = "VMPCMAC";
AlgorithmMap["SIPHASH"] = "SIPHASH-2-4";
AlgorithmMap["PBEWITHHMACSHA"] = "PBEWITHHMACSHA1";
AlgorithmOidMap[OiwObjectIdentifiers.IdSha1] = "PBEWITHHMACSHA1";
#if DEBUG
foreach (var key in AlgorithmMap.Keys)
{
if (DerObjectIdentifier.TryFromID(key, out var ignore))
throw new Exception("OID mapping belongs in AlgorithmOidMap: " + key);
}
var mechanisms = new HashSet(AlgorithmMap.Values);
mechanisms.UnionWith(AlgorithmOidMap.Values);
foreach (var mechanism in mechanisms)
{
if (AlgorithmMap.TryGetValue(mechanism, out var check))
{
if (mechanism != check)
throw new Exception("Mechanism mapping MUST be to self: " + mechanism);
}
else
{
if (!mechanism.Equals(mechanism.ToUpperInvariant()))
throw new Exception("Unmapped mechanism MUST be uppercase: " + mechanism);
}
}
#endif
}
public static byte[] CalculateMac(string algorithm, ICipherParameters cp, byte[] input)
{
IMac mac = GetMac(algorithm);
mac.Init(cp);
mac.BlockUpdate(input, 0, input.Length);
return DoFinal(mac);
}
public static byte[] DoFinal(IMac mac)
{
byte[] b = new byte[mac.GetMacSize()];
mac.DoFinal(b, 0);
return b;
}
public static byte[] DoFinal(IMac mac, byte[] input)
{
mac.BlockUpdate(input, 0, input.Length);
return DoFinal(mac);
}
public static string GetAlgorithmName(DerObjectIdentifier oid)
{
return CollectionUtilities.GetValueOrNull(AlgorithmOidMap, oid);
}
// TODO[api] Change parameter name to 'oid'
public static IMac GetMac(DerObjectIdentifier id)
{
if (id == null)
throw new ArgumentNullException(nameof(id));
if (AlgorithmOidMap.TryGetValue(id, out var mechanism))
{
var mac = GetMacForMechanism(mechanism);
if (mac != null)
return mac;
}
throw new SecurityUtilityException("Mac OID not recognised.");
}
public static IMac GetMac(string algorithm)
{
if (algorithm == null)
throw new ArgumentNullException(nameof(algorithm));
string mechanism = GetMechanism(algorithm) ?? algorithm.ToUpperInvariant();
var mac = GetMacForMechanism(mechanism);
if (mac != null)
return mac;
throw new SecurityUtilityException("Mac " + algorithm + " not recognised.");
}
private static IMac GetMacForMechanism(string mechanism)
{
if (Platform.StartsWith(mechanism, "PBEWITH"))
{
mechanism = mechanism.Substring("PBEWITH".Length);
}
if (Platform.StartsWith(mechanism, "HMAC"))
{
string digestName;
if (Platform.StartsWith(mechanism, "HMAC-") || Platform.StartsWith(mechanism, "HMAC/"))
{
digestName = mechanism.Substring(5);
}
else
{
digestName = mechanism.Substring(4);
}
return new HMac(DigestUtilities.GetDigest(digestName));
}
if (mechanism == "AESCMAC")
{
return new CMac(AesUtilities.CreateEngine());
}
if (mechanism == "DESMAC")
{
return new CbcBlockCipherMac(new DesEngine());
}
if (mechanism == "DESMAC/CFB8")
{
return new CfbBlockCipherMac(new DesEngine());
}
if (mechanism == "DESMAC64")
{
return new CbcBlockCipherMac(new DesEngine(), 64);
}
if (mechanism == "DESEDECMAC")
{
return new CMac(new DesEdeEngine());
}
if (mechanism == "DESEDEMAC")
{
return new CbcBlockCipherMac(new DesEdeEngine());
}
if (mechanism == "DESEDEMAC/CFB8")
{
return new CfbBlockCipherMac(new DesEdeEngine());
}
if (mechanism == "DESEDEMAC64")
{
return new CbcBlockCipherMac(new DesEdeEngine(), 64);
}
if (mechanism == "DESEDEMAC64WITHISO7816-4PADDING")
{
return new CbcBlockCipherMac(new DesEdeEngine(), 64, new ISO7816d4Padding());
}
if (mechanism == "DESWITHISO9797"
|| mechanism == "ISO9797ALG3MAC")
{
return new ISO9797Alg3Mac(new DesEngine());
}
if (mechanism == "ISO9797ALG3WITHISO7816-4PADDING")
{
return new ISO9797Alg3Mac(new DesEngine(), new ISO7816d4Padding());
}
if (mechanism == "SKIPJACKMAC")
{
return new CbcBlockCipherMac(new SkipjackEngine());
}
if (mechanism == "SKIPJACKMAC/CFB8")
{
return new CfbBlockCipherMac(new SkipjackEngine());
}
if (mechanism == "IDEAMAC")
{
return new CbcBlockCipherMac(new IdeaEngine());
}
if (mechanism == "IDEAMAC/CFB8")
{
return new CfbBlockCipherMac(new IdeaEngine());
}
if (mechanism == "RC2MAC")
{
return new CbcBlockCipherMac(new RC2Engine());
}
if (mechanism == "RC2MAC/CFB8")
{
return new CfbBlockCipherMac(new RC2Engine());
}
if (mechanism == "RC5MAC")
{
return new CbcBlockCipherMac(new RC532Engine());
}
if (mechanism == "RC5MAC/CFB8")
{
return new CfbBlockCipherMac(new RC532Engine());
}
if (mechanism == "GOST28147MAC")
{
return new Gost28147Mac();
}
if (mechanism == "VMPCMAC")
{
return new VmpcMac();
}
if (mechanism == "SIPHASH-2-4")
{
return new SipHash();
}
return null;
}
private static string GetMechanism(string algorithm)
{
if (AlgorithmMap.TryGetValue(algorithm, out var mechanism1))
return mechanism1;
if (DerObjectIdentifier.TryFromID(algorithm, out var oid))
{
if (AlgorithmOidMap.TryGetValue(oid, out var mechanism2))
return mechanism2;
}
return null;
}
}
}