anastasis-gtk

Demonstrator GUI for Anastasis
Log | Files | Refs | README | LICENSE

commit c07ae7d8d5f39a94b3792c9267d8463746f79ba8
parent c03840e32b6ac72497d317c3646304b30a9e9793
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 26 Sep 2021 15:36:34 +0200

preliminary totp support for anastasis-gtk

Diffstat:
Acontrib/freeotp.png | 0
Acontrib/freeotp.svg | 381+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/anastasis/Makefile.am | 1+
Msrc/anastasis/anastasis-gtk_action.c | 197++++++++++++++++---------------------------------------------------------------
Asrc/anastasis/anastasis-gtk_handle-method-totp.c | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/anastasis/anastasis-gtk_helper.c | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/anastasis/anastasis-gtk_helper.h | 15+++++++++++++++
7 files changed, 805 insertions(+), 157 deletions(-)

diff --git a/contrib/freeotp.png b/contrib/freeotp.png Binary files differ. diff --git a/contrib/freeotp.svg b/contrib/freeotp.svg @@ -0,0 +1,381 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="512" + height="512" + viewBox="0 0 512 512" + id="svg2" + version="1.1" + inkscape:version="0.91 r" + sodipodi:docname="freeOTP.svg" + inkscape:export-filename="freeOTP.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <defs + id="defs4"> + <linearGradient + id="linearGradient4441" + inkscape:collect="always"> + <stop + id="stop4443" + offset="0" + style="stop-color:#000000;stop-opacity:1" /> + <stop + id="stop4445" + offset="1" + style="stop-color:#000000;stop-opacity:0" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4381"> + <stop + style="stop-color:#ffffff;stop-opacity:1" + offset="0" + id="stop4383" /> + <stop + style="stop-color:#d3d7cf;stop-opacity:1" + offset="1" + id="stop4385" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4381" + id="radialGradient7758" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,2.3853119,-2.3853119,0,2048.8338,853.53214)" + cx="1.0000017" + cy="860.36218" + fx="1.0000017" + fy="860.36218" + r="84" /> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath7754-9"> + <ellipse + ry="87.889091" + rx="87.889084" + cy="956.36218" + cx="96" + id="ellipse7756-2" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#radialGradient7758);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.95451474px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </clipPath> + <linearGradient + inkscape:collect="always" + id="linearGradient7905"> + <stop + style="stop-color:#6ceeff;stop-opacity:1" + offset="0" + id="stop7901" /> + <stop + style="stop-color:#00bcd4;stop-opacity:0.92913383" + offset="1" + id="stop7903" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient7905" + id="radialGradient7907-1" + cx="46.771385" + cy="908.61334" + fx="46.771385" + fy="908.61334" + r="87.889084" + gradientTransform="matrix(1.5985222,0,0,1.5985222,-36.104626,-1412.2984)" + gradientUnits="userSpaceOnUse" /> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath4261"> + <circle + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ba68c8;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.95451474px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="ellipse4263" + cx="96.133591" + cy="956.5036" + r="89.81665" /> + </clipPath> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4441" + id="radialGradient4482" + cx="3.2591954e-06" + cy="-17.959076" + fx="3.2591954e-06" + fy="-17.959076" + r="296.00043" + gradientTransform="matrix(-1.2231018,1.2231018,-1.7297272,-1.7297272,-31.0643,-31.064307)" + gradientUnits="userSpaceOnUse" /> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath4196"> + <ellipse + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#e91e63;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.56690216px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="ellipse4198" + cx="744.13251" + cy="-382.09381" + rx="232" + ry="232.00002" + transform="matrix(0.70710678,0.70710678,0.70710678,-0.70710678,0,0)" /> + </clipPath> + <filter + inkscape:collect="always" + style="color-interpolation-filters:sRGB" + id="filter4215" + x="-0.06" + width="1.12" + y="-0.06" + height="1.12"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="7.2" + id="feGaussianBlur4217" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="55.675026" + inkscape:cy="194.3525" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:showpageshadow="false" + showguides="false" + inkscape:guide-bbox="true" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true" + inkscape:bbox-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:window-width="1920" + inkscape:window-height="1024" + inkscape:window-x="0" + inkscape:window-y="30" + inkscape:window-maximized="1" + borderlayer="true" + inkscape:object-paths="false" + inkscape:snap-intersection-paths="false" + inkscape:object-nodes="true" + inkscape:snap-midpoints="true"> + <inkscape:grid + type="xygrid" + id="grid4136" + empspacing="4" /> + <sodipodi:guide + position="4,188" + orientation="1,0" + id="guide4138" + inkscape:locked="false" /> + <sodipodi:guide + position="4,188" + orientation="0,1" + id="guide4140" + inkscape:locked="false" /> + <sodipodi:guide + position="188,4" + orientation="0,1" + id="guide4142" + inkscape:locked="false" /> + <sodipodi:guide + position="188,4" + orientation="1,0" + id="guide4144" + inkscape:locked="false" /> + <sodipodi:guide + position="96,96" + orientation="1,0" + id="guide4176" + inkscape:locked="false" /> + <sodipodi:guide + position="96,96" + orientation="0,1" + id="guide4178" + inkscape:locked="false" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="icon" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-540.3622)" + style="display:inline"> + <ellipse + ry="232.00002" + rx="232" + cy="798.36218" + cx="256" + id="ellipse7832" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#455a64;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.56690216px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <ellipse + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#546e7a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.56690216px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="path4157" + cx="256" + cy="796.36218" + rx="232" + ry="232.00002" /> + <g + id="g4310" + style="opacity:1;stroke:#90a4ae;stroke-opacity:1"> + <path + d="m 256,956.36223 0,55.99987 m 0,-431.99991 0,56" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4562-3" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + id="path4203" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 95.999915,796.36214 -55.99987,0 m 431.999915,0 -56,0" /> + <path + d="m 142.86286,909.49929 -39.59789,39.59788 m 305.47007,-305.47006 -39.59798,39.59798" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4205" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + id="path4207" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 369.13715,909.49929 39.59788,39.59789 m -305.47005,-305.47007 39.59797,39.59798" /> + <path + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + id="path4209" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 348.00003,955.71087 15.99995,27.71272 M 148.00002,609.3007 l 16.00001,27.71282" /> + <path + d="m 415.34873,888.36217 27.71272,15.99995 M 68.938551,688.36217 l 27.712825,16" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4211" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + d="m 415.34872,704.36211 27.71273,-15.99995 M 68.938555,904.36213 96.651372,888.36211" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4215" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + id="path4217" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 348.00002,637.01342 15.99996,-27.71273 M 148.00003,983.4236 164.00002,955.71077" /> + <path + d="M 78.2696,748.73944 47.36006,740.45725 M 464.63994,852.26703 433.7303,843.98484" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4219" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + id="path4221" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 208.37731,974.09256 -8.2822,30.90954 m 111.80978,-417.27989 -8.28219,30.90964" /> + <path + d="m 303.62275,974.09254 8.28216,30.90956 m -111.80983,-417.27988 8.28224,30.90962" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4223" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + id="path4225" + style="fill:#ffffff;fill-rule:evenodd;stroke:#90a4ae;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 433.73039,748.73942 30.90956,-8.28217 M 47.360072,852.26707 78.269693,843.98486" /> + </g> + <g + id="g4467" + transform="matrix(0.70710678,0.70710678,0.70710678,-0.70710678,-488.13248,1178.456)" + clip-path="url(#clipPath4196)"> + <path + style="opacity:0.5;fill:url(#radialGradient4482);fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 384,128 357.82422,154.17578 154.17578,357.82422 480,680 704,448 Z" + id="rect4460" + transform="matrix(0.70710678,0.70710678,0.70710678,-0.70710678,-106.03867,796.36224)" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccc" /> + </g> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.2;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.56690216px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 256 24 A 232 232.00002 0 0 0 24 256 A 232 232.00002 0 0 0 24.041016 257.16992 A 232 232.00002 0 0 1 256 26 A 232 232.00002 0 0 1 487.95898 256.83008 A 232 232.00002 0 0 0 488 256 A 232 232.00002 0 0 0 256 24 z " + transform="translate(0,540.3622)" + id="ellipse4219" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="icon 1" + style="display:inline"> + <path + id="path4372" + d="M 256,122 A 144.00003,144.00005 0 0 0 112,266 144.00003,144.00005 0 0 0 256,410 144.00003,144.00005 0 0 0 400,266 144.00003,144.00005 0 0 0 380,193.03906 L 380,142 328.58594,142 A 144.00003,144.00005 0 0 0 256,122 Z" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.3;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:22.36660767;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter:url(#filter4215);color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + inkscape:connector-curvature="0" /> + <rect + transform="translate(0,-540.3622)" + style="opacity:1;fill:#b0bec5;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect4458" + width="80" + height="80" + x="304" + y="668.36218" /> + <path + transform="translate(0,-540.3622)" + style="opacity:0.15;fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 384,668.3622 -80,80 0,-80 z" + id="path4380" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <ellipse + ry="144.00003" + rx="144" + cy="796.36218" + cx="256" + id="ellipse4378" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cfd8dc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.56690216px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + transform="translate(0,-540.3622)" /> + <ellipse + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.56700039;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="path4157-3" + cx="256" + cy="796.36218" + rx="112" + ry="112.00002" + transform="translate(0,-540.3622)" /> + <path + style="display:inline;opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new" + d="m 240,175.99998 0,48 12,12 -12,12 0,88 32,0 0,-48 -12,-12 12,-12 0,-88 z" + id="rect4465" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccccccc" /> + </g> +</svg> diff --git a/src/anastasis/Makefile.am b/src/anastasis/Makefile.am @@ -44,6 +44,7 @@ anastasis_gtk_SOURCES = \ anastasis-gtk_handle-method-post.c \ anastasis-gtk_handle-method-question.c \ anastasis-gtk_handle-method-sms.c \ + anastasis-gtk_handle-method-totp.c \ anastasis-gtk_handle-payqr-selection-changed.c \ anastasis-gtk_handle-policy-activate.c \ anastasis-gtk_handle-policy-button.c \ diff --git a/src/anastasis/anastasis-gtk_action.c b/src/anastasis/anastasis-gtk_action.c @@ -1334,143 +1334,6 @@ action_secret_editing (void) } -/** - * Create the QR code image for our zone. - * - * @param scale factor for scaling up the size of the image to create - * @param text text to encode - * @return NULL on error - */ -static GdkPixbuf * -create_qrcode (unsigned int scale, - const char *text, - size_t text_size) -{ - QRinput *qri; - QRcode *qrc; - GdkPixbuf *pb; - guchar *pixels; - int n_channels; - const char *dir; - char *fn; - unsigned int size; - - qri = QRinput_new2 (0, QR_ECLEVEL_M); - if (NULL == qri) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2"); - return NULL; - } - /* first try encoding as uppercase-only alpha-numerical - QR code (much smaller encoding); if that fails, also - try using binary encoding (in case nick contains - special characters). */ - if ((0 != QRinput_append (qri, - QR_MODE_AN, - text_size, - (unsigned char *) text)) && - (0 != QRinput_append (qri, - QR_MODE_8, - text_size, - (unsigned char *) text))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "QRinput_append"); - return NULL; - } - qrc = QRcode_encodeInput (qri); - if (NULL == qrc) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "QRcode_encodeInput"); - QRinput_free (qri); - return NULL; - } - /* We use a trick to create a pixbuf in a way that works for both Gtk2 and - Gtk3 by loading a dummy file from disk; all other methods are not portable - to both Gtk2 and Gtk3. */ - dir = GNUNET_GTK_get_data_dir (); - GNUNET_asprintf (&fn, - "%s%s", - dir, - "qr_dummy.png"); - size = (qrc->width + 8) * scale; - size += 8 - (size % 8); - pb = gdk_pixbuf_new_from_file_at_size (fn, - size, - size, - NULL); - GNUNET_free (fn); - if (NULL == pb) - { - QRcode_free (qrc); - QRinput_free (qri); - return NULL; - } - pixels = gdk_pixbuf_get_pixels (pb); - n_channels = gdk_pixbuf_get_n_channels (pb); - for (unsigned int x = 4 * scale; x < size - 4 * scale; x++) - for (unsigned int y = 4 * scale; y < size - 4 * scale; y++) - { - unsigned int xx = x - 4 * scale; - unsigned int yy = y - 4 * scale; - unsigned int ss = size - 8 * scale; - unsigned int off = - (xx * qrc->width / ss) + (yy * qrc->width / ss) * qrc->width; - for (int c = 0; c < n_channels; c++) - pixels[(y * size + x) * n_channels + c] = - (0 == (qrc->data[off] & 1)) ? 0xFF : 0; - } - QRcode_free (qrc); - QRinput_free (qri); - return pb; -} - - -/** - * Create the QR code image for our zone. - * - * @param text text to encode - * @return NULL on error - */ -static GdkPixbuf * -setup_qrcode (const char *widget, - const char *text, - size_t text_size) -{ - GtkWidget *image; - GdkScreen *screen; - GtkSettings *settings; - gint dpi; - int scale; - - image = GTK_WIDGET (GCG_get_main_window_object (widget)); - if (NULL == image) - { - GNUNET_break (0); - return NULL; - } - /* adjust scale to screen resolution */ - screen = gtk_widget_get_screen (GTK_WIDGET (image)); - settings = gtk_settings_get_for_screen (screen); - g_object_get (G_OBJECT (settings), - "gtk-xft-dpi", - &dpi, - NULL); - if (-1 == dpi) - scale = 2; - else if (dpi >= 122800) - scale = 4; - else if (dpi >= 98304) - scale = 3; - else - scale = 2; - return create_qrcode (3 * scale, - text, - text_size); -} - - static void action_truths_paying (void) { @@ -1489,15 +1352,22 @@ action_truths_paying (void) { const char *payto = json_string_value (pt); GdkPixbuf *pb; + GtkWidget *w; + w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview")); + if (NULL == w) + { + GNUNET_break (0); + continue; + } if (NULL == payto) { GNUNET_break (0); continue; } - pb = setup_qrcode ("unpaid_qr_treeview", - payto, - strlen (payto)); + pb = AG_setup_qrcode (w, + payto, + strlen (payto)); if (NULL == pb) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -1569,7 +1439,14 @@ action_policies_paying (void) GNUNET_JSON_spec_end () }; GdkPixbuf *pb; + GtkWidget *w; + w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview")); + if (NULL == w) + { + GNUNET_break (0); + continue; + } if (GNUNET_OK != GNUNET_JSON_parse (ppr, spec, @@ -1578,9 +1455,9 @@ action_policies_paying (void) GNUNET_break (0); continue; } - pb = setup_qrcode ("unpaid_qr_treeview", - payto, - strlen (payto)); + pb = AG_setup_qrcode (w, + payto, + strlen (payto)); if (NULL == pb) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -1978,9 +1855,13 @@ show_challenge_feedback (void) } if (NULL != taler_pay_uri) { - qr = setup_qrcode ("anastasis_gtk_challenge_status_treeview", - taler_pay_uri, - strlen (taler_pay_uri)); + GtkWidget*w; + + w = GTK_WIDGET (GCG_get_main_window_object ( + "anastasis_gtk_challenge_status_treeview")); + qr = AG_setup_qrcode (w, + taler_pay_uri, + strlen (taler_pay_uri)); } if (TALER_EC_NONE != ec) emsg = TALER_ErrorCode_get_hint (ec); @@ -2575,6 +2456,7 @@ action_challenge_paying (void) GNUNET_JSON_spec_end () }; GdkPixbuf *pb; + GtkWidget *w; if (GNUNET_OK != GNUNET_JSON_parse (ppr, @@ -2593,9 +2475,10 @@ action_challenge_paying (void) "payment")) continue; found = true; - pb = setup_qrcode ("unpaid_qr_treeview", - payto, - strlen (payto)); + w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview")); + pb = AG_setup_qrcode (w, + payto, + strlen (payto)); if (NULL == pb) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Failed to initialize QR-code pixbuf for `%s'\n"), @@ -3179,22 +3062,22 @@ action_recovery_finished (void) AG_show ("anastasis_gtk_secret_copy_button"); } pb = NULL; + img = GTK_IMAGE (GCG_get_main_window_object ( + "anastasis_gtk_secret_qr_image")); if (NULL != text) { - pb = setup_qrcode ("anastasis_gtk_secret_qr_image", - text, - strlen (text)); + pb = AG_setup_qrcode (GTK_WIDGET (img), + text, + strlen (text)); } else { - pb = setup_qrcode ("anastasis_gtk_secret_qr_image", - data, - data_size); + pb = AG_setup_qrcode (GTK_WIDGET (img), + data, + data_size); } if (NULL != pb) { - img = GTK_IMAGE (GCG_get_main_window_object ( - "anastasis_gtk_secret_qr_image")); gtk_image_set_from_pixbuf (img, pb); g_object_unref (pb); diff --git a/src/anastasis/anastasis-gtk_handle-method-totp.c b/src/anastasis/anastasis-gtk_handle-method-totp.c @@ -0,0 +1,235 @@ +/* + This file is part of anastasis-gtk. + Copyright (C) 2021 Anastasis SARL + + Anastasis 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 3, or (at your + option) any later version. + + Anastasis 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 Anastasis; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file src/anastasis/anastasis-gtk_handle-method-totp.c + * @brief Handle dialogs for TOTP (RFC 6238) + * @author Christian Grothoff + */ +#include <gnunet/platform.h> +#include <gnunet/gnunet_util_lib.h> +#include "anastasis-gtk_action.h" +#include "anastasis-gtk_helper.h" +#include "anastasis-gtk_handle-identity-changed.h" +#include <jansson.h> + + +/** + * Random secret used in the current dialog. + */ +static char totp[32]; + + +/** + * Compute RFC 4648 base32 encoding of @a val and write + * result to @a enc. + * + * @param val value to encode + * @param val_size number of bytes in @a val + * @param[out] enc where to write the 0-terminated result + */ +static void +base32enc (const void *val, + size_t val_size, + char *enc) +{ + /** + * 32 characters for encoding, using RFC 4648. + */ + static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTV"; + unsigned int wpos; + unsigned int rpos; + unsigned int bits; + unsigned int vbit; + const unsigned char *udata; + + udata = val; + vbit = 0; + wpos = 0; + rpos = 0; + bits = 0; + while ((rpos < val_size) || (vbit > 0)) + { + if ((rpos < val_size) && (vbit < 5)) + { + bits = (bits << 8) | udata[rpos++]; /* eat 8 more bits */ + vbit += 8; + } + if (vbit < 5) + { + bits <<= (5 - vbit); /* zero-padding */ + GNUNET_assert (vbit == ((val_size * 8) % 5)); + vbit = 5; + } + enc[wpos++] = encTable__[(bits >> (vbit - 5)) & 31]; + vbit -= 5; + } + GNUNET_assert (0 == vbit); + if (wpos < val_size) + enc[wpos] = '\0'; +} + + +/** + * Recompute the QR code shown in @a builder from + * totp and the user's name for the secret. + * + * @param builder the dialog builder + */ +static void +refresh_totp (GtkBuilder *builder) +{ + GtkEntry *q; + const char *name; + char *u_name; + char *uri; + char base_sec[sizeof (totp) * 2]; + GdkPixbuf *pb; + GtkImage *img; + + gtk_widget_set_sensitive ( + GTK_WIDGET (gtk_builder_get_object (builder, + "anastasis_gtk_b_totp_dialog_btn_ok")), + false); + q = GTK_ENTRY (gtk_builder_get_object (builder, + "anastasis_gtk_b_totp_dialog_name_entry")); + name = gtk_entry_get_text (q); + u_name = TALER_urlencode (name); + base32enc (totp, + sizeof (totp), + base_sec); + GNUNET_asprintf (&uri, + "otpauth://totp/%s?secret=%s", + u_name, + base_sec); + GNUNET_free (u_name); + img = GTK_IMAGE (gtk_builder_get_object (builder, + "qr_image")); + pb = AG_setup_qrcode (GTK_WIDGET (img), + uri, + strlen (uri)); + if (NULL != pb) + { + gtk_image_set_from_pixbuf (img, + pb); + g_object_unref (pb); + gtk_widget_set_sensitive ( + GTK_WIDGET (gtk_builder_get_object (builder, + "anastasis_gtk_b_totp_dialog_btn_ok")), + true); + } +} + + +/** + * Function called from the totp dialog upon completion. + * + * @param dialog the pseudonym selection dialog + * @param response_id response code from the dialog + * @param user_data the builder of the dialog + */ +void +anastasis_gtk_b_totp_dialog_response_cb (GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + GtkBuilder *builder = GTK_BUILDER (user_data); + GtkEntry *q; + const char *name; + json_t *args; + + if (GTK_RESPONSE_OK != response_id) + { + gtk_widget_destroy (GTK_WIDGET (dialog)); + g_object_unref (G_OBJECT (builder)); + return; + } + q = GTK_ENTRY (gtk_builder_get_object (builder, + "anastasis_gtk_b_totp_dialog_name_entry")); + name = gtk_entry_get_text (q); + args = json_pack ("{ s:{s:s, s:o, s:s}}", + "authentication_method", + "type", + "totp", + "challenge", + GNUNET_JSON_from_data (totp, + sizeof (totp)), + "instructions", + name); + gtk_widget_destroy (GTK_WIDGET (dialog)); + g_object_unref (G_OBJECT (builder)); + memset (totp, + 0, + sizeof (totp)); + AG_freeze (); + AG_ra = ANASTASIS_redux_action (AG_redux_state, + "add_authentication", + args, + &AG_action_cb, + NULL); + json_decref (args); +} + + +void +anastasis_gtk_b_totp_dialog_name_entry_changed_cb (GtkEntry *entry, + gpointer user_data) +{ + GtkBuilder *builder = GTK_BUILDER (user_data); + + refresh_totp (builder); +} + + +/** + * Callback invoked if the the "totp"-button is clicked. + * + * @param object + * @param user_data unused + */ +void +anastasis_gtk_btn_add_auth_totp_clicked_cb (GObject *object, + gpointer user_data) +{ + GtkWidget *ad; + GtkBuilder *builder; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + totp, + sizeof (totp)); + builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_totp.glade", + NULL); + if (NULL == builder) + { + GNUNET_break (0); + return; + } + ad = GTK_WIDGET (gtk_builder_get_object (builder, + "anastasis_gtk_b_totp_dialog")); + refresh_totp (builder); + { + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object)); + gtk_window_set_transient_for (GTK_WINDOW (ad), + GTK_WINDOW (toplevel)); + gtk_window_present (GTK_WINDOW (ad)); + } +} diff --git a/src/anastasis/anastasis-gtk_helper.c b/src/anastasis/anastasis-gtk_helper.c @@ -28,6 +28,8 @@ #include <gnunet/gnunet_util_lib.h> #include "anastasis-gtk_helper.h" #include <jansson.h> +#include <qrencode.h> +#include <gdk-pixbuf/gdk-pixbuf.h> /** @@ -263,3 +265,134 @@ AG_error (const char *format, AG_have_error = true; gtk_widget_show (GTK_WIDGET (l)); } + + +/** + * Create a the QR code image from a given @a text. + * + * @param scale factor for scaling up the size of the image to create + * @param text text to encode + * @return NULL on error + */ +static GdkPixbuf * +create_qrcode (unsigned int scale, + const char *text, + size_t text_size) +{ + QRinput *qri; + QRcode *qrc; + GdkPixbuf *pb; + guchar *pixels; + int n_channels; + const char *dir; + char *fn; + unsigned int size; + + qri = QRinput_new2 (0, QR_ECLEVEL_M); + if (NULL == qri) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2"); + return NULL; + } + /* first try encoding as uppercase-only alpha-numerical + QR code (much smaller encoding); if that fails, also + try using binary encoding (in case nick contains + special characters). */ + if ((0 != QRinput_append (qri, + QR_MODE_AN, + text_size, + (unsigned char *) text)) && + (0 != QRinput_append (qri, + QR_MODE_8, + text_size, + (unsigned char *) text))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "QRinput_append"); + return NULL; + } + qrc = QRcode_encodeInput (qri); + if (NULL == qrc) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "QRcode_encodeInput"); + QRinput_free (qri); + return NULL; + } + /* We use a trick to create a pixbuf in a way that works for both Gtk2 and + Gtk3 by loading a dummy file from disk; all other methods are not portable + to both Gtk2 and Gtk3. */ + dir = GNUNET_GTK_get_data_dir (); + GNUNET_asprintf (&fn, + "%s%s", + dir, + "qr_dummy.png"); + size = (qrc->width + 8) * scale; + size += 8 - (size % 8); + pb = gdk_pixbuf_new_from_file_at_size (fn, + size, + size, + NULL); + GNUNET_free (fn); + if (NULL == pb) + { + QRcode_free (qrc); + QRinput_free (qri); + return NULL; + } + pixels = gdk_pixbuf_get_pixels (pb); + n_channels = gdk_pixbuf_get_n_channels (pb); + for (unsigned int x = 4 * scale; x < size - 4 * scale; x++) + for (unsigned int y = 4 * scale; y < size - 4 * scale; y++) + { + unsigned int xx = x - 4 * scale; + unsigned int yy = y - 4 * scale; + unsigned int ss = size - 8 * scale; + unsigned int off = + (xx * qrc->width / ss) + (yy * qrc->width / ss) * qrc->width; + for (int c = 0; c < n_channels; c++) + pixels[(y * size + x) * n_channels + c] = + (0 == (qrc->data[off] & 1)) ? 0xFF : 0; + } + QRcode_free (qrc); + QRinput_free (qri); + return pb; +} + + +GdkPixbuf * +AG_setup_qrcode (GtkWidget *w, + const char *text, + size_t text_size) +{ + GtkWidget *image; + GdkScreen *screen; + GtkSettings *settings; + gint dpi; + int scale; + GdkPixbuf *pb; + + if (NULL == w) + { + GNUNET_break (0); + return NULL; + } + /* adjust scale to screen resolution */ + screen = gtk_widget_get_screen (w); + settings = gtk_settings_get_for_screen (screen); + g_object_get (G_OBJECT (settings), + "gtk-xft-dpi", + &dpi, + NULL); + if (-1 == dpi) + scale = 2; + else if (dpi >= 122800) + scale = 4; + else if (dpi >= 98304) + scale = 3; + else + scale = 2; + return create_qrcode (3 * scale, + text, + text_size); +} diff --git a/src/anastasis/anastasis-gtk_helper.h b/src/anastasis/anastasis-gtk_helper.h @@ -31,6 +31,7 @@ #include <anastasis/anastasis_service.h> #include <anastasis/anastasis_redux.h> #include "anastasis-gtk.h" +#include <gdk-pixbuf/gdk-pixbuf.h> /** @@ -476,4 +477,18 @@ void AG_error_clear (void); +/** + * Setup QR code image for a widget @a w. + * + * @param w widget to use to determine screen resolution + * @param text text to encode + * @param text_size number of bytes in @a text + * @return NULL on failure + */ +GdkPixbuf * +AG_setup_qrcode (GtkWidget *w, + const char *text, + size_t text_size); + + #endif