Jelajahi Sumber

DIU side fare QR additions

* WIP
* fareqr can act as a standalone program to encode and decode strings
* passbd needs further development
clementinecomputing 4 tahun lalu
induk
melakukan
426b007bbf

+ 200 - 0
busunit/passdb/b64.c

@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/* Base64 encoder/decoder. Originally Apache file ap_base64.c
+ */
+
+#include <string.h>
+
+#include "b64.h"
+
+/* aaaack but it's fast and const should make it shared text page. */
+/* ASCII table */
+static const unsigned char pr2six[256] = {
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
+    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
+    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+};
+
+int Base64decode_len(const char *bufcoded) {
+  int nbytesdecoded;
+  register const unsigned char *bufin;
+  register int nprbytes;
+
+  bufin = (const unsigned char *) bufcoded;
+  while (pr2six[*(bufin++)] <= 63);
+
+  nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
+  nbytesdecoded = ((nprbytes + 3) / 4) * 3;
+
+  return nbytesdecoded + 1;
+}
+
+int Base64decode(char *bufplain, const char *bufcoded) {
+  int nbytesdecoded;
+  register const unsigned char *bufin;
+  register unsigned char *bufout;
+  register int nprbytes;
+
+  bufin = (const unsigned char *) bufcoded;
+  while (pr2six[*(bufin++)] <= 63);
+  nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
+  nbytesdecoded = ((nprbytes + 3) / 4) * 3;
+
+  bufout = (unsigned char *) bufplain;
+  bufin = (const unsigned char *) bufcoded;
+
+  while (nprbytes > 4) {
+    *(bufout++) = (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
+    *(bufout++) = (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
+    *(bufout++) = (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
+    bufin += 4;
+    nprbytes -= 4;
+  }
+
+  /* Note: (nprbytes == 1) would be an error, so just ingore that case */
+  if (nprbytes > 1) {
+    *(bufout++) = (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
+  }
+  if (nprbytes > 2) {
+    *(bufout++) = (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
+  }
+  if (nprbytes > 3) {
+    *(bufout++) = (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
+  }
+
+  *(bufout++) = '\0';
+  nbytesdecoded -= (4 - nprbytes) & 3;
+  return nbytesdecoded;
+}
+
+static const char basis_64[] =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int Base64encode_len(int len) {
+  return ((len + 2) / 3 * 4) + 1;
+}
+
+int Base64encode(char *encoded, const char *string, int len) {
+  int i;
+  char *p;
+
+  p = encoded;
+  for (i = 0; i < len - 2; i += 3) {
+    *p++ = basis_64[(string[i] >> 2) & 0x3F];
+    *p++ = basis_64[((string[i] & 0x3) << 4) |
+                    ((int) (string[i + 1] & 0xF0) >> 4)];
+    *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
+                    ((int) (string[i + 2] & 0xC0) >> 6)];
+    *p++ = basis_64[string[i + 2] & 0x3F];
+  }
+  if (i < len) {
+    *p++ = basis_64[(string[i] >> 2) & 0x3F];
+    if (i == (len - 1)) {
+      *p++ = basis_64[((string[i] & 0x3) << 4)];
+      *p++ = '=';
+    }
+    else {
+      *p++ = basis_64[((string[i] & 0x3) << 4) |
+                      ((int) (string[i + 1] & 0xF0) >> 4)];
+      *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
+    }
+    *p++ = '=';
+  }
+
+  *p++ = '\0';
+  return p - encoded;
+}
+

+ 96 - 0
busunit/passdb/b64.h

@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/* Base64 encoder/decoder. Originally Apache file ap_base64.c
+ */
+
+#ifndef B64_H
+#define B64_H
+
+#include <string.h>
+
+int Base64decode_len(const char *bufcoded);
+int Base64decode(char *bufplain, const char *bufcoded);
+int Base64encode_len(int len);
+int Base64encode(char *encoded, const char *string, int len);
+
+#endif

+ 1 - 1
busunit/passdb/buildit.sh

@@ -6,7 +6,7 @@ rm -f passdb scheme.o
 
 #$target_cc $tinyscheme_opts tinyscheme1.39/scheme.c
 $target_cc $tinyscheme_opts tinyscheme/scheme.c
-$target_cc $target_ccopts -g -o passdb passdb.c rfid_decoder.c pass_communication.c rules.c ../common/common_defs.c ../common/gpsmath.c ../commhub/commhub.c ../commhub/client_utils.c scheme.o -lm -lz
+$target_cc $target_ccopts -g -o passdb passdb.c rfid_decoder.c pass_communication.c rules.c qr_lookup.c ../common/common_defs.c ../common/gpsmath.c ../commhub/commhub.c ../commhub/client_utils.c scheme.o -lm -lz
 
 rm -f send_magstripe
 $target_cc $target_ccopts -o send_magstripe send_magstripe.c ../commhub/commhub.c ../common/common_defs.c

+ 351 - 0
busunit/passdb/fareqr.c

@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2021 Clementine Computing LLC.
+ *
+ * This file is part of PopuFare.
+ *
+ * PopuFare is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PopuFare 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "fareqr.h"
+
+int fareqr_encode(char *tok_public, char *tok_secret, char *tok_cred, char *fareqr_str) {
+  int i, r;
+  uint8_t x,y,z;
+  uint8_t *src_data = NULL, *dst_data = NULL;
+  int src_data_n = 0, dst_data_n = 0;
+
+  src_data = (uint8_t *)malloc(sizeof(uint8_t)*LINE_BUFFER_SIZE);
+  dst_data = (uint8_t *)malloc(sizeof(uint8_t)*LINE_BUFFER_SIZE);
+
+  for (i=0; tok_cred[i]; i++) {
+    x = (uint8_t)tok_secret[i];
+    y = (uint8_t)tok_cred[i];
+    z = x^y;
+    src_data[i] = z;
+    src_data_n++;
+  }
+
+  for (i=0; i<src_data_n; i++) {
+    x = (uint8_t)tok_secret[i];
+  }
+
+  dst_data_n = Base64encode_len(src_data_n);
+
+  r = Base64encode((char *)dst_data, (const char *)src_data, src_data_n);
+
+  if (fareqr_str) {
+    snprintf(fareqr_str, LINE_BUFFER_SIZE-1, "@%s%%%s$\n", tok_public, dst_data);
+  }
+
+  free(src_data);
+  free(dst_data);
+
+  return 0;
+}
+
+
+int fareqr_decode(char *fareqr_s, char *check_pub, char *tok_secret, char *dst_cred) {
+  int i, n;
+  char *p, *stop_tok;
+  int pub_tok_read_len = 0, check_pub_len = 0;
+
+  char *enc_str = NULL, *dec_str = NULL, *plain_str=NULL;
+  uint8_t x,y,z;
+
+  if (fareqr_s[0] != '@') { return -1; }
+  stop_tok = strchr(fareqr_s, '%');
+  if (!stop_tok) { return -2; }
+  if (!tok_secret) { return -3; }
+
+  if (check_pub) {
+    check_pub_len = strlen(check_pub);
+    for ( p = (fareqr_s+1); p < stop_tok; p++) {
+      if (pub_tok_read_len >= check_pub_len) { return -3; }
+      if ( (*p) != check_pub[pub_tok_read_len] ) { return -4; }
+      pub_tok_read_len++;
+    }
+  }
+
+  enc_str = (char *)malloc(sizeof(char)*LINE_BUFFER_SIZE);
+  dec_str = (char *)malloc(sizeof(char)*LINE_BUFFER_SIZE);
+  plain_str = (char *)malloc(sizeof(char)*LINE_BUFFER_SIZE);
+  for (n=0, p = (stop_tok+1); (*p) && ((*p) != '$'); p++, n++) {
+    enc_str[n] = *p;
+  }
+  enc_str[n] = '\0';
+  Base64decode(dec_str, enc_str);
+
+  for (i=0; dec_str[i]; i++) {
+    if (tok_secret[i]==0) { return -5; }
+    x = (uint8_t)dec_str[i];
+    y = (uint8_t)tok_secret[i];
+    z = (x^y);
+    plain_str[i] = (char)z;
+  }
+  plain_str[i]='\0';
+
+
+
+  printf("%s\n", plain_str);
+
+  if (dst_cred) {
+    for (i=0; dec_str[i]; i++) {
+      dst_cred[i] = dec_str[i];
+    }
+    dst_cred[i] = '\0';
+  }
+
+  free(enc_str);
+  free(dec_str);
+  free(plain_str);
+
+  return 0;
+}
+
+// return negative on error or not found
+// 0 on success (found)
+//
+int fareqr_lookup_seed_secret(char *seedfn, char *pub, char *priv) {
+  int i;
+  FILE *fp;
+  char buf[LINE_BUFFER_SIZE] = {0};
+  int pos = 0, ch=0, line_no=0, src_n;
+  char *p = NULL;
+  char *tok0_ptr=NULL,
+       *tok1_ptr=NULL;
+
+  uint8_t z;
+
+  fp = fopen(seedfn, "r");
+  if (!fp) { perror(seedfn); return -2;}
+  while (!feof(fp)) {
+    ch = fgetc(fp);
+
+    // process line if we reach a newline or eof
+    //
+    if (feof(fp) || (ch == '\n')) {
+
+      // if the line is empty or a comment, skip
+      //
+      if ((pos==0) || (buf[0] == '#')) {
+
+      }
+
+      // get tokens out of line, using ' ' as the
+      // delimeter
+      //
+      else {
+        tok0_ptr = buf;
+        tok1_ptr = strchr(buf, ' ');
+        if (tok1_ptr) {
+          *tok1_ptr = '\0';
+          tok1_ptr++;
+
+
+          // Cechk it against our supplied 'pulbic' key
+          //
+          if (strcmp(pub, tok0_ptr)==0) {
+
+            // If we've found it, copy it over to the `priv`
+            // above and return
+            //
+            for (i=0; tok1_ptr[i]; i++) {
+              priv[i] = tok1_ptr[i];
+            }
+            priv[i]='\0';
+            return 0;
+
+          }
+        }
+
+        line_no++;
+      }
+
+      pos=0;
+      buf[0]='\0';
+      continue;
+    }
+
+    buf[pos] = ch;
+    pos++;
+    buf[pos] = '\0';
+  }
+  fclose(fp);
+
+  return -1;
+}
+
+// to compile:
+// gcc -D__FAREQR_MAIN__ fareqr.c b64.c -o fareqr
+//
+
+#ifdef __FAREQR_MAIN__
+
+void show_help(FILE *ofp) {
+  fprintf(ofp, "\nusage:\n\n");
+  fprintf(ofp, "  fareqr encode <pubkey> <privatekey> <str>\n");
+  fprintf(ofp, "  fareqr decode <privatekey> <encstr>\n");
+  fprintf(ofp, "  fareqr dbdecode <qrseedfile> <pubkey> <encstr>\n");
+  fprintf(ofp, "  fareqr help\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "fareqr is a program to help with encoding and decoding 'fareqr' strings.\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "A fareqr string is of the form:\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "  @<pubkey>%%<b64(xor(privkey,credential))>$\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "Where `<b64(xor(privkey,credential))>` is the base64 encoded XOR of the private key\n");
+  fprintf(ofp, "and the credential to be presented. The reasoning behind the XOR is to not allow a\n");
+  fprintf(ofp, "snooper to get credential information if exposed to the string and the base64\n");
+  fprintf(ofp, "encoding is to make it easily transportable.\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "The <qrseedfile> is a text file of <pubkey> <privkey> pairs.\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "Here is some example usage:\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "  $ fareqr encode 'wu9XouSh' 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' ';123456789060535?'\n");
+  fprintf(ofp, "  @wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=\n");
+  fprintf(ofp, "  $ fareqr decode 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' '@wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=$'\n");
+  fprintf(ofp, "  ;123456789060535?\n");
+  fprintf(ofp, "  $ echo 'wu9XouSh ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' > ./qr.seed\n");
+  fprintf(ofp, "  $ fareqr dbdecode ./qr.seed 'wu9XouSh' '@wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=$'\n");
+  fprintf(ofp, "  ;123456789060535?\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "Where 'wu9XouSh' is the public key, 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' is the private key and\n");
+  fprintf(ofp, "';123456789060535?' is the credential information to be encoded.\n");
+  fprintf(ofp, "\n");
+  fprintf(ofp, "\n");
+  fflush(ofp);
+}
+
+/* 
+ *
+ * quick test/start:
+ *
+ * $ fareqr encode 'wu9XouSh' 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' ';123456789060535?'
+ * @wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=
+ *
+ * $ fareqr decode 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' '@wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=$' 
+ * ;123456789060535?
+ *
+ * $ fareqr dbdecode 'wu9XouSh' '@wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=$'
+ * ;123456789060535?
+ *
+ */
+
+int main(int argc, char **argv) {
+  int i, r;
+  char *tok_public = NULL,
+       *tok_secret = NULL,
+       *tok_cred = NULL,
+       *fareqr_str = NULL;
+
+  uint8_t x,y,z;
+
+  uint8_t *src_data = NULL, *dst_data = NULL;
+  int src_data_n = 0, dst_data_n = 0;
+
+  char *fn = NULL;
+
+  if (argc <= 1) {
+    show_help(stderr);
+    exit(1);
+  }
+
+  if (strcmp(argv[1], "encode")==0) {
+    if (argc>2) {
+      tok_public = strdup(argv[2]);
+      if (argc>3) {
+        tok_secret = strdup(argv[3]);
+        if (argc>4) {
+          tok_cred = strdup(argv[4]);
+        }
+      }
+    }
+
+    if ((!tok_public) || (!tok_secret) || (!tok_cred)) {
+      show_help(stderr);
+      if (tok_public) { free(tok_public); }
+      if (tok_secret) { free(tok_secret); }
+      if (tok_cred) { free(tok_cred); }
+      exit(2);
+    }
+
+    fareqr_str = (char *)malloc(sizeof(char)*LINE_BUFFER_SIZE);
+    r = fareqr_encode(tok_public, tok_secret, tok_cred, fareqr_str);
+    if (r==0) {
+      printf("%s\n", fareqr_str);
+    }
+  }
+
+  else if (strcmp(argv[1], "decode")==0) {
+    if (argc>2) {
+      tok_secret = strdup(argv[2]);
+      if (argc>3) {
+        fareqr_str = strdup(argv[3]);
+      }
+    }
+    r = fareqr_decode(fareqr_str, tok_public, tok_secret, NULL);
+    if (r<0) {
+      fprintf(stderr, "error, failed to decode qr fare string (%i)\n", r);
+    }
+  }
+
+  else if (strcmp(argv[1], "dbdecode")==0) {
+    if (argc>2) {
+      fn = strdup(argv[2]);
+      if (argc>3) {
+        tok_public = strdup(argv[3]);
+        if (argc>4) {
+          fareqr_str = strdup(argv[4]);
+        }
+      }
+    }
+    tok_secret = (char *)malloc(sizeof(char)*LINE_BUFFER_SIZE);
+    tok_secret[0] = '\0';
+    r = fareqr_lookup_seed_secret(fn, tok_public, tok_secret);
+    if (r<0) {
+      fprintf(stderr, "could not find '%s' in db '%s', exiting (got %i)\n",
+          tok_public, fn, r);
+    }
+    else {
+      r = fareqr_decode(fareqr_str, tok_public, tok_secret, NULL);
+      if (r<0) {
+        fprintf(stderr, "error, failed to decode qr fare string (%i)\n", r);
+      }
+    }
+  }
+
+  else if (strcmp(argv[1], "help")==0) {
+    show_help(stdout);
+    exit(0);
+  }
+  else {
+    fprintf(stderr, "unknown operation '%s'\n", argv[1]);
+    show_help(stderr);
+    exit(3);
+  }
+
+  if (tok_public) { free(tok_public); }
+  if (tok_secret) { free(tok_secret); }
+  if (tok_cred) { free(tok_cred); }
+  if (fareqr_str) { free(fareqr_str); }
+
+  if (src_data) { free(src_data); }
+  if (dst_data) { free(dst_data); }
+  if (fn) { free(fn); }
+}
+
+#endif

+ 40 - 0
busunit/passdb/fareqr.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021 Clementine Computing LLC.
+ *
+ * This file is part of PopuFare.
+ *
+ * PopuFare is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PopuFare 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef POPUFARE_FAREQR_H
+#define POPUFARE_FAREQR_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+#include <stdint.h>
+
+#include "../common/common_config.h"
+
+#include "b64.h"
+
+
+
+int fareqr_encode(char *tok_public, char *tok_secret, char *tok_cred, char *fareqr_str);
+int fareqr_decode(char *fareqr_s, char *check_pub, char *tok_secret, char *dst_cred);
+int fareqr_lookup_seed_secret(char *seedfn, char *pub, char *priv);
+
+#endif

+ 36 - 0
busunit/passdb/pass_communication.c

@@ -953,6 +953,41 @@ message_callback_return handle_token_rfid_message(struct message_record *msg, vo
     return MESSAGE_HANDLED_CONT;
 }
 
+message_callback_return handle_token_qr_message(struct message_record *msg, void *param) {
+  int idx;
+  char cred[LINE_BUFFER_SIZE] = {0};
+  passdb_context *ctx = (passdb_context *)param;
+
+  char clear_payload[MAX_PAYLOAD_LENGTH] = {0};
+
+  if (!ctx) { return MESSAGE_HANDLED_CONT; }
+
+  update_piu_status_message(1);
+
+  #ifdef REJECT_IF_NO_DRIVER
+  if( (driver_stat.logged_in_driver <= 0) || (stop_stat.paddle <= 0) ) {
+    reject_no_driver();
+    return MESSAGE_HANDLED_CONT;
+  }
+  #endif
+
+  //---
+
+  idx = lookup_qr_credential(ctx, (char *)(msg->payload), clear_payload);
+  if (idx < 0) {
+    reject_unknown_card(cred);
+    return MESSAGE_HANDLED_CONT;
+  }
+
+
+  //---
+
+  idx = smart_find_mag(ctx, (char *)clear_payload, cred);
+  if (idx < 0)  { reject_unknown_card(cred); }
+  else          { process_rider(ctx, idx, cred); }
+  return MESSAGE_HANDLED_CONT;
+}
+
 message_callback_return update_anti_passback_cache(struct message_record *msg, void *param)
 {
     apb_flush_if_needed();
@@ -1095,6 +1130,7 @@ int main(int argc, char **argv)
     register_dispatch_callback(MAILBOX_STATUS_REQUEST, CALLBACK_USER(3), handle_status_request_message, NULL);
     register_dispatch_callback(MAILBOX_TOKEN_RFID, CALLBACK_USER(4), handle_token_rfid_message, &ctx);
     register_dispatch_callback(MAILBOX_TOKEN_MAG, CALLBACK_USER(5), handle_token_mag_message, &ctx);
+    register_dispatch_callback(MAILBOX_TOKEN_MAG, CALLBACK_USER(5), handle_token_qr_message, &ctx);
     register_dispatch_callback(MAILBOX_RULE_CALL, CALLBACK_USER(6), handle_rule_call, &ctx);
 
     //Handle status updates which require us to check expiration of the anti-passback cache

+ 2 - 0
busunit/passdb/passdb.h

@@ -96,5 +96,7 @@ void dump_hashes(passdb_context *ctx);
 int find_mag_in_hash(passdb_context *ctx, char *mag);
 int find_rf_in_hash(passdb_context *ctx, char *rfid);
 
+int lookup_qr_credential(passdb_context *ctx, char *payload, char *cred);
+
 #endif