PLAP: support for webauth

Instead of opening the browser window in PLAP context (which is insecure),
display a dialog with QR code and ask user to scan it on mobile device.

QR code generation is taken from MIT-licensed library:

  https://github.com/nayuki/QR-Code-generator/blob/master/c/qrcodegen.c

Before I tried vcpkg libqrencode package but gave up after
10 mins of dependencies building (it requires msys2 etc).

Note that you need to add

  setenv IV_SSO webauth

to PLAP profile to make it work.

Fixes: https://github.com/OpenVPN/openvpn-gui/issues/687

Signed-off-by: Lev Stipakov <lev@openvpn.net>
pull/738/head
Lev Stipakov 2025-03-31 16:11:13 +03:00 committed by Selva Nair
parent 2242c1cca4
commit d8249daf8f
16 changed files with 2005 additions and 16 deletions

1
.gitignore vendored
View File

@ -39,3 +39,4 @@ plap/credentialprovider.h
x64
Debug
out
build

View File

@ -25,7 +25,10 @@ add_executable(${PROJECT_NAME} WIN32
as.c
pkcs11.c
config_parser.c
res/openvpn-gui-res.rc)
qr.c
qrcodegen/qrcodegen.c
res/openvpn-gui-res.rc
)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} /guard:cf /Qspectre")
@ -117,6 +120,8 @@ add_library(${PROJECT_NAME_PLAP} SHARED
registry.c
config_parser.c
service.c
qr.c
qrcodegen/qrcodegen.c
plap/ui_glue.c
plap/stub.c
plap/plap_common.c

View File

@ -110,6 +110,8 @@ openvpn_gui_SOURCES = \
as.c as.h \
pkcs11.c pkcs11.h \
config_parser.c config_parser.h \
qr.c qr.h \
qrcodegen/qrcodegen.c qrcodegen/qrcodegen.h
openvpn-gui-res.h
openvpn_gui_LDFLAGS = -mwindows

View File

@ -158,6 +158,13 @@ screen are setup in the `config-auto` folder, `OpenVPNService`
is running, and these connections are visible and controllable
from the GUI.
In addition to that, PLAP profiles must contain::
setenv IV_SSO webauth
to indicate that client supports web-based (as QR code for PLAP)
authentication method.
Once those pre-requisites are satisfied, the login screen will
display an icon for `Pre-Logon Access Providers`, clicking which
will bring up a list of OpenVPN connection profiles available, and

15
misc.c
View File

@ -544,6 +544,21 @@ CheckFileAccess(const TCHAR *path, int access)
return ret;
}
char *
WCharToUTF8(const WCHAR *wstr)
{
int utf8_len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
if (utf8_len == 0)
return NULL;
char *utf8_str = (char *)malloc(utf8_len);
if (!utf8_str)
return NULL;
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8_str, utf8_len, NULL, NULL);
return utf8_str;
}
/**
* Convert a NUL terminated narrow string to wide string using
* specified codepage. The caller must free

6
misc.h
View File

@ -58,6 +58,12 @@ WCHAR *Widen(const char *utf8);
WCHAR *WidenEx(UINT codepage, const char *utf8);
/**
* Convert a wide string to a UTF-8 string. The caller must
* free the returned pointer. Return NULL on error.
*/
char *WCharToUTF8(const WCHAR *wstr);
BOOL validate_input(const WCHAR *input, const WCHAR *exclude);
/* Concatenate two wide strings with a separator */

View File

@ -166,6 +166,11 @@
#define ID_LVW_PKCS11 451
#define ID_TXT_PKCS11 452
/* QR code dialog */
#define ID_DLG_QR 500
#define ID_STATIC_QR 501
#define ID_TXT_QR 502
/* General settings contd.. */
#define ID_CHK_CONCAT_OTP 470

View File

