| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- /// Provides encode and decode functions for the otpauth-migration URI format used to import into and export 2FA secrets from the Google Authenticator app
- library otpauth_migration;
- import 'dart:convert';
- import 'dart:typed_data';
- import 'generated/GoogleAuthenticatorImport.pb.dart';
- /// A stateless class (not a singleton) that provides encode and decode functions for the otpauth-migration URI format used to import into and export 2FA secrets from the Google Authenticator app
- class OtpAuthMigration {
- final List<String> _rfc3548 = [
- "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"
- ];
- /// encode given list of optauth URIs into a single otpauth-migration URI
- String encode(List<String> otpAuths,
- {bool debug = false,
- int version = -1,
- int batchSize = -1,
- int batchIndex = -1,
- int batchId = -1}) {
- var gai = GoogleAuthenticatorImport();
- for (var otp in otpAuths) {
- var uri = Uri.parse(otp);
- //uri.queryParameters.forEach((k, v) {
- // print('key: $k - value: $v');
- // });
- var gaip = GoogleAuthenticatorImport_OtpParameters();
- gaip.name = Uri.decodeFull(uri.path.substring(1));
- //print("name = ${gaip.name}");
- try {
- gaip.secret = _encodeBase32(uri.queryParameters['secret']);
- } catch (e) {
- return "";
- }
- //print("issuer = ${uri.queryParameters['issuer']}");
- var issuer = uri.queryParameters['issuer'];
- if (issuer != null && issuer != "") {
- gaip.issuer = issuer;
- }
- gaip.type = GoogleAuthenticatorImport_OtpType.OTP_TYPE_TOTP;
- gaip.algorithm = GoogleAuthenticatorImport_Algorithm.ALGORITHM_SHA1;
- gaip.digits = GoogleAuthenticatorImport_DigitCount.DIGIT_COUNT_SIX;
- gai.otpParameters.add(gaip);
- if (debug) print(gaip);
- }
- if (version >= 0) gai.version = version;
- if (batchSize >= 0) gai.batchSize = batchSize;
- if (batchIndex >= 0) gai.batchIndex = batchIndex;
- if (batchId >= 0) gai.batchId = batchId;
- final bytes = gai.writeToBuffer();
- return "otpauth-migration://offline?data=${base64.encode(bytes)}";
- }
-
- int batchSize(String value) {
- RegExp exp = RegExp(r"otpauth-migration\:\/\/offline\?data=(.*)$");
- final match = exp.firstMatch(value);
- final encodedUrl = match?.group(1);
- if (encodedUrl != null) {
- final encoded = Uri.decodeComponent(encodedUrl);
- var decoded = base64.decode(encoded);
- try {
- final gai = GoogleAuthenticatorImport.fromBuffer(decoded);
- return gai.batchSize;
- } catch(e) {
- return -1;
- }
- } else {
- return -1;
- }
- }
-
- int batchIndex(String value) {
- RegExp exp = RegExp(r"otpauth-migration\:\/\/offline\?data=(.*)$");
- final match = exp.firstMatch(value);
- final encodedUrl = match?.group(1);
- if (encodedUrl != null) {
- final encoded = Uri.decodeComponent(encodedUrl);
- var decoded = base64.decode(encoded);
- try {
- final gai = GoogleAuthenticatorImport.fromBuffer(decoded);
- return gai.batchIndex;
- } catch(e) {
- return -1;
- }
- } else {
- return -1;
- }
- }
- /// decode a given otpauth-migration URI into a list of one or more otpauth URIs
- List<String> decode(String value, {bool debug = false}) {
- // check prefix "otpauth-migration://offline?data="
- // extract suffix - Base64 encode
- List<String> results = [];
- RegExp exp = RegExp(r"otpauth-migration\:\/\/offline\?data=(.*)$");
- final match = exp.firstMatch(value);
- final encodedUrl = match?.group(1);
- if (encodedUrl != null) {
- final encoded = Uri.decodeComponent(encodedUrl);
- var decoded = base64.decode(encoded);
- try {
- final gai = GoogleAuthenticatorImport.fromBuffer(decoded);
- if (debug) print(gai);
- //print(gai.otpParameters.length);
- for (var param in gai.otpParameters) {
- //print(param);
- var base32 = _decodeBase32(param.secret);
- //print("otpauth://totp/${param.name}?secret=${base32}");
- final name = Uri.encodeFull(param.name);
- final issuer = Uri.encodeComponent(param.issuer);
- String algorithm = "";
- switch(param.algorithm) {
- case GoogleAuthenticatorImport_Algorithm.ALGORITHM_SHA1:
- algorithm = "&algorithm=SHA1";
- break;
- case GoogleAuthenticatorImport_Algorithm.ALGORITHM_SHA256:
- algorithm = "&algorithm=SHA256";
- break;
- case GoogleAuthenticatorImport_Algorithm.ALGORITHM_SHA512:
- algorithm = "&algorithm=SHA512";
- break;
- case GoogleAuthenticatorImport_Algorithm.ALGORITHM_MD5:
- algorithm = "&algorithm=MD5";
- break;
- default:
- algorithm = "";
- break;
- }
- String digits = "";
- switch(param.digits) {
- case GoogleAuthenticatorImport_DigitCount.DIGIT_COUNT_SIX:
- digits = "&digits=6";
- break;
- case GoogleAuthenticatorImport_DigitCount.DIGIT_COUNT_EIGHT:
- digits = "&digits=8";
- break;
- default:
- digits = "";
- break;
- }
- results.add("otpauth://totp/$name?secret=$base32&issuer=$issuer$algorithm$digits&period=30");
- }
- //print("good");
- return results;
- } catch (e) {
- return results;
- }
- } else {
- //print("bad");
- return [];
- }
- }
- List<int> _encodeBase32(String? s) {
- int i = 0;
- var j = 0;
- if (s != null) {
- Uint8List result = Uint8List((s.length * 5) ~/ 8);
- while (i < s.length) {
- // ZERO
- var c = s[i];
- var k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = k << 3;
- i++;
- c = s[i];
- k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = result[j] | (k >> 2);
- //print("---> ${result[j]}");
- j++;
- // ONE
- result[j] = k << 6;
- i++;
- c = s[i];
- k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = result[j] | (k << 1);
- i++;
- c = s[i];
- k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = result[j] | (k >> 4);
- //print("---> ${result[j]}");
- j++;
- // TWO
- result[j] = k << 4;
- i++;
- c = s[i];
- k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = result[j] | (k >> 1);
- //print("---> ${result[j]}");
- j++;
- // THREE
- result[j] = k << 7;
- i++;
- c = s[i];
- k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = result[j] | (k << 2);
- i++;
- c = s[i];
- k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = result[j] | (k >> 3);
- //print("---> ${result[j]}");
- j++;
- // FOUR
- result[j] = k << 5;
- i++;
- c = s[i];
- k = _rfc3548.indexOf(c);
- //print(k);
- result[j] = result[j] | k;
- i++;
- //print("---> ${result[j]}");
- j++;
- }
- //print(result);
- return result;
- }
- return Uint8List(0);
- }
- String _decodeBase32(List<int> s) {
- //print(s);
- Uint8List ulist = s as Uint8List;
- String result = "";
- var i = 0;
- while (i < ulist.length) {
- var q0 = ulist[i] & 0xF8;
- q0 = q0 >> 3;
- //print(_rfc3548[q0]);
- result += _rfc3548[q0];
- var q1 = ulist[i++] & 0x07;
- q1 = q1 << 2;
- var temp = ulist[i] & 0xC0;
- temp = temp >> 6;
- q1 = q1 + temp;
- //print(_rfc3548[q1]);
- result += _rfc3548[q1];
- var q2 = ulist[i] & 0x3E;
- q2 = q2 >> 1;
- //print(_rfc3548[q2]);
- result += _rfc3548[q2];
- var q3 = ulist[i++] & 0x01;
- q3 = q3 << 4;
- temp = ulist[i] & 0xF0;
- temp = temp >> 4;
- q3 = q3 + temp;
- //print(_rfc3548[q3]);
- result += _rfc3548[q3];
- var q4 = ulist[i++] & 0x0F;
- q4 = q4 << 1;
- temp = ulist[i] & 0x80;
- temp = temp >> 7;
- q4 = q4 + temp;
- //print(_rfc3548[q4]);
- result += _rfc3548[q4];
- var q5 = ulist[i] & 0x7c;
- q5 = q5 >> 2;
- //print(_rfc3548[q5]);
- result += _rfc3548[q5];
- var q6 = ulist[i++] & 0x03;
- q6 = q6 << 3;
- temp = ulist[i] & 0xE0;
- temp = temp >> 5;
- q6 = q6 + temp;
- //print(_rfc3548[q6]);
- result += _rfc3548[q6];
- var q7 = ulist[i++] & 0x1F;
- //print(_rfc3548[q7]);
- result += _rfc3548[q7];
- }
- //print(result);
- return result;
- }
- }
|