aboutsummaryrefslogtreecommitdiff
path: root/test/import/AegisTest.mc
blob: eaad0393106c1ac3973ebdce96bc52336c4bc0e5 (plain) (blame)
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 = reader.items;
    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.items[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 = reader.items;
    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;
}