@@ -139,7 +139,7 @@ private static void DeriveBytesOneShot(
139139 // The KDF is defined as K(i) := PRF (KI, [i]2 || Label || 0x00 || Context || [L]2)
140140 // We know L is already less than 0x1FFFFFFF. h = ceil(L / h) where H is the hash length in bits.
141141 // So we don't expect i to overflow.
142- using ( IncrementalHash hash = IncrementalHash . CreateHMAC ( hashAlgorithm , key ) )
142+ using ( HMAC hash = CreateHMAC ( hashAlgorithm , key ) )
143143 {
144144 // We use this rented buffer for three things. The first two uints for i and L, and last byte
145145 // for the zero separator. So this is
@@ -165,19 +165,30 @@ private static void DeriveBytesOneShot(
165165 for ( uint i = 1 ; ! destination . IsEmpty ; i ++ )
166166 {
167167 WriteUInt32BigEndian ( i , rentedBuffer . AsSpan ( IOffset , ILength ) ) ;
168- hash . AppendData ( rentedBuffer , IOffset , ILength ) ;
169- hash . AppendData ( label , 0 , labelLength ) ;
170- hash . AppendData ( rentedBuffer , ZeroOffset , ZeroLength ) ;
171- hash . AppendData ( context , 0 , contextLength ) ;
172- hash . AppendData ( rentedBuffer , LOffset , LLength ) ;
173-
174- byte [ ] hmac = hash . GetHashAndReset ( ) ;
168+ int written ;
169+ written = hash . TransformBlock ( rentedBuffer , IOffset , ILength , null , 0 ) ;
170+ Debug . Assert ( written == ILength ) ;
171+ written = hash . TransformBlock ( label , 0 , labelLength , null , 0 ) ;
172+ Debug . Assert ( written == labelLength ) ;
173+ written = hash . TransformBlock ( rentedBuffer , ZeroOffset , ZeroLength , null , 0 ) ;
174+ Debug . Assert ( written == ZeroLength ) ;
175+ written = hash . TransformBlock ( context , 0 , contextLength , null , 0 ) ;
176+ Debug . Assert ( written == contextLength ) ;
177+ written = hash . TransformBlock ( rentedBuffer , LOffset , LLength , null , 0 ) ;
178+ Debug . Assert ( written == LLength ) ;
179+
180+ // Use an empty input for the final transform so that the returned value isn't something
181+ // we need to clear since the return value is the same as the input.
182+ hash . TransformFinalBlock ( Array . Empty < byte > ( ) , 0 , 0 ) ;
183+
184+ byte [ ] hmac = hash . Hash ;
175185 int needed = Math . Min ( destination . Length , hmac . Length ) ;
176186 hmac . AsSpan ( 0 , needed ) . CopyTo ( destination ) ;
177187 destination = destination . Slice ( needed ) ;
178188
179189 // Best effort to zero out the key material.
180190 CryptographicOperations . ZeroMemory ( hmac ) ;
191+ hash . Initialize ( ) ;
181192 }
182193 }
183194 finally
@@ -199,5 +210,24 @@ private static void WriteUInt32BigEndian(uint value, Span<byte> destination)
199210 destination [ 2 ] = ( byte ) ( value >> 8 ) ;
200211 destination [ 3 ] = ( byte ) ( value ) ;
201212 }
213+
214+ [ System . Diagnostics . CodeAnalysis . SuppressMessage ( "Microsoft.Security" , "CA5350" , Justification = "Weak algorithms are used as instructed by the caller" ) ]
215+ private static HMAC CreateHMAC ( HashAlgorithmName hashAlgorithm , byte [ ] key )
216+ {
217+ switch ( hashAlgorithm . Name )
218+ {
219+ case HashAlgorithmNames . SHA1 :
220+ return new HMACSHA1 ( key ) ;
221+ case HashAlgorithmNames . SHA256 :
222+ return new HMACSHA256 ( key ) ;
223+ case HashAlgorithmNames . SHA384 :
224+ return new HMACSHA384 ( key ) ;
225+ case HashAlgorithmNames . SHA512 :
226+ return new HMACSHA512 ( key ) ;
227+ default :
228+ Debug . Fail ( $ "Unexpected HMAC algorithm '{ hashAlgorithm . Name } '.") ;
229+ throw new CryptographicException ( SR . Format ( SR . Cryptography_UnknownHashAlgorithm , hashAlgorithm . Name ) ) ;
230+ }
231+ }
202232 }
203233}
0 commit comments