最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Decode a base45 string that will lead to a cbor compressed file - Stack Overflow

programmeradmin0浏览0评论

Hi im trying to read my greenpass certificate, and i know that when you scan the QR code it will appear something like this HC1:NHFDFGDF......, i also know this is encoded as base45 so i made a little javascript decoder, this is my code:

const base45 = require('base45');

//of course my personal HC1: isnt here
const encodedData = 'C0C9BQF2LVU.TMBX4KDL*XD/GPWBILC9GGBYPLR-SAG1CSQ6U7SSQY%SJWLK34JWLG56H0API0TUL:12>'

const decodedData = base45.decode(encodedData).toString('utf-8');

console.log(decodedData);

And this is the response:

xڻ�⻈Q��C#?��-E�����K�BX���ֳI%|�zl�ȼ�qIbY㪤��
�Ң<�Ҳ�L�� ?��0gO+C��+�`��`׀0�H'W�P���WweϤ�|��I)yLI)%YFF��f�FfI�ť��y��%�We�+�$*'ޕ�������kh�f��I�9�����F����F�I�)LI%�&�ƖfIeY��������)�IY���F {
                                                                                                                                            �sVp����*8���z�8*��:;'������
     pMN�+*��*�+JN�+��  �
                         ��**K-J5�3�3�p8���yM]�o����E�_��O.�ϩ����
�g�˽��\�����="��]۷
����������{��Kq ��

what i read is that its decoding of base45 will lead to zlib pressed file, where the depression will lead a CBOR web token, but im stuck, can you help me ? Is this result normal ? also im still learning

Hi im trying to read my greenpass certificate, and i know that when you scan the QR code it will appear something like this HC1:NHFDFGDF......, i also know this is encoded as base45 so i made a little javascript decoder, this is my code:

const base45 = require('base45');

//of course my personal HC1: isnt here
const encodedData = 'C0C9BQF2LVU.TMBX4KDL*XD/GPWBILC9GGBYPLR-SAG1CSQ6U7SSQY%SJWLK34JWLG56H0API0TUL:12>'

const decodedData = base45.decode(encodedData).toString('utf-8');

console.log(decodedData);

And this is the response:

xڻ�⻈Q��C#?��-E�����K�BX���ֳI%|�zl�ȼ�qIbY㪤��
�Ң<�Ҳ�L�� ?��0gO+C��+�`��`׀0�H'W�P���WweϤ�|��I)yLI)%YFF��f�FfI�ť��y��%�We�+�$*'ޕ�������kh�f��I�9�����F����F�I�)LI%�&�ƖfIeY��������)�IY���F {
                                                                                                                                            �sVp����*8���z�8*��:;'������
     pMN�+*��*�+JN�+��  �
                         ��**K-J5�3�3�p8���yM]�o����E�_��O.�ϩ����
