I have a requirement to implement Pbkdf2 to secure a very old application that uses aspnet membership provider. I am following this answer which is recent and useful, but unable to follow what is inside MyRfc2898DeriveBytes class and it is giving me following issue.. (also I am a c# dev but for this project working in vb.net) So the issue could be related to language conversion.
I am getting
Non-negative number required. Parameter name: srcOffset
at the following
Buffer.BlockCopy(m_buffer, m_startIndex, password, 0, size)
the m_startIndex here somehow becomes -1
Here is my code in vb
Imports System.IO
Imports System.Security.Cryptography
Namespace custom.hashing.keyderivation
Public Class PBKDF2Hash
Inherits KeyedHashAlgorithm
Private Const kHashBytes As Integer = 64
Private _ms As System.IO.MemoryStream
Public Property WorkFactor As Integer
Public Sub New()
MyBase.New()
Me.WorkFactor = 128000
Me.Key = New Byte(31) {}
Using rngCsp = New RNGCryptoServiceProvider()
rngCsp.GetBytes(Me.Key)
End Using
End Sub
Public Overrides ReadOnly Property HashSize As Integer
Get
Return kHashBytes * 8
End Get
End Property
Protected Overrides Sub HashCore(ByVal array As Byte(), ByVal ibStart As Integer, ByVal cbSize As Integer)
If IsNothing(_ms) Then
_ms = New MemoryStream()
End If
_ms.Write(array, ibStart, cbSize)
'(CSharpImpl.__Assign(_ms, If(_ms, New System.IO.MemoryStream()))).Write(array, ibStart, cbSize)
End Sub
Protected Overrides Function HashFinal() As Byte()
If Me.Key Is Nothing OrElse Me.Key.Length = 0 Then
Throw New CryptographicException("Missing KeyedAlgorithm key")
End If
_ms.Flush()
Dim arr = _ms.ToArray()
_ms = Nothing
Using hmac As HMACSHA512 = New HMACSHA512()
Return New MyRfc2898DeriveBytes(arr, Me.Key, Me.WorkFactor, hmac).GetBytes(kHashBytes)
End Using
End Function
Public Overrides Sub Initialize()
_ms = Nothing
End Sub
End Class
End Namespace
Imports System.Diagnostics.Contracts
Imports System.Security
Imports System.Security.Cryptography
Public Class MyRfc2898DeriveBytes
Inherits DeriveBytes
Private m_buffer As Byte()
Private m_salt As Byte()
Private m_hmac As HMAC
Private m_iterations As UInteger
Private m_block As UInteger
Private m_startIndex As Integer = 0
Private m_endIndex As Integer = 0
Private m_blockSize As Integer = 0
<SecuritySafeCritical>
Public Sub New(ByVal password As Byte(), ByVal salt As Byte(), ByVal iterations As Integer, ByVal hmac As HMAC)
salt = salt
IterationCount = iterations
hmac.Key = password
m_hmac = hmac
m_blockSize = hmac.HashSize >> 3
'''
Initialize()
End Sub
Public Property IterationCount As Integer
Get
Return CInt(m_iterations)
End Get
Set(ByVal value As Integer)
If value <= 0 Then Throw New ArgumentOutOfRangeException("value", "Error: Iteration count is zero or less")
m_iterations = CUInt(value)
Initialize()
End Set
End Property
Public Property Salt As Byte()
Get
Return CType(m_salt.Clone(), Byte())
End Get
Set(ByVal value As Byte())
If value Is Nothing Then Throw New ArgumentNullException("value")
If value.Length < 8 Then Throw New ArgumentException("Error: Salt size is less than 8")
m_salt = CType(value.Clone(), Byte())
Initialize()
End Set
End Property
Public Overrides Function GetBytes(ByVal cb As Integer) As Byte()
If cb <= 0 Then
Throw New ArgumentOutOfRangeException("cb", "Error: Hash size is zero or less")
End If
Contract.Assert(m_blockSize > 0)
Dim password As Byte() = New Byte(cb - 1) {}
Dim offset As Integer = 0
Dim size As Integer = m_endIndex - m_startIndex
If size > 0 Then
If cb >= size Then
Buffer.BlockCopy(m_buffer, m_startIndex, password, 0, size)
m_startIndex = m_endIndex = 0
offset += size
Else
Buffer.BlockCopy(m_buffer, m_startIndex, password, 0, cb)
m_startIndex += cb
Return password
End If
End If
Contract.Assert(m_startIndex = 0 AndAlso m_endIndex = 0, "Invalid start or end index in the internal buffer.")
While offset < cb
Dim T_block As Byte() = Func()
Dim remainder As Integer = cb - offset
If remainder > m_blockSize Then
Buffer.BlockCopy(T_block, 0, password, offset, m_blockSize)
offset += m_blockSize
Else
Buffer.BlockCopy(T_block, 0, password, offset, remainder)
offset += remainder
Buffer.BlockCopy(T_block, remainder, m_buffer, m_startIndex, m_blockSize - remainder)
m_endIndex += (m_blockSize - remainder)
Return password
End If
End While
Return password
End Function
Public Overrides Sub Reset()
Initialize()
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
MyBase.Dispose(disposing)
If disposing Then
If m_hmac IsNot Nothing Then
m_hmac.Dispose()
End If
If m_buffer IsNot Nothing Then
Array.Clear(m_buffer, 0, m_buffer.Length)
End If
If m_salt IsNot Nothing Then
Array.Clear(m_salt, 0, m_salt.Length)
End If
End If
End Sub
Private Sub Initialize()
If m_buffer IsNot Nothing Then Array.Clear(m_buffer, 0, m_buffer.Length)
m_buffer = New Byte(m_blockSize - 1) {}
m_block = 1
m_startIndex = m_endIndex = 0
End Sub
Friend Shared Function GetBytesFromInt(ByVal i As UInteger) As Byte()
End Function
Private Function Func() As Byte()
Dim INT_block As Byte() = GetBytesFromInt(m_block)
m_hmac.TransformBlock(m_salt, 0, m_salt.Length, Nothing, 0)
m_hmac.TransformBlock(INT_block, 0, INT_block.Length, Nothing, 0)
m_hmac.TransformFinalBlock(New Byte(-1) {}, 0, 0)
Dim temp As Byte() = m_hmac.Hash
m_hmac.Initialize()
Dim ret As Byte() = temp
For i As Integer = 2 To m_iterations
m_hmac.TransformBlock(temp, 0, temp.Length, Nothing, 0)
m_hmac.TransformFinalBlock(New Byte(-1) {}, 0, 0)
temp = m_hmac.Hash
For j As Integer = 0 To m_blockSize - 1
ret(j) = ret(j) Xor temp(j)
Next
m_hmac.Initialize()
Next
If m_block = UInteger.MaxValue Then
Throw New InvalidOperationException("Derived key too long.")
End If
m_block += 1
Return ret
End Function
End Class
So yes it was c# to vb conversion issues:
double assignment does not work in vb, so the following line was changed from
to
Property with same name as variable was not converted properly so
was really supposed to be
as Salt was a property
GetBytesFromInt(m_block) was not properly converted to vb