Carries out the GCM authenticated encryption operation using the key
set up by an earlier call to GCM_InitKey
.
Public Declare Function GCM_NextEncrypt Lib "diCryptoSys.dll" (ByVal hContext As Long, ByRef lpOutput As Byte, ByVal nOutLen As Long,
ByRef lpTagOut As Byte, ByVal nTagLen As Long, ByRef lpData As Byte, ByVal nDataLen As Long,
ByRef lpIV As Byte, ByVal nIvLen As Long, ByRef lpAAD As Byte, ByVal nAadLen As Long) As Long
nRet = GCM_NextEncrypt(hContext, lpOutput(0), nOutLen, abTagOut(0), nTagLen, abData(0), nDataLen,
abIV(0), nIvLen, abAAD(0), nAadLen)
long __stdcall GCM_NextEncrypt(long hContext, unsigned char *lpOutput, long nOutLen, unsigned char *lpTagOut, long nTagLen, const unsigned char *lpData, long nDataLen, const unsigned char *lpIV, long nIvLen, const unsigned char *lpAAD, long nAadLen);
If successful, the return value is 0; otherwise it returns a non-zero error code.
See the remarks for GCM_Encrypt
and the
Security considerations for AEAD encryption.
This example uses AES-GCM to create some demo authenticated payloads for IPsec.
Public Sub Test_GCM_IPsec() ' Refs: RFC4106, RFC4303, RFC3602 Dim abKey() As Byte Dim abSPI() As Byte Dim abSalt() As Byte Dim abIV() As Byte Dim abNonce() As Byte Dim nKeyLen As Long Dim nDataLen As Long Dim nNonceLen As Long Dim nAadLen As Long Dim nTagLen As Long Dim nRet As Long Dim hContext As Long Dim abTail() As Byte Dim nSeq As Long Dim abPlain() As Byte Dim abAAD() As Byte Dim abTag() As Byte Dim abCipher() As Byte Dim abPayload() As Byte Dim abCheck() As Byte Dim strCheck As String Dim asBlocks As Variant Dim nBlocks As Long ' Data we will send = text in blocks of 44 bytes ' (We use an array of strings here for our demo ' -- in practice, deal with input and its length as it comes) asBlocks = Array( _ "From fairest creatures we desire increase, ", _ "That thereby beauty's rose might never die, ", _ "But as the riper should by time decease, ", _ "His tender heir might bear his memory: " _ ) nBlocks = UBound(asBlocks) + 1 ' Test values -- in practice, these should be generated uniquely abKey = cnvBytesFromHexStr("90d382b410eeba7ad938c46cec1a82bf") abSalt = cnvBytesFromHexStr("e96e8c08") abSPI = cnvBytesFromHexStr("00004321") ' Constant lengths nKeyLen = 16 nNonceLen = 12 nAadLen = 8 nTagLen = 8 ReDim abTag(nTagLen - 1) ' IP padding + pad length + next header ' -- in this case always the same abTail = cnvBytesFromHexStr("01020201") ' Initialize AES-GCM key hContext = GCM_InitKey(abKey(0), nKeyLen, 0) If hContext = 0 Then Debug.Print "ERROR: GCM_InitKey failed" Exit Sub End If Debug.Print "KEY=" & cnvHexStrFromBytes(abKey) ' Process each block of data For nSeq = 1 To nBlocks Debug.Print "Seq #=" & nSeq ' 8-byte IV to ESP = 4-byte SPI || 4-byte SeqNum abIV = BytesConcat(abSPI, BytesFromWord32(nSeq)) Debug.Print "IV =" & cnvHexStrFromBytes(abIV) ' 12-byte nonce to AES-GCM = 4-byte salt || 8-byte IV abNonce = BytesConcat(abSalt, abIV) Debug.Print "NON=" & cnvHexStrFromBytes(abNonce) ' Create AAD = 4-byte SPI || 4-byte SeqNum abAAD = BytesConcat(abSPI, BytesFromWord32(nSeq)) Debug.Print "AAD=" & cnvHexStrFromBytes(abAAD) ' Create pre-encryption data = text block converted to byte array || tail abPlain = StrConv(asBlocks(nSeq - 1), vbFromUnicode) ' in this case, the padding is always the same... abPlain = BytesConcat(abPlain, abTail) Debug.Print "Pre-encryption Data (" & UBound(abPlain) + 1 & " bytes):" & vbCrLf & cnvHexStrFromBytes(abPlain) nDataLen = UBound(abPlain) + 1 ' Encrypt it ReDim abCipher(nDataLen - 1) nRet = GCM_NextEncrypt(hContext, abCipher(0), nDataLen, abTag(0), nTagLen, _ abPlain(0), nDataLen, abNonce(0), nNonceLen, abAAD(0), nAadLen) Debug.Print "GCM_NextEncrypt returns " & nRet & " (expected 0)" ' Display the parts... Debug.Print "IP DATAGRAM PAYLOAD:" Debug.Print "SPI/Seq #: " & cnvHexStrFromBytes(abSPI) & " " & cnvHexStrFromBytes(BytesFromWord32(nSeq)) Debug.Print "IV: " & cnvHexStrFromBytes(abIV) Debug.Print "Encrypted Data (" & UBound(abCipher) + 1 & " bytes):" & vbCrLf & cnvHexStrFromBytes(abCipher) Debug.Print "ICV: " & cnvHexStrFromBytes(abTag) ' Compose IP payload abPayload = BytesConcat(abSPI, BytesFromWord32(nSeq)) abPayload = BytesConcat(abPayload, abIV) abPayload = BytesConcat(abPayload, abCipher) abPayload = BytesConcat(abPayload, abTag) Debug.Print "Payload (" & UBound(abPayload) + 1 & " bytes):" & vbCrLf & cnvHexStrFromBytes(abPayload) ' To show this works, decrypt it as we go ' -- in practice, you'd need to recreate the nonce and the AAD ReDim abCheck(nDataLen - 1) nRet = GCM_NextDecrypt(hContext, abCheck(0), nDataLen, abCipher(0), nDataLen, _ abNonce(0), nNonceLen, abAAD(0), nAadLen, abTag(0), nTagLen) Debug.Print "GCM_NextDecrypt returns " & nRet & " (expected 0)" strCheck = StrConv(abCheck, vbUnicode) ' -- in practice, you'd need to strip the padding, but we fudge here... Debug.Print "PT: [" & Left(strCheck, 44) & "]" Next Call GCM_FinishKey(hContext) End Sub ' Some helper functions to deal with Byte arrays... Private Function BytesConcat(ab1() As Byte, ab2() As Byte) As Variant ' Return ab1 || ab2 Dim ab3() As Byte Dim i As Long ab3 = ab1 ReDim Preserve ab3(UBound(ab1) + UBound(ab2) + 1) For i = 0 To UBound(ab2) ab3(i + UBound(ab1) + 1) = ab2(i) Next BytesConcat = ab3 End Function Private Function BytesFromWord32(ByVal w As Long) As Variant ' Split 32-bit word w into 4-byte long Byte array (network order) Dim abArray() As Byte ReDim abArray(3) abArray(0) = CByte(((w And &HFF000000) \ &H1000000) And &HFF) abArray(1) = CByte(((w And &HFF0000) \ &H10000) And &HFF) abArray(2) = CByte(((w And &HFF00) \ &H100) And &HFF) abArray(3) = CByte((w And &HFF) And &HFF) BytesFromWord32 = abArray End Function