@ -62,6 +62,7 @@
#include "echo.h"
#include "pkcs11.h"
#include "service.h"
#include "qr.h"
#define OPENVPN_SERVICE_PIPE_NAME_OVPN2 L"\\\\.\\pipe\\openvpn\\service"
#define OPENVPN_SERVICE_PIPE_NAME_OVPN3 L"\\\\.\\pipe\\ovpnagent"
@ -447,6 +448,14 @@ OnStateChange(connection_t *c, char *data)
SetDlgItemTextW(c->hwndStatus, ID_TXT_IP, L"");
SetStatusWinIcon(c->hwndStatus, ID_ICO_CONNECTING);
}
/* close QR dialog, if any */
CloseQRDialog();
}
else if (strcmp(state, "ASSIGN_IP") == 0)
{
/* first state after completion of web-based auth, now it is safe to close QR dialog */
CloseQRDialog();
}
else
{
@ -1755,9 +1764,16 @@ OnInfoMsg(connection_t *c, char *msg)
if (url)
{
if (!open_url(url))
if (o.use_qr_for_url)
{
WriteStatusLog(c, L"GUI> ", L"Error: failed to open url from info msg", false);
OpenQRDialog(url, c->config_name);
}
else
{
if (!open_url(url))
{
WriteStatusLog(c, L"GUI> ", L"Error: failed to open url from info msg", false);
}
}
free(url);
}

View File

@ -259,6 +259,8 @@ typedef struct
TCHAR *action_arg;
HANDLE session_semaphore;
HANDLE event_log;
DWORD use_qr_for_url; /* display QR code instead of opening a web browser with a URL, used by
PLAP */
} options_t;
void InitOptions(options_t *);

View File

@ -102,6 +102,10 @@ libopenvpn_plap_la_SOURCES = \
$(top_srcdir)/config_parser.c \
$(top_srcdir)/pkcs11.c \
$(top_srcdir)/service.c \
$(top_srcdir)/qr.c \
$(top_srcdir)/qr.h \
$(top_srcdir)/qrcodegen/qrcodegen.c \
$(top_srcdir)/qrcodegen/qrcodegen.h \
openvpn-plap-res.rc
libopenvpn_plap_la_LIBADD = \

View File

@ -66,23 +66,16 @@ OnStop_(connection_t *c, UNUSED char *msg)
SendMessage(c->hwndStatus, WM_CLOSE, 0, 0);
}
/* Override OnInfoMsg: We filter out anything other
* than CR_TEXT: In particular, OPEN_URL is not supported
* in PLAP context.
*/
static void
OnInfoMsg_(connection_t *c, char *msg)
{
if (strbegins(msg, "CR_TEXT:"))
if (c == active_profile)
{
if (c == active_profile)
{
OnInfoMsg(c, msg);
}
else
{
DetachOpenVPN(c); /* next attach will handle it */
}
OnInfoMsg(c, msg);
}
else
{
DetachOpenVPN(c); /* next attach will handle it */
}
}
@ -237,6 +230,9 @@ InitializeUI(HINSTANCE hinstance)
*/
o.service_state = service_connected;
/* PLAP always uses QR */
o.use_qr_for_url = TRUE;
o.hInstance = hinstance;
DWORD status = WSAStartup(MAKEWORD(2, 2), &wsaData);

207
qr.c Normal file
View File

