RSA ECDSA

Windows specific questions.
Post Reply
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

RSA ECDSA

Post by deltarho[1859] »

Most of you will know what public key encryption and signing is but some of you may not. For those of you that do not here are two excellent sites worth checking out.

RSA and DSA

Both methods are used here in the context of sending a message encrypted by a symmetric key algorithm suchas AES.

The Sender

1) Encrypts the message.
2) The encryption key/password is encrypted using recipient's RSA public key.
3) A hash of the encrypted message is determined.
4) The hash is then signed using the sender's DSA private key.

If the key/password is very strong, preferably a 256 bit binary key, then the encrypted key/password will not need to be changed that often.

RSA encryption could be used on the message but the message size is limited to the RSA bit strength employed and reduced even further if padding is used. The recommended padding is the Optimal Asymmetric Encryption Padding (OAEP) scheme, which uses a hash function, resulting in this simple formula for determining the maximum message size: (Bit strength)\8 - 2 - 2x(Hash length)\8. So, with a bit strength of 1024 and a 160 bit hash we get 86 bytes. We have a very different animal then to AES, for example, which can handle GBs of message. Actually, the term padding is a misnomer. If we applied RSA 1024 encryption to 160 bits, say, then the encrypted output would be 1024 bits, the RSA bit strength, but we would not recognise the structure of the 160 bits. It has the opposite effect to a message digest.

The Recipient

1) The signature is verified using the sender's DSA public key.
2) A hash of the encrypted message is determined and if that equates with the sent hash then the encrypted message is effectively authenticated.
3) The encrypted key/password is decrypted using the recipient's RSA private key.
4) The decrypted message is then decrypted.

If Eve, a dastardly eavesdropper, intercepts any of the files sent then they will be of no use to her without the recipient's RSA private key. The plaintext version of the key/password never enters the public domain.

Bit strength

RSA 1024 was broken in the middle of 2017 and the US National Institute of Standards and Technology (NIST) is recommending we now use at least RSA 2048. There is a correspondence between the RSA modulus/bit strength and security strength. AES 128 has a security strength of 128 bits. SHA512, for example, because of the birthday paradox, has a security strength of 256 bits. Here is a comparison taken from NIST Special Publication 800-57 Part 1 Revision 4 Recommendation for Key ManagementJanuary 2016 Table 2 page 53.

Code: Select all

Security
Strength  RSA modulus ECDSA
  80        1024       160
 112        2048       224
 128        3072       256
 192        7680       384
 256       15360       512

The DSA bit strengths are the same as RSA. More on Elliptic Curve Digital Signature Algorithm (ECDSA) later.

The correspondence between RSA bit strength and security strength is more precisely defined in Implementation Guidance for FIPS 140-2 and the Cryptographic Module Validation Program page 106.

The NIST reckon that RSA 2048 should hold us in good stead until about 2030 beyond which we should move up to 3072 bits. The NIST are not currently 'pushing' over and above RSA 3072. The NIST are not the only authority making recommendations and, after much reading, the following code, GenerateKeyPairs, a console application, defaults to a bit strength of 3072 bits; exceeding all minimum recommendations at this time (2018).

With regard DSA the NIST Digital Signature Standard specifies the following choices for the pair L and N where L is the bit strength and N is the length of the hash function to be signed.

(1024, 160) NIST FIPS-186-2 for earlier than Windows 8
(2048, 224) Above plus NIST FIPS-186-3 for Windows 8 and later
(2048, 256) -do-
(3072, 256) -do-

DSA (2048, 224) does not work with Microsoft APIs because SHA224 is not supported by Windows. FIPS-186-4 was published July 2013 but the four choices above were not expanded upon. DSA (1024, 160) has not been broken yet but it is no longer recommended by the NIST. So, the DSA used in Windows 7 is no longer recommended.

I found DSA to be very slow. For a 256 bit hash the signing and verifying times for DSA (2048, 256) and (3072, 256) were in the seconds whereas with ECDSA 256 I got, with one test, 0.9ms and 1.2ms for signing and verifying respectively. There are not many DSA/ECDSA performance comparisons on the Internet but of those that I found they did not report such a marked difference as I found. Perhaps the Microsoft DSA implementation is a poor one. With DSA I would have been lumbered with writing code to cater for Windows 7 (and earlier) and Windows 8 (and later).

Clearly, ECDSA wins hands down with regard to Microsoft's APIs and is OK for Windows Vista and later. We have a few ECDSA bit strengths to chose from but with ECDSA 256 having a security strength of 128 bits it is an ideal partner for RSA 3072 at 128 bits.

A little off topic but until recently the NIST recommended that passwords should have a bit strength/entropy of at least 80 bits. This has now been updated to 112 bits.

Console application GenerateKeyPairs.

We could have a separate folder for each person we deal with but there will be the resulting file navigation to contend with. I have opted to have everything in one folder: My keys, other people's keys and the applications. To this end GenerateKeyPairs asks for a prefix to add to the key names. For my keys I simply chose DR_ and got these keys.

DR_RSAPublicKey3072.dat
DR_RSAPrivateKey3072.dat
DR_ECDSAPublicKey256.dat
DR_ECDSAPrivateKey256.dat

The bit strengths are got from the code assignments.

On my machine using '-gen gcc -Wc -O3' the keys were generated in a little more than four seconds. If you have FreeBASIC 64 bit then use it because generation is a lot faster. Just to put things into perspective I generated a public and private key pair for RSA 16384, with 32 bit, and it took over 8 minutes. The law of diminishing returns is very noticeable with both RSA and DSA key generation and this is reflected when using the keys.

GUI application RSA-ECDSA

This was written using José Roca's WinFBX Windows framework. If you have Paul Squires' WinFBE then you will already have the necessary files on board and it is a simple matter to add the WinFBX path to the Editor's Compiler Setup. If you are using another IDE then go to JoseRoca / WinFBX at GitHub and click on the green 'Clone or download' button; then click on 'Download ZIP'. Within the unzipped WinFBX-master folder is a folder called Afx. Copy Afx to your FreeBASIC's installation's inc folder. That is it. If you have FreeBASIC 64 bit then do the same into it's inc folder; wherever you put that. I am suggesting the GitHub link as it has the latest Afx folder. In the WinFBX folder there are a lot of examples and there is also FBWinSpy for 'dissecting' GUIs. You can get a copy of the WinFBX help file here at the bottom of the first post.

I am compiling RSA-ECDSA.bas with '-gen gcc -Wc -O3 RSA-ECDA.rc'.

This what RSA-ECDSA looks like.

Image

We don't need a help file for that, do we? <smile>

The Microsoft open file dialog is used extensively and I am using a heavily adapted version of José's code. Since there are a lot of different files the filtering is done programmatically. If we clicked on 'Verify a .sig file', for example, then we get a list of *ECDSAPublicKey*.dat files and nothing else. We are not left scratching our heads as to which algorithm we should use and what type of key. After selection we get another open file dialog with a list of *Hash.dat files and nothing else. After that selection we get another open file dialog with a list of *Hash.dat.sig files and nothing else. The only output with this task is a message advising whether we have a successful verification or not. The open file dialog title bar keeps us informed; as does the filter.

The Function Hash is one of my stock functions. It could have ben trimmed down for RSA-ECDSA but that would defeat the object of a stock function. <smile>

The first three tasks are obvious and don't require an explanation.

Hash a file option.

This is for hashing an encrypted message - the one subject to symmetric encryption, such as AES. If we hash <Somefile.ext> then we get <Somefile.ext_Hash.dat> and that will be a 256 bit (32 byte) binary hash; as opposed to a 64 character hex string. The open file dialog here has an all files ("*.*") filter; I don't know how you are naming your encrypted message.

Verify hashes option.

The first open file dialog needs the encrypted message which is then hashed for us. The second open file dialog needs a hash file, <Somefile.ext_Hash.dat>. This operation is similar to 'Verify a .sig file' in that the only output with this task is a message advising whether we have a successful authentication or not.

RSA-ECDSA specifics

The padlock icon in the window caption is placed there by

Code: Select all

pWindow.SmallIcon = LoadImage(hInstance, MAKEINTRESOURCE(100), IMAGE_ICON, 32, 32, LR_SHARED)
and the entry '100 icon padlock.ico' in the resource file.

The 'signature' bitmap at the bottom right is placed there by

Code: Select all

LoadImage(hInstance, MAKEINTRESOURCE(101), IMAGE_BITMAP, 0, 0, LR_SHARED)
pWindow.AddControl("BitMapButton", , ID_DR, "#101", 223, 266, 26, 33)
and the entry '101 bitmap DRmed.bmp' in the resource file.

