Windows: Encryption with password using the Windows API

Share your advanced PureBasic knowledge/code with the community.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Excellent. Nicely done. :D

Any problems with zero's in this one? That is, is it possible that one of the encrypted characters could be Chr(0) ?
I may look like a mule, but I'm not a complete ass.
User avatar
HeX0R
Addict
Addict
Posts: 1188
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Post by HeX0R »

No problem with zero-bytes!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Perfect. Thank you for this.
I may look like a mule, but I'm not a complete ass.
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

@HeXOR - Can you help me with this? I took your modified RC4 routine and am trying to convert it to Triple-DES. It seems to be working, but the decrypted string has trailing chars. Maybe this is due to it being a block cipher instead of a stream cipher. I'm stumped.

Code: Select all

;Crypt-Constants
#PROV_RSA_FULL = 1
#ALG_SID_MD5 = 3
#ALG_SID_RC4 = 1
#ALG_SID_RC5 = 13
#ALG_CLASS_DATA_ENCRYPT = 3<<13 ;$6000
#ALG_CLASS_HASH = 4<<13 ;$8000
#ALG_TYPE_ANY = 0
#ALG_TYPE_STREAM = 4<<9
#CALG_MD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD5
#ALG_TYPE_BLOCK = 1536
#CRYPT_OAEP = $40
; Valid hashing algorithms:
;
; #ALG_SID_HMAC = 9
; #ALG_SID_MAC = 5
; #ALG_SID_MD2 = 1
; #ALG_SID_SHA = 4
; #ALG_SID_SHA1 = 4
; #ALG_SID_SSL3SHAMD5 = 8
; #CALG_HMAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_HMAC
; #CALG_MAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MAC
; #CALG_MD2 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD2
; #CALG_SHA = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA
; #CALG_SHA1 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA1
; #CALG_SSL3_SHAMD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SSL3SHAMD5
#CALG_RC4 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_STREAM|#ALG_SID_RC4
#CALG_RC5 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_BLOCK|#ALG_SID_RC5
#CRYPT_CREATE_SALT = 4
#CRYPT_EXPORTABLE = 1
#CRYPT_NEWKEYSET = 8
#MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"

; added for Triple DES
#ALG_SID_3DES = 3
#CALG_3DES = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_BLOCK|#ALG_SID_3DES
#MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0"