�g�˽��\�����="��]۷
����������{��Kq ��

what i read is that its decoding of base45 will lead to zlib pressed file, where the depression will lead a CBOR web token, but im stuck, can you help me ? Is this result normal ? also im still learning

Share Improve this question edited Aug 1, 2021 at 17:41 Sven Eberth 3,11612 gold badges25 silver badges31 bronze badges asked Aug 1, 2021 at 17:21 Sh4dowSh4dow 311 gold badge1 silver badge4 bronze badges 7
  • That is not valid Base45 input. Please recheck that you have correctly copied and pasted it into the code. Your decoding does in fact start with two bytes indicating a zlib header. However you should never post binary data in printed form, since it cannot be read. All those white-on-black question marks are parts of the binary that could not be printed, and so are lost and pletely useless to someone trying to help you. Always put binary data in a question as hexadecimal or Base-64. – Mark Adler Commented Aug 1, 2021 at 23:49
  • this is part of the string that follows HC1:, when we scan the QR code: NCFOXN%TSMAHN-H.L8%38Q%T6$823S0IIYMJ the one that i read that is encoded as base45, here is where i've seen it, but i wanna make it a little different: dev.to/lmillucci/…, and i changed the utf-8 to hex, this was what i got, its just a part of it, not all: 78dabbd4e2bb88518dc543233ff5ec2d45cfde05918c9a4b1893024258a4128ff2d6b349257ca87a6cc9c8bc9071496259e3aaa4e4cc0ac3d2a23cabd2b2e44 – Sh4dow Commented Aug 2, 2021 at 0:57
  • In the question. Put the Base64 in the question. Not in these ments. (As you noticed, ments are length limited. And they are not where information needed to answer the question belong. That place is called the question.) And please delete the attempted printout of the binary in the question. – Mark Adler Commented Aug 2, 2021 at 1:17
  • That is a valid zlib stream. So yes, your result normal and correct. – Mark Adler Commented Aug 2, 2021 at 1:19
  • thanks, in the web site i showed he was using pako to unpress it, but is giving me an error, is it possible to use zlib to unpress it ? – Sh4dow Commented Aug 2, 2021 at 1:32
 |  Show 2 more ments

2 Answers 2

Reset to default 5

I solved the problem using only browser resources (no node.js); I also found some official sources for explanations, test data, field decoding (see at the end of the answer):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="generator" content="PSPad editor, www.pspad.">
  <title></title>
  </head>
  <body>
  <textarea id="encoded" name="encoded" cols=100 rows=10></textarea><br>
  <textarea id="decoded" name="decoded" cols=100 rows=10 value = "prova">hello</textarea><br>
  <span id="plain1" name="plain1">-</span> --&gt;
  <span id ="cod" name="cod">-</span> --&gt;
  <span id="plain2" name="plain2">-</span><br>
  <button onclick="test()">Test</button><br>
  <button onclick="vai()">Vai</button><br>
  <script src="pako.min.js"></script>
  <script src="my_base45_2.js"></script>
  <script src="cbor.js" type="text/javascript"></script>

<script>

// https://dev.to/lmillucci/javascript-how-to-decode-the-greenpass-qr-code-3dh0

function convert(decoded) {
    text="";
    for (var i = 0; i < decoded.length; i++) {
        text +=  String.fromCharCode(decoded[i]);
    }
    console.log("Text=",text);
    values = text.split(",");
    console.log("values: " ,values);
    final = "";
    for (var i=0; i<values.length; i++) {
        final += String.fromCharCode(values[i])
    }
    return final;
}

function test() {

    source = document.getElementById("decoded").value;
    console.log("source=",source);
    document.getElementById("plain1").innerHTML = source;

    var enc = new TextEncoder();
    buff= enc.encode(source);
    console.log("buff:" , buff);

    encoded2 = encode(buff);
    console.log("encoded2:" , encoded2);
    document.getElementById("cod").innerHTML = encoded2;

    source = encoded2;
    console.log("Encoded source=",source);
    decoded = decode(source).enc;
    converted = convert(decoded);

    console.log("Decoded=", decoded);
    console.log("converted=", converted);

    document.getElementById("plain2").innerHTML = converted;
}


function buf2hex(buffer) {
// https://stackoverflow./questions/34309988/byte-array-to-hex-string-conversion-in-javascript
    var u = new Uint8Array(buffer),
        a = new Array(u.length),
        i = u.length;
    while (i--) // map to hex
        a[i] = (u[i] < 16 ? '0' : '') + u[i].toString(16);
    u = null; // free memory
    return a.join('');
};


function typedArrayToBuffer(array) {
// https://stackoverflow./questions/37228285/uint8array-to-arraybuffer
    return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
}


function vai() {
    source = document.getElementById("encoded").value;
    console.log("Encoded source=",source);

    // Decode BASE45:
    decoded = decode(source).enc;

    // Unzip the decoded:
    COSEbin =  pako.inflate(decode(source).raw);
        COSE = buf2hex(COSEbin);

        var typedArray = new Uint8Array(COSE.match(/[\da-f]{2}/gi).map(function (h) {
          return parseInt(h, 16)
        })) // https://stackoverflow./questions/43131242/how-to-convert-a-hexadecimal-string-of-data-to-an-arraybuffer-in-javascript
        var unzipped = typedArray.buffer;

        [headers1, headers2, cbor_data, signature] = CBOR.decode(unzipped);
        cbor_dataArr = typedArrayToBuffer(cbor_data);
        greenpassData  = CBOR.decode(cbor_dataArr);

        console.log(greenpassData);
}
</script>
  </body>
</html>

The key is the "double CBOR decoding": once is not enough:

CBOR decoding:

[headers1, headers2, cbor_data, signature] = CBOR.decode(unzipped);

Conversion to Array:

cbor_dataArr = typedArrayToBuffer(cbor_data);

Further CBOR conversion:

greenpassData  = CBOR.decode(cbor_dataArr);

My version of base45 decoder ("my_base45_2.js"), adapted from a node.js version:

 function encode(uint8array) {
    var output = [];

    for (var i = 0, length = uint8array.length; i < length; i+=2) {
      if (uint8array.length -i > 1) {
         var x = (uint8array[i]<<8)+ uint8array[i+1]
         var [ e, x ]  = divmod(x, 45*45)
         var [ d, c ] = divmod(x, 45)
         output.push(fromCharCode(c) + fromCharCode(d) + fromCharCode(e))
     } else {
         var x = uint8array[i]
         var [ d, c ] = divmod(x, 45)
         output.push(fromCharCode(c) + fromCharCode(d))
     }
    }
    return output.join('')
  };

  var divmod = function divmod(a,b) {
    var remainder = a
    var quotient = 0
    if (a >= b) {
        remainder = a % b
    quotient = (a - remainder) / b
    }
    return [ quotient, remainder ]
  }

  const BASE45_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
  var fromCharCode = function fromCharCode(c) {
    return BASE45_CHARSET.charAt(c);
  };

function decode(str) {
    var output = []
    var buf = []

    for(var i = 0, length=str.length; i < length; i++) {
//console.log(i);    
       var j = BASE45_CHARSET.indexOf(str[i])
       if (j < 0)
                console.log('Base45 decode: unknown character n.', i, j);
              //throw new Error('Base45 decode: unknown character');
       buf.push(j)
    }

    for(var i = 0, length=buf.length; i < length; i+=3) {
       var x = buf[i] + buf[i + 1] * 45
       if (length - i >= 3) {
          var [d, c] = divmod(x + buf[i + 2] * 45 * 45,256)
          output.push(d)
          output.push(c)
       } else {
         output.push(x)
       }
    }
console.log("output",output);    
    var enc = new TextEncoder();
    return {"enc" : enc.encode(output), "raw" : output};
    //return Buffer.from(output);
  };

These are the decoding steps:

QR code --> QR DECODER --> RAW QR-decoded string --> BASE45 decoder --> zlib pressed string --> pako library --> COSE string --> CBOR decoder --> CBOR string --> CBOR decoder --> final JSON file

  • Original base45 node.js decoder: https://github./dirkx/base45-js/blob/main/lib/base45-js.js
  • Zlib unpacker library for browser: https://github./nodeca/pako/blob/master/dist/pako.min.js
  • CBOR unpacker: https://github./paroga/cbor-js
  • Dummy greenpass for testing: https://gir.st/blog/img/greenpass-demo.png
  • Official data for testing: https://github./eu-digital-green-certificates/dgc-testdata/tree/main/IT/png
  • Documentation on json fields: https://ec.europa.eu/health/sites/default/files/ehealth/docs/digital-green-certificates_dt-specifications_en.pdf
  • Databases for decoding fields values: https://github./ehn-dcc-development/ehn-dcc-schema/tree/release/1.3.0/valuesets

My reference guides for decoding greenpass:

  • https://dev.to/lmillucci/javascript-how-to-decode-the-greenpass-qr-code-3dh0

  • https://gir.st/blog/greenpass.html

  • Sample data containing 5 strings:

    • Original QR-decoded string
    • BASE45 string
    • Compressed string
    • COSE string
    • CBOR string
const base45 = require('base45');
const encodedData = 'NCFOXN%TSMAHN-H.L8%38Q%T6$823S0IIYMJ 43%C0C9BQF2LVU.TMBX4KDL*XD/GPWBILC9GGBYPLR-SAG1CSQ6U7SSQY%SJWLK34JWLG56H0API0TUL:12'
const decodedData = base45.decode(encodedData).toString('hex');
console.log(decodedData);

Result:

78dabbd4e2bb88518dc543233ff5ec2d45cfde05918c9a4b1893024258a4128ff2d6b349257ca87a6cc9c8bc9071496259e3aaa4e4cc0ac3d2a23cabd2b2e44cabd0203fabd030674f2b0343ab80102bdf60aba060d78030e34813275713f750b7d0c8c808577765cfa4e47ca0894929794c4929255946064686ba0666ba46664999c5a5be997999c52587571665e62ba4242a04271ede95929a949b98eb1fe4ae6b6800048666969649b90539aea1fa86fa4686fa86a6469649c5294c4925e999162606a6c6960606664965055986868696c606a60606a6c929f949598696a646207b0ccd92f312739724a5e515b8b82a38faf8ba7aba382af8843a3b0627a5e7e5f8841eee0d5670f3f4f10c704d4ecb2b012ab2812ab2012b4a4ecf2bc9f609f50cb6812a2a4b2d4a35d433d033887038a4b4a4794d5dc66f8fcdf9b545815fb9a64f2ebfcfa9f9f2f7c71b458d67a6cbbdb38d0f5cf1f1c7d9e7013d22d1de1c5ddbb70ac9fd88b9cff2ecdf14c6e77bbe854b0100710988a7
发布评论

评论列表(0)

  1. 暂无评论