Imports System.Xml
Imports System.Xml.Serialization
Imports System.Text
Imports Mercury.Mobile.Security.Cryptography
Imports System.Security.Cryptography

Namespace Security

    ''' <summary>
    ''' Represents a set of evidence to verify a user identity.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class UserEvidence
        Implements IXmlSerializable

#Region "Constructors"

        Public Sub New()
            Me.New(String.Empty, 0, String.Empty)
        End Sub

        Public Sub New(ByVal username As String, ByVal salt As Integer, ByVal credential As String)
            Me.New(username, salt, credential, String.Empty, String.Empty)
        End Sub

        Public Sub New(ByVal username As String, ByVal salt As Integer, ByVal credential As String, ByVal machineId As String, ByVal deviceType As String)
            Me._username = username
            Me._salt = salt
            Me._credential = credential
            Me._machineId = machineId
            Me._deviceType = deviceType
        End Sub

#End Region

#Region "IXmlSerializable"

        Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
            Return Nothing
        End Function

        Public Sub ReadXml(reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml
            If reader.Read() Then
                If reader.NodeType = XmlNodeType.Element Then
                    If reader.LocalName = "Username" Then
                        Me._username = reader.ReadString()
                        reader.Read()
                    End If

                    If reader.LocalName = "Salt" Then
                        Dim saltString As String = reader.ReadString()
                        If Not (String.IsNullOrEmpty(saltString) AndAlso IsNumeric(saltString)) Then _
                            Me._salt = Integer.Parse(saltString)
                        reader.Read()
                    End If

                    If reader.LocalName = "Credential" Then
                        Me._credential = reader.ReadString()
                        reader.Read()
                    End If

                    If reader.LocalName = "MachineId" Then
                        Me._machineId = reader.ReadString()
                        reader.Read()
                    End If

                    If reader.LocalName = "DeviceType" Then
                        Me._deviceType = reader.ReadString()
                        reader.Read()
                    End If
                End If
            End If
            reader.Read()
        End Sub

        Public Sub WriteXml(writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml
            ' Write xml
            If Not (String.IsNullOrEmpty(Me.Username)) Then _
                writer.WriteElementString("Username", Me.Username)
            If Me.Salt <> 0 Then _
                writer.WriteElementString("Salt", Me.Salt.ToString())
            If Not (String.IsNullOrEmpty(Me.Credential)) Then _
                writer.WriteElementString("Credential", Me.Credential)
            If Not (String.IsNullOrEmpty(Me.MachineId)) Then _
                writer.WriteElementString("MachineId", Me.MachineId)
            If Not (String.IsNullOrEmpty(Me.DeviceType)) Then _
                writer.WriteElementString("DeviceType", Me.DeviceType)
        End Sub

#End Region

#Region "Properties"

        Private _username As String

        ''' <summary>
        ''' Gets the username associated with this evidence.
        ''' </summary>
        ''' <returns>The username associated with this evidence.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Username As String
            Get
                Return Me._username
            End Get
        End Property

        Private _salt As Integer

        ''' <summary>
        ''' Gets the salt value used to hash the credential evidence.
        ''' </summary>
        ''' <returns>The salt value used to hash the credential evidence.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Salt As Integer
            Get
                Return Me._salt
            End Get
        End Property

        Private _credential As String

        ''' <summary>
        ''' Gets the credential evidence for this user.
        ''' </summary>
        ''' <returns>The credential evidence for this user.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Credential As String
            Get
                Return Me._credential
            End Get
        End Property

        Private _machineId As String

        ''' <summary>
        ''' Gets the name of the machine on which the evidence was generated.
        ''' </summary>
        ''' <returns>The name of the machine on which the evidence was generated.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property MachineId As String
            Get
                Return Me._machineId
            End Get
        End Property

        Private _deviceType As String

        ''' <summary>
        ''' Gets the type of device on which the evidence was generated.
        ''' </summary>
        ''' <returns>The type of device on which the evidence was generated.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property DeviceType As String
            Get
                Return Me._deviceType
            End Get
        End Property

#End Region

        ''' <summary>
        ''' Returns an object that represents the client user evidence for authentication.
        ''' </summary>
        ''' <param name="username">The username to validate.</param>
        ''' <param name="salt">The salt value with which to hash the user credential.</param>
        ''' <param name="credential">The user credential.</param>
        ''' <param name="machineId">The client machine identifier.</param>
        ''' <param name="deviceType">The client device type.</param>
        ''' <returns>An object that represents the client user evidence for authentication.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetClientEvidence(ByVal username As String, ByVal salt As Integer, ByVal credential As String, ByVal machineId As String, ByVal deviceType As String) As UserEvidence
            ' Hash to expected value
            Dim info = New HashInfo(SHA1.Create(), Encoding.Unicode)
            Return UserEvidence.GetClientEvidence(info, username, salt, credential, machineId, deviceType)
        End Function

        ''' <summary>
        ''' Returns an object that represents the client user evidence for authentication.
        ''' </summary>
        ''' <param name="hashInfo">An object that provides information on hash value construction.</param>
        ''' <param name="username">The username to validate.</param>
        ''' <param name="salt">The salt value with which to hash the user credential.</param>
        ''' <param name="credential">The user credential.</param>
        ''' <param name="machineId">The client machine identifier.</param>
        ''' <param name="deviceType">The client device type.</param>
        ''' <returns>An object that represents the client user evidence for authentication.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetClientEvidence(ByVal hashInfo As HashInfo, ByVal username As String, ByVal salt As Integer, ByVal credential As String, ByVal machineId As String, ByVal deviceType As String) As UserEvidence
            Dim newCredential = UserEvidence.CreateHash(username, salt, credential, hashInfo)
            ' Hash for broadcast on network
            Dim transferSalt = HashUtil.CreateRandomSalt()
            newCredential = UserEvidence.CreateHash(String.Empty, transferSalt, newCredential, hashInfo)
            Return New UserEvidence(username, transferSalt, newCredential, machineId, deviceType)
        End Function

        ''' <summary>
        ''' Determines whether the specified credential matches is validated by this evidence.
        ''' </summary>
        ''' <param name="credential">The user credential to validate.</param>
        ''' <returns>True if the specified credential matches is validated by this evidence; otherwise, false.</returns>
        ''' <remarks></remarks>
        Public Function IsValid(ByVal credential As String) As Boolean
            Dim info = New HashInfo(SHA1.Create(), Encoding.Unicode)
            Return Me.IsValid(credential, info)
        End Function

        ''' <summary>
        ''' Determines whether the specified credential matches is validated by this evidence.
        ''' </summary>
        ''' <param name="credential">The user credential to validate.</param>
        ''' <param name="hashInfo">An object that provides information on hash value construction.</param>
        ''' <returns>True if the specified credential matches is validated by this evidence; otherwise, false.</returns>
        ''' <remarks></remarks>
        Public Function IsValid(ByVal credential As String, ByVal hashInfo As HashInfo) As Boolean
            Dim test = UserEvidence.CreateHash(String.Empty, Me.Salt, credential, hashInfo)
            Return Me.Credential = test
        End Function

        Friend Shared Function CreateHash(ByVal username As String, ByVal salt As Integer, ByVal credential As String, ByVal info As HashInfo) As String
            Return HashUtil.GetHash(username + credential, salt, info)
        End Function

    End Class

End Namespace