Merge pull request #1027 from alexbakker/2fas-schema3

Add support for importing 2FAS schema v3 backups
This commit is contained in:
Alexander Bakker 2022-11-15 22:45:18 +01:00 committed by GitHub
commit f8d60fb1b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 202 additions and 8 deletions

View File

@ -7,6 +7,7 @@ import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.encoding.Base32;
import com.beemdevelopment.aegis.encoding.Base64;
import com.beemdevelopment.aegis.encoding.EncodingException;
import com.beemdevelopment.aegis.otp.HotpInfo;
import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.otp.OtpInfoException;
import com.beemdevelopment.aegis.otp.TotpInfo;
@ -59,7 +60,7 @@ public class TwoFASImporter extends DatabaseImporter {
String json = new String(IOUtils.readAll(stream), StandardCharsets.UTF_8);
JSONObject obj = new JSONObject(json);
int version = obj.getInt("schemaVersion");
if (version > 2) {
if (version > 3) {
throw new DatabaseImporterException(String.format("Unsupported schema version: %d", version));
}
@ -173,13 +174,23 @@ public class TwoFASImporter extends DatabaseImporter {
try {
byte[] secret = Base32.decode(obj.getString("secret"));
JSONObject info = obj.getJSONObject("otp");
String issuer = info.getString("issuer");
String issuer = info.optString("issuer");
String name = info.optString("account");
int digits = info.optInt("digits", TotpInfo.DEFAULT_DIGITS);
int period = info.optInt("period", TotpInfo.DEFAULT_PERIOD);
String algorithm = info.optString("algorithm", TotpInfo.DEFAULT_ALGORITHM);
OtpInfo otp = new TotpInfo(secret, algorithm, digits, period);
OtpInfo otp;
String tokenType = JsonUtils.optString(info, "tokenType");
if (tokenType == null || tokenType.equals("TOTP")) {
int period = info.optInt("period", TotpInfo.DEFAULT_PERIOD);
otp = new TotpInfo(secret, algorithm, digits, period);
} else if (tokenType.equals("HOTP")) {
long counter = info.optLong("counter", 0);
otp = new HotpInfo(secret, algorithm, digits, counter);
} else {
throw new DatabaseImporterEntryException(String.format("Unrecognized tokenType: %s", tokenType), obj.toString());
}
return new VaultEntry(otp, name, issuer);
} catch (OtpInfoException | JSONException | EncodingException e) {
throw new DatabaseImporterEntryException(e, obj.toString());

View File

@ -238,7 +238,7 @@ public class DatabaseImporterTest {
public void testImportTwoFASAuthenticatorSchema1() throws DatabaseImporterException, IOException, OtpInfoException {
List<VaultEntry> entries = importPlain(TwoFASImporter.class, "2fas_authenticator.json");
for (VaultEntry entry : entries) {
// 2FAS Authenticator doesn't support HOTP, different hash algorithms, periods or digits, so fix those up here
// 2FAS Authenticator schema v1 doesn't support HOTP, different hash algorithms, periods or digits, so fix those up here
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.setInfo(new TotpInfo(entryVector.getInfo().getSecret()));
checkImportedEntry(entryVector, entry);
@ -248,7 +248,7 @@ public class DatabaseImporterTest {
@Test
public void testImportTwoFASAuthenticatorSchema2Plain() throws DatabaseImporterException, IOException, OtpInfoException {
List<VaultEntry> entries = importPlain(TwoFASImporter.class, "2fas_authenticator_plain.2fas");
checkImportedTwoFASEntries(entries);
checkImportedTwoFASSchema2Entries(entries);
}
@Test
@ -257,7 +257,22 @@ public class DatabaseImporterTest {
final char[] password = "test".toCharArray();
return ((TwoFASImporter.EncryptedState) encryptedState).decrypt(password);
});
checkImportedTwoFASEntries(entries);
checkImportedTwoFASSchema2Entries(entries);
}
@Test
public void testImportTwoFASAuthenticatorSchema3Plain() throws DatabaseImporterException, IOException, OtpInfoException {
List<VaultEntry> entries = importPlain(TwoFASImporter.class, "2fas_authenticator_plain_v3.2fas");
checkImportedEntries(entries);
}
@Test
public void testImportTwoFASAuthenticatorSchema3Encrypted() throws DatabaseImporterException, IOException, OtpInfoException {
List<VaultEntry> entries = importEncrypted(TwoFASImporter.class, "2fas_authenticator_encrypted_v3.2fas", encryptedState -> {
final char[] password = "test".toCharArray();
return ((TwoFASImporter.EncryptedState) encryptedState).decrypt(password);
});
checkImportedEntries(entries);
}
private List<VaultEntry> importPlain(Class<? extends DatabaseImporter> type, String resName)
@ -302,7 +317,7 @@ public class DatabaseImporterTest {
return result.getEntries();
}
private void checkImportedTwoFASEntries(List<VaultEntry> entries) throws OtpInfoException {
private void checkImportedTwoFASSchema2Entries(List<VaultEntry> entries) throws OtpInfoException {
for (VaultEntry entry : entries) {
// 2FAS Authenticator doesn't support certain features, so fix those entries up here
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());

View File

@ -0,0 +1,11 @@
{
"services": [],
"updatedAt": 1668548484538,
"schemaVersion": 3,
"appVersionCode": 4000004,
"appVersionName": "4.0.0",
"appOrigin": "android",
"groups": [],
"servicesEncrypted": "oAAWclilpn+Y9ol1SENxfw2c1zejXyYdEkC6+G3tpbep7MXXvC\/xAgzwS+RFt\/jXoivKsrpVCFrRbuhziyHUfaWvOmHCydro5NlrQH9SHbeasoGBeoKH+x3H7v5oDXE4PO4L2+rShWfHILmgkum1HHPfc85ojm2qp5NgsCbVxuMPUnnwb+k8BLAsrPZMx708LccaoZLYz+Bkd7Vb6ONYn\/8gkOulNp9Ue9O\/3Q\/ZVLHpR+nveszobXoHAukRNDQ8kNDnmetjqH1xbuJf\/0UMO6kgRs0fdxZvrBg3afFasymlNH6Iwz8S+yfOT32b5YuLcZNo2OkhaV1oqcL+aZEYXSpcPZM3iqB817agI+qeUPQvsqwkgu\/whzMrGVBb1DAf+b7aPEHsxXCGk5E72xgU5dnZwAR6x12vO8LUhuRywEXi7X2klQ1cpiOSzi7ZefnE61dTgJAmbVoMBNM5tXtvjZmuM+zpLHBzl6Z6lIFWabGT2G3b6KQ0yadE3Wmw4FK\/zG0i6bC4qRnA2I0V75CJCIboxOTiN2QodhITZAQvQFfn5AzOUk0bdwJP5XeNKXl1eAWaDzBzo1vNELlF0m3WH9bEXLHzE6Q1IoJmZX\/XY9e7TemwSIIRl0NvZ3Vi\/LrNpYzCQJ8UrcBQzjevuvWuBU4k8xyVIHo9CGF5Y10BeWi1dC2Ws5OpXK9hfni92RWYRVcaBgQjFAJ\/TpE\/ynL33FD\/KTGVwp6wSE6CCedWFdsxk3ushckU53yoUqHAwvRB76Kk3\/TrkWFj3Oph1cTdgPnHz\/0\/6VaphOuNkMtebeAzDtrV55IKqzQZ+6GxRgr5sFDVcy2djrUYGOPemCvYIuXyVhpRq1dbTfJ0lsJ+fiVmnXxmb3hNXskT87QW+TVDFxqfTD7h5RXVFY7IFdXTd5bwq6EyAfvJtuZqu7fX\/Ft1Y3MCPEFbQw9QLyz6CbSZJmG1CafgrIS+R2PlBrJM8LlhId\/BebkwfxXX4tGwI4vAVdw2XmPjeAjZzXiTEpneSz5tFp\/BHoolFnmJkCm+z+F\/zrZbf0K4qtrOiqEdKs+qNWww6h7sEFPPvaXyYBdNGm23F+Rb2N2ZZnUs8tHPaltsI4LSwi+7gNgRnVdI3Ljpp85xbRfEI0OZ7ZJIeS5w3nBWUOu3\/pOBpEsCtwSkpTr9scHwiCC5YDSdYvFSAzZEgIDNVxT28AHFhoQJBcG7JVPIa52mtACYExqoRdbTlQ4DJqMG2WxtnhV\/AHzYqahPD+\/RHmV9yC23vddgMykDrzSQHVxOXzODpioGYSZIZrYIBCGaxIEa7F4wTp7XjR60jvWtUkInr0RontbE7lYMnMTIxttgMqQto2WDxKWMqroMEWHnUnMbfIL5B+7+UpoWN7Zp09z8p\/znXrMn25\/EmcXTcmGXBdP9\/Qh5CBa0v5XUafVvHrTDBoZCDE7RaHnRcou5RBTWIgqDNO+yfKc08PDpw0UJ0QdtxLrKgzkgbIXYvHS+ksGPq5wubp1IrkjYuUwjKKgSaWAuEyB5r4Oa54jdz8KNuJskMLSxqndz0TV2qSBGbB7YdyxR8KyGpG6hfqqudkGmYUkl+nTdeppTGnry67ya9B8iYcijMXBLvk\/3DKVDisv8K4r9kSxvPIfzd5MO\/ui8pRXA+FCRThBxqSS\/lThpcyEIj90VVYxmVyDaA47XE255\/0kEqQm4v67HIwGqIVsK7AfjJkQUAKlVzraKyJB441pDiNParlI5ln0gbpeSnc9iH92zRXfwKZCyF1\/009\/JVbrKIcfNEvDpTUaexwcjetYqc8aLLSEdatqa515Nxhp6nPvHdV1sj1it81Fl+\/nCGrTZ\/YMcYoPoA9Sv7pZU3RMDbCA\/dREsSPGQ0+mCNcwHHe2ZTuki5DIB+nsAsV16g\/OH0vxDr25BLqXuLjhx8Jc9Rlr6jg+32HOJXrRuoSAsFCzyCmtfcwxU0v4+IM\/6cq8\/qdlK8D2zvxMZPL7SUje3\/VB77PHDkceof+DvtmJ\/pUlmXHdSApdXbfWjlfP3DWWYmCZhHlO3pg0TqPKufVSZWFU7AfRuIimNgRs4s\/2ixNs9p9DZbzno6bb5YLh7ql3TqsL2xeYXBTQWpSkGH\/+0BvEOkXGjxbNxdbsjYBxvOp2bVjwCX0omL3C8K84IVnQDZSMEftrLtIlbBmnabN7g5qKzUtNtfN1hB4EFqq7LVvGgkOUCZCZx2dWwmgHqeUEvtKhdlvFuPNEVtd5co0JieFhFJtGqVbGkjMBg22x0uMclaGhma2xYAcgEjiBdg1QVdHq+1gnYyowKG4YGCpXrshC3e4\/ujk+iUmPGkYGYrweglZAnVm+U15OXoJn0lzjBO29jhfZi8d\/DirG5b4lT0C\/6S\/UhG5bGrNcirQsvr5SIbQfoSaLXlJ2SCKuUw51wlF9khENW3o2GPn3L2tlFOaMf5mbuHi4P8eJDhWY2KA6uTEWddjbfVILSPhhKcpWdBu01CxIP\/2gQS5Ou1hjcqyN1jCSxIFVANYkr+IpKooers03bPVmTrLGYalmU+CI8FBYgkZnL4P+OuSqivMxvcscLN25xJ7kz+i2eRz98HoNsOA1Dx+lZEfeailmJh8F\/a\/agUCVWufAem0vC6mZMWjj8QKORpOwh+0wgV2rmWJK6DTPA6lH1ivnqdrE\/JE8V4wOTCE+NeeBb7VIRe0Q4q2\/ORMVPK\/YyrKwARYhmC1L3FuEsFmeimhMweiIzAc7AWeykRY8xS0Laujy7VRzal9y3C6TUSUSeriApPhfhpQmNO8jjYHR22vaA530W4SKOiNEAYdFOolUyoLrUwgADvoH2MWv+Z4JpO4GBGTKUCUbnqn32gMpDgNmc3FzXtpyASF0=:F\/kH8kq9g0PjEEuQmL8eBnoN6eI0oz8AM9UJr\/ADTHoTrqDnbUY4joUQw2dyeNRYxsw1Oj0nGBUxg90dnDhs79X5C\/D2tsgns03tADJUe5I\/hnS8O+njyDcYVODUoS5eC5pHTXzhLmeKNZjhKuVr+XGlKdhnv72gn8JvwjEiwNh52T6RnD10pWwbxLJaMdkBKps7sFDfXxJofCsU8u\/aYmAAolMkViKsTJ1tHcaEz8eG0R4jhB3Lm8m8Cdtx+nGX57pJk+ptmEv+Px\/5rANfDxnNJ6iB+zg9xBFJk+YbJlQ6Cf6dQFye0gAlT3KWi26jA+Ozp+EoGHMm2TCRKnpemw==:q5DxA6tYE2A2AHWz",
"reference": "tQdQEgDTckuE\/Q02KeQJxxDYKAsn6mdAW6EYhtI4l9vN7RhVMFyXMYVDnH2Ujnj\/zWlV8lRtUfl3FWBpnBWM8YINff6Un3zTM9jEf0YYftj0HQzd8dnbM0u2ZxCftRblqk0dAiekjA05Vo\/ma6Y+mreGJpj\/awMP8OvgYWSu03F01PfklUOEmxSUrIMk6\/A8HiAZ\/PH9ayDvjOSZHZ05zahndayzqBSecdt0w+bejNFUM4SRWWC+njStxZGz4IfMHkkiGJoYyg0D5ezRHwINcw3u+1IaEzKJ8hqta7qXh+cx4Jig6rqB0cUQbUqY2LoCsEB9POKdlfBihVxKLKFss+TuLZW6DxtUNbAAaImSMBc=:F\/kH8kq9g0PjEEuQmL8eBnoN6eI0oz8AM9UJr\/ADTHoTrqDnbUY4joUQw2dyeNRYxsw1Oj0nGBUxg90dnDhs79X5C\/D2tsgns03tADJUe5I\/hnS8O+njyDcYVODUoS5eC5pHTXzhLmeKNZjhKuVr+XGlKdhnv72gn8JvwjEiwNh52T6RnD10pWwbxLJaMdkBKps7sFDfXxJofCsU8u\/aYmAAolMkViKsTJ1tHcaEz8eG0R4jhB3Lm8m8Cdtx+nGX57pJk+ptmEv+Px\/5rANfDxnNJ6iB+zg9xBFJk+YbJlQ6Cf6dQFye0gAlT3KWi26jA+Ozp+EoGHMm2TCRKnpemw==:giXb4rEQl9ofTh2T"
}

View File

@ -0,0 +1,157 @@
{
"services": [
{
"name": "Deno",
"secret": "4SJHB4GSD43FZBAI7C2HLRJGPQ",
"updatedAt": 1668540633217,
"otp": {
"label": "Deno:Mason",
"account": "Mason",
"issuer": "Deno",
"digits": 6,
"period": 30,
"algorithm": "SHA1",
"tokenType": "TOTP",
"source": "Link"
},
"order": {
"position": 0
},
"icon": {
"selected": "IconCollection",
"iconCollection": {
"id": "a5b3fb65-4ec5-43e6-8ec1-49e24ca9e7ad"
}
}
},
{
"name": "SPDX",
"secret": "5OM4WOOGPLQEF6UGN3CPEOOLWU",
"updatedAt": 1668540644738,
"otp": {
"label": "SPDX:James",
"account": "James",
"issuer": "SPDX",
"digits": 7,
"period": 20,
"algorithm": "SHA256",
"tokenType": "TOTP",
"source": "Link"
},
"order": {
"position": 1
},
"icon": {
"selected": "IconCollection",
"iconCollection": {
"id": "a5b3fb65-4ec5-43e6-8ec1-49e24ca9e7ad"
}
}
},
{
"name": "Airbnb",
"secret": "7ELGJSGXNCCTV3O6LKJWYFV2RA",
"updatedAt": 1668540650005,
"otp": {
"label": "Airbnb:Elijah",
"account": "Elijah",
"issuer": "Airbnb",
"digits": 8,
"period": 50,
"algorithm": "SHA512",
"tokenType": "TOTP",
"source": "Link"
},
"order": {
"position": 2
},
"icon": {
"selected": "IconCollection",
"iconCollection": {
"id": "a5b3fb65-4ec5-43e6-8ec1-49e24ca9e7ad"
}
}
},
{
"name": "Issuu",
"secret": "YOOMIXWS5GN6RTBPUFFWKTW5M4",
"updatedAt": 1668540656157,
"otp": {
"label": "Issuu:James",
"account": "James",
"issuer": "Issuu",
"digits": 6,
"period": 30,
"algorithm": "SHA1",
"counter": 1,
"tokenType": "HOTP",
"source": "Link"
},
"order": {
"position": 3
},
"icon": {
"selected": "IconCollection",
"iconCollection": {
"id": "a5b3fb65-4ec5-43e6-8ec1-49e24ca9e7ad"
}
}
},
{
"name": "Air Canada",
"secret": "KUVJJOM753IHTNDSZVCNKL7GII",
"updatedAt": 1668540661393,
"otp": {
"label": "Air Canada:Benjamin",
"account": "Benjamin",
"issuer": "Air Canada",
"digits": 7,
"period": 30,
"algorithm": "SHA256",
"counter": 50,
"tokenType": "HOTP",
"source": "Link"
},
"order": {
"position": 4
},
"icon": {
"selected": "IconCollection",
"iconCollection": {
"id": "a5b3fb65-4ec5-43e6-8ec1-49e24ca9e7ad"
}
}
},
{
"name": "WWE",
"secret": "5VAML3X35THCEBVRLV24CGBKOY",
"updatedAt": 1668540666422,
"otp": {
"label": "WWE:Mason",
"account": "Mason",
"issuer": "WWE",
"digits": 8,
"period": 30,
"algorithm": "SHA512",
"counter": 10300,
"tokenType": "HOTP",
"source": "Link"
},
"order": {
"position": 5
},
"icon": {
"selected": "IconCollection",
"iconCollection": {
"id": "a5b3fb65-4ec5-43e6-8ec1-49e24ca9e7ad"
}
}
}
],
"updatedAt": 1668540786699,
"schemaVersion": 3,
"appVersionCode": 4000004,
"appVersionName": "4.0.0",
"appOrigin": "android",
"groups": []
}