11using CryptoBase . Abstractions ;
22using CryptoBase . Digests ;
33using CryptoBase . Macs . Hmac ;
4+ using System . Security . Cryptography ;
45
56namespace CryptoBase . KDF ;
67
@@ -14,11 +15,6 @@ public static int Extract(DigestType type, ReadOnlySpan<byte> ikm, ReadOnlySpan<
1415 int hashLength = HashLength ( type ) ;
1516 ArgumentOutOfRangeException . ThrowIfLessThan ( prk . Length , hashLength , nameof ( prk ) ) ;
1617
17- if ( prk . Length > hashLength )
18- {
19- prk = prk [ ..hashLength ] ;
20- }
21-
2218 ExtractInternal ( type , ikm , salt , prk ) ;
2319
2420 return hashLength ;
@@ -38,6 +34,8 @@ public static void Expand(DigestType type, ReadOnlySpan<byte> prk, Span<byte> ou
3834
3935 ArgumentOutOfRangeException . ThrowIfZero ( output . Length , nameof ( output ) ) ;
4036
37+ ArgumentOutOfRangeException . ThrowIfLessThan ( prk . Length , hashLength , nameof ( prk ) ) ;
38+
4139 int maxOkmLength = 255 * hashLength ;
4240 ArgumentOutOfRangeException . ThrowIfGreaterThan ( output . Length , maxOkmLength , nameof ( output ) ) ;
4341
@@ -46,46 +44,68 @@ public static void Expand(DigestType type, ReadOnlySpan<byte> prk, Span<byte> ou
4644
4745 private static void ExpandInternal ( DigestType type , int hashLength , ReadOnlySpan < byte > prk , Span < byte > output , ReadOnlySpan < byte > info )
4846 {
49- ArgumentOutOfRangeException . ThrowIfLessThan ( prk . Length , hashLength , nameof ( prk ) ) ;
50-
51- if ( output . Overlaps ( info ) )
52- {
53- throw new InvalidOperationException ( @"the info input overlaps with the output destination" ) ;
54- }
55-
56- Span < byte > counterSpan = stackalloc byte [ 1 ] ;
57- ref byte counter = ref counterSpan [ 0 ] ;
47+ byte counter = 0 ;
48+ Span < byte > counterSpan = new ( ref counter ) ;
5849 Span < byte > t = Span < byte > . Empty ;
5950 Span < byte > remainingOutput = output ;
6051
61- using IMac hmac = HmacUtils . Create ( type , prk ) ;
52+ const int maxStackInfoBuffer = 64 ;
53+ Span < byte > tempInfoBuffer = stackalloc byte [ maxStackInfoBuffer ] ;
54+ scoped ReadOnlySpan < byte > infoBuffer ;
55+ byte [ ] ? rentedTempInfoBuffer = null ;
6256
63- for ( int i = 1 ; ; ++ i )
57+ if ( output . Overlaps ( info ) )
6458 {
65- hmac . Update ( t ) ;
66- hmac . Update ( info ) ;
67- counter = ( byte ) i ;
68- hmac . Update ( counterSpan ) ;
69-
70- if ( remainingOutput . Length >= hashLength )
59+ if ( info . Length > maxStackInfoBuffer )
7160 {
72- t = remainingOutput [ ..hashLength ] ;
73- remainingOutput = remainingOutput [ hashLength ..] ;
74- hmac . GetMac ( t ) ;
61+ rentedTempInfoBuffer = ArrayPool < byte > . Shared . Rent ( info . Length ) ;
62+ tempInfoBuffer = rentedTempInfoBuffer ;
7563 }
76- else
64+
65+ tempInfoBuffer = tempInfoBuffer . Slice ( 0 , info . Length ) ;
66+ info . CopyTo ( tempInfoBuffer ) ;
67+ infoBuffer = tempInfoBuffer ;
68+ }
69+ else
70+ {
71+ infoBuffer = info ;
72+ }
73+
74+ using ( IMac hmac = HmacUtils . Create ( type , prk ) )
75+ {
76+ for ( int i = 1 ; ; ++ i )
7777 {
78- if ( remainingOutput . Length > 0 )
78+ hmac . Update ( t ) ;
79+ hmac . Update ( infoBuffer ) ;
80+ counter = ( byte ) i ;
81+ hmac . Update ( counterSpan ) ;
82+
83+ if ( remainingOutput . Length >= hashLength )
7984 {
80- // ReSharper disable once StackAllocInsideLoop
81- Span < byte > lastChunk = stackalloc byte [ hashLength ] ;
82- hmac . GetMac ( lastChunk ) ;
83- lastChunk [ ..remainingOutput . Length ] . CopyTo ( remainingOutput ) ;
85+ t = remainingOutput . Slice ( 0 , hashLength ) ;
86+ remainingOutput = remainingOutput . Slice ( hashLength ) ;
87+ hmac . GetMac ( t ) ;
88+ }
89+ else
90+ {
91+ if ( remainingOutput . Length > 0 )
92+ {
93+ // ReSharper disable once StackAllocInsideLoop
94+ Span < byte > lastChunk = stackalloc byte [ hashLength ] ;
95+ hmac . GetMac ( lastChunk ) ;
96+ lastChunk . Slice ( 0 , remainingOutput . Length ) . CopyTo ( remainingOutput ) ;
97+ }
98+
99+ break ;
84100 }
85-
86- break ;
87101 }
88102 }
103+
104+ if ( rentedTempInfoBuffer is not null )
105+ {
106+ CryptographicOperations . ZeroMemory ( rentedTempInfoBuffer . AsSpan ( 0 , info . Length ) ) ;
107+ ArrayPool < byte > . Shared . Return ( rentedTempInfoBuffer ) ;
108+ }
89109 }
90110
91111 public static void DeriveKey ( DigestType type , ReadOnlySpan < byte > ikm , Span < byte > output , ReadOnlySpan < byte > salt , ReadOnlySpan < byte > info )
@@ -97,10 +117,16 @@ public static void DeriveKey(DigestType type, ReadOnlySpan<byte> ikm, Span<byte>
97117 int maxOkmLength = 255 * hashLength ;
98118 ArgumentOutOfRangeException . ThrowIfGreaterThan ( output . Length , maxOkmLength , nameof ( output ) ) ;
99119
120+ DeriveKeyInternal ( type , hashLength , ikm , output , salt , info ) ;
121+ }
122+
123+ private static void DeriveKeyInternal ( DigestType type , int hashLength , ReadOnlySpan < byte > ikm , Span < byte > output , ReadOnlySpan < byte > salt , ReadOnlySpan < byte > info )
124+ {
100125 Span < byte > prk = stackalloc byte [ hashLength ] ;
101126
102127 ExtractInternal ( type , ikm , salt , prk ) ;
103128 ExpandInternal ( type , hashLength , prk , output , info ) ;
129+ CryptographicOperations . ZeroMemory ( prk ) ;
104130 }
105131
106132 private static int HashLength ( DigestType type )
0 commit comments