Page 1 of 1

Encyrpting data (like a password)

Posted: Thu Jun 30, 2011 6:46 pm
by purebuilt
Hello all,

I am storing passwords in a database and I would like to encrypt them with just a key of some kind. I have been through the methods and I don't really get them. I need ascii output. I need to be able to encyrpt the password then decypt it later. Both functions.

The passwords are sometimes longer than 8 characters too. Any suggestions? I am looking for the simplest solution. I just need something basic to obsure them in case the database winds up in the wrong hands.

Thanks.

DB

Re: Encyrpting data (like a password)

Posted: Thu Jun 30, 2011 8:11 pm
by Foz
The correct way of doing it is to generate a hash code (i.e. SHA1Fingerprint()) from the password + "salt" (salt being your own random set of characters), and then store that. This way, there is no chance of decrypting the password - the hash is irreversable.

Then on the client you take the password, generate the hash + "salt" and pass that up to the server to compare with what is on the database!

If you require ascii transmission, then also use the Base64 functions to do just that.

Note that you will never be able to recover a password, only replace it with a new one.

Re: Encyrpting data (like a password)

Posted: Thu Jun 30, 2011 8:16 pm
by purebuilt
Actually I am storing the passwords so that the system logs you on and this is not comparing two hashes. I need to be able to reverse the hash so that the system can send the password to the system it is calling. In other words, I have a tool that allows you to pick a system and it logs you one providing user name and password. So the passwords in the database need to be encrypted and then decrypted for use.

Is there an easy way to do that?

Thanks.

DB

Re: Encyrpting data (like a password)

Posted: Thu Jun 30, 2011 8:24 pm
by Foz
Sure, it'll never be completely safe though.

The simplest method is to XOR the string with a key. See this tip here:
http://www.purebasic.fr/english/viewtop ... 12&t=46613

A more secure, though more complex encryption is blowfish:
http://www.purebasic.fr/english/viewtop ... 03#p346403

Re: Encyrpting data (like a password)

Posted: Thu Jun 30, 2011 11:24 pm
by ultralazor
keygen off typed pass for some block cipher like aes or twofish, if result equals typed then it's correct..public key connection to prevent MITM via self-signed ssl or cgi rsa..

RSA has export laws though even though most governments have the computing power to brute it in days..


keygen off typed pass is 'proper' though

Re: Encyrpting data (like a password)

Posted: Thu Jun 30, 2011 11:33 pm
by kenmo
PB has AES functions built in... these should be fine for this. The only catch is their output (and decryption input) is binary data in memory buffers, not normal strings. But like Foz said, you can use the Base64 functions also built in, to convert between binary buffers and decently compact strings. In fact, I would just write "wrapper" procedures that take string inputs and handle the decode / encode / buffer / encrypt / decrypt together.

Re: Encyrpting data (like a password)

Posted: Fri Jul 01, 2011 4:42 pm
by purebuilt
I will check it out. It does not have to be all that secure, just obscured. The database is protected by security also.

I was looking for something like,

password = ecyrypt(pw,key)
password = decrypt(pw,key)

Thanks.

DB

Re: Encyrpting data (like a password)

Posted: Fri Jul 01, 2011 5:19 pm
by netmaestro
purebuilt, give this a try (password max length: 32 characters):

Code: Select all

