Last active 10 months ago

mdAesCtr.bas Raw
1'--- mdAesCtr.bas
2Option Explicit
3DefObj A-Z
4
5#Const HasPtrSafe = (VBA7 <> 0) Or (TWINBASIC <> 0)
6
7'=========================================================================
8' API
9'=========================================================================
10
11#If Win64 Then
12 Private Const PTR_SIZE As Long = 8
13#Else
14 Private Const PTR_SIZE As Long = 4
15#End If
16
17#If HasPtrSafe Then
18Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpvDest As Any, lpvSource As Any, ByVal cbCopy As LongPtr)
19Private Declare PtrSafe Function ArrPtr Lib "vbe7" Alias "VarPtr" (Ptr() As Any) As LongPtr
20Private Declare PtrSafe Function htonl Lib "ws2_32" (ByVal hostlong As Long) As Long
21Private Declare PtrSafe Function RtlGenRandom Lib "advapi32" Alias "SystemFunction036" (RandomBuffer As Any, ByVal RandomBufferLength As Long) As Long
22'--- bcrypt
23Private Declare PtrSafe Function BCryptOpenAlgorithmProvider Lib "bcrypt" (phAlgorithm As LongPtr, ByVal pszAlgId As LongPtr, ByVal pszImplementation As LongPtr, ByVal dwFlags As Long) As Long
24Private Declare PtrSafe Function BCryptCloseAlgorithmProvider Lib "bcrypt" (ByVal hAlgorithm As LongPtr, ByVal dwFlags As Long) As Long
25Private Declare PtrSafe Function BCryptGetProperty Lib "bcrypt" (ByVal hObject As LongPtr, ByVal pszProperty As LongPtr, pbOutput As Any, ByVal cbOutput As Long, cbResult As Long, ByVal dwFlags As Long) As Long
26Private Declare PtrSafe Function BCryptSetProperty Lib "bcrypt" (ByVal hObject As LongPtr, ByVal pszProperty As LongPtr, ByVal pbInput As LongPtr, ByVal cbInput As Long, ByVal dwFlags As Long) As Long
27Private Declare PtrSafe Function BCryptGenerateSymmetricKey Lib "bcrypt" (ByVal hAlgorithm As LongPtr, phKey As LongPtr, pbKeyObject As Any, ByVal cbKeyObject As Long, pbSecret As Any, ByVal cbSecret As Long, ByVal dwFlags As Long) As Long
28Private Declare PtrSafe Function BCryptDestroyKey Lib "bcrypt" (ByVal hKey As LongPtr) As Long
29Private Declare PtrSafe Function BCryptEncrypt Lib "bcrypt" (ByVal hKey As LongPtr, pbInput As Any, ByVal cbInput As Long, ByVal pPaddingInfo As LongPtr, ByVal pbIV As LongPtr, ByVal cbIV As Long, pbOutput As Any, ByVal cbOutput As Long, pcbResult As Long, ByVal dwFlags As Long) As Long
30Private Declare PtrSafe Function BCryptDeriveKeyPBKDF2 Lib "bcrypt" (ByVal hPrf As LongPtr, pbPassword As Any, ByVal cbPassword As Long, pbSalt As Any, ByVal cbSalt As Long, ByVal cIterations As currency, pbDerivedKey As Any, ByVal cbDerivedKey As Long, ByVal dwFlags As Long) As Long
31Private Declare PtrSafe Function BCryptCreateHash Lib "bcrypt" (ByVal hAlgorithm As LongPtr, phHash As LongPtr, ByVal pbHashObject As LongPtr, ByVal cbHashObject As Long, pbSecret As Any, ByVal cbSecret As Long, ByVal dwFlags As Long) As Long
32Private Declare PtrSafe Function BCryptDestroyHash Lib "bcrypt" (ByVal hHash As LongPtr) As Long
33Private Declare PtrSafe Function BCryptHashData Lib "bcrypt" (ByVal hHash As LongPtr, pbInput As Any, ByVal cbInput As Long, ByVal dwFlags As Long) As Long
34Private Declare PtrSafe Function BCryptFinishHash Lib "bcrypt" (ByVal hHash As LongPtr, pbOutput As Any, ByVal cbOutput As Long, ByVal dwFlags As Long) As Long
35#Else
36Private Enum LongPtr
37 [_]
38End Enum
39Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpvDest As Any, lpvSource As Any, ByVal cbCopy As LongPtr)
40Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (Ptr() As Any) As LongPtr
41Private Declare Function htonl Lib "ws2_32" (ByVal hostlong As Long) As Long
42Private Declare Function RtlGenRandom Lib "advapi32" Alias "SystemFunction036" (RandomBuffer As Any, ByVal RandomBufferLength As Long) As Long
43'--- bcrypt
44Private Declare Function BCryptOpenAlgorithmProvider Lib "bcrypt" (phAlgorithm As LongPtr, ByVal pszAlgId As LongPtr, ByVal pszImplementation As LongPtr, ByVal dwFlags As Long) As Long
45Private Declare Function BCryptCloseAlgorithmProvider Lib "bcrypt" (ByVal hAlgorithm As LongPtr, ByVal dwFlags As Long) As Long
46Private Declare Function BCryptGetProperty Lib "bcrypt" (ByVal hObject As LongPtr, ByVal pszProperty As LongPtr, pbOutput As Any, ByVal cbOutput As Long, cbResult As Long, ByVal dwFlags As Long) As Long
47Private Declare Function BCryptSetProperty Lib "bcrypt" (ByVal hObject As LongPtr, ByVal pszProperty As LongPtr, ByVal pbInput As LongPtr, ByVal cbInput As Long, ByVal dwFlags As Long) As Long
48Private Declare Function BCryptGenerateSymmetricKey Lib "bcrypt" (ByVal hAlgorithm As LongPtr, phKey As LongPtr, pbKeyObject As Any, ByVal cbKeyObject As Long, pbSecret As Any, ByVal cbSecret As Long, ByVal dwFlags As Long) As Long
49Private Declare Function BCryptDestroyKey Lib "bcrypt" (ByVal hKey As LongPtr) As Long
50Private Declare Function BCryptEncrypt Lib "bcrypt" (ByVal hKey As LongPtr, pbInput As Any, ByVal cbInput As Long, ByVal pPaddingInfo As LongPtr, ByVal pbIV As LongPtr, ByVal cbIV As Long, pbOutput As Any, ByVal cbOutput As Long, pcbResult As Long, ByVal dwFlags As Long) As Long
51Private Declare Function BCryptDeriveKeyPBKDF2 Lib "bcrypt" (ByVal hPrf As LongPtr, pbPassword As Any, ByVal cbPassword As Long, pbSalt As Any, ByVal cbSalt As Long, ByVal cIterations As Currency, pbDerivedKey As Any, ByVal cbDerivedKey As Long, ByVal dwFlags As Long) As Long
52Private Declare Function BCryptCreateHash Lib "bcrypt" (ByVal hAlgorithm As LongPtr, phHash As LongPtr, ByVal pbHashObject As LongPtr, ByVal cbHashObject As Long, pbSecret As Any, ByVal cbSecret As Long, ByVal dwFlags As Long) As Long
53Private Declare Function BCryptDestroyHash Lib "bcrypt" (ByVal hHash As LongPtr) As Long
54Private Declare Function BCryptHashData Lib "bcrypt" (ByVal hHash As LongPtr, pbInput As Any, ByVal cbInput As Long, ByVal dwFlags As Long) As Long
55Private Declare Function BCryptFinishHash Lib "bcrypt" (ByVal hHash As LongPtr, pbOutput As Any, ByVal cbOutput As Long, ByVal dwFlags As Long) As Long
56#End If
57#If Not ImplUseShared Then
58 #If HasPtrSafe Then
59 Private Declare PtrSafe Function CryptStringToBinary Lib "crypt32" Alias "CryptStringToBinaryW" (ByVal pszString As LongPtr, ByVal cchString As Long, ByVal dwFlags As Long, ByVal pbBinary As LongPtr, pcbBinary As Long, pdwSkip As Long, pdwFlags As Long) As Long
60 Private Declare PtrSafe Function CryptBinaryToString Lib "crypt32" Alias "CryptBinaryToStringW" (ByVal pbBinary As LongPtr, ByVal cbBinary As Long, ByVal dwFlags As Long, ByVal pszString As LongPtr, pcchString As Long) As Long
61 Private Declare PtrSafe Function WideCharToMultiByte Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpWideCharStr As LongPtr, ByVal cchWideChar As Long, lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByVal lpDefaultChar As LongPtr, ByVal lpUsedDefaultChar As LongPtr) As Long
62 Private Declare PtrSafe Function MultiByteToWideChar Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByVal lpWideCharStr As LongPtr, ByVal cchWideChar As Long) As Long
63 Private Declare PtrSafe Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, ByVal lpSource As LongPtr, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, ByVal Args As LongPtr) As Long
64 #Else
65 Private Declare Function CryptStringToBinary Lib "crypt32" Alias "CryptStringToBinaryW" (ByVal pszString As LongPtr, ByVal cchString As Long, ByVal dwFlags As Long, ByVal pbBinary As LongPtr, pcbBinary As Long, pdwSkip As Long, pdwFlags As Long) As Long
66 Private Declare Function CryptBinaryToString Lib "crypt32" Alias "CryptBinaryToStringW" (ByVal pbBinary As LongPtr, ByVal cbBinary As Long, ByVal dwFlags As Long, ByVal pszString As LongPtr, pcchString As Long) As Long
67 Private Declare Function WideCharToMultiByte Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpWideCharStr As LongPtr, ByVal cchWideChar As Long, lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByVal lpDefaultChar As LongPtr, ByVal lpUsedDefaultChar As LongPtr) As Long
68 Private Declare Function MultiByteToWideChar Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByVal lpWideCharStr As LongPtr, ByVal cchWideChar As Long) As Long
69 Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, ByVal lpSource As LongPtr, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, ByVal Args As LongPtr) As Long
70 #End If
71#End If
72
73'=========================================================================
74' Constants and member variables
75'=========================================================================
76
77Private Const AES_BLOCK_SIZE As Long = 16
78Private Const AES_KEYLEN As Long = 32 '-- 32 -> AES-256, 24 -> AES-196, 16 -> AES-128
79Private Const AES_IVLEN As Long = AES_BLOCK_SIZE
80Private Const KDF_SALTLEN As Long = 8
81Private Const KDF_ITER As Long = 10000
82Private Const KDF_HASH As String = "SHA512"
83Private Const HMAC_HASH As String = "SHA256"
84Private Const OPENSSL_MAGIC As String = "Salted__" '-- for openssl compatibility
85Private Const OPENSSL_MAGICLEN As Long = 8
86Private Const ERR_UNSUPPORTED_ENCR As String = "Unsupported encryption"
87Private Const ERR_CHUNKED_NOT_INIT As String = "AES chunked context not initialized"
88
89Private Type UcsCryptoContextType
90 hPbkdf2Alg As LongPtr
91 hHmacAlg As LongPtr
92 hHmacHash As LongPtr
93 HashLen As Long
94 hAesAlg As LongPtr
95 hAesKey As LongPtr
96 AesKeyObjData() As Byte
97 AesKeyObjLen As Long
98 Nonce(0 To 3) As Long
99 EncrData() As Byte
100 EncrPos As Long
101 LastError As String
102End Type
103
104Private m_uChunkedCtx As UcsCryptoContextType
105
106'=========================================================================
107' Functions
108'=========================================================================
109
110'--- equivalent to `openssl aes-256-ctr -pbkdf2 -md sha512 -pass pass:{Password} -in {sText}.file -a`
111Public Function AesEncryptString(sText As String, Optional Password As Variant) As String
112 Const PREFIXLEN As Long = OPENSSL_MAGICLEN + KDF_SALTLEN
113 Dim baData() As Byte
114 Dim baPass() As Byte
115 Dim baSalt() As Byte
116 Dim baKey() As Byte
117 Dim sError As String
118
119 baData = ToUtf8Array(sText)
120 baPass = vbNullString
121 baSalt = vbNullString
122 If Not IsArray(Password) Then
123 If Not IsMissing(Password) Then
124 baPass = ToUtf8Array(Password & vbNullString)
125 End If
126 ReDim baSalt(0 To KDF_SALTLEN - 1) As Byte
127 Call RtlGenRandom(baSalt(0), KDF_SALTLEN)
128 Else
129 baKey = Password
130 End If
131 If Not AesCryptArray(baData, baPass, baSalt, baKey, Error:=sError) Then
132 Err.Raise vbObjectError, , sError
133 End If
134 If Not IsArray(Password) Then
135 ReDim Preserve baData(0 To UBound(baData) + PREFIXLEN) As Byte
136 If UBound(baData) >= PREFIXLEN Then
137 Call CopyMemory(baData(PREFIXLEN), baData(0), UBound(baData) + 1 - PREFIXLEN)
138 End If
139 Call CopyMemory(baData(OPENSSL_MAGICLEN), baSalt(0), KDF_SALTLEN)
140 Call CopyMemory(baData(0), ByVal OPENSSL_MAGIC, OPENSSL_MAGICLEN)
141 End If
142 AesEncryptString = Replace(ToBase64Array(baData), vbCrLf, vbNullString)
143End Function
144
145'--- equivalent to `openssl aes-256-ctr -pbkdf2 -md sha512 -pass pass:{Password} -in {sEncr}.file -a -d`
146Public Function AesDecryptString(sEncr As String, Optional Password As Variant) As String
147 Const PREFIXLEN As Long = OPENSSL_MAGICLEN + KDF_SALTLEN
148 Dim baData() As Byte
149 Dim baPass() As Byte
150 Dim baSalt() As Byte
151 Dim baKey() As Byte
152 Dim sMagic As String
153 Dim sError As String
154
155 baData = FromBase64Array(sEncr)
156 baPass = vbNullString
157 baSalt = vbNullString
158 If Not IsArray(Password) Then
159 If Not IsMissing(Password) Then
160 baPass = ToUtf8Array(Password & vbNullString)
161 End If
162 If UBound(baData) >= PREFIXLEN - 1 Then
163 sMagic = String$(OPENSSL_MAGICLEN, 0)
164 Call CopyMemory(ByVal sMagic, baData(0), OPENSSL_MAGICLEN)
165 If sMagic = OPENSSL_MAGIC Then
166 ReDim baSalt(0 To KDF_SALTLEN - 1) As Byte
167 Call CopyMemory(baSalt(0), baData(OPENSSL_MAGICLEN), KDF_SALTLEN)
168 If UBound(baData) >= PREFIXLEN Then
169 Call CopyMemory(baData(0), baData(PREFIXLEN), UBound(baData) + 1 - PREFIXLEN)
170 ReDim Preserve baData(0 To UBound(baData) - PREFIXLEN) As Byte
171 Else
172 baData = vbNullString
173 End If
174 End If
175 End If
176 Else
177 baKey = Password
178 End If
179 If Not AesCryptArray(baData, baPass, baSalt, baKey, Error:=sError) Then
180 Err.Raise vbObjectError, , sError
181 End If
182 AesDecryptString = FromUtf8Array(baData)
183End Function
184
185Public Function AesCryptArray( _
186 baData() As Byte, _
187 Optional Password As Variant, _
188 Optional Salt As Variant, _
189 Optional Key As Variant, _
190 Optional ByVal KeyLen As Long, _
191 Optional Error As String, _
192 Optional Hmac As Variant) As Boolean
193 Const VT_BYREF As Long = &H4000
194 Dim uCtx As UcsCryptoContextType
195 Dim vErr As Variant
196 Dim bHashBefore As Boolean
197 Dim bHashAfter As Boolean
198 Dim baPass() As Byte
199 Dim baSalt() As Byte
200 Dim baKey() As Byte
201 Dim baTemp() As Byte
202 Dim lPtr As LongPtr
203
204 On Error GoTo EH
205 If IsArray(Hmac) Then
206 bHashBefore = (Hmac(0) <= 0)
207 bHashAfter = (Hmac(0) > 0)
208 End If
209 If IsMissing(Password) Then
210 baPass = vbNullString
211 ElseIf IsArray(Password) Then
212 baPass = Password
213 Else
214 baPass = ToUtf8Array(Password & vbNullString)
215 End If
216 If IsMissing(Salt) Then
217 baSalt = baPass
218 ElseIf IsArray(Salt) Then
219 baSalt = Salt
220 Else
221 baSalt = ToUtf8Array(Salt & vbNullString)
222 End If
223 If IsArray(Key) Then
224 baKey = Key
225 End If
226 If KeyLen <= 0 Then
227 KeyLen = AES_KEYLEN
228 End If
229 If Not pvCryptoAesCtrInit(uCtx, baPass, baSalt, baKey, KeyLen) Then
230 Error = uCtx.LastError
231 GoTo QH
232 End If
233 If Not pvCryptoAesCtrCrypt(uCtx, baData, HashBefore:=bHashBefore, HashAfter:=bHashAfter) Then
234 Error = uCtx.LastError
235 GoTo QH
236 End If
237 If IsArray(Hmac) Then
238 baTemp = pvCryptoGetFinalHash(uCtx, UBound(Hmac) + 1)
239 #If Win64 Then
240 lPtr = PeekPtr(VarPtr(Hmac) + 8)
241 #Else
242 lPtr = PeekPtr((VarPtr(Hmac) Xor &H80000000) + 8 Xor &H80000000)
243 #End If
244 If (PeekPtr(VarPtr(Hmac)) And VT_BYREF) <> 0 Then
245 lPtr = PeekPtr(lPtr)
246 End If
247 #If Win64 Then
248 lPtr = PeekPtr(lPtr + 16)
249 #Else
250 lPtr = PeekPtr((lPtr Xor &H80000000) + 12 Xor &H80000000)
251 #End If
252 Call CopyMemory(ByVal lPtr, baTemp(0), UBound(baTemp) + 1)
253 End If
254 '--- success
255 AesCryptArray = True
256QH:
257 pvCryptoAesCtrTerminate uCtx
258 Exit Function
259EH:
260 vErr = Array(Err.Number, Err.Source, Err.Description)
261 pvCryptoAesCtrTerminate uCtx
262 Err.Raise vErr(0), vErr(1), vErr(2)
263End Function
264
265Public Function AesChunkedInit(Optional Key As Variant, Optional ByVal KeyLen As Long) As Boolean
266 Dim baEmpty() As Byte
267 Dim baKey() As Byte
268
269 pvCryptoAesCtrTerminate m_uChunkedCtx
270 baEmpty = vbNullString
271 If IsArray(Key) Then
272 baKey = Key
273 End If
274 If KeyLen <= 0 Then
275 KeyLen = AES_KEYLEN
276 End If
277 AesChunkedInit = pvCryptoAesCtrInit(m_uChunkedCtx, baEmpty, baEmpty, baKey, KeyLen)
278End Function
279
280Public Function AesChunkedCryptArray(baInput() As Byte, baOutput() As Byte, Optional ByVal Final As Boolean = True) As Boolean
281 If m_uChunkedCtx.hAesAlg = 0 Then
282 m_uChunkedCtx.LastError = ERR_CHUNKED_NOT_INIT
283 Exit Function
284 End If
285 baOutput = baInput
286 AesChunkedCryptArray = pvCryptoAesCtrCrypt(m_uChunkedCtx, baOutput)
287 If Final Then
288 pvCryptoAesCtrTerminate m_uChunkedCtx
289 End If
290End Function
291
292Public Function AesChunkedGetLastError() As String
293 AesChunkedGetLastError = m_uChunkedCtx.LastError
294End Function
295
296'= private ===============================================================
297
298Private Function pvCryptoAesCtrInit(uCtx As UcsCryptoContextType, baPass() As Byte, baSalt() As Byte, baDerivedKey() As Byte, ByVal lKeyLen As Long) As Boolean
299 Const MS_PRIMITIVE_PROVIDER As String = "Microsoft Primitive Provider"
300 Const BCRYPT_ALG_HANDLE_HMAC_FLAG As Long = 8
301 Dim hResult As Long
302
303 With uCtx
304 '--- init member vars
305 .EncrData = vbNullString
306 .EncrPos = 0
307 .LastError = vbNullString
308 ReDim Preserve baDerivedKey(0 To lKeyLen + AES_IVLEN - 1) As Byte
309 If UBound(baPass) >= 0 Or UBound(baSalt) >= 0 Then
310 '--- generate RFC 2898 based derived key
311 On Error GoTo EH_Unsupported '--- PBKDF2 API missing on Vista
312 hResult = BCryptOpenAlgorithmProvider(.hPbkdf2Alg, StrPtr(KDF_HASH), StrPtr(MS_PRIMITIVE_PROVIDER), BCRYPT_ALG_HANDLE_HMAC_FLAG)
313 If hResult < 0 Then
314 GoTo QH
315 End If
316 hResult = BCryptDeriveKeyPBKDF2(.hPbkdf2Alg, ByVal pvArrayPtr(baPass), pvArraySize(baPass), ByVal pvArrayPtr(baSalt), pvArraySize(baSalt), _
317 KDF_ITER / 10000@, baDerivedKey(0), UBound(baDerivedKey) + 1, 0)
318 If hResult < 0 Then
319 GoTo QH
320 End If
321 On Error GoTo 0
322 End If
323 '--- init AES key from first half of derived key
324 On Error GoTo EH_Unsupported '--- CNG API missing on XP
325 hResult = BCryptOpenAlgorithmProvider(.hAesAlg, StrPtr("AES"), StrPtr(MS_PRIMITIVE_PROVIDER), 0)
326 If hResult < 0 Then
327 GoTo QH
328 End If
329 On Error GoTo 0
330 hResult = BCryptGetProperty(.hAesAlg, StrPtr("ObjectLength"), .AesKeyObjLen, 4, 0, 0)
331 If hResult < 0 Then
332 GoTo QH
333 End If
334 hResult = BCryptSetProperty(.hAesAlg, StrPtr("ChainingMode"), StrPtr("ChainingModeECB"), 30, 0) ' 30 = LenB("ChainingModeECB")
335 If hResult < 0 Then
336 GoTo QH
337 End If
338 ReDim .AesKeyObjData(0 To .AesKeyObjLen - 1) As Byte
339 hResult = BCryptGenerateSymmetricKey(.hAesAlg, .hAesKey, .AesKeyObjData(0), .AesKeyObjLen, baDerivedKey(0), lKeyLen, 0)
340 If hResult < 0 Then
341 GoTo QH
342 End If
343 '--- init AES IV from second half of derived key
344 Call CopyMemory(.Nonce(0), baDerivedKey(lKeyLen), AES_IVLEN)
345 '--- init HMAC key from last HashLen bytes of derived key
346 hResult = BCryptOpenAlgorithmProvider(.hHmacAlg, StrPtr(HMAC_HASH), StrPtr(MS_PRIMITIVE_PROVIDER), BCRYPT_ALG_HANDLE_HMAC_FLAG)
347 If hResult < 0 Then
348 GoTo QH
349 End If
350 hResult = BCryptGetProperty(.hHmacAlg, StrPtr("HashDigestLength"), .HashLen, 4, 0, 0)
351 If hResult < 0 Then
352 GoTo QH
353 End If
354 hResult = BCryptCreateHash(.hHmacAlg, .hHmacHash, 0, 0, baDerivedKey(lKeyLen + AES_IVLEN - .HashLen), .HashLen, 0)
355 If hResult < 0 Then
356 GoTo QH
357 End If
358 End With
359 '--- success
360 pvCryptoAesCtrInit = True
361 Exit Function
362QH:
363 uCtx.LastError = GetSystemMessage(hResult)
364 Exit Function
365EH_Unsupported:
366 uCtx.LastError = ERR_UNSUPPORTED_ENCR
367End Function
368
369Private Sub pvCryptoAesCtrTerminate(uCtx As UcsCryptoContextType)
370 With uCtx
371 If .hPbkdf2Alg <> 0 Then
372 Call BCryptCloseAlgorithmProvider(.hPbkdf2Alg, 0)
373 .hPbkdf2Alg = 0
374 End If
375 If .hHmacHash <> 0 Then
376 Call BCryptDestroyHash(.hHmacHash)
377 .hHmacHash = 0
378 End If
379 If .hHmacAlg <> 0 Then
380 Call BCryptCloseAlgorithmProvider(.hHmacAlg, 0)
381 .hHmacAlg = 0
382 End If
383 If .hAesKey <> 0 Then
384 Call BCryptDestroyKey(.hAesKey)
385 .hAesKey = 0
386 End If
387 If .hAesAlg <> 0 Then
388 Call BCryptCloseAlgorithmProvider(.hAesAlg, 0)
389 .hAesAlg = 0
390 End If
391 End With
392End Sub
393
394Private Function pvCryptoAesCtrCrypt( _
395 uCtx As UcsCryptoContextType, _
396 baData() As Byte, _
397 Optional ByVal Offset As Long, _
398 Optional ByVal Size As Long = -1, _
399 Optional ByVal HashBefore As Boolean, _
400 Optional ByVal HashAfter As Boolean) As Boolean
401 Dim lIdx As Long
402 Dim lJdx As Long
403 Dim lPadSize As Long
404 Dim hResult As Long
405
406 With uCtx
407 If Size < 0 Then
408 Size = pvArraySize(baData) - Offset
409 End If
410 If HashBefore Then
411 hResult = BCryptHashData(.hHmacHash, ByVal pvArrayPtr(baData, Offset), Size, 0)
412 If hResult < 0 Then
413 GoTo QH
414 End If
415 End If
416 '--- reuse .EncrData from prev call until next AES_BLOCK_SIZE boundary
417 For lIdx = Offset To Offset + Size - 1
418 If (.EncrPos And (AES_BLOCK_SIZE - 1)) = 0 Then
419 Exit For
420 End If
421 baData(lIdx) = baData(lIdx) Xor .EncrData(.EncrPos)
422 .EncrPos = .EncrPos + 1
423 Next
424 If lIdx < Offset + Size Then
425 '--- pad remaining input size to AES_BLOCK_SIZE
426 lPadSize = (Offset + Size - lIdx + AES_BLOCK_SIZE - 1) And -AES_BLOCK_SIZE
427 If UBound(.EncrData) + 1 < lPadSize Then
428 ReDim .EncrData(0 To lPadSize - 1) As Byte
429 End If
430 '--- encrypt incremental Nonce in .EncrData
431 For lJdx = 0 To lPadSize - 1 Step AES_BLOCK_SIZE
432 Call CopyMemory(.EncrData(lJdx), .Nonce(0), AES_BLOCK_SIZE)
433 If pvInc(.Nonce(3)) Then
434 If pvInc(.Nonce(2)) Then
435 If pvInc(.Nonce(1)) Then
436 If pvInc(.Nonce(0)) Then
437 '--- do nothing
438 End If
439 End If
440 End If
441 End If
442 Next
443 hResult = BCryptEncrypt(.hAesKey, .EncrData(0), lPadSize, 0, 0, 0, .EncrData(0), lPadSize, lJdx, 0)
444 If hResult < 0 Then
445 GoTo QH
446 End If
447 '--- XOR remaining input and leave anything extra in .EncrData for reuse
448 For .EncrPos = 0 To Offset + Size - lIdx - 1
449 baData(lIdx) = baData(lIdx) Xor .EncrData(.EncrPos)
450 lIdx = lIdx + 1
451 Next
452 End If
453 If HashAfter Then
454 hResult = BCryptHashData(.hHmacHash, ByVal pvArrayPtr(baData, Offset), Size, 0)
455 If hResult < 0 Then
456 GoTo QH
457 End If
458 End If
459 End With
460 '--- success
461 pvCryptoAesCtrCrypt = True
462 Exit Function
463QH:
464 uCtx.LastError = GetSystemMessage(hResult)
465End Function
466
467Private Function pvCryptoGetFinalHash(uCtx As UcsCryptoContextType, ByVal lSize As Long) As Byte()
468 Dim baResult() As Byte
469
470 ReDim baResult(0 To uCtx.HashLen - 1) As Byte
471 Call BCryptFinishHash(uCtx.hHmacHash, baResult(0), uCtx.HashLen, 0)
472 ReDim Preserve baResult(0 To lSize - 1) As Byte
473 pvCryptoGetFinalHash = baResult
474End Function
475
476Private Function pvInc(lValue As Long) As Boolean
477 lValue = htonl(lValue)
478 If lValue = -1 Then
479 lValue = 0
480 '--- signal carry
481 pvInc = True
482 Else
483 lValue = (lValue Xor &H80000000) + 1 Xor &H80000000
484 lValue = htonl(lValue)
485 End If
486End Function
487
488Private Property Get pvArrayPtr(baArray() As Byte, Optional ByVal Index As Long) As LongPtr
489 Dim lPtr As LongPtr
490
491 '--- peek long at ArrPtr(baArray)
492 Call CopyMemory(lPtr, ByVal ArrPtr(baArray), PTR_SIZE)
493 If lPtr <> 0 Then
494 If 0 <= Index And Index <= UBound(baArray) - LBound(baArray) Then
495 pvArrayPtr = VarPtr(baArray(LBound(baArray) + Index))
496 End If
497 End If
498End Property
499
500Private Property Get pvArraySize(baArray() As Byte) As Long
501 Dim lPtr As LongPtr
502
503 '--- peek long at ArrPtr(baArray)
504 Call CopyMemory(lPtr, ByVal ArrPtr(baArray), PTR_SIZE)
505 If lPtr <> 0 Then
506 pvArraySize = UBound(baArray) + 1 - LBound(baArray)
507 End If
508End Property
509
510'= shared ================================================================
511
512#If Not ImplUseShared Then
513Public Function ToBase64Array(baData() As Byte) As String
514 Const CRYPT_STRING_BASE64 As Long = 1
515 Dim lSize As Long
516
517 If UBound(baData) >= 0 Then
518 ToBase64Array = String$(2 * UBound(baData) + 6, 0)
519 lSize = Len(ToBase64Array) + 1
520 Call CryptBinaryToString(VarPtr(baData(0)), UBound(baData) + 1, CRYPT_STRING_BASE64, StrPtr(ToBase64Array), lSize)
521 ToBase64Array = Left$(ToBase64Array, lSize)
522 End If
523End Function
524
525Public Function FromBase64Array(sText As String) As Byte()
526 Const CRYPT_STRING_BASE64 As Long = 1
527 Dim lSize As Long
528 Dim baOutput() As Byte
529
530 lSize = Len(sText) + 1
531 ReDim baOutput(0 To lSize - 1) As Byte
532 Call CryptStringToBinary(StrPtr(sText), Len(sText), CRYPT_STRING_BASE64, VarPtr(baOutput(0)), lSize, 0, 0)
533 If lSize > 0 Then
534 ReDim Preserve baOutput(0 To lSize - 1) As Byte
535 FromBase64Array = baOutput
536 Else
537 FromBase64Array = vbNullString
538 End If
539End Function
540
541Public Function ToUtf8Array(sText As String) As Byte()
542 Const CP_UTF8 As Long = 65001
543 Dim baRetVal() As Byte
544 Dim lSize As Long
545
546 lSize = WideCharToMultiByte(CP_UTF8, 0, StrPtr(sText), Len(sText), ByVal 0, 0, 0, 0)
547 If lSize > 0 Then
548 ReDim baRetVal(0 To lSize - 1) As Byte
549 Call WideCharToMultiByte(CP_UTF8, 0, StrPtr(sText), Len(sText), baRetVal(0), lSize, 0, 0)
550 Else
551 baRetVal = vbNullString
552 End If
553 ToUtf8Array = baRetVal
554End Function
555
556Public Function FromUtf8Array(baText() As Byte) As String
557 Const CP_UTF8 As Long = 65001
558 Dim lSize As Long
559
560 If UBound(baText) >= 0 Then
561 FromUtf8Array = String$(2 * (UBound(baText) + 1), 0)
562 lSize = MultiByteToWideChar(CP_UTF8, 0, baText(0), UBound(baText) + 1, StrPtr(FromUtf8Array), Len(FromUtf8Array))
563 FromUtf8Array = Left$(FromUtf8Array, lSize)
564 End If
565End Function
566
567Public Function GetSystemMessage(ByVal lLastDllError As Long) As String
568 Const FORMAT_MESSAGE_FROM_SYSTEM As Long = &H1000
569 Const FORMAT_MESSAGE_IGNORE_INSERTS As Long = &H200
570 Dim lSize As Long
571
572 GetSystemMessage = Space$(2000)
573 lSize = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, 0, lLastDllError, 0, GetSystemMessage, Len(GetSystemMessage), 0)
574 If lSize > 2 Then
575 If Mid$(GetSystemMessage, lSize - 1, 2) = vbCrLf Then
576 lSize = lSize - 2
577 End If
578 End If
579 GetSystemMessage = Left$(GetSystemMessage, lSize) & " &H" & Hex(lLastDllError)
580End Function
581
582Private Function PeekPtr(ByVal lPtr As LongPtr) As LongPtr
583 Call CopyMemory(PeekPtr, ByVal lPtr, PTR_SIZE)
584End Function
585#End If