@ -0,0 +1,207 @@
/*
* OpenVPN-GUI -- A Windows GUI for OpenVPN.
*
* Copyright (C) 2025 Lev Stipakov <lstipakov@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <windows.h>
#include "qrcodegen/qrcodegen.h"
#include <stdlib.h>
#include "main.h"
#include "qr.h"
#include "misc.h"
#include "openvpn-gui-res.h"
struct url_and_config_name
{
const wchar_t *url;
const wchar_t *config_name;
};
typedef struct url_and_config_name url_and_config_name_t;
/* global handle to be able to close the dialog from outside */
static HWND g_hwndQR = NULL;
/* Generate QR bitmap from wide string */
HBITMAP
CreateQRBitmapFromWchar(const wchar_t *wtext, int scale)
{
char *utf8_text = WCharToUTF8(wtext);
if (!utf8_text)
return NULL;
uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX];
uint8_t temp[qrcodegen_BUFFER_LEN_MAX];
bool ok = qrcodegen_encodeText(utf8_text,
temp,
qrcode,
qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN,
qrcodegen_VERSION_MAX,
qrcodegen_Mask_AUTO,
true);
free(utf8_text);
if (!ok)
return NULL;
int size = qrcodegen_getSize(qrcode);
int imgSize = size * scale;
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, imgSize, imgSize);
SelectObject(hdcMem, hBitmap);
RECT rc = { 0, 0, imgSize, imgSize };
FillRect(hdcMem, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
HBRUSH blackBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
for (int y = 0; y < size; ++y)
{
for (int x = 0; x < size; ++x)
{
if (qrcodegen_getModule(qrcode, x, y))
{
RECT pixelRect = { x * scale, y * scale, (x + 1) * scale, (y + 1) * scale };
FillRect(hdcMem, &pixelRect, blackBrush);
}
}
}
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
return hBitmap;
}
INT_PTR CALLBACK
QrDialogProc(HWND hwnd, UINT msg, UNUSED WPARAM wp, LPARAM lp)
{
static HBITMAP hQRBitmap = NULL;
switch (msg)
{
case WM_INITDIALOG:
{
url_and_config_name_t *uc = (url_and_config_name_t *)lp;
const int padding = 15;
const int qrMaxSize = 300; /* max QR bitmap size in pixels */
/* Generate QR initially at largest acceptable size */
int scale = 6;
HBITMAP hQRBitmap = CreateQRBitmapFromWchar(uc->url, scale);
if (!hQRBitmap)
{
MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Failed to generate QR");
EndDialog(hwnd, 0);
return TRUE;
}
BITMAP bm;
GetObject(hQRBitmap, sizeof(BITMAP), &bm);
/* Adjust scale down if bitmap too big */
while ((bm.bmWidth > qrMaxSize) && scale > 1)
{
DeleteObject(hQRBitmap);
scale--;
hQRBitmap = CreateQRBitmapFromWchar(uc->url, scale);
if (!hQRBitmap)
{
MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Failed to generate QR");
EndDialog(hwnd, 0);
return TRUE;
}
GetObject(hQRBitmap, sizeof(BITMAP), &bm);
}
int qrWidth = bm.bmWidth;
int qrHeight = bm.bmHeight;
HWND hwndStaticQR = GetDlgItem(hwnd, ID_STATIC_QR);
SetWindowPos(hwndStaticQR, NULL, padding, padding, qrWidth, qrHeight, SWP_NOZORDER);
SendMessage(hwndStaticQR, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hQRBitmap);
/* Position text control below QR bitmap */
HWND hwndStaticText = GetDlgItem(hwnd, ID_TXT_QR);
int textWidth = qrWidth;
int textHeight = 50;
SetWindowPos(hwndStaticText,
NULL,
padding,
qrHeight + padding + 10,
textWidth,
textHeight,
SWP_NOZORDER);
/* Calculate total dialog size */
int dialogWidth = qrWidth + padding * 2;
int dialogHeight = qrHeight + textHeight + padding * 3;
RECT rcDialog = { 0, 0, dialogWidth, dialogHeight };
AdjustWindowRectEx(
&rcDialog, GetWindowLong(hwnd, GWL_STYLE), FALSE, GetWindowLong(hwnd, GWL_EXSTYLE));
int dlgWidth = rcDialog.right - rcDialog.left;
int dlgHeight = rcDialog.bottom - rcDialog.top;
/* Center the dialog on the screen */
int posX = (GetSystemMetrics(SM_CXSCREEN) - dlgWidth) / 2;
int posY = (GetSystemMetrics(SM_CYSCREEN) - dlgHeight) / 2;
SetWindowPos(hwnd, NULL, posX, posY, dlgWidth, dlgHeight, SWP_NOZORDER);
SetWindowText(hwnd, uc->config_name);
g_hwndQR = hwnd;
}
return TRUE;
case WM_CLOSE:
EndDialog(hwnd, 0);
return TRUE;
case WM_DESTROY:
if (hQRBitmap)
DeleteObject(hQRBitmap);
g_hwndQR = NULL;
return TRUE;
}
return FALSE;
}
void
CloseQRDialog()
{
if (g_hwndQR && IsWindow(g_hwndQR))
PostMessage(g_hwndQR, WM_CLOSE, 0, 0);
}
extern options_t o;
void
OpenQRDialog(const wchar_t *url, const wchar_t *config_name)
{
url_and_config_name_t uc;
uc.url = url;
uc.config_name = config_name;
DialogBoxParam(o.hInstance, MAKEINTRESOURCE(ID_DLG_QR), o.hWnd, QrDialogProc, (LPARAM)&uc);
}

