/
HashAlgorithmName.cs
174 lines (153 loc) · 6.39 KB
/
HashAlgorithmName.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
namespace System.Security.Cryptography
{
// Strongly typed string representing the name of a hash algorithm.
// Open ended to allow extensibility while giving the discoverable feel of an enum for common values.
/// <summary>
/// Specifies the name of a cryptographic hash algorithm.
/// </summary>
/// <remarks>
/// Asymmetric Algorithms implemented using Microsoft's CNG (Cryptography Next Generation) API
/// will interpret the underlying string value as a CNG algorithm identifier:
/// * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375534(v=vs.85).aspx
///
/// As with CNG, the names are case-sensitive.
///
/// Asymmetric Algorithms implemented using other technologies:
/// * Must recognize at least "MD5", "SHA1", "SHA256", "SHA384", and "SHA512".
/// * Should recognize additional CNG IDs for any other hash algorithms that they also support.
/// </remarks>
public readonly struct HashAlgorithmName : IEquatable<HashAlgorithmName>
{
// Returning a new instance every time is free here since HashAlgorithmName is a struct with
// a single string field. The optimized codegen should be equivalent to return "MD5".
/// <summary>
/// Gets a <see cref="HashAlgorithmName" /> representing "MD5"
/// </summary>
public static HashAlgorithmName MD5 { get { return new HashAlgorithmName("MD5"); } }
/// <summary>
/// Gets a <see cref="HashAlgorithmName" /> representing "SHA1"
/// </summary>
public static HashAlgorithmName SHA1 { get { return new HashAlgorithmName("SHA1"); } }
/// <summary>
/// Gets a <see cref="HashAlgorithmName" /> representing "SHA256"
/// </summary>
public static HashAlgorithmName SHA256 { get { return new HashAlgorithmName("SHA256"); } }
/// <summary>
/// Gets a <see cref="HashAlgorithmName" /> representing "SHA384"
/// </summary>
public static HashAlgorithmName SHA384 { get { return new HashAlgorithmName("SHA384"); } }
/// <summary>
/// Gets a <see cref="HashAlgorithmName" /> representing "SHA512"
/// </summary>
public static HashAlgorithmName SHA512 { get { return new HashAlgorithmName("SHA512"); } }
private readonly string? _name;
/// <summary>
/// Gets a <see cref="HashAlgorithmName" /> representing a custom name.
/// </summary>
/// <param name="name">The custom hash algorithm name.</param>
public HashAlgorithmName(string? name)
{
// Note: No validation because we have to deal with default(HashAlgorithmName) regardless.
_name = name;
}
/// <summary>
/// Gets the underlying string representation of the algorithm name.
/// </summary>
/// <remarks>
/// May be null or empty to indicate that no hash algorithm is applicable.
/// </remarks>
public string? Name
{
get { return _name; }
}
public override string ToString()
{
return _name ?? string.Empty;
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is HashAlgorithmName && Equals((HashAlgorithmName)obj);
}
public bool Equals(HashAlgorithmName other)
{
// NOTE: intentionally ordinal and case sensitive, matches CNG.
return _name == other._name;
}
public override int GetHashCode()
{
return _name == null ? 0 : _name.GetHashCode();
}
public static bool operator ==(HashAlgorithmName left, HashAlgorithmName right)
{
return left.Equals(right);
}
public static bool operator !=(HashAlgorithmName left, HashAlgorithmName right)
{
return !(left == right);
}
/// <summary>
/// Tries to convert the specified OID to a hash algorithm name.
/// </summary>
/// <param name="oidValue">The OID of the hash algorithm.</param>
/// <param name="value">
/// When this method returns <c>true</c>, the hash algorithm. When this
/// method returns <c>false</c>, contains <c>default</c>.
/// </param>
/// <returns>
/// <c>true</c> if the OID was successfully mapped to a hash
/// algorithm; otherwise <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="oidValue" /> is null.
/// </exception>
public static bool TryFromOid(string oidValue, out HashAlgorithmName value)
{
ArgumentNullException.ThrowIfNull(oidValue);
switch (oidValue)
{
case Oids.Md5:
value = MD5;
return true;
case Oids.Sha1:
value = SHA1;
return true;
case Oids.Sha256:
value = SHA256;
return true;
case Oids.Sha384:
value = SHA384;
return true;
case Oids.Sha512:
value = SHA512;
return true;
default:
value = default;
return false;
}
}
/// <summary>
/// Converts the specified OID to a hash algorithm name.
/// </summary>
/// <param name="oidValue">The OID of the hash algorithm.</param>
/// <returns>
/// The hash algorithm name identified by the OID.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="oidValue" /> is null.
/// </exception>
/// <exception cref="CryptographicException">
/// <paramref name="oidValue" /> does not represent a known hash algorithm.
/// </exception>
public static HashAlgorithmName FromOid(string oidValue)
{
if (TryFromOid(oidValue, out HashAlgorithmName value))
{
return value;
}
throw new CryptographicException(SR.Format(SR.Cryptography_InvalidHashAlgorithmOid, oidValue));
}
}
}