freeotp-export/export.html

162 wiersze
5.0 KiB
HTML

<!--
* Based on php code by Philip Sharp, converted to javascript by Viljo Viitanen
*
* FreeOTP Decoder Javascript
*
* Copyright 2015 Philip Sharp, Viljo Viitanen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<style>
p{margin:50px}
div{margin:10px}
</style>
<a href="https://forge.citizen4.eu/miklo/freeotp-export">Instructions at git</a>
<br>
FreeOTP adb backup file: <input id="file" type="file">
<script src="pako/dist/pako_inflate.js"></script>
<script src="js-untar/build/dist/untar.js"></script>
<script src="qrcodejs/qrcode.min.js"></script>
<script type="text/javascript">
//ported from original PHP function
/**
* Convert stored secret into otpauth URI parameter
*
* Port of encodeInternal() method from Google Authenticator via FreeOTP
* @link https://fedorahosted.org/freeotp/browser/android/app/src/main/java/com/google/android/apps/authenticator/Base32String.java
*
* @param array $data Internal representation of the secret as byte array
* @return string Secret as Base32 encode string
*/
function encodeSecretBytes(data) {
DIGITS = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','2','3','4','5','6','7','']; // 32 chars
SHIFT = 5; // trailing zeros in binary representation of the size of the alphabet
MASK = 31; // one less than size of alphabet
// SHIFT is the number of bits per output character, so the length of the
// output is the length of the input multiplied by 8/SHIFT, rounded up.
if (data.length >= (1 << 28)) {
alert('Bad Secret');
}
outputLength = (data.length * 8 + SHIFT - 1) / SHIFT;
result = '';
buffer = data[0];
next = 1;
bitsLeft = 8;
while (bitsLeft > 0 || next < data.length) {
if (bitsLeft < SHIFT) {
if (next < data.length) {
buffer <<= 8;
buffer |= (data[next++] & 0xff);
bitsLeft += 8;
} else {
pad = SHIFT - bitsLeft;
buffer <<= pad;
bitsLeft += pad;
}
}
index = MASK & (buffer >> (bitsLeft - SHIFT));
bitsLeft -= SHIFT;
result += DIGITS[index];
}
return result;
}
//small helper function
function querydata(p)
{
var r= [];
for (var d in p) {
r.push(encodeURIComponent(d)+"="+encodeURIComponent(p[d]));
}
return r.join("&");
}
function parsexml(param) {
parser = new DOMParser();
xml=parser.parseFromString(param, "text/xml");
s=xml.getElementsByTagName('string');
if (s.length==0) {
alert('Cannot parse the file');
}
for (var i = 0, len = s.length; i < len; i++) {
name=s[i].attributes['name'].value;
if (name == 'tokenOrder') {
continue;
}
j=JSON.parse(s[i].textContent);
console.log(j);
issuer = j['issuerInt'] ? j['issuerInt'] : j['issuerExt'];
issuer = issuer ? issuer : j['issuerAlt'];
issuer = issuer ? issuer : j['labelAlt'];
issuer = issuer ? issuer : 'Unknown'+i;
param={
'secret' : encodeSecretBytes(j['secret']),
'issuer' : issuer,
'algorithm' : j['algo'],
'digits' : j['digits'],
'period' : j['period'],
}
label = (j['issuerExt']) ? j['issuerExt']+':'+j['label'] : j['label'];
uri='otpauth://'+j['type'].toLowerCase()+'/'+encodeURIComponent(label)+'?'+querydata(param);
console.log(uri);
document.getElementById("file").insertAdjacentHTML('afterend', '<p><div>'+name+'</div><div id="'+i+'"></div>' );
new QRCode(document.getElementById(i),uri);
}
}
var reader = new FileReader();
var decoder = new TextDecoder();
if (!decoder) {
alert ("error: browser does not support TextDecoder")
}
reader.onload = function(event) {
try {
decompressed = pako.inflate(event.target.result.slice(24));
}
catch(err) {
alert("cannot decompress the adb backup file: "+err);
return;
}
//argh.. pako doesn't return an arraybuffer, and untar expects an arraybuffer. So...
var arbuf = new ArrayBuffer(decompressed.byteLength);
new Uint8Array(arbuf).set(new Uint8Array(decompressed));
untar(arbuf).then(function(files) {
var found=0;
for (var i = 0, len = files.length; i < len; i++) {
if (files[i].name == "apps/org.fedorahosted.freeotp/sp/tokens.xml") {
found=1;
break;
}
}
if (!found) {
alert("did not find tokens.xml in adb backup file.");
}
else {
parsexml(decoder.decode(files[i].buffer));
}
});
}
document.getElementById("file").onchange=function() {
reader.readAsArrayBuffer(this.files[0]);
};
</script>