The 'signature' is just a bit of fun. I figured that I couldn't write an application for signing without signing the dialog. <smile> Just click on the bitmap for another reason for it's existence.

The images can be downloded at the beginning of the next post.

The 256 bit hashing done in 'Hash a file' and 'Verify hashes' uses a truncated SHA512 and adapted to overcome the length extension bug in the SHA-2 family of hash functions.

If RSA-ECDSA's closing position differs to that of its opening position it's closing position will be remembered and used at the next opening. I do this often as I have two monitors and prefer some applications to open on the secondary monitor. No big deal but it is there if required.

TaskDialog, as opposed to messagebox, has been used throughout. José's code makes it very easy to implement. Some of the parameters are the same for each execution so I wrote a little wrapper which takes just four parameters; the same number as messagebox. TaskDialog is nothing like as powerfull as TaskDialogIndirect and is only a small step up from messagebox but here is an example.

Image

That was got from

Code: Select all

TDWrapper Hwnd, "Hash comparison", "Hash of " + EncMainMessage + " and given hash match.", TD_INFORMATION_ICON
All the source code is in the next post and any revisions will take place there. The files for theming are also there.

Code: Select all

RSA 3072/ECDSA 256 (128) versus RSA 7680/ECDSA 384 (192) on key sizes 256 bits and 384 bits respectively.
 
enc 0.2ms                         0.6ms
dec 9.3ms                       119.1ms
sig 0.3ms                         0.7ms
ver 0.5ms                         1.0ms
Here we can see the performance of RSA suffering very badly moving from a 128 bit security strength to a 192 bit security strength. The ECDSA performance, on the other hand, is very much less noticable. Having said that enc/dec on a key/password will be a rare event, perhaps every year or so. Organisations involved in mass sig/ver operations will be looking for a balance between speed and security and will need a very good reason to be using a security level of 192 bits in 2018. NIST clearance levels are 128 bits up to Secret and 192/256 bits up to Top Secret. For most of us RSA 3072/ECDSA 256 (128) is more than adequate.

As written RSA-ECDSA will not accomodate ECDSA 521. It could, but I am not going to go there. I have no need to rub shoulders with the NSA, GCHQ and their like.

Special thanks to José Roca - WinFBX is absolutely awesome.

Additional notes

Still on topic but minus the encryption/decryption aspect. Suppose you have a pdf which anyone can have but want to send it to someone in the knowledge that they know for certain it was from you and that is has not been tampered with; a licence agreement, for example. The message now is a pdf and not an encrypted file. Hash the pdf file and then sign the hash. When the recipient verifies the signature and then verifies the hashes using options 4 and 6 of the RSA-ECDSA dialog then they will know that you sent the pdf and it has not been tampered with. We can sign anything - a jpg or whatever.

You may have wondered what the difference between a digital signature and a HMAC is. HMAC is symmetrical: Both the sender and receiver use the same secret key. 'A' could produce a licence agreement and a HMAC but 'B' may repudiate sending anything. Digital Signatures are asymmetric and provide integrity plus non-repudiation whereas HMAC provides only integrity. 'B' cannot repudiate the signature as his/her private key is not known to anyone else and 'A' cannot try and pull a 'fast one'.

Quantum computers

The D-Wave 2000 qubit quantum computer, which costs $15 million dollars and will need a spare room to put it in, is about 100 million times faster than a desktop PC or 3600 times faster than the current super computers. I do not know what the running costs are but if anyone can afford £15 million dollars they can probably afford getting some solar panels fitted on their roof. <smile> It is reckoned not to be a fully fledged quantum computer as, it seems, it does not exploit quantum entanglement. It may be some years before quantum computers become main stream, if ever they do. Remembering that 2^256 = 2^128 x 2^128 > 10^38 x 2^128 then AES256 is more powerful than 10^38 x AES128. It is thought that AES256 is post-quantum computer proof. I have not seen anything with regard ECDSA but the 'arithmetic' used by RSA and DSA is such that both are destined to be dead in the water in the post-quantum computer era. In other words the security strength of RSA and DSA based upon classical computers collapses with quantum computer computing. The NIST recently held a competition with regard to post-quantum computer cryptography. All being well we should be well prepared.

Finally, I should mention that cryptlib has both RSA, up to RSA 4096, and ECDSA, up to ECDSA 521, giving both a Windows and Linux route but my comfort zone is the Windows API so please forgive me for preferring to keep comfortable. <big smile>
Last edited by deltarho[1859] on Feb 13, 2018 20:12, edited 1 time in total.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: RSA ECDSA

Post by deltarho[1859] »

Here are the images

Images

GenerateKeypairs.bas

Code: Select all

#include once "windows.bi"
#inclib "bcrypt"
#include once "win/wincrypt.bi"
#include once "file.bi"

Const HashLen = 256 ' Corresponds to SHA256
' Only ever use a HashLen of 256, 384 or 521

#define STATUS_SUCCESS 0

Dim As Ulong cbOutput, pcbResult
Dim As dword dwStatus
Dim As BCRYPT_KEY_HANDLE hKeyHandle
Dim As BCRYPT_ALG_HANDLE hAlgHandle
Dim As Long lError, i, BitStrength
Dim As String sPrefix
Dim Blob() As Byte
Dim As Double t

' Console specifics
SetConsoleTitle("GenerateKeyPairs")
SetWindowPos( GetConsoleWindow, HWND_TOPMOST, 200, 400, 0, 0, SWP_NOSIZE )

Do
  Input "Task file prefix: "; sPrefix
Loop Until sPrefix <> ""

Print "Please wait" : Print

' RSA

BitStrength = 3072 ' Security strength: 128 bits

t = Timer

dwStatus = BCryptOpenAlgorithmProvider( @hAlgHandle, BCRYPT_RSA_ALGORITHM, "", 0 )
If dwStatus <> STATUS_SUCCESS Then lError = 10 : Goto ErrorTrap

' Create an empty object
dwStatus = BCryptGenerateKeyPair(hAlgHandle, @hKeyHandle, BitStrength, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 20 : Goto ErrorTrap
' Populate object
dwStatus = BCryptFinalizeKeyPair(hKeyHandle, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 30 : Goto ErrorTrap

' Generate RSA public key encrypt
' Get public key memory blob length in pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_RSAPUBLIC_BLOB, 0, 0, @pcbResult,0)
If dwStatus <> STATUS_SUCCESS Then lError = 40: Goto ErrorTrap
Redim Blob(1 To pcbResult) As Byte
cbOutput = pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_RSAPUBLIC_BLOB, @Blob(1), cbOutput, @pcbResult,0)
If dwStatus <> STATUS_SUCCESS Then lError = 50: Goto ErrorTrap
If Fileexists( sPrefix + "RSAPublicKey" + Str(BitStrength) + ".dat" ) Then Kill sPrefix + "RSAPublicKey" + Str(BitStrength) + ".dat"
Open sPrefix+"RSAPublicKey" + Str(BitStrength) + ".dat" For Binary As #1
Put #1, , Blob()
Close #1
Print sPrefix + "RSA public key generated"
Print

' Generate RSA private key decrypt
' Get private key memory blob length in pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_RSAPRIVATE_BLOB, 0, 0, @pcbResult,0)
If dwStatus <> STATUS_SUCCESS Then lError = 60: Goto ErrorTrap
Redim Blob(1 To pcbResult) As Byte
cbOutput = pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_RSAPRIVATE_BLOB, @Blob(1), cbOutput, @pcbResult,0)
If dwStatus <> STATUS_SUCCESS Then lError = 70: Goto ErrorTrap
If Fileexists( sPrefix + "RSAPrivateKey" + Str(BitStrength) + ".dat" ) Then Kill sPrefix + "RSAPrivateKey" + Str(BitStrength) + ".dat"
Open sPrefix + "RSAPrivateKey" + Str(BitStrength) + ".dat" For Binary As #1
Put #1, , Blob()
Close #1
Print sPrefix+"RSA private key generated"
Print

BCryptCloseAlgorithmProvider(hAlgHandle, 0)
BCryptDestroyKey hKeyHandle

' ECDSA

BitStrength = 256 ' Security strength: 128 bits

dwStatus = BCryptOpenAlgorithmProvider( @hAlgHandle, wstr( "ECDSA_P" + Str(HashLen) ), "", 0 )
If dwStatus <> STATUS_SUCCESS Then lError = 80 : Goto ErrorTrap

