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
|
// Author: cirakg & claude ai
// Summary: Builders for fake Aegis exports and otpauth text used by the import tests (all secrets are RFC test keys).
import Toybox.Lang;
import Toybox.Test;
(:test)
module AegisFixtures {
const SECRET_1 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; // lint: allow-literal (RFC 4226 key "12345678901234567890")
const SECRET_2 = "JBSWY3DPEHPK3PXP"; // lint: allow-literal (the classic "Hello!" demo key)
const SECRET_3 = "MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43U"; // lint: allow-literal (Base32 of "abcdefghijklmnopqrst")
// One Aegis entry. extra is inserted verbatim (other fields, icon...).
function entry(type as String, issuer as String, name as String, secret as String, algo as String, digits as Number,
period as Number, extra as String) as String {
return "{\"type\": \"" + type + "\", \"uuid\": \"0000\", \"name\": \"" + name + "\", \"issuer\": \"" + issuer
+ "\", \"note\": \"private note\", " + extra
+ "\"info\": {\"secret\": \"" + secret + "\", \"algo\": \"" + algo + "\", \"digits\": " + digits
+ ", \"period\": " + period + "}}";
}
function totp(issuer as String, name as String, secret as String) as String {
return entry("totp", issuer, name, secret, "SHA1", 6, 30, "");
}
// A whole unencrypted export (vault version 1) of the given database
// version, with entries joined by commas and groups after the entries.
function export(dbVersion as Number, entries as Array<String>, groups as String) as String {
var joined = "";
for (var i = 0; i < entries.size(); i++) {
joined += (i > 0 ? ",\n" : "") + entries[i];
}
return "{\"version\": 1, \"header\": {\"slots\": null, \"params\": null}, \"db\": {\"version\": " + dbVersion
+ ", \"entries\": [\n" + joined + "\n]" + (groups.length() > 0 ? ", \"groups\": " + groups : "") + "}}";
}
// Runs the reader to the end (or a non-running status).
// Every parsed item, unpacked.
function items(reader as AegisReader) as Array<ImportItem> {
var out = [] as Array<ImportItem>;
for (var i = 0; i < reader.items.size(); i++) {
out.add(reader.item(i));
}
return out;
}
function read(text as String) as AegisReader {
var reader = new AegisReader(Bytes.fromUtf8(text));
for (var i = 0; i < 10000; i++) {
if (reader.step() != AegisReader.RUNNING) {
return reader;
}
}
return reader;
}
function status(text as String) as Number {
var reader = new AegisReader(Bytes.fromUtf8(text));
for (var i = 0; i < 10000; i++) {
var s = reader.step();
if (s != AegisReader.RUNNING) {
return s;
}
}
return -1;
}
// Runs an import to completion on vault and returns its report.
function runImport(vault as Vault, text as String, mode as Number, locked as Boolean, now as Long) as ImportReport {
var job = new Importer(vault, text, mode, locked, now);
Jobs.runToCompletion(job, 1);
return job.report;
}
// Some detail line contains needle.
function hasDetail(report as ImportReport, needle as String) as Boolean {
for (var i = 0; i < report.details.size(); i++) {
if (report.details[i].find(needle) != null) {
return true;
}
}
return false;
}
}
|