''' <summary>
''' Questa classe deve essere tenuta nascosta. Serve per generare un codice
''' seriale e/ verificarne la correttezza.
''' </summary>
Public Class CodeGenerator
Private _CodeRank As Int16 = 8
Private _DerivingIterations As Int16 = 5
Private _SaltBytes As Byte()
Private Shared ReadOnly Rnd As Random
''' <summary>
''' Definisce la forza del codice, direttamente legata alla sua lunghezza. Minimo 3.
''' </summary>
Public Property CodeRank() As Int16
Get
Return _CodeRank
End Get
Set(ByVal value As Int16)
If value >= 3 Then
_CodeRank = value
Else
_CodeRank = 3
End If
End Set
End Property
''' <summary>
''' Salt crittografico. Questo array di bytes viene usato per derivare il codice finito.
''' E' il punto di forza dell'algoritmo, in quanto ogni programmatore che usa questa classe
''' può utilizzare un array di bytes diverso da tutti gli altri, e ciò produrra anche un
''' risultato diverso. Questo sorgente, quindi, è riusabile al 100%.
''' </summary>
Public Property SaltBytes() As Byte()
Get
Return _SaltBytes
End Get
Set(ByVal value As Byte())
_SaltBytes = value
End Set
End Property
''' <summary>
''' Numero di iterazioni compiute dall'algoritmo di derivazione. Anche questo è un fattore
''' cambiando il quale cambia il risultato.
''' </summary>
Public Property DerivingIterations() As Int16
Get
Return _DerivingIterations
End Get
Set(ByVal value As Int16)
If value >= 2 Then
_DerivingIterations = value
Else
_DerivingIterations = 2
End If
End Set
End Property
Shared Sub New()
Rnd = New Random()
End Sub
''' <summary>
''' Randomizza i parametri del generatore. NOTA BENE: per verificare un codice seriale, è
''' necessario che il generatore mantenga le stesse proprietà dell'oggetto che ha creato il codice.
''' Quindi non bisogna abusare di questa funzione.
''' </summary>
Public Sub Randomize()
Me.CodeRank = Rnd.Next(6, 14)
Me.DerivingIterations = Rnd.Next(2, 10)
Dim Bytes(Rnd.Next(10, 40)) As Byte
For I As Int16 = 0 To Bytes.Length - 1
Bytes(I) = Rnd.Next(0, 256)
Next
Me.SaltBytes = Bytes
End Sub
''' <summary>
''' Genera un codice seriale esadecimale casuale basandosi sui dati immessi nelle proprietà
''' della classe.
''' </summary>
Public Function GenerateCode() As String
Dim Derive As Rfc2898DeriveBytes
Dim DerivedBytes() As Byte
Dim Result As New System.Text.StringBuilder
Dim RandomSequence(Me.CodeRank - 1) As Byte
For I As Int16 = 0 To RandomSequence.Length - 1
RandomSequence(I) = Rnd.Next(0, 256)
Next
Derive = New Rfc2898DeriveBytes(RandomSequence, Me.SaltBytes, Me.DerivingIterations)
DerivedBytes = Derive.GetBytes(Me.CodeRank)
For I As Int16 = 0 To Me.CodeRank - 1
Result.AppendFormat("{0:X2}{1:X2}", RandomSequence(I), DerivedBytes(I))
Next
Derive = Nothing
DerivedBytes = Nothing
RandomSequence = Nothing
Return Result.ToString()
End Function
''' <summary>
''' Controlla se il codice immesso è valido. Restituisce True in caso di validità.
''' </summary>
''' <param name="Code">Codice seriale proposto per la verifica.</param>
Public Function ValidateCode(ByVal Code As String) As Boolean
Dim OriginalCode As String = Regex.Replace(Code, "[\w\W-[A-Z0-9]]", "", RegexOptions.IgnoreCase)
Dim RandomSequence(Me.CodeRank - 1), CodeBytes(Me.CodeRank - 1), DerivedBytes() As Byte
Dim Derive As Rfc2898DeriveBytes
If OriginalCode.Length <> Me.CodeRank * 4 Then
Return False
End If
For I As Int16 = 0 To Me.CodeRank - 1
Try
RandomSequence(I) = Int32.Parse(OriginalCode.Substring(I * 4, 2), Globalization.NumberStyles.AllowHexSpecifier)
CodeBytes(I) = Int32.Parse(OriginalCode.Substring(I * 4 + 2, 2), Globalization.NumberStyles.AllowHexSpecifier)
Catch Ex As Exception
Return False
End Try
Next
Derive = New Rfc2898DeriveBytes(RandomSequence, Me.SaltBytes, Me.DerivingIterations)
DerivedBytes = Derive.GetBytes(Me.CodeRank)
OriginalCode = Nothing
Derive = Nothing
RandomSequence = Nothing
For I As Int16 = 0 To Me.CodeRank - 1
If CodeBytes(I) <> DerivedBytes(I) Then
Return False
End If
Next
CodeBytes = Nothing
DerivedBytes = Nothing
Return True
End Function
''' <summary>
''' Spezza il codice in parti di lunghezza specificata.
''' </summary>
Public Shared Function SplitCode(ByVal Code As String, ByVal ParamArray Lengths() As Int16) As String()
Dim CodeReader As New IO.StringReader(Code)
Dim Result As New List(Of String)
Dim Buffer() As Char
For Each Len As Int16 In Lengths
ReDim Buffer(Len - 1)
If CodeReader.ReadBlock(Buffer, 0, Len) > 0 Then
Result.Add(Buffer)
Else
Exit For
End If
Next
Return Result.ToArray()
End Function
End Class