Procedure.s EnCrypt(String$, Key$)
  Protected *Buffer
  *Buffer = AllocateMemory(Len(String$) * 2 + 64)
  DataLength.l = Len(String$)
  PokeS(*Buffer, String$)

  If CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      ;CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      CryptDeriveKey_(hProv, #CALG_3DES, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptEncrypt_(hKey, 0, #True, #Null, *Buffer, @DataLength, Len(String$) * 2 + 64)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf
  
  result.s = Space(DataLength * 3)
  Base64Encoder(*Buffer, DataLength, @result, DataLength * 3)
  FreeMemory(*Buffer)
  ProcedureReturn result
EndProcedure

Procedure.s Decrypt(String$, Key$)
 
  Protected *Buffer
  *Buffer = AllocateMemory(Len(String$) * 2 + 64)
  DataLength.l = Base64Decoder(@String$, Len(String$), *Buffer, Len(String$) * 2 + 64)
  
  If CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      CryptDeriveKey_(hProv, #CALG_3DES, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptDecrypt_(hKey, 0, #True, 0, *Buffer, @DataLength)
          result.s = PeekS(*Buffer)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf
  FreeMemory(*Buffer)
  ProcedureReturn result
EndProcedure

lStringToEncrypt.s = "macromedia"
lKey.s = "key"

Debug lStringToEncrypt.s

lEncrypted.s = EnCrypt(lStringToEncrypt.s,lKey.s)

Debug lEncrypted.s

lDeCrypted.s = Decrypt(lEncrypted.s,"key")

Debug lDeCrypted.s
Thanks in advance.
Image Image
User avatar
HeX0R
Addict
Addict
Posts: 1188
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Post by HeX0R »

Sorry, i am very stressed at the moment, so i didn't see your post earlier ;)

Just make use of the DataLength-Variable like this:

Code: Select all

;Crypt-Constants
#PROV_RSA_FULL = 1
#ALG_SID_MD5 = 3
#ALG_SID_RC4 = 1
#ALG_SID_RC5 = 13
#ALG_CLASS_DATA_ENCRYPT = 3<<13 ;$6000
#ALG_CLASS_HASH = 4<<13 ;$8000
#ALG_TYPE_ANY = 0
#ALG_TYPE_STREAM = 4<<9
#CALG_MD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD5
#ALG_TYPE_BLOCK = 1536
#CRYPT_OAEP = $40
; Valid hashing algorithms:
;
; #ALG_SID_HMAC = 9
; #ALG_SID_MAC = 5
; #ALG_SID_MD2 = 1
; #ALG_SID_SHA = 4
; #ALG_SID_SHA1 = 4
; #ALG_SID_SSL3SHAMD5 = 8
; #CALG_HMAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_HMAC
; #CALG_MAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MAC
; #CALG_MD2 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD2
; #CALG_SHA = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA
; #CALG_SHA1 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA1
; #CALG_SSL3_SHAMD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SSL3SHAMD5
#CALG_RC4 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_STREAM|#ALG_SID_RC4
#CALG_RC5 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_BLOCK|#ALG_SID_RC5
#CRYPT_CREATE_SALT = 4
#CRYPT_EXPORTABLE = 1
#CRYPT_NEWKEYSET = 8
#MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"

; added for Triple DES
#ALG_SID_3DES = 3
#CALG_3DES = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_BLOCK|#ALG_SID_3DES
#MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0"

Procedure.s EnCrypt(String$, Key$)
  Protected *Buffer
  *Buffer = AllocateMemory(Len(String$) * 2 + 64)
  DataLength.l = Len(String$)
  PokeS(*Buffer, String$)

  If CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      ;CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      CryptDeriveKey_(hProv, #CALG_3DES, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptEncrypt_(hKey, 0, #True, #Null, *Buffer, @DataLength, Len(String$) * 2 + 64)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf
 
  result.s = Space(DataLength * 3)
  Base64Encoder(*Buffer, DataLength, @result, DataLength * 3)
  FreeMemory(*Buffer)
  ProcedureReturn result
EndProcedure

Procedure.s Decrypt(String$, Key$)
 
  Protected *Buffer
  *Buffer = AllocateMemory(Len(String$) * 2 + 64)
  DataLength.l = Base64Decoder(@String$, Len(String$), *Buffer, Len(String$) * 2 + 64)
 
  If CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #Null, #MS_ENHANCED_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      CryptDeriveKey_(hProv, #CALG_3DES, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptDecrypt_(hKey, 0, #True, 0, *Buffer, @DataLength)
          result.s = PeekS(*Buffer, DataLength)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf
  FreeMemory(*Buffer)
  ProcedureReturn result
EndProcedure

lStringToEncrypt.s = "macromedia"
lKey.s = "key"

Debug lStringToEncrypt.s

lEncrypted.s = EnCrypt(lStringToEncrypt.s,lKey.s)

Debug lEncrypted.s

lDeCrypted.s = Decrypt(lEncrypted.s,lKey)

Debug lDeCrypted.s
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Re: Windows: Encryption with password using the Windows API

Post by NoahPhense »

Nice .. yeah rc4 is good, and modified rc4 is very tight. Especially when
you want it to be machine dependant.

- np
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

Works great HeX0R! Thanks.

And congratulations.
Image Image
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Post by utopiomania »

I'm using Hexor's modified RC4 sample below. The problem is, it fails if compiled to a unicode executable.

Have any of you managed to get them to work in both unicode and non-unicode executables?

Code: Select all

#PROV_RSA_FULL = 1
#CRYPT_NEWKEYSET = 8 
#CRYPT_EXPORTABLE = 1 
#CALG_MD5 = 4 << 13 | 0 | 3 
#CALG_RC4 = 3 << 13 | 4 << 9 | 1
#MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0" 

procedure.s enCrypt(string.s, key.s) 
  protected *buffer 
  *buffer = allocateMemory(1024)
  
  dataLength = len(string)
  
  pokeS(*buffer, string) 
  if cryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, 0) = 0 
    cryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET) 
  endIf 
  if hProv 
    cryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash) 
    if hHash 
      cryptHashData_(hHash, @key, len(key), 0) 
      cryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey) 
      if hKey 
        if cryptEncrypt_(hKey, 0, #TRUE, #NULL, *buffer, @dataLength, 1024) 
        endIf 
        cryptDestroyKey_(hKey) 
      endIf 
      cryptDestroyHash_(hHash) 
    endIf 
    cryptReleaseContext_(hProv, 0) 
  endIf 

  result.s = "" 
  for i = 0 to dataLength - 1 
    result + rset(hex(peekB(*buffer + i) & $FF), 2, "0") 
  next i 
  freeMemory(*buffer) 
  procedureReturn result 
endProcedure 

procedure.s decrypt(string.s, key.s) 
  protected *buffer 
  *buffer = allocateMemory(1024) 
  result.s = "" 
  o = 0 
  for i = 1 to len(string) step 2 
    hi = asc(mid(string, i, 1)) - 48 
    if hi > 9 
      hi - 7 
    endIf 
    lo = asc(mid(string, i + 1, 1)) - 48 
    if lo > 9 
      lo - 7 
    endIf 
    pokeB(*buffer + o, (hi * 16) + lo) 
    o + 1 
  next i
    
  datalength = len(string) / 2
  
  if cryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, 0) = 0 
    cryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET) 
  endIf 
  if hProv 
    cryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash) 
    if hHash 
      cryptHashData_(hHash, @key, len(key), 0) 
      cryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey) 
      if hKey 
        if cryptDecrypt_(hKey, 0, #TRUE, 0, *buffer, @dataLength) 
          result = peekS(*buffer) 
        endIf 
        cryptDestroyKey_(hKey) 
      endIf 
      cryptDestroyHash_(hHash) 
    endIf 
    cryptReleaseContext_(hProv, 0) 
  endIf 
  freeMemory(*buffer) 
  procedureReturn result 
endProcedure

e.s = "14c4b06b824ec593239362517f538b29"
d.s = encrypt(e, "abcd")
messageRequester("Result", "Encode: " + e + #CRLF$ + "Decode: " + decrypt(d, "abcd")) 

end
Last edited by utopiomania on Mon Feb 12, 2007 10:21 pm, edited 1 time in total.
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Post by ABBKlaus »

only a suggestion :oops: , but base64Encoder/Decoder is not unicode compatible.
Memory Viewer shows this in unicode
008947D0 74 4B 7A 63 73 58 34 54 4D 32 79 4E 6D 58 70 32 tKzcsX4TM2yNmXp2
008947E0 44 4A 63 44 31 4F 33 30 7A 4E 45 34 47 2B 32 45 DJcD1O30zNE4G+2E
008947F0 69 50 76 31 51 2F 48 42 46 71 67 3D 00 00 20 00 iPv1Q/HBFqg=.. .
00894800 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 . . . . . . . .
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Post by utopiomania »

Seems from the helpfile that you're right. I've posted another above without the base64 stuff.
If compiled to unicode, it decrypts only half the string:

Code: Select all

Encode: 14c4b06b824ec593239362517f538b29
Decode: 14c4b06b824ec593
So, we're half-way there :) but I can't figure out where things go wrong.
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Post by utopiomania »

Maybe this is it. I went back to El-Chonis original post, and used a memory buffer instead of a string
to avoid problems with zeroes in the cipher string.

Code: Select all

#PROV_RSA_FULL = 1
#CRYPT_NEWKEYSET = 8 
#CRYPT_EXPORTABLE = 1 
#CALG_MD5 = 4 << 13 | 0 | 3 
#CALG_RC4 = 3 << 13 | 4 << 9 | 1
#MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0" 

procedure encrypt(*mem, length, password$) 
  if cryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, 0) = 0 
    cryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, #CRYPT_NEWKEYSET) 
  endIf 
  cryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash) 
  cryptHashData_(hHash, password$, Len(password$), 0) 
  cryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey) 
  cryptEncrypt_(hKey, 0, #TRUE, #NULL, *mem, @length, length) 
  cryptDestroyKey_(hKey) 
  cryptDestroyHash_(hHash) 
  cryptReleaseContext_(hProv, 0) 
endProcedure 

procedure decrypt(*mem, length, password$) 
  if cryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, 0) = 0 
    cryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, #CRYPT_NEWKEYSET) 
  endIf 
  cryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash) 
  cryptHashData_(hHash, password$, len(password$), 0) 
  cryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey) 
  cryptDecrypt_(hKey, 0, #TRUE, 0, *mem, @length) 
  cryptDestroyKey_(hKey) 
  cryptDestroyHash_(hHash) 
  cryptReleaseContext_(hProv, 0) 
endProcedure 

e.s = "1089338MSHMFSFSYUS3783264P973AS3M3R8YSN4RTR67NSZ187TS3T170SEMS3298YMYMAY31"
lim = len(e) * 2
*mem = allocateMemory(lim)
*ptr = *mem
copyMemoryString(@e, @*ptr)

debug peekS(*mem)
encrypt(*mem, lim, "abcd")
debug peekS(*mem)
debug str(peekC(*mem + 57)) + " at offset 57"
decrypt(*mem, lim, "abcd")
debug peekS(*mem)

end
Elias Montoya
New User
New User
Posts: 8
Joined: Tue May 08, 2007 9:31 pm
Location: Mexico

Post by Elias Montoya »

I see this thread is not very old, so i might as well try.
im trying to port this pyton code to pb:

Code: Select all

def mbi_encrypt(key, nonce):
    def derive_key(key, magic):
        hash1 = HMAC.new(key, magic, SHA).digest()
        
        hash2 = HMAC.new(key, hash1 + magic, SHA).digest()
        hash3 = HMAC.new(key, hash1, SHA).digest()
        
        hash4 = HMAC.new(key, hash3 + magic, SHA).digest()
        
        return hash2 + hash4[0:4]

    #
    # Read key and generate two derived keys
    #
    
    key1 = standard_b64decode(key)
    key2 = derive_key(key1, "WS-SecureConversationSESSION KEY HASH")
    key3 = derive_key(key1, "WS-SecureConversationSESSION KEY ENCRYPTION")
    
    #
    # Create a HMAC-SHA-1 hash of nonce using key2
    #
    
    hash = HMAC.new(key2, nonce, SHA).digest()
    
    #
    # Encrypt nonce with DES3 using key3
    #
    
    # IV: 8 bytes of random data
    iv = randpool.KeyboardRandomPool().get_bytes(8)
    obj = DES3.new(key3, DES3.MODE_CBC, iv)
    
    # XXX: win32's Crypt API seems to pad the input with 0x08 bytes to align on 72/36/18/9 boundary
    ciph = obj.encrypt(nonce + "\x08\x08\x08\x08\x08\x08\x08\x08")

    #
    # Generate the blob
    #

    blob = struct.pack("<LLLLLLL", 28, CRYPT_MODE_CBC, CALC_3DES, CALG_SHA1,
                       len(iv), len(hash), len(ciph))
    blob += iv + hash + ciph
    
    return standard_b64encode(blob)
Complete reference in this link:
http://www.openrce.org/blog/view/449/MS ... scheme_REd

The part that is giving me problems is the "# Encrypt nonce with DES3 using key3 ". i think the DES3 code that hexor posted can be used. But where do i use the IV part??

Would you guys help me? :)
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

Just wanted to mention that this RC4 code using the Windows API is not
reliable between PCs. What it encodes on my home PC doesn't decode
on my work PC, making it useless for portability. Bear that in mind.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Post Reply