1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// Author: cirakg & claude ai
// Summary: Tests for the Aegis reader: database versions 1-3, types, parameters, groups, icons, encryption and bad input.
import Toybox.Lang;
import Toybox.Test;
// Database version 3: types, algorithms, digits, periods, groups listed
// after the entries, favourites, icons and Unicode.
(:test)
function aegisReadsDatabaseVersion3(logger as Test.Logger) as Boolean {
var icon = "";
for (var i = 0; i < 200; i++) {
icon += "iVBORw0KGgoAAAANSUhEUgAAA";
}
var entries = [
AegisFixtures.entry("totp", "GitHub", "alice", AegisFixtures.SECRET_1, "SHA1", 6, 30,
"\"icon\": \"" + icon + "\", \"icon_mime\": \"image/png\", \"groups\": [\"g-work\", \"g-other\"], "),
AegisFixtures.entry("totp", "Bänk €", "bob", AegisFixtures.SECRET_2, "SHA256", 8, 60, "\"favorite\": true, "),
"{\"type\": \"hotp\", \"name\": \"carol\", \"issuer\": \"VPN\", \"info\": {\"secret\": \"" + AegisFixtures.SECRET_3
+ "\", \"algo\": \"SHA512\", \"digits\": 7, \"period\": 30, \"counter\": 12}}",
AegisFixtures.entry("steam", "Steam", "dave", AegisFixtures.SECRET_1, "SHA1", 5, 30, ""),
AegisFixtures.entry("motp", "Old", "erin", AegisFixtures.SECRET_1, "MD5", 6, 10, ""),
AegisFixtures.entry("yandex", "Ya", "frank", AegisFixtures.SECRET_1, "SHA256", 8, 30, ""),
AegisFixtures.entry("totp", "Odd", "gina", AegisFixtures.SECRET_1, "SHA1", 6, 20, "\"groups\": [\"g-missing\"], "),
AegisFixtures.entry("totp", "Slow", "hal", AegisFixtures.SECRET_1, "SHA1", 6, 50, "")
];
var groups = "[{\"uuid\": \"g-work\", \"name\": \"Work\"}, {\"uuid\": \"g-other\", \"name\": \"Other\"}]";
var reader = AegisFixtures.read(AegisFixtures.export(3, entries, groups));
if (reader.error != null || reader.items.size() != 8) {
logger.error("error " + reader.error + ", items " + reader.items.size());
return false;
}
var it = AegisFixtures.items(reader);
var github = it[0].entry;
var bank = it[1].entry;
var vpn = it[2].entry;
var steam = it[3].entry;
var odd = it[6].entry;
var slow = it[7].entry;
if (github == null || bank == null || vpn == null || steam == null || odd == null || slow == null) {
for (var i = 0; i < it.size(); i++) {
logger.error("item " + i + ": " + it[i].problem + " / " + it[i].skipped);
}
return false;
}
var checks = [
github.name.equals("GitHub") && github.account.equals("alice"),
github.group.equals("Work"),
bank.name.equals("Bänk €") && bank.algorithm == Hashes.SHA256 && bank.digits == 8 && bank.period == 60,
bank.favorite,
vpn.type == Entry.OTP_HOTP && vpn.counter == 12l && vpn.digits == 7 && vpn.algorithm == Hashes.SHA512,
steam.type == Entry.OTP_STEAM,
it[4].skipped != null && it[5].skipped != null,
odd.group.equals("") && odd.period == 20 && slow.period == 50,
Bytes.equal(github.secret as ByteArray, Bytes.fromUtf8("12345678901234567890"))
];
for (var i = 0; i < checks.size(); i++) {
if (!(checks[i] as Boolean)) {
logger.error("check " + i + " failed");
return false;
}
}
return true;
}
// Database versions 1 and 2 name the group directly.
(:test)
function aegisReadsOlderDatabaseVersions(logger as Test.Logger) as Boolean {
for (var v = 1; v <= 2; v++) {
var reader = AegisFixtures.read(AegisFixtures.export(v,
[AegisFixtures.entry("totp", "Mail", "ann", AegisFixtures.SECRET_1, "SHA1", 6, 30, "\"group\": \"Private\", ")], ""));
var e = reader.items.size() == 1 ? reader.item(0).entry : null;
if (e == null || !e.group.equals("Private")) {
logger.error("database version " + v + " failed: " + reader.error);
return false;
}
}
return true;
}
(:test)
function aegisRefusesEncryptedExports(logger as Test.Logger) as Boolean {
var slots = "{\"version\": 1, \"header\": {\"slots\": [{\"type\": 1}], \"params\": {}}, \"db\": \"c2VjcmV0\"}";
var dbString = "{\"version\": 1, \"header\": {\"slots\": null}, \"db\": \"c2VjcmV0\"}";
var a = AegisFixtures.read(slots);
var b = AegisFixtures.read(dbString);
return a.error != null && (a.error as String).find("encrypted") != null
&& b.error != null && (b.error as String).find("encrypted") != null;
}
(:test)
function aegisReportsBadEntriesAndDocuments(logger as Test.Logger) as Boolean {
var entries = [
AegisFixtures.entry("totp", "Nine", "x", AegisFixtures.SECRET_1, "SHA1", 9, 30, ""),
"{\"type\": \"totp\", \"name\": \"dup\", \"info\": {\"secret\": \"" + AegisFixtures.SECRET_1 + "\", \"secret\": \""
+ AegisFixtures.SECRET_2 + "\", \"algo\": \"SHA1\", \"digits\": 6, \"period\": 30}}",
AegisFixtures.entry("totp", "Bad", "y", "NOT-BASE32!", "SHA1", 6, 30, ""),
"{\"type\": \"totp\", \"name\": \"z\", \"info\": {\"secret\": \"" + AegisFixtures.SECRET_1 + "\", \"digits\": 6.5}}"
];
var reader = AegisFixtures.read(AegisFixtures.export(3, entries, ""));
var it = AegisFixtures.items(reader);
if (reader.error != null || it.size() != 4) {
logger.error("error " + reader.error);
return false;
}
var problems = [it[0].problem, it[1].problem, it[2].problem, it[3].problem];
var needles = ["9 digits", "twice", "Base32", "whole number"];
for (var i = 0; i < 4; i++) {
var p = problems[i];
if (p == null || (p as String).find(needles[i]) == null) {
logger.error("entry " + i + ": " + p);
return false;
}
}
var notJson = AegisFixtures.read("{\"version\": 1, \"db\": [1,]}");
var notAegis = AegisFixtures.read("{\"hello\": \"world\"}");
var newer = AegisFixtures.read("{\"version\": 2, \"db\": {\"version\": 3, \"entries\": []}}");
var deep = "{\"version\": 1, \"x\": " + "[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]" + "}";
return notJson.error != null && notAegis.error != null && newer.error != null
&& (newer.error as String).find("version 2") != null && AegisFixtures.read(deep).error != null;
}
(:test)
function aegisSaysWhenTheTextIsIncomplete(logger as Test.Logger) as Boolean {
var full = AegisFixtures.export(3, [AegisFixtures.totp("A", "a", AegisFixtures.SECRET_1), AegisFixtures.totp("B", "b", AegisFixtures.SECRET_2)], "");
return AegisFixtures.status(full.substring(0, full.length() / 2) as String) == AegisReader.INCOMPLETE
&& AegisFixtures.status(full) == AegisReader.DONE;
}
|