/* version 2023-10-04 */ /* cbor format is https://datatracker.ietf.org/doc/html/rfc8949 */ /* qr code data is in format cose (itself using cbor) https://datatracker.ietf.org/doc/draft-ietf-cose-rfc8152bis-struct/15/ */ /* in my case it's tag 18, array of size 4 * item 0 is a byte string, which is "protected header" which you can feed to * cbor again, which gives a map of size 2. First item has key 1 * and value -7 which is COSE Algorithms -7 defined at * https://www.iana.org/assignments/cose/cose.xhtml#algorithms * it is : ES256 -7 ECDSA w/ SHA-256 [kty] [RFC-ietf-cose-rfc8152bis-algs-12] Yes * then second item has key 4 and a value as byte string of length 8 * which is a key identifier * item 1 is a pair map of size 0 * item 2 contains the data of the qr code to be fed to cbor again * you can use hex2bin for that, you copy the bytes as printed * and you do: echo [the bytes] | ./hex2bin | ./cbor * in my case, it's vaccine (field 'v') and has name/date of birth/date * of injection/who issued the thing, etc. * see https://blog.callicode.fr/post/2021/covid_pass.html (in french) * in it: item with key 4 is a date (expiration date), for me it's a * number and I do date -d@ to decode. Item with key 6 is also * a date (issuing date), also a number, same command to decode. * document for format is: * https://ec.europa.eu/health/sites/default/files/ehealth/docs/digital-green-certificates_v3_en.pdf * item 3 is a byte string of length 64, the signature */ /* should not be here, but... The QR code data, once decoded is zipped and * saved in base45. So you do: ./base45 < qr_code.bin.z.b45 > qr_code.bin.z * then: ./zunlib qr_code.bin.z > qr_code.bin * and you good. * * ah, and to get the qr code from the image use external program zbarimg * you do: zbarimg image.jpg (or whatever image format zbarimg accepts) * you get the qr code printed in the terminal as a string starting * with QR-Code:HC1: * you copy this string (without QR-Code:HC1:) and you put it in the file * qr_code.bin.z.b45 */ /* base45.c is at: http://sedcore.eu.org/base45.c * zunlib.c is at: http://sedcore.eu.org/zunlib.c * hex2bin.c is at http://sedcore.eu.org/hex2bin.c */ #include #include #include #include #define NORMAL 0 #define INDEFINITE 1 #define TERMINATE 2 #define Printf(...) do { {int i; for (i = 0; i < level; i++) putchar(' ');} printf(__VA_ARGS__); } while (0) void decode_item(int level) { int i; int c; int type; int value; uint64_t len; int special_len; c = getchar(); if (c == EOF) abort(); type = c >> 5; value = c & 0x1f; len = value; special_len = NORMAL; switch (value) { case 24: c = getchar(); if (c == EOF) abort(); len = c; break; case 25: len = 0; for (i = 0; i < 2; i++) { len <<= 8; c = getchar(); if (c == EOF) abort(); len |= c; } break; case 26: len = 0; for (i = 0; i < 4; i++) { len <<= 8; c = getchar(); if (c == EOF) abort(); len |= c; } break; case 27: len = 0; for (i = 0; i < 8; i++) { len <<= 8; c = getchar(); if (c == EOF) abort(); len |= c; } break; case 28: case 29: case 30: abort(); case 31: if (type == 0 || type == 1 || type == 6) abort(); if (type >= 2 && type <= 5) special_len = INDEFINITE; if (type == 7) special_len = TERMINATE; break; } if (special_len) { printf("special len: todo\n"); exit(1); } switch (type) { case 0: Printf("number %"PRIu64"\n", len); break; case 1: Printf("number %"PRId64"\n", -1-len); break; case 2: Printf("byte string[%"PRIu64"]:", len); for (i = 0; i < len; i++) { c = getchar(); if (c == EOF) abort(); printf(" %2.2x", c); } printf("\n"); break; case 3: Printf("char string[%"PRIu64"]: '", len); for (i = 0; i < len; i++) { c = getchar(); if (c == EOF) abort(); printf("%c", c); } printf("'\n"); break; case 4: Printf("array [%"PRIu64"]\n", len); for (i = 0; i < len; i++) { Printf(" item %d:\n", i); decode_item(level + 4); } break; case 5: Printf("pair map [%"PRIu64"]\n", len); for (i = 0; i < len; i++) { Printf(" item %d:\n", i); Printf(" key: \n"); decode_item(level + 6); Printf(" value: \n"); decode_item(level + 6); } break; case 6: Printf("tag %"PRIu64"\n", len); decode_item(level + 2); break; case 7: Printf("float/simple value\n"); exit(1); break; } } int main(void) { int c; while (1) { c = getchar(); if (c == EOF) break; ungetc(c, stdin); decode_item(0); } return 0; }