24
qr.h Normal file
View File

@ -0,0 +1,24 @@
/*
* OpenVPN-GUI -- A Windows GUI for OpenVPN.
*
* Copyright (C) 2025 Lev Stipakov <lstipakov@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
void CloseQRDialog();
void OpenQRDialog(const wchar_t *url, const wchar_t *config_name);

1270
qrcodegen/qrcodegen.c Normal file

File diff suppressed because it is too large Load Diff

421
qrcodegen/qrcodegen.h Normal file
View File

@ -0,0 +1,421 @@
/*
* QR Code generator library (C)
*
* Copyright (c) Project Nayuki. (MIT License)
* https://www.nayuki.io/page/qr-code-generator-library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/*
* This library creates QR Code symbols, which is a type of two-dimension barcode.
* Invented by Denso Wave and described in the ISO/IEC 18004 standard.
* A QR Code structure is an immutable square grid of dark and light cells.
* The library provides functions to create a QR Code from text or binary data.
* The library covers the QR Code Model 2 specification, supporting all versions (sizes)
* from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
*
* Ways to create a QR Code object:
* - High level: Take the payload data and call qrcodegen_encodeText() or
* qrcodegen_encodeBinary().
* - Low level: Custom-make the list of segments and call
* qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced().
* (Note that all ways require supplying the desired error correction level and various byte
* buffers.)
*/
/*---- Enum and struct types----*/
/*
* The error correction level in a QR Code symbol.
*/
enum qrcodegen_Ecc
{
// Must be declared in ascending order of error protection
// so that an internal qrcodegen function works properly
qrcodegen_Ecc_LOW = 0, // The QR Code can tolerate about 7% erroneous codewords
qrcodegen_Ecc_MEDIUM, // The QR Code can tolerate about 15% erroneous codewords
qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords
qrcodegen_Ecc_HIGH, // The QR Code can tolerate about 30% erroneous codewords
};
/*
* The mask pattern used in a QR Code symbol.
*/
enum qrcodegen_Mask
{
// A special value to tell the QR Code encoder to
// automatically select an appropriate mask pattern
qrcodegen_Mask_AUTO = -1,
// The eight actual mask patterns
qrcodegen_Mask_0 = 0,
qrcodegen_Mask_1,
qrcodegen_Mask_2,
qrcodegen_Mask_3,
qrcodegen_Mask_4,
qrcodegen_Mask_5,
qrcodegen_Mask_6,
qrcodegen_Mask_7,
};
/*
* Describes how a segment's data bits are interpreted.
*/
enum qrcodegen_Mode
{
qrcodegen_Mode_NUMERIC = 0x1,
qrcodegen_Mode_ALPHANUMERIC = 0x2,
qrcodegen_Mode_BYTE = 0x4,
qrcodegen_Mode_KANJI = 0x8,
qrcodegen_Mode_ECI = 0x7,
};
/*
* A segment of character/binary/control data in a QR Code symbol.
* The mid-level way to create a segment is to take the payload data
* and call a factory function such as qrcodegen_makeNumeric().
* The low-level way to create a segment is to custom-make the bit buffer
* and initialize a qrcodegen_Segment struct with appropriate values.
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
* Moreover, the maximum allowed bit length is 32767 because
* the largest QR Code (version 40) has 31329 modules.
*/
struct qrcodegen_Segment
{
// The mode indicator of this segment.
enum qrcodegen_Mode mode;
// The length of this segment's unencoded data. Measured in characters for
// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
// Always zero or positive. Not the same as the data's bit length.
int numChars;
// The data bits of this segment, packed in bitwise big endian.
// Can be null if the bit length is zero.
uint8_t *data;
// The number of valid data bits used in the buffer. Requires
// 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
// The character count (numChars) must agree with the mode and the bit buffer length.
int bitLength;
};
/*---- Macro constants and functions ----*/
#define qrcodegen_VERSION_MIN \
1 // The minimum version number supported in the QR Code Model 2 standard
#define qrcodegen_VERSION_MAX \
40 // The maximum version number supported in the QR Code Model 2 standard
// Calculates the number of bytes needed to store any QR Code up to and including the given version
// number, as a compile-time constant. For example, 'uint8_t
// buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' can store any single QR Code from version 1 to 25
// (inclusive). The result fits in an int (or int16). Requires qrcodegen_VERSION_MIN <= n <=
// qrcodegen_VERSION_MAX.
#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1)
// The worst-case number of bytes needed to store one QR Code, up to and including
// version 40. This value equals 3918, which is just under 4 kilobytes.
// Use this more convenient value to avoid calculating tighter memory bounds for buffers.
#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)
/*---- Functions (high level) to generate QR Codes ----*/
/*
* Encodes the given text string to a QR Code, returning true if successful.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
*
* The input text must be encoded in UTF-8 and contain no NULs.
* Requires 1 <= minVersion <= maxVersion <= 40.
*
* The smallest possible QR Code version within the given range is automatically
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
* may be higher than the ecl argument if it can be done without increasing the
* version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
* qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
*
* About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion):
* - Before calling the function:
* - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
* reading and writing; hence each array must have a length of at least len.
* - The two ranges must not overlap (aliasing).
* - The initial state of both ranges can be uninitialized
* because the function always writes before reading.
* - After the function returns:
* - Both ranges have no guarantee on which elements are initialized and what values are
* stored.
* - tempBuffer contains no useful data and should be treated as entirely uninitialized.
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
*
* If successful, the resulting QR Code may use numeric,
* alphanumeric, or byte mode to encode the text.
*
* In the most optimistic case, a QR Code at version 40 with low ECC
* can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string
* up to 4296 characters, or any digit string up to 7089 characters.
* These numbers represent the hard upper limit of the QR Code standard.
*
* Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeText(const char *text,
uint8_t tempBuffer[],
uint8_t qrcode[],
enum qrcodegen_Ecc ecl,
int minVersion,
int maxVersion,
enum qrcodegen_Mask mask,
bool boostEcl);
/*
* Encodes the given binary data to a QR Code, returning true if successful.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
*
* Requires 1 <= minVersion <= maxVersion <= 40.
*
* The smallest possible QR Code version within the given range is automatically
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
* may be higher than the ecl argument if it can be done without increasing the
* version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
* qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
*
* About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion):
* - Before calling the function:
* - The array ranges dataAndTemp[0 : len] and qrcode[0 : len] must allow
* reading and writing; hence each array must have a length of at least len.
* - The two ranges must not overlap (aliasing).
* - The input array range dataAndTemp[0 : dataLen] should normally be
* valid UTF-8 text, but is not required by the QR Code standard.
* - The initial state of dataAndTemp[dataLen : len] and qrcode[0 : len]
* can be uninitialized because the function always writes before reading.
* - After the function returns:
* - Both ranges have no guarantee on which elements are initialized and what values are
* stored.
* - dataAndTemp contains no useful data and should be treated as entirely uninitialized.
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
*
* If successful, the resulting QR Code will use byte mode to encode the data.
*
* In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte
* sequence up to length 2953. This is the hard upper limit of the QR Code standard.
*
* Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeBinary(uint8_t dataAndTemp[],
size_t dataLen,
uint8_t qrcode[],
enum qrcodegen_Ecc ecl,
int minVersion,
int maxVersion,
enum qrcodegen_Mask mask,
bool boostEcl);
/*---- Functions (low level) to generate QR Codes ----*/
/*
* Encodes the given segments to a QR Code, returning true if successful.
* If the data is too long to fit in any version at the given ECC level,
* then false is returned.
*
* The smallest possible QR Code version is automatically chosen for
* the output. The ECC level of the result may be higher than the
* ecl argument if it can be done without increasing the version.
*
* About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX):
* - Before calling the function:
* - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
* reading and writing; hence each array must have a length of at least len.
* - The two ranges must not overlap (aliasing).
* - The initial state of both ranges can be uninitialized
* because the function always writes before reading.
* - The input array segs can contain segments whose data buffers overlap with tempBuffer.
* - After the function returns:
* - Both ranges have no guarantee on which elements are initialized and what values are
* stored.
* - tempBuffer contains no useful data and should be treated as entirely uninitialized.
* - Any segment whose data buffer overlaps with tempBuffer[0 : len]
* must be treated as having invalid values in that array.
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
*
* Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and
* qrcodegen_encodeBinary().
*/
bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[],
size_t len,
enum qrcodegen_Ecc ecl,
uint8_t tempBuffer[],
uint8_t qrcode[]);
/*
* Encodes the given segments to a QR Code, returning true if successful.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
*
* Requires 1 <= minVersion <= maxVersion <= 40.
*
* The smallest possible QR Code version within the given range is automatically
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
* may be higher than the ecl argument if it can be done without increasing the
* version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or
* qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow).
*
* About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX):
* - Before calling the function:
* - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow
* reading and writing; hence each array must have a length of at least len.
* - The two ranges must not overlap (aliasing).
* - The initial state of both ranges can be uninitialized
* because the function always writes before reading.
* - The input array segs can contain segments whose data buffers overlap with tempBuffer.
* - After the function returns:
* - Both ranges have no guarantee on which elements are initialized and what values are
* stored.
* - tempBuffer contains no useful data and should be treated as entirely uninitialized.
* - Any segment whose data buffer overlaps with tempBuffer[0 : len]
* must be treated as having invalid values in that array.
* - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule().
*
* Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and
* qrcodegen_encodeBinary().
*/
bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[],
size_t len,
enum qrcodegen_Ecc ecl,
int minVersion,
int maxVersion,
enum qrcodegen_Mask mask,
bool boostEcl,
uint8_t tempBuffer[],
uint8_t qrcode[]);
/*
* Tests whether the given string can be encoded as a segment in numeric mode.
* A string is encodable iff each character is in the range 0 to 9.
*/
bool qrcodegen_isNumeric(const char *text);
/*
* Tests whether the given string can be encoded as a segment in alphanumeric mode.
* A string is encodable iff each character is in the following set: 0 to 9, A to Z
* (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
bool qrcodegen_isAlphanumeric(const char *text);
/*
* Returns the number of bytes (uint8_t) needed for the data buffer of a segment
* containing the given number of characters using the given mode. Notes:
* - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or the internal
* calculation of the number of needed bits exceeds INT16_MAX (i.e. 32767).
* - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096.
* - It is okay for the user to allocate more bytes for the buffer than needed.
* - For byte mode, numChars measures the number of bytes, not Unicode code points.
* - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned.
* An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
*/
size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars);
/*
* Returns a segment representing the given binary data encoded in
* byte mode. All input byte arrays are acceptable. Any text string
* can be converted to UTF-8 bytes and encoded as a byte mode segment.
*/
struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]);
/*
* Returns a segment representing the given string of decimal digits encoded in numeric mode.
*/
struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]);
/*
* Returns a segment representing the given text string encoded in alphanumeric mode.
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]);
/*
* Returns a segment representing an Extended Channel Interpretation
* (ECI) designator with the given assignment value.
*/
struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]);
/*---- Functions to extract raw data from QR Codes ----*/
/*
* Returns the side length of the given QR Code, assuming that encoding succeeded.
* The result is in the range [21, 177]. Note that the length of the array buffer
* is related to the side length - every 'uint8_t qrcode[]' must have length at least
* qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1).
*/
int qrcodegen_getSize(const uint8_t qrcode[]);
/*
* Returns the color of the module (pixel) at the given coordinates, which is false
* for light or true for dark. The top left corner has the coordinates (x=0, y=0).
* If the given coordinates are out of bounds, then false (light) is returned.
*/
bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y);
#ifdef __cplusplus
}
#endif

View File

@ -319,6 +319,14 @@ BEGIN
LTEXT "", ID_TXT_WARNING, 6, 222, 190, 10
END
ID_DLG_QR DIALOGEX 0, 0, 10, 10
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "Segoe UI"
BEGIN
CONTROL "", ID_STATIC_QR, "Static", SS_BITMAP | WS_VISIBLE | SS_CENTERIMAGE, 0, 0, 10, 10
LTEXT "Scan this QR code on your mobile to proceed with authentication.", ID_TXT_QR, 0, 0, 10, 10, SS_CENTER | WS_VISIBLE | SS_EDITCONTROL
END
STRINGTABLE
LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
BEGIN