Procedure$ EncryptPassword(password$, key$)
  Protected passin$        = LSet(password$, 32, Chr(32))                   ; Pad the password with spaces to make 32 characters   
  Protected keyin$         = LSet(key$, 16, Chr(32))                        ; key for 128bit encryption needs length=16
  Protected *encodedbinary = AllocateMemory(32)                             ; 32 bytes for encrypted binary result, see 2 lines down 
  Protected *encodedtext   = AllocateMemory(64)                             ; Destination for Base64Encoder must be 33% larger- we double it
  AESEncoder(@passin$, *encodedbinary, 32, @keyin$, 128, 0, #PB_Cipher_ECB) ; We don't encrypt the trailing zero so 32 bytes is enough 
  Base64Encoder(*encodedbinary, 32, *encodedtext, 64)                       ; Convert encrypted binary data to ascii for return                      
  ProcedureReturn PeekS(*encodedtext)                                       ; Return the completed ascii result
EndProcedure

Procedure$ DecryptPassword(password$, key$)
  Protected keyin$         = LSet(key$, 16, Chr(32))                            ; key for 128bit decryption needs length=16
  Protected *encodedbinary = AllocateMemory(33)                                 ; 32 bytes for data + one safety byte
  Protected *decodedtext   = AllocateMemory(33)                                 ; 32 bytes for characters + one byte for terminating zero
  Base64Decoder(@password$, Len(password$), *encodedbinary, 32)                 ; Convert the ascii encoded password back to binary
  AESDecoder(*encodedbinary, *decodedtext, 32, @keyin$, 128, 0, #PB_Cipher_ECB) ; Convert the encrypted password back to original
  ProcedureReturn Trim(PeekS(*decodedtext, 32))                                 ; Remove any trailing spaces and return result
EndProcedure

this$ = EncryptPassword("Holy Smoke, Batman!  ", "somekey")
that$ = DecryptPassword(this$, "somekey")

Debug "Encrypted password: "+this$
Debug "Decrypted password: "+that$
It's actually reasonably strong, simple as it is. If the user puts more than 32 characters for the password, no error will happen but only the first 32 will count.

Re: Encyrpting data (like a password)

Posted: Fri Jul 01, 2011 7:27 pm
by purebuilt
Thanks... You're the best! Viva la PureBasic!!!!

DB

Re: Encyrpting data (like a password)

Posted: Fri Nov 18, 2011 7:10 pm
by Blue
@NetMaestro

Many, many thanks !

I've been using these encryption functions, in a similar way, for a long time, but it never occured to me to do...

Code: Select all

[...]
  AESEncoder(@passin$, *encodedbinary, 32, @keyin$, 128, 0, #PB_Cipher_ECB)  ; ...
  Base64Encoder(*encodedbinary, 32, *encodedtext, 64)        ; Convert encrypted binary data to ascii for return
[...]
That combination (and its inverse) makes coding so much simpler. I've just learned something important here.
Great. Thank you for sharing that code.

Re: Encyrpting data (like a password)

Posted: Fri Nov 18, 2011 9:01 pm
by jesperbrannmark
If you are going to transfer this over a public network, it could be smart to put some extra "salt" on the steak..
Put in something like the date today somewhere... So you know it changes every day.

Re: Encyrpting data (like a password)

Posted: Sat Nov 19, 2011 2:39 pm
by ultralazor
You use meta data and encrypt with the contained password, then check for meta data by decrypting with typed password. Using a cipher like AES it can only be brute forced..

Re: Encyrpting data (like a password)

Posted: Sat Jun 07, 2014 6:22 am
by rsts
Once again netmaestro, one of your fine contributions provides a perfect solution.

I realize I'm a bit late to the party, but just now had the need for something like this.

cheers

Re: Encyrpting data (like a password)

Posted: Sun Apr 20, 2025 12:44 pm
by dibor
netmaestro wrote: Fri Jul 01, 2011 5:19 pm purebuilt, give this a try (password max length: 32 characters):

Code: Select all

Procedure$ EncryptPassword(password$, key$)
  Protected passin$        = LSet(password$, 32, Chr(32))                   ; Pad the password with spaces to make 32 characters   
  Protected keyin$         = LSet(key$, 16, Chr(32))                        ; key for 128bit encryption needs length=16
  Protected *encodedbinary = AllocateMemory(32)                             ; 32 bytes for encrypted binary result, see 2 lines down 
  Protected *encodedtext   = AllocateMemory(64)                             ; Destination for Base64Encoder must be 33% larger- we double it
  AESEncoder(@passin$, *encodedbinary, 32, @keyin$, 128, 0, #PB_Cipher_ECB) ; We don't encrypt the trailing zero so 32 bytes is enough 
  Base64Encoder(*encodedbinary, 32, *encodedtext, 64)                       ; Convert encrypted binary data to ascii for return                      
  ProcedureReturn PeekS(*encodedtext)                                       ; Return the completed ascii result
EndProcedure

Procedure$ DecryptPassword(password$, key$)
  Protected keyin$         = LSet(key$, 16, Chr(32))                            ; key for 128bit decryption needs length=16
  Protected *encodedbinary = AllocateMemory(33)                                 ; 32 bytes for data + one safety byte
  Protected *decodedtext   = AllocateMemory(33)                                 ; 32 bytes for characters + one byte for terminating zero
  Base64Decoder(@password$, Len(password$), *encodedbinary, 32)                 ; Convert the ascii encoded password back to binary
  AESDecoder(*encodedbinary, *decodedtext, 32, @keyin$, 128, 0, #PB_Cipher_ECB) ; Convert the encrypted password back to original
  ProcedureReturn Trim(PeekS(*decodedtext, 32))                                 ; Remove any trailing spaces and return result
EndProcedure

this$ = EncryptPassword("Holy Smoke, Batman!  ", "somekey")
that$ = DecryptPassword(this$, "somekey")

Debug "Encrypted password: "+this$
Debug "Decrypted password: "+that$
It's actually reasonably strong, simple as it is. If the user puts more than 32 characters for the password, no error will happen but only the first 32 will count.
This code doesn't work anymore:(
[14:25:24] [COMPILER] Line 7: Base64Encoder(): Incorrect number of parameters.

Looks like Base64Decoder() and Base64Encoder() were changed.

Re: Encyrpting data (like a password)

Posted: Sun Apr 20, 2025 2:47 pm
by NicTheQuick
jesperbrannmark wrote: Fri Nov 18, 2011 9:01 pm If you are going to transfer this over a public network, it could be smart to put some extra "salt" on the steak..
Put in something like the date today somewhere... So you know it changes every day.
When en/decrypting the initialization vector can be used as salt. Before encryption it can be generated using CryptRandomData() and then stored besides the encryted data. Here's a simple example that's meant for strings:

Code: Select all

EnableExplicit
UseSHA2Fingerprint()
UseCRC32Fingerprint()

Structure EncryptedData
	iv.a[16]
	bits.w
	iterations.w
	inputSize.l
	crc32.l
	*data.Byte[0]
EndStructure

Procedure.s EncryptString(string.s, passphrase.s, bits.i = 192, iterations.i = 100)
	; Create cryptographic secure inititialization vector
	Protected *iv = AllocateMemory(16)
	If Not OpenCryptRandom()
		DebuggerError("OpenCryptRandom failed.")
		ProcedureReturn ""
	EndIf
	CryptRandomData(*iv, 16)
	CloseCryptRandom()
	
	; Derive encryption key from passphrase
	Protected *key = AllocateMemory(bits / 8)
	If Not DeriveCipherKey(passphrase, Base64Encoder(*iv, 16), iterations, *key, bits, #PB_Cipher_SHA2, 512)
		DebuggerError("DeriveCipherKey failed.")
		FreeMemory(*iv)
		FreeMemory(*key)
		ProcedureReturn ""
	EndIf
	
	; Pad string if needed
	Protected inputSize.i = StringByteLength(string, #PB_UTF8)
	If inputSize < 16
		string + Space(16 - inputSize)
	EndIf
	Protected *input = UTF8(string)
	Protected inputSizePadded.i = MemorySize(*input) - 1 ; ignore terminating null byte
	Protected *output.EncryptedData = AllocateMemory(SizeOf(EncryptedData) + inputSizePadded)
	
	If Not AESEncoder(*input, *output + SizeOf(EncryptedData), inputSizePadded, *key, bits, *iv, #PB_Cipher_CBC)
		DebuggerError("AESEncoder failed")
		FreeMemory(*iv)
		FreeMemory(*key)
		FreeMemory(*input)
		FreeMemory(*output)
		ProcedureReturn ""
	EndIf
	With *output
		CopyMemory(*iv, @\iv, 16)
		\inputSize = inputSize
		\bits = bits
		\iterations = iterations
		\crc32 = Val("$" + Fingerprint(*input, inputSizePadded, #PB_Cipher_CRC32))
	EndWith
	
	Protected result.s = Base64Encoder(*output, inputSizePadded + SizeOf(EncryptedData))
	FreeMemory(*iv)
	FreeMemory(*key)
	FreeMemory(*input)
	FreeMemory(*output)
	
	ProcedureReturn result
EndProcedure

Procedure.s DecryptString(encryptedString.s, passphrase.s)
	; Expect the decoded data to be 20% less
	Protected expectedInputSize.i = Len(encryptedString) * 4 / 5
	Protected *input.EncryptedData = AllocateMemory(expectedInputSize)
	Protected realInputSize.i = Base64Decoder(encryptedString, *input, expectedInputSize)
	; Check encrypted data size for validity
	If realInputSize < SizeOf(EncryptedData) + 16
		DebuggerError("encryptedString seems to be too short.")
		FreeMemory(*input)
		ProcedureReturn ""
	EndIf
	
	Protected inputSizePadded.i = realInputSize - SizeOf(EncryptedData)
	
	; Derive encryption key from passphrase
	Protected *key = AllocateMemory(*input\bits / 8)
	If Not DeriveCipherKey(passphrase, Base64Encoder(@*input\iv, 16), *input\iterations, *key, *input\bits, #PB_Cipher_SHA2, 512)
		DebuggerError("DeriveCipherKey failed.")
		FreeMemory(*input)
		FreeMemory(*key)
		ProcedureReturn ""
	EndIf
	
	Protected *output = AllocateMemory(inputSizePadded)
	If Not AESDecoder(*input + SizeOf(EncryptedData), *output, inputSizePadded, *key, *input\bits, @*input\iv, #PB_Cipher_CBC)
		DebuggerError("AESDecoder failed.")
		FreeMemory(*output)
		FreeMemory(*input)
		ProcedureReturn ""
	EndIf
	FreeMemory(*key)
	
	Protected crc32.l = Val("$" + Fingerprint(*output, inputSizePadded, #PB_Cipher_CRC32))
	If crc32 <> *input\crc32
		FreeMemory(*output)
		FreeMemory(*input)
		ProcedureReturn "[WRONG PASSPHRASE]"
	EndIf
	
	Protected result.s = PeekS(*output, *input\inputSize, #PB_UTF8 | #PB_ByteLength)
	FreeMemory(*output)
	FreeMemory(*input)
	
	ProcedureReturn result
	
EndProcedure

Define encrypted.s = EncryptString("Hello World", "my passphrase", 256)
Debug encrypted
Debug DecryptString(encrypted, "wrong passphrase")
Debug DecryptString(encrypted, "my passphrase")