Attribute VB_Name = "basSignExample"
Option Explicit

' This example shows how you could use the RSA_Raw functions
' to create and verify a digital signatue of a short message
' using a 512-bit RSA key. Please read the comments below.

' Last updated:
'   $Date: 2006-06-27 04:20:00 $

' Include the module basCrPKI in your project
' from the CryptoSys PKI toolkit

' ******************************************************************
' COMMENTS ON THIS CODE
'
' 1. This code is given as an example of how you could use RSA to
' create a digital signature of a short message directly.
'
' 2. In practice, a message digest hash of the longer message is
' computed first and this shorter hash value is "encrypted" in the
' RSA block.
'
' 3. The methods used here to create and read the RSA block
' can be done much faster using the RSA_EncodeMsg and RSA_DecodeMsg
' functions.
'
' 4. In practice, too, the public key is usually provided in a
' public key certificate file (X.509 file).
'
' 5. In an earlier version we hinted that the key strings could be
' hardcoded. We lied. ALWAYS read in the private and public key
' values from the files each time you use them.
'
' 6. Treat the private key string as the very important secret that
' it is. NEVER print it out or save it to disk. Always use the
' WIPE_String function immediately you have finished with it.
' ******************************************************************

Public Function TestSign()
    Dim strPlain As String
    Dim lngRet As Long
    Dim nBlockLen As Long
    Dim nBytes As Long
    Dim nPad As Long
    Dim sPublicKeyFile As String
    Dim sPrivateKeyFile As String
    Dim strPublicKey As String
    Dim strPrivateKey As String
    Dim strPassword As String
    Dim abBlock() As Byte
    Dim abPlain() As Byte
    Dim i As Integer
    Dim iOffset As Integer
    Dim abResult() As Byte
    Dim strResult As String
    
    ' Some keys we prepared earlier (see CreateTestKeys)
    sPublicKeyFile = App.Path & "\" & "ourkeypub.bin"
    sPrivateKeyFile = App.Path & "\" & "ourkeypri.bin"
    ' This is just a demo, OK. NEVER hard-code real passwords.
    strPassword = "password"
    
    ' (1) In this first part of the example, we are the issuing authority
    ' We have the private key and its password (hopefully a better one than 'password')
    
    ' Read in the private key as base64 string
    ' NB There is a deliberate 500ms pause in this function
    strPrivateKey = rsaReadPrivateKey(sPrivateKeyFile, strPassword)
    
    ' Check for error
    If Len(strPrivateKey) <= 0 Then
        MsgBox "Could not read private key file " & sPrivateKeyFile, vbCritical
        Exit Function
    End If
    
    ' Check its length - it should be 64 bytes (512 bits)
    nBlockLen = RSA_KeyBytes(strPrivateKey)
    If nBlockLen <= 0 Then  '(you could be more specific here and check for the exact length)
        MsgBox "Error with private key length", vbCritical
        Exit Function
    End If
    Debug.Print "Private key is " & nBlockLen & " bytes long (" & RSA_KeyBits(strPrivateKey) & " bits)"
    
    ' Get the string of characters we want to sign (max 61 bytes long for a 512-bit key)
    strPlain = "What we want to sign...!"
    Debug.Print "DATA=" & strPlain
    
    ' Convert to bytes
    abPlain = StrConv(strPlain, vbFromUnicode)
    ' Show in hex
    Debug.Print "DATA=" & cnvHexStrFromBytes(abPlain)
    ' Check exact length in bytes
    nBytes = UBound(abPlain) - LBound(abPlain) + 1
    
    ' Construct encryption block of exactly 128 bytes
    ReDim abBlock(nBlockLen - 1)
    
    ' Required encryption block - this is a simplified version of PKCS#1
    '|<-------------------(64 bytes)----------------->|
    '+--+--+---------+--+-----------------------------+
    '|00|01|FFFF...FF|00|      DATA TO SIGN           |
    '+--+--+---------+--+-----------------------------+
    
    ' First byte is always 00
    abBlock(0) = 0
    ' Second byte is always 01
    abBlock(1) = 1
    ' Then pad with 0xFF
    nPad = nBlockLen - 3 - nBytes
    
    If nPad < 0 Then
        MsgBox "Data is too long/ key is too short"
        Exit Function
    End If
    
    iOffset = 2
    
    For i = 0 To nPad - 1
        abBlock(iOffset + i) = &HFF
    Next
    
    ' Separating NUL
    iOffset = iOffset + nPad
    abBlock(iOffset) = 0
    
    ' Then the data to be signed
    iOffset = iOffset + 1
    For i = 0 To nBytes - 1
        abBlock(iOffset + i) = abPlain(i)
    Next
    
    ' Show the block in hex
    Debug.Print "BLCK=" & cnvHexStrFromBytes(abBlock)
    
    ' Now we use the RSA function to encrypt the input block using the private key
    ' this creates a `signature'
    lngRet = RSA_RawPrivate(abBlock(0), nBlockLen, strPrivateKey, 0)
    Debug.Print "RSA_RawPrivate returns " & lngRet
    If lngRet <> 0 Then
        Debug.Print pkiGetLastError()
    Else
        ' Display our results in hex format
        Debug.Print "ENCR=" & cnvHexStrFromBytes(abBlock)
    End If
    
    ' We send our `signature' as the 64 bytes now in abBlock
    ' Sending in hex or base64 format is usually simpler.
    
    ' -------
    
    ' (2) In this second part of the example, we are the person authenticating the data
    ' We only have the public key, which can be made freely available.
    ' No one can use the public key to create the signature
    
    ' Read in the public key as base64 string
    strPublicKey = rsaReadPublicKey(sPublicKeyFile)
    
    ' Check for error
    If Len(strPublicKey) = 0 Then
        MsgBox "Could not read public key file " & sPublicKeyFile, vbCritical
        Exit Function
    End If
    
    ' Check its length - it should be 64 bytes (512 bits)
    nBlockLen = RSA_KeyBytes(strPublicKey)
    If nBlockLen <= 0 Then
        MsgBox "Error with public key length", vbCritical
        Exit Function
    End If
    Debug.Print "Public key is " & nBlockLen & " bytes long (" & RSA_KeyBits(strPublicKey) & " bits)"
    
    ' [Do not hardcode this key - always read it in from the public key file or certificate]
    
    ' Given the encrypted data in abBlock,
    ' we use the RSA function to decrypt the input block using the public key
    lngRet = RSA_RawPublic(abBlock(0), nBlockLen, strPublicKey, 0)
    Debug.Print "RSA_RawPublic returns " & lngRet
    If lngRet <> 0 Then
        Debug.Print pkiGetLastError()
    Else
        ' Display our results in hex format
        Debug.Print "DECR=" & cnvHexStrFromBytes(abBlock)
    End If
    
    ' Extract the plaintext from the resulting block
    ' And check the first two bytes are what we expect
    If abBlock(0) <> 0 Then
        MsgBox "Decryption error"
        Exit Function
    End If
    If abBlock(1) <> 1 Then
        MsgBox "Decryption error"
        Exit Function
    End If
    
    ' If we've got this far, we have almost certainly succeeded
    ' but we'll keep checking - we should have all 0xff's until we find a 0
    For i = 2 To nBlockLen - 1
        If abBlock(i) = 0 Then
            ' Found start of real data we want
            Exit For
        ElseIf abBlock(i) <> &HFF Then
            ' Unexpected character
            MsgBox "Decryption error"
            Exit Function
        End If
    Next
    
    ' Copy result we want
    nBytes = nBlockLen - i - 1
    ReDim abResult(nBytes - 1)
    For i = i + 1 To nBlockLen - 1
        abResult(i - nBlockLen + nBytes) = abBlock(i)
    Next
    
    ' Convert to a string
    strResult = StrConv(abResult, vbUnicode)
    
    Debug.Print strResult
    
    
End Function