' Create an empty object
dwStatus = BCryptGenerateKeyPair(hAlgHandle, @hKeyHandle, BitStrength, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 90 : Goto ErrorTrap
' Populate object
dwStatus = BCryptFinalizeKeyPair(hKeyHandle, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 100 : Goto ErrorTrap

' Generate ECDSA public key verify
' Get public key memory blob length in pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_ECCPUBLIC_BLOB, 0, 0, @pcbResult, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 110: Goto ErrorTrap
Redim Blob(1 To pcbResult) As Byte
cbOutput = pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_ECCPUBLIC_BLOB, @Blob(1), cbOutput, @pcbResult, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 120: Goto ErrorTrap
If Fileexists( sPrefix + "ECDSAPublicKey" + Str(BitStrength) + ".dat" ) Then Kill sPrefix + "ECDSAPublicKey" + Str(BitStrength) + ".dat"
Open sPrefix+"ECDSAPublicKey" + Str(BitStrength) + ".dat" For Binary As #1
Put #1, , Blob()
Close #1
Print sPrefix + "ECDSA public key generated"
Print

' Generate ECDSA private key sign
' Get private key memory blob length in pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_ECCPRIVATE_BLOB, 0, 0, @pcbResult, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 130: Goto ErrorTrap
Redim Blob(1 To pcbResult) As Byte
cbOutput = pcbResult
dwStatus = BCryptExportKey(hKeyHandle, 0, BCRYPT_ECCPRIVATE_BLOB, @Blob(1), cbOutput, @pcbResult, 0)
If dwStatus <> STATUS_SUCCESS Then lError = 140: Goto ErrorTrap
If Fileexists( sPrefix + "ECDSAPrivateKey" + Str(BitStrength) + ".dat" ) Then Kill sPrefix + "ECDSAPrivateKey" + Str(BitStrength) + ".dat"
Open sPrefix + "ECDSAPrivateKey" + Str(BitStrength) + ".dat" For Binary As #1
Put #1, , Blob()
Close #1
Print sPrefix+"ECDSA private key generated"
Print

t = Timer - t
Print "Time taken:"; t; "s"
Goto TidyUp

ErrorTrap:

Print "Error at location " + Str(lError);", Windows error code ";Hex(dwStatus)

TidyUp:
If hAlgHandle <> 0 Then BCryptCloseAlgorithmProvider(hAlgHandle, 0)
If hKeyHandle <> 0 Then BCryptDestroyKey hKeyHandle

Print : Print "Press any key"

Sleep
AES-RSA-ECDSA.bas ( March 5, 2018 )

Code: Select all

' ########################################################################################
' With regard Afx material:
' Copyright (c) 2016 José Roca. Freeware. Use at your own risk.
' This CODE And INFORMATION Is PROVIDED "As Is" WITHOUT WARRANTY OF Any KIND, EITHER
' EXPRESSED Or IMPLIED, INCLUDING BUT Not LIMITED To THE IMPLIED WARRANTIES OF
' MERCHANTABILITY And/Or FITNESS For A PARTICULAR PURPOSE.
' 
' ########################################################################################


#Define UNICODE
#Define _WIN32_WINNT &h0602
#Include Once "Afx/CWindow.inc"
#Include Once "Afx/CFileSys.inc"
#Inclib "bcrypt"
#Inclib "crypt32"
#Include Once "windows.bi"
#Include Once "String.bi"

Using Afx

Const HashLen = 256 ' Corresponds To SHA256
' Only ever use a HashLen of 256, 384 Or 521
Const STATUS_SUCCESS = 0
Const STATUS_INVALID_SIGNATURE = &HC000A000
Const IDC_GROUPBOX = 1001
Const IDC_OPTION1 = 1002
Const IDC_OPTION2 = 1003
Const IDC_OPTION3 = 1004
Const IDC_OPTION4 = 1005
Const IDC_OPTION5 = 1006
Const IDC_OPTION6 = 1007
Const IDC_OPTION7 = 1008
Const IDC_OPTION8 = 1009
Const ID_OK = 1010
Const ID_DR = 1011
Const EncAES = 1
Const DecAES = 2
Const Enc = 3
Const Dec = 4
Const Sig = 5
Const Ver = 6
Const HashFile = 7
Const HashCompare = 8
Const Enc_File = 9
Const Sig_File = 10
Const Hash_File = 11
Const All_Files1 = 12
Const All_Files2 = 13
Const AES128 = 128
Const AES192 = 192
Const AES256 = 256
const BlockSize = 16
const BufferSize = 256 * 1024

#Define CrLf Chr(10) + Chr(13)
' Following courtesy of Mr Swiss
#Define IsFalse(e)  ( Not CBool(e) )
#Define IsTrue(e)   ( CBool(e) )

Declare Function WinMain (Byval hInstance As HINSTANCE, _
Byval hPrevInstance As HINSTANCE, _
Byval szCmdLine As Zstring Ptr, _
Byval nCmdShow As Long) As Long

End WinMain(GetModuleHandleW(NULL), NULL, Command(), SW_NORMAL)

' // Forward declaration
Declare Function WndProc (Byval hwnd As HWND, Byval uMsg As UINT, Byval wParam As WPARAM, Byval lParam As LPARAM) As LRESULT
Declare Function AfxIFileOpenDialog (Byval hwndOwner As HWND, Byval DisplyType As Long, _
Byval sigdnName As SIGDN = SIGDN_FILESYSPATH) As Wstring Ptr
Declare Function IsHex( As String ) As Long
Declare Function Hash( As lpcwstr, As String, As Long, As Long, As Long, As String = Chr(0)) As String
Declare Function GetFileHashEx( As String, As Long ) As String
Declare Sub TDWrapper( As HWND, As PCWSTR, As PCWSTR, As PCWSTR )
declare Function AES( As Long, As String, As String, As Long ) As Long

Dim Shared As Long xWindow, yWindow
Dim shared pFileSys As CFileSys
  
' Main

Function WinMain (Byval hInstance As HINSTANCE, _
  Byval hPrevInstance As HINSTANCE, _
  Byval szCmdLine As Zstring Ptr, _
  Byval nCmdShow As Long) As Long
  
  Dim As Long f
   
  ' // Set process DPI aware
  AfxSetProcessDPIAware
  
  ' // Create the main Window
  Dim pWindow As CWindow
  '
  pWindow.SetFont("Tahoma", 10, FW_NORMAL, , , , DEFAULT_CHARSET)
  pWindow.Create(NULL, "AES-RSA-ECDSA", @WndProc, ,,,, WS_SYSMENU Or WS_CAPTION, WS_EX_TOPMOST)
  pWindow.SetClientSize(250, 360)
  
  ' Open at last position
  
  If pFileSys.Fileexists("Position.dat") Then
    f = Freefile
    Open "Position.dat" For Binary As #f
    Get #f, , xWindow
    Get #f, , yWindow
    Close #f
    Dim hwnd As Hwnd = pWindow.hWindow
    SetWindowPos( HWnd, HWND_NOTOPMOST, xWindow, yWindow, 0, 0, SWP_NOSIZE )
  Else
    pWindow.Center
  End If
  
  ' Get small icon For titlebar
  pWindow.SmallIcon = LoadImage(hInstance, MAKEINTRESOURCE(100), IMAGE_ICON, 32, 32, LR_SHARED)
  
  ' // Add a group box control
  Dim As Long h
  h = 60
  pWindow.AddControl("GroupBox", , IDC_GROUPBOX, "Choose one of the following tasks", 20, 12, 210, 224+h)
  ' // Add radio buttons (the first one should have the WS_GROUP style)
  pWindow.AddControl("RadioButton", , IDC_OPTION1, "Encrypt a file AES", 64, 48, 120, 16, WS_GROUP)
  pWindow.AddControl("RadioButton", , IDC_OPTION2, "Decrypt a .aes file", 64, 78, 120, 16)
  pWindow.AddControl("RadioButton", , IDC_OPTION3, "Encrypt a key RSA", 64, 48+h, 120, 16)
  pWindow.AddControl("RadioButton", , IDC_OPTION4, "Decrypt a .enc file", 64, 78+h, 130, 16)
  pWindow.AddControl("RadioButton", , IDC_OPTION5, "Sign a hash", 64, 108+h, 130, 16)
  pWindow.AddControl("RadioButton", , IDC_OPTION6, "Verify a .sig file", 64, 138+h, 130, 16)
  pWindow.AddControl("RadioButton", , IDC_OPTION7, "Hash a file", 64, 168+h, 130, 16)
  pWindow.AddControl("RadioButton", , IDC_OPTION8, "Verify hashes", 64, 198+h, 130, 16)
  ' // Add OK button
  pWindow.AddControl("Button", , ID_OK, "OK", 80, 250+h, 88, 38)
  
  'Get dialog image
  LoadImage(hInstance, MAKEINTRESOURCE(101), IMAGE_BITMAP, 0, 0, LR_SHARED)
  pWindow.AddControl("BitMapButton", , ID_DR, "#101", 223, 266+h, 26, 33)
  
  LoadImage(hInstance, MAKEINTRESOURCE(102), IMAGE_ICON, 0, 0, LR_SHARED)
  
  ' // Check the first radio button
  'CheckDlgButton pWindow.hWindow, IDC_OPTION1, BST_CHECKED
  
  ' // Dispatch Windows messages
  Function = pWindow.DoEvents(nCmdShow)
  
End Function

' Window procedure

Function WndProc (Byval hwnd As HWND, Byval uMsg As UINT, Byval wParam As WPARAM, Byval lParam As LPARAM) As LRESULT
  Dim As Boolean FirstPass = True
  Static As BCRYPT_ALG_HANDLE hRSAHandle, hECDSAHandle
  Static As Long lChoice = Enc
  Static As Long dllIsLoaded
  'Static As Any Ptr AES
  Dim As BCRYPT_KEY_HANDLE hKeyHandle
  Dim As String sInFile, sOutFile, sInputFile, sHash, sSigFile, sHashFile, EncMainMessage, sDummy
  Dim As Long lError, f, MaxFileSize
  Dim As Ulong dwstatus, cbInput, dummy, cbOutPut, pcbResult, Stretch
  Dim pbOutput() As Byte
  Dim Blob() As Byte
  Dim ptrBlob As Ulong Ptr
  Dim pwszName As Wstring Ptr
  Dim As Double t
  Dim pFileSys As CFileSys
  Dim As string PassKey, AESName
    
  If FirstPass = True Then
    If BCryptOpenAlgorithmProvider( @hRSAHandle, BCRYPT_RSA_ALGORITHM, "", 0 ) <> STATUS_SUCCESS Then
      TDWrapper 0, "Error", "Unable To Open RSA algorithm provider.", TD_ERROR_ICON
      End
    End If
    If BCryptOpenAlgorithmProvider( @hECDSAHandle, Wstr( "ECDSA_P" + Str(HashLen) ), "", 0 ) <> STATUS_SUCCESS Then
      TDWrapper 0, "Error", "Unable To Open ECDSA algorithm provider.", TD_ERROR_ICON
      End
    End If
    FirstPass = False
  End If
  
  Select Case uMsg
    
  
  Case WM_COMMAND
    
    Select Case Loword(wParam)
    
    Case IDC_OPTION1
      lChoice = EncAES
        
    Case IDC_OPTION2
      lChoice = DecAES
      
    Case IDC_OPTION3
      lChoice = Enc
      
    Case IDC_OPTION4
      lChoice = Dec
      
    Case IDC_OPTION5
      lChoice = Sig
      
    Case IDC_OPTION6
      lChoice = Ver
     
    Case IDC_OPTION7
      lChoice = HashFile
      
    Case IDC_OPTION8
      lChoice = HashCompare
      
    Case ID_OK
      
      Select Case lChoice
        
      Case EncAES, DecAES 
        if lChoice = EncAES then
          pwszName = AfxIFileOpenDialog(hwnd, All_Files2)
        else
          pwszName = AfxIFileOpenDialog(hwnd, DecAES )
        end if
        If pwszName Then
          AESName = *pwszName
          CoTaskMemFree(pwszName)
          Dim rc As RECT
          GetWindowRect( hWnd, @rc )
          dim as long x, y
          x = rc.Left - 36
          y = rc.Top + 110
          PassKey = AfxInputBox( Hwnd, x, y, "AES", "Please provide password/passkey", , , True )
          if PassKey = "" Then exit function
          If ( Len( Passkey ) Mod 2 <> 0 ) orelse ( Len( Passkey ) < 64 ) orelse IsHex( Passkey ) = 0 then
            sDummy = right( Passkey, 2 )
            Stretch = valint( sDummy )
            If Stretch >= 32 Then Stretch = 24
          End If
          BlockInput True
          ' Remove Close button
          EnableMenuItem GetSystemMenu(hWnd, False), SC_CLOSE, MF_BYCOMMAND or MF_DISABLED
          t = timer
          dwStatus = AES( AES256, AESName, PassKey, Stretch )
          t = timer - t
          ' Restore Close button
          EnableMenuItem GetSystemMenu(hWnd, False), SC_CLOSE, MF_BYCOMMAND or MF_ENABLED
          BlockInput False
          if dwStatus <> 0 then
            select case dwStatus
              case -5
                TDWrapper Hwnd, AfxGetFileNameX(AESName) + " is a zero byte file.", "", TD_ERROR_ICON
              case -6
                TDWrapper Hwnd, "Password/Passkey verification failed", "", TD_ERROR_ICON
            end select
            exit function
          end if
          if lChoice = EncAES Then
            TDWrapper Hwnd, "AES256 encryption", AfxGetFileNameX(AESName) + CrLf + CrLf + "Time taken: " _
            + Format(t*1000, "0.0") + "ms", TD_INFORMATION_ICON
          Else
            TDWrapper Hwnd, "AES256 decryption", AfxGetFileNameX(AESName) + CrLf + CrLf + "Time taken: " _
            + Format(t*1000, "0.0") + "ms", TD_INFORMATION_ICON
          End If
        End If
             
      Case Enc, Dec, Sig, Ver
        ' Get task Blob
        pwszName = AfxIFileOpenDialog(hwnd, lChoice)
        If pwszName Then
          sInfile = *pwszName
          CoTaskMemFree(pwszName)
          f = Freefile
          Open sInFile For Binary As #f
          cbInput = Lof(f)
          Redim Blob(1 To cbInput) As Byte
          Get #f, , Blob()
          Close #f
        Else
          Exit Function
        End If
        
        If lChoice = Enc Then
          pwszName = AfxIFileOpenDialog(hwnd, All_Files1)
        Elseif lChoice = Dec Then
          pwszName = AfxIFileOpenDialog(hwnd, Enc_File)
        Else
          pwszName = AfxIFileOpenDialog(hwnd, Hash_File)
        End If
        If pwszName = 0 Then Exit Function
        
        sInfile = *pwszName
        CoTaskMemFree(pwszName)
        Dim pFileSys As CFileSys
        Dim sExtn As CWSTR = pFileSys.GetExtensionName(sInFile)
        If sExtn <> "enc" And sExtn <> "sig" Then
          If lChoice = Enc Then
            sOutFile = sInFile & ".enc"
          Elseif lChoice = Sig Then
            sOutFile = sInFile & ".sig"
          End If
        Else
          sOutFile = Left( sInFile, Len( sInFile ) - 4 )
        End If
        
        ' Get target file And make sure it Is a valid size For RSA encryption
        f = Freefile
        Open sInFile For Binary As #f
        sInputFile = String(Lof(f), 0)
        Get #f, , sInputFile
        If lChoice = Enc Then
          ' 2nd Dword of header Is Bit strength
          ptrBlob = Cast( Ulong Ptr, @Blob(5) )
          MaxFileSize = Peek(Ulong, ptrBlob)\8 -2 - 2*HashLen\8
          If Lof(f) > MaxFileSize Then 
            TDWrapper Hwnd, "RSA message size", "Target file must be less than Or equal To " _
            + Str( MaxFileSize ) + " bytes.", TD_ERROR_ICON
            Close #f
            Exit Function
          End If
        End If
        Close #f
        
        Dim paddinginfo As BCRYPT_OAEP_PADDING_INFO
        Dim algo As Wstring * 8
        Dim buffer As String
        algo = "SHA" + Str(HashLen)
        buffer = String(HashLen, 0)
        paddinginfo.pszAlgid = Varptr(algo)
        paddinginfo.pblabel = Strptr(buffer)
        paddinginfo.cblabel = HashLen
        
        If lChoice = Enc Then
          t = Timer
          dwStatus = BCryptImportKeyPair( hRSAHandle, Null, BCRYPT_RSAPublic_BLOB, @hKeyHandle, _
          @Blob(1), cbInput, 0 )
          If dwStatus <> STATUS_SUCCESS Then lError = 10 : Goto ErrorTrap
          ' Get Output length in pcbResult
          dwStatus = Bcryptencrypt( hKeyhandle, Strptr(sInputFile), Len(sInputFile), @paddinginfo, _
          Null, dummy, null, 0, @pcbResult, BCRYPT_PAD_OAEP)
          If dwStatus <> STATUS_SUCCESS Then lError = 20 : Goto ErrorTrap
          Redim pbOutput( 1 To pcbResult) As Byte
          cbOutput = pcbResult
          dwStatus = Bcryptencrypt( hKeyhandle, Strptr(sInputFile), Len(sInputFile), @paddinginfo, _
          Null, dummy, @pbOutput(1), cbOutput, @pcbResult, BCRYPT_PAD_OAEP)
          If dwStatus <> STATUS_SUCCESS Then lError = 30 : Goto ErrorTrap
          t = Timer - t
        Elseif lChoice = Dec Then
          t = Timer
          dwStatus = BCryptImportKeyPair( hRSAHandle, Null, BCRYPT_RSAPrivate_BLOB, @hKeyHandle, _
          @Blob(1), cbInput, 0 )
          If dwStatus <> STATUS_SUCCESS Then lError = 40 : Goto ErrorTrap
          ' Get Output length in pcbResult
          dwStatus = Bcryptdecrypt( hKeyhandle, Strptr(sInputFile), Len(sInputFile), @paddinginfo, _
          Null, dummy, null, 0, @pcbResult, BCRYPT_PAD_OAEP)
          If dwStatus <> STATUS_SUCCESS Then lError = 50 : Goto ErrorTrap
          Redim pbOutput( 1 To pcbResult) As Byte
          cbOutput = pcbResult
          dwStatus = Bcryptdecrypt( hKeyhandle, Strptr(sInputFile), Len(sInputFile), @paddinginfo, _
          Null, dummy, @pbOutput(1), cbOutPut, @pcbResult, BCRYPT_PAD_OAEP)
          If dwStatus <> STATUS_SUCCESS Then lError = 60 : Goto ErrorTrap
          t = Timer - t
        Elseif lChoice = Sig Then
          t = Timer
          dwStatus = BCryptImportKeyPair( hECDSAHandle, Null, BCRYPT_ECCPRIVATE_BLOB, @hKeyHandle, _
          @Blob(1), cbInput, 0 )
          If dwStatus <> STATUS_SUCCESS Then lError = 70 : Goto ErrorTrap
          ' Get Output length in pcbResult
          dwStatus = BCryptSignHash( hKeyhandle, null, Strptr(sInputFile), Len(sInputFile), null, _
          0, @pcbResult, 0)
          If dwStatus <> STATUS_SUCCESS Then lError = 80 : Goto ErrorTrap
          Redim pbOutput( 1 To pcbResult) As Byte
          cbOutput = pcbResult
          dwStatus = BcryptSignhash( hKeyhandle, null, Strptr(sInputFile), Len(sInputFile), _
          @pbOutput(1), cbOutput, @pcbResult, 0)
          If dwStatus <> STATUS_SUCCESS Then lError = 90 : Goto ErrorTrap
          t = Timer - t
        Else
          pwszName = AfxIFileOpenDialog(hwnd, Sig_File)
          If pwszName Then
            sInfile = *pwszName
            CoTaskMemFree(pwszName)
            t = Timer
            f = Freefile
            Open sInFile For Binary As #f
            sSigFile = String(Lof(f), 0)
            Get #f, , sSigFile
            Close #f
            dwStatus = BCryptImportKeyPair( hECDSAHandle, Null, BCRYPT_ECCPUBLIC_BLOB, @hKeyHandle, @Blob(1), cbInput, 0 )
            If dwStatus <> STATUS_SUCCESS Then lError = 100 : Goto ErrorTrap
            ' Get Output length in pcbResult
            dwStatus = BCryptVerifySignature( hKeyhandle, 0, Strptr(sInputFile), Len(sInputFile),_
            Strptr(sSigFile), Len(sSigFile), 0)
            t = Timer - t
            If dwStatus = STATUS_SUCCESS Then
              TDWrapper Hwnd, "Signature verification", AfxGetFileNameX(sInFile) + " has been verified." + _
              CrLf + CrLf + "Time taken: " + Format(t*1000, "0.0") + "ms", TD_INFORMATION_ICON
            Elseif dwStatus = STATUS_INVALID_SIGNATURE Then
              TDWrapper Hwnd, "Signature verification", AfxGetFileNameX(sInFile) + " was Not verified." + _
              CrLf + CrLf + "Time taken: " + Format(t*1000, "0.0") + "ms", TD_WARNING_ICON
            Else
              lError = 110 : Goto ErrorTrap
            End If
          Else
            Exit Function
          End If
        End If
        
        If lChoice <> Ver Then
          If pFileSys.Fileexists(sOutFile) Then Kill sOutfile
          f = Freefile
          Open sOutFile For Binary As #f
          Put #f, , pbOutput()
          Close #f
          TDWrapper Hwnd, "File creation", AfxGetFileNameX(sOutFile) + " has been created." + _
          CrLf + CrLf + "Time taken: " + Format(t*1000, "0.0") + "ms", TD_INFORMATION_ICON
        End If
        
        Goto TidyUp
        
        ErrorTrap:
        
        TDWrapper Hwnd, "Error", "Error at Position " + Str(lError) + " " + Hex(dwStatus), TD_ERROR_ICON
        
        TidyUp:
        If hKeyHandle <> 0 Then BCryptDestroyKey hKeyHandle
        
      Case HashFile
        ' Get encrypted message
        pwszName = AfxIFileOpenDialog(hwnd, All_Files2)
        If pwszName Then
          sInfile = *pwszName
          CoTaskMemFree(pwszName)
          t = Timer
          sHash = GetFileHashEx( sInFile, False )
          t = Timer - t
          f = Freefile
          sHashFile = AfxGetFilenameX( sInFile ) + "_Hash.dat"
          Open  sHashFile For Binary As #f
          Put #f, , sHash
          Close #f
          TDWrapper Hwnd, "Hash file", sHashFile + " has been created." + _
          CrLf + CrLf + "Time taken: " + Format(t*1000, "0.0") + "ms", TD_INFORMATION_ICON
        Else
          Exit Function
        End If
        
      Case HashCompare
        ' Get encrypted message
        pwszName = AfxIFileOpenDialog(hwnd, All_Files2)
        If pwszName Then
          sInfile = *pwszName
          EncMainMessage = AfxGetFileNameX(sInFile)
          CoTaskMemFree(pwszName)
          sHash = GetFileHashEx( sInFile, False )
          pwszName = AfxIFileOpenDialog(hwnd, Hash_File)
          If pwszName Then
            sInfile = *pwszName
            CoTaskMemFree(pwszName)
            f = Freefile
            Open sInfile For Binary As #f
            Dim As String sGetHash = String(Lof(f),0)
            Get #f, , sGetHash
            Close #f
            If sHash = sGetHash Then
              TDWrapper Hwnd, "Hash comparison", "Hash of " + EncMainMessage + " And given hash match.",_
              TD_INFORMATION_ICON
            Else
              TDWrapper Hwnd, "Hash comparison", "Hash of " + EncMainMessage + " And given hash Do Not match.",_
              TD_WARNING_ICON
            End If
          Else
            Exit Function
          End If
        Else
          Exit Function
        End If
        
      End Select
      
    Case ID_DR
      Dim nClickedButton As Long
      TaskDialog( hWnd, GetModuleHandle(NULL), "AES-RSA-ECDSA", "Written in FreeBASIC With José Roca's WinFBX", "by deltarho[1859]", 0, "#102", @nClickedButton)
    
    End Select 
    
  Case WM_DESTROY
    ' Clean up
    If hRSAHandle <> 0 Then BCryptCloseAlgorithmProvider(hRSAHandle, 0)
    If hECDSAHandle <> 0 Then BCryptCloseAlgorithmProvider(hECDSAHandle, 0)
    PostQuitMessage(0)
    Exit Function
    
  Case WM_SYSCOMMAND
    If Loword(wParam) = SC_CLOSE Then ' Close message
      Dim As Long x, y, f
      Dim rc As RECT
      GetWindowRect( hWnd, @rc )
      ' If Window has moved Then save current position
      If xWindow <> rc.Left Orelse yWindow <> rc.top Then
        If pFileSys.Fileexists("Position.dat") Then Kill "Position.dat"
        f = Freefile
        Open "Position.dat" For Binary As #f
        Put #f, , rc.Left
        Put #f, , rc.top
        Close #f
      End If
    End If
    
  End Select
  
  ' // Default processing of Windows messages
  Function = DefWindowProcW(hWnd, uMsg, wParam, lParam)
  
End Function

' Display the File Open Dialog
' The returned Pointer must be freed With CoTaskMemFree
' Adapted by deltarho[1859]

Function AfxIFileOpenDialog ( hwndOwner As HWND, DisplayType As Long, sigdnName As SIGDN = SIGDN_FILESYSPATH) As Wstring Ptr
  
  ' // Create an instance of the FileOpenDialog interface
  Dim hr As Long
  Dim pofd As IFileOpenDialog Ptr
  hr = CoCreateInstance(@CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, @IID_IFileOpenDialog, @pofd)
  If pofd = NULL Then Return NULL
  Dim rgFileTypes As COMDLG_FILTERSPEC
  
  Select Case DisplayType
    
  case DecAES
    rgFileTypes.pszName = @Wstr("Decryption AES")
    rgFileTypes.pszSpec = @Wstr("*.aes")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select encrypted file")
  
  Case Enc
    rgFileTypes.pszName = @Wstr("Encryption")
    rgFileTypes.pszSpec = @Wstr("*RSAPublicKey*.dat")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select encrypted task file")
    
  Case Dec
    rgFileTypes.pszName = @Wstr("Decryption RSA")
    rgFileTypes.pszSpec = @Wstr("*RSAPrivateKey*.dat")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select RSA decryption task file")
    
  Case Sig
    rgFileTypes.pszName = @Wstr("Signing")
    rgFileTypes.pszSpec = @Wstr("*ECDSAPrivateKey*.dat")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select signing task file")
    
  Case Ver
    rgFileTypes.pszName = @Wstr("Verifying")
    rgFileTypes.pszSpec = @Wstr("*ECDSAPublicKey*.dat")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select verifying task file")    
    
  Case Enc_File
    rgFileTypes.pszName = @Wstr("Encrypted file")
    rgFileTypes.pszSpec = @Wstr("*.enc")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select an encrypted key/password file")
    
  Case Sig_File
    rgFileTypes.pszName = @Wstr("Signed file")
    rgFileTypes.pszSpec = @Wstr("*.sig")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select a signed hash file")
    
  Case Hash_File
    rgFileTypes.pszName = @Wstr("Hash files")
    rgFileTypes.pszSpec = @Wstr("*Hash.dat")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select a hash file")
    
  Case All_Files1
    rgFileTypes.pszName = @Wstr("All files")
    rgFileTypes.pszSpec = @Wstr("*.*")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select a key/password")
    
  Case All_Files2
    rgFileTypes.pszName = @Wstr("All files")
    rgFileTypes.pszSpec = @Wstr("*.*")
    pofd->lpVtbl->SetFileTypes(pofd, 1, @rgFileTypes)
    hr = pofd->lpVtbl->SetTitle(pofd, "Select a message")  
    
  End Select
  
   ' // Display the dialog
  hr = pofd->lpVtbl->Show(pofd, hwndOwner)
  
  ' // Set the default folder
  Dim pFolder As IShellItem Ptr
  SHCreateItemFromParsingName (Curdir, NULL, @IID_IShellItem, @pFolder)
  If pFolder Then
    pofd->lpVtbl->SetDefaultFolder(pofd, pFolder)
    pFolder->lpVtbl->Release(pFolder)
  End If
  
  ' // Get the result
  Dim pItem As IShellItem Ptr
  Dim pwszName As Wstring Ptr
  If SUCCEEDED(hr) Then
    hr = pofd->lpVtbl->GetResult(pofd, @pItem)
    If SUCCEEDED(hr) Then
      hr = pItem->lpVtbl->GetDisplayName(pItem, sigdnName, @pwszName)
      Function = pwszName
    End If
  End If
  
  ' // Cleanup
  If pItem Then pItem->lpVtbl->Release(pItem)
  If pofd Then pofd->lpVtbl->Release(pofd)
  
End Function

' Check If a String Is wholly hexadecimal Or Not, returns True If so

Function IsHex(s As String) As Long
  Dim i As Long
  
  For i = 0 To Len(s) - 1
    Select Case s[i]
    Case 48 To 57, 65 To 70, 97 To 102
    Case Else
      Return False
    End Select
  Next i
  Return true
  
End Function

' General Hash Function. See https://freebasic.net/forum/viewtopic.php?f=7&t=25320 For full details

Function Hash( hashalg As lpcwstr, stext As String, index As Long, mode As Long, final As Long, _
  pbsecret As String = Chr(0)) As String
  
  Static phalg(1 To 8) As Byte Ptr
  Static phhash(1 To 8) As Byte Ptr
  Dim sbinary As String
  Dim shex As String
  Dim nlength As Long
  
  ' Initialize section
  
  If phhash(index) = 0 Then ' will be the Case On, And perhaps only, first pass
    If pbsecret = Chr(0) Then ' Not hmac
      BcryptOpenAlgorithmprovider @phalg(index), hashalg, 0, 0 ' we want phalg(index)
      BcryptCreatehash phalg(index), @phhash(index), null, 0, 0, 0, 0 ' we want phhash(index)
    Else ' Is hmac
      ' We want phalg(index)
      BcryptOpenAlgorithmprovider @phalg(index), hashalg, 0, bcrypt_alg_handle_hmac_flag' we want phalg(index)
      If Right( pbsecret, 1 ) <> Chr(0) Then ' ascii Not forced
        ' are we going To use pbsecret As ascii Or Binary?
        If ( Len( pbsecret ) Mod 2 = False ) And IsHex( pbsecret ) = true Then
          nlength = Len(pbsecret)\2
          sbinary = Space( nlength )
          CryptStringToBinarya Strptr(pbsecret), Len(pbsecret), crypt_string_hexraw, Strptr(sbinary), @nlength, 0, 0
          pbsecret = sbinary
        End If
      Else
        pbsecret = Left( pbsecret, Len(pbsecret) - 1)
      End If
      ' We want phhash(index)
      BcryptCreatehash phalg(index), @phhash(index), null, 0, Strptr( pbsecret ), Len( pbsecret ), 0
    End If
  End If
  
  ' update section
  
  BcryptHashData( phhash(index), Strptr( stext), Len( stext ), 0 )
  
  ' finalization section
  
  If final = true Then ' finalize hash
    Dim As Ulong lhashlength
    Dim As Ulong lresult
    Dim sbinhash As String
    
    BcryptGetProperty phalg(index), bcrypt_hash_length, Cast( Puchar, @lhashlength ), 4, Varptr(lresult), 0
    sbinhash = String( lhashlength, 0 )
    BcryptFinishHash phhash(index), Strptr( sbinhash ), lhashlength, 0
    BcryptDestroyHash phhash(index) 
    BcryptCloseAlgorithmprovider phalg(index), 0
    phhash(index) = 0 ' ensures a New hash Is created On another pass of hash() With This index
    If mode = 0 Then
      Return sbinhash
    Else
      nlength = Len(sbinhash)*2 + 1 ' + 1 To accomodate a null terminator
      shex = Space( nlength )
      CryptBinaryToStringa Strptr(sbinhash), Len(sbinhash), crypt_string_hexraw + crypt_string_nocrlf, _
      Strptr(shex), @nlength ' at msdn nlength 'Out' Is Len(sbinhash) * 2, so
      Return Ucase( Left( shex, nlength ) )
    End If
  End If
  
End Function

' Hash a file - specific For AES-RSA-ECDSA

Function GetFileHashEx( sFile As String, Flag As Long ) As String
  ' If Flag Is 0 Then Binary Is returned Else hexadecimal
  Dim As String * 262144 sBuffer
  Dim As String sHash
  Dim As Ulong f
  Dim As Uinteger BytesRead
  
  ' Prepend message With blocksize of zeros - security measure
  ' SHA512 Is used And reduced according To HashLen
  Hash Wstr("SHA512"), String(128,0), 1, Flag, False
  f = Freefile
  Open sFile For Binary As #f
  While Not Eof(f)
    Get #f, , sBuffer, , BytesRead
    Hash Wstr("SHA512"), Left( sBuffer, BytesRead ), 1, Flag, False
  Wend
  Close #f
  ' The hash has Not been finalized so employ hash() again With an empty String.
  sHash = Hash( "", "", 1, Flag, true )
  Return Left(sHash, Len(sHash)*HashLen\512)   
End Function

' Digest of TaskDialog

Sub TDWrapper( hWndParent As HWND, pszMainInstruction As PCWSTR, pszContent As PCWSTR, pszIcon As PCWSTR )
  Dim nClickedButton As Long
  TaskDialog( hWndParent, NULL, "AES-RSA-ECDSA", pszMainInstruction, pszContent, TDCBF_OK_BUTTON, pszIcon, @nClickedButton)
End Sub

Function AES( AESstrength As Long, sInFile As String, sPassword As String, lStretch As Long ) As Long
' If sInFile has an aes extension Then decrypt Else encrypt

Dim As BCRYPT_ALG_HANDLE Ptr hRand, hAESAlg
Dim As BCRYPT_KEY_HANDLE Ptr hKey
Dim OutText() As Byte
Dim As Long i, Final, lFileLen, FinalSection, SectionCount, IsInputEncrypted, LenInText, LenOutText, nLength
Dim As Ulong ntStatus, lError, dwResult, phHashAES, hIn, hOut
Dim sIV As String * 16
Dim sSalt As String * 32
Dim As String sBinary, sOutFile, sDummy, sKey
Dim As String*32 sKeyVerify, sSavedKeyVerify
Dim As String * 262144 pbdata
Dim As Uinteger nbloaded
 
  AESstrength /= 8
  lStretch += 2
 
  ' File Input/Output section

  If pFileSys.FileExists( sInfile ) = False Then ' Unable To find file
    Function = -1
    Goto TidyUp
  End If
  
  hIn = Freefile
  Open sInfile For Binary As #hIn
    lFilelen = Lof( hIn )
     If lFileLen = 0 Then ' Zer0 Byte file
    Close #hIn
    Function = -5
    Goto TidyUp
  End If
 
  If Lcase( Right( sInFile, 4 ) ) <> ".aes" Then
    sOutFile = sInFile & ".aes"
    ' eg f:\folder\myapp.Exe ==> f:\folder\myapp.Exe.aes
    IsInputEncrypted = False
  Else
    sOutFile = Left$( sInFile, Len( sInFile ) - 4 )
    ' eg f:\folder\myapp.Exe.aes ==> f:\folder\myapp.Exe
    IsInputEncrypted = True
  End If
  
  ' Read Or generate header items
  
  If IsInputEncrypted = True Then
    Get #hIn,, sIV
    Get #hIn,, sSalt
    Get #hIn,, sSavedKeyVerify
  Else
    ntStatus = BCryptOpenAlgorithmProvider(@hRand, BCRYPT_RNG_ALGORITHM, "", 0) ' Prepare For Random number generation
    If ntStatus <> STATUS_SUCCESS Then lError = 120 : Goto ErrorTrap
    ntStatus = BCryptGenRandom(hRand, Strptr(sIV), BlockSize, 0)
    If ntStatus <> STATUS_SUCCESS Then lError = 130 : Goto ErrorTrap
    ntStatus = BCryptGenRandom(hRand, Strptr(sSalt), 32, 0)
    If ntStatus <> STATUS_SUCCESS Then lError = 140 : Goto ErrorTrap
    ntStatus = BCryptCloseAlgorithmProvider(hRand, 0)
    If ntStatus <> STATUS_SUCCESS Then lError = 150 : Goto ErrorTrap
  End If
 
  ' Are we going To use sPassword As ascii Or Binary?
 
  If ( sPassword <> "" ) And ( Len( sPassword ) Mod 2 = 0 ) And ( Len( sPassword ) >= 2 * AESStrength ) And IsHex( sPassword ) <> 0 Then
    ' Treat As a Binary String
    nLength = Len(sPassword)\2
    sBinary = Space$( nlength )
    CryptStringToBinarya Strptr(sPassword), Len(sPassword), crypt_string_hexraw, Strptr(sBinary), @nLength, 0, 0
    sPassword = sBinary
  End If
 
  ' Use SHA256 And stretch sPassword
 
  ' Algorithm employed x0 = 0 : xi = Hash( xi-1 + sPassword + sSalt ) For i = 1 To 2^lStretch
  ' From Cryptography Engineering: ISBN 978-0-470-47424-2
 
  ' Calculate 2^lStretch - 1 hashes 
  
  For i = 1 To 2^lStretch - 1
    sDummy = sKey + sPassword + sSalt
    sKey = Hash( Wstr("SHA256"), sDummy, 1, False, False )
  Next
 
  ' Calculate password verification Data
  
  sDummy = String$(64,0) + sKey + sPassword + sSalt
  sKeyVerify = Hash( Wstr("SHA256"), sDummy, 1, False, True )
  
  If IsTrue( IsInputEncrypted ) Then
    If sSavedKeyVerify <> sKeyVerify Then ' Password failed
      Function = -6
      Goto TidyUp
    End If
  End If
 
  ' Now calculate final iterate
  
  sDummy = sKey + sPassword + sSalt
  sKey = Hash( Wstr("SHA256"), sDummy, 1, False, True )
 
  ' Set up Output file
  
  If IsTrue( pFileSys.Fileexists(sOutFile) ) Then
    Kill sOutFile
  End If
  hOut = Freefile
  Open sOutFile For Binary As hOut
  If Err Then
    Function = Err
    Goto TidyUp
  End If
 
  If IsFalse( IsInputEncrypted ) Then ' Save Header items
    Put #hOut, , sIV
    Put #hOut, , sSalt
    Put #hOut, , sKeyVerify
  End If
 
  ' AES section
 
  ntStatus = BCryptOpenAlgorithmProvider( @hAESAlg, BCRYPT_AES_ALGORITHM, "", 0 ) ' We want hAESAlg
  If ntStatus <> STATUS_SUCCESS Then lError = 160 : Goto ErrorTrap
  ntStatus = BCryptGenerateSymmetricKey( hAESAlg, @hKey, 0, 0, Strptr(sKey), AESstrength, 0 ) ' We want hKey
  If ntStatus <> STATUS_SUCCESS Then lError = 170 : Goto ErrorTrap
 
  ' Encryption/Decryption section
 
  Redim OutText( 1 To BufferSize ) As Byte
  LenOutText = BufferSize
  LenInText = BufferSize
  
  If IsTrue( IsInputEncrypted ) Then
    lFileLen -= 80 ' To account For sIV, sSalt And sSavedKeyVerify
  End If
  FinalSection = lFileLen\BufferSize
  If lFileLen > FinalSection*BufferSize Then FinalSection += 1
 
  ' Encryption/Decryption Loop
  
  Do
    SectionCount += 1
    Get #hIn,, pbdata, , nbLoaded
    If SectionCount = FinalSection Then
      Final = BCRYPT_BLOCK_PADDING
    End If
 
    If IsFalse( IsInputEncrypted ) Then ' Encrypt
      If Final = BCRYPT_BLOCK_PADDING Then
        ntStatus = BCryptEnCrypt( hKey, Strptr(pbData), nbLoaded, 0, Cast( Byte Ptr, @sIV ), BlockSize, 0, 0, @LenOutText, Final )  'We want LenOutText
        If ntStatus <> 0 Then lError = 180 : Goto ErrorTrap
        
        ' If we are On blocksize boundary we will need BufferSize + BlockSize otherwise GPF
        If IsFalse( LenOutText Mod BlockSize ) Then Redim OutText( 1 To BufferSize + BlockSize) As Byte
        LenInText = nbLoaded
      End If
 
      ' LenOutText = BufferSize unless revised above
      ntStatus = BCryptEnCrypt( hKey, Strptr(pbData), LenInText, 0, Cast( Byte Ptr, @sIV ), BlockSize, @OutText(1), LenOutText, @dwResult, Final )
      If ntStatus <> 0 Then lError = 190 : Goto ErrorTrap
 
    Else ' Decrypt
      If Final = BCRYPT_BLOCK_PADDING Then
        ntStatus = BCryptDecrypt( hKey, Strptr(pbData), nbLoaded, 0, Cast( Byte Ptr, @sIV ), BlockSize, 0, 0, @LenOutText, Final )  ' We want LenOutText
        If ntStatus <> 0 Then lError = 200 : Goto ErrorTrap
        If IsFalse( LenOutText Mod BlockSize ) Then Redim OutText( 1 To BufferSize + BlockSize) As Byte
        LenInText = nbLoaded
      End If
 
      ntStatus = BCryptDecrypt( hKey, Strptr(pbData), LenInText, 0, Cast( Byte Ptr, @sIV ), BlockSize, @OutText(1), LenOutText, @dwResult, Final )
      If ntStatus <> 0 Then lError = 210 : Goto ErrorTrap
    End If
 
    ' If Output file Is an exact multiple of bufferSize
    ' Then dwResult will be zero And we have nothing To dump
 
    If dwResult > 0 Then
      If IsTrue( Final ) Then
        Redim Preserve OutText(1 To dwResult) As Byte
      End If
      Put #hOut, , OutText()
    End If
 
  Loop Until Final
  
Goto TidyUp

ErrorTrap:

MessageBox ( 0, "Error at Position " + Str(lError) + " " + Hex(ntStatus), "EncDec", MB_OK )
  
TidyUp:
  If hAESAlg <> 0 Then BCryptCloseAlgorithmProvider( hAESAlg, 0 )
  If hKey <> 0 Then BCryptDestroyKey( hKey )
  Close hIn
  Close hOut
  
End Function
You are more than welcome to edit the last two bas files for your own needs but if you do then the following rc file will need to be edited to remove 'yours truly' before handy the exe to anyone else. If you compile the above as is then stay with the rc file as is. I will never publish cryptographic code for financial gain but I will also not publish the same anonymously. <smile>

RSA-ECDSA.rc

Code: Select all

#define IDR_VERSION 1

100 icon padlock.ico
101 bitmap DRmed.bmp
102 icon FB_Icon.ico

IDR_VERSION VERSIONINFO
FILEVERSION 1,0,0,0000
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
BEGIN
  BLOCK "StringFileInfo"
  BEGIN
    BLOCK "080904E4"
    BEGIN
      VALUE "FileDescription", "AES, RSA and ECDSA\0"
      VALUE "ProductName", "AES-RSA-ECDSA\0"
      VALUE "LegalCopyright", " \251 2018 David Roberts\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x0809, 0x04E4
  END
END

1 24 "Theme.xml"
Theme.xml

Code: Select all

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">

    <assemblyIdentity
        version="1.0.0.1"
        processorArchitecture="*"
        name="MyAppName.exe"
        type="win32"
    />
    <description>Optional MyDescription for MyAppName.exe</description>
    
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
        <dpiAware>true</dpiAware>
        </asmv3:windowsSettings>
    </asmv3:application>

    <!-- Compatibility section for Program Compatibility Assistant (PCA) -->
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
            <!-- Windows Vista -->
            <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
            <!-- Windows 7 -->
            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
            <!-- Windows 8 -->
            <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
            <!-- Windows 8.1 -->
            <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
            <!-- Windows 10 -->
            <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
        </application>
    </compatibility>

    <!-- Trustinfo section for User Account Control (UAC) -->
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges>
                <!-- level   = "asInvoker"            -->
                <!-- level   = "highestAvailable"     -->
                <!-- level   = "requireAdministrator" -->
                <requestedExecutionLevel
                    level    = "asInvoker"
                    uiAccess = "false"
                />
            </requestedPrivileges>
        </security>
    </trustInfo>

    <!-- Dependency section -->
    <dependency>
        <dependentAssembly>
            <assemblyIdentity
                type="win32"
                name="Microsoft.Windows.Common-Controls"
                version="6.0.0.0"
                processorArchitecture="*"
                publicKeyToken="6595b64144ccf1df"
                language="*"
            />
        </dependentAssembly>
    </dependency>

</assembly>
A quick test run

After you have created your keys create a key/password file. Use your name for a password, for example, and call the file Key.dat

Run RSA-ECDSA and chose 'Encrypt a key'. You should get Key.dat.enc created. Now delete Key.dat and then chose 'Decrypt a .enc file'. You should get your Key.dat back in all its glory.

Now chose 'Hash a file'. Hash RSA-ECDSA.exe, for example. You should get RSA-ECDSA.exe_Hash.dat created. Normally the file to hash would be your AES, or whatever, encrypted message.

Now chose 'Sign a hash'. You should get 'RSA-ECDSA.exe_Hash.dat.sig created.

Now chose 'verify a .sig file'. Work your way through that and you should get a successful verification.

Now chose 'Verify hashes' and you get a hash match.

Be honest, that was fun wasn't it? <laugh>
Last edited by deltarho[1859] on Mar 05, 2018 13:01, edited 2 times in total.
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: RSA ECDSA

Post by srvaldez »

thank you deltarho[1859]
that's a very good explanation of cryptography, I was able to compile and run your code with no problems, though I am a bit confused on the password generation, need to re-read your explanation :-)
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: RSA ECDSA

Post by deltarho[1859] »

@srvaldez

Thanks for the feedback.

Usually, with symmetric encryption/decryption applications, we type in a password into an edit box. RSA-ECDSA is entirely file based so it expects a password to be in a file. There is, of course, a security risk in having a password in a file if that file is on an internal drive. A simple solution to that is to have a password file on removable media such as a usb stick. However, people tend to slide into convenient ways of working and don't stay with the discipline needed for key/password management. My way of working is to use passwords of random characters, which I could never remember, and put them into a password manager. I have written some encryption/decryption applications which accept passwords as hexadecimal strings. These are converted into binary strings and then used as a password/key. So, I would have, for example, a 64 character hex string in my password manager. The application would convert that into a 256 bit binary string. A 256 bit binary password/key cannot be brute forced even by a 'super computer' and, of course, dictionary attacks, for example, are a waste of time.

As the protocol above stands both the sender and user will need to use the same symmetric encryption/decryption application. The next version of RSA-ECDSA will have that built in. I have some AES PowerBASIC code which I can port in. That could use a key in a file but I will be looking at other ways of getting our passwords/keys into RSA-ECDSA.

RSA-ECDSA is not of 'commercial quality' but was written primarily as an exercise in using José Roca's WinFBX which had been on my to do list for too long. If the cryptography aspect was removed from RSA-ECDSA then we would have a template for writing other GUI applications; which is what I will do.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: RSA ECDSA

Post by deltarho[1859] »

One way of using RSA-ECDSA more securely is to create a password like this: '32a?v@6o0?@$Grm+H'2*Daw/*vAE-xb' and put that into a password manager.

That would be our symmetric key for AES, or whatever.

That password has an entropy of over 205 bits so is, at the present time, uncrackable.

If we saved that password in, say, DRPassword.dat and used RSA-ECDSA's 'Encrypt a key' option with Alice's RSA public key on that file then we would get DRPassword.dat.enc. If we now deleted DRPassword.dat we no longer have our password in plaintext form on our PC. Once Alice has a copy of DRPassword.dat.enc we can delete DRPassword.dat.enc as well. We can always recreate it, if needs be, since our password, which does not exist in plaintext on our PC, is in our password manager. Since 205 bits should be good for a few years, at least, we would only need to create a DRPassword.dat.enc every few years. Alice would decrypt DRPassword.dat.enc with her RSA private key and get DRPassword.dat to use to decrypt our encrypted message; the message we used AES on. Ideally Alice should then delete DRPassword.dat so that our symmetric key password is not on her PC in plaintext form either.

So, provided Alice is diligent she will not have a plaintext copy of our symmetric key in the 'open' for very long, we will have it in our password manager and it never enters the Internet in plaintext form.

I use KeePass Password Safe and have over 50 website passwords, all different, all random and as long as the respective websites will allow me.
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: RSA ECDSA

Post by srvaldez »

thank you deltarho[1859] for the link :-)
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: RSA ECDSA

Post by deltarho[1859] »

I forgot to mention that the random password above was generated by KeePass. Had I asked for a 64 random character hex string I would get something like this 'D4CF2A99544ADFDF70189AE87E53B14CAFFCC072E445A6331CB85F4B647A0060'. My AES applications would convert that into a 256 bit binary key.

So, what do I use to open my password manager? I have a separate application which requires three inputs and it generates a 64 character password which uses both low and high ansi characters. It has an entropy of 495.45 bits. The three inputs? In my head and one of them is a pin number which hashes that many times, times 1000. It takes 348ms to generate those 64 characters. Eat your heart out security services - you won't scratch the paint on that - ever. <smile> Paranoid? Guilty!
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: RSA ECDSA

Post by deltarho[1859] »

AES-RSA-ECDSA replaces RSA-ECDSA in the second post and the rc file has been changed.

Image

AES256 has been added: See AES file encryption for details. Ignore the timing - on this occasion 100MB.txt was coming out of the filecache. After choosing a message to encrypt/decrypt we are presented with an input box requesting a password/passkey.

The password may be 'stretched' according to the above link and this is achieved by appending the password with a stretch value eg MyPassword16. Two numeric characters are required otherwise the stretch value will be taken as zero. So, for less than 10 we would use, say, 08. Whatever we choose the stretch will limited to 24 - there is no escape once the encryption/decryption starts. On my machine a value of 24 will stretch for about 20 seconds. The recommended stretch is around 250 milliseconds so you will need to experiment bearing in mind that our recipient's machine maybe slower or faster than ours. I do not use passwords. I use binary keys with at least 64 random hex characters kept in a password manager.

This time I am attaching a zip of AES-RSA-ECDSA.exe (32 bit). The exe is surprisingly small coming in at only 166KB.

AES-RSA-ECDSA
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: RSA ECDSA

Post by deltarho[1859] »

Bug fix in AES-RSA-ECDSA.bas . It has nothing to do with the encryption/decryption but how errors are handled. coderJeff's error handling is better than mine. With his code this bug would not have crept in. I will be using an adaptation of his error handling in future. <smile>

The zipped download has been updated.
Post Reply