Browse Source

Initial commit

Signed-off-by: jolheiser <john.olheiser@gmail.com>
main
jolheiser 3 weeks ago
commit
df661268a2
Signed by: jolheiser GPG Key ID: B853ADA5DA7BBF7A
  1. 7
      .gitignore
  2. 3
      CHANGELOG.md
  3. 1
      README.md
  4. 30
      analysis_options.yaml
  5. 3
      lib/spectre.dart
  6. 44
      lib/src/scope.dart
  7. 84
      lib/src/spectre.dart
  8. 77
      lib/src/template.dart
  9. 362
      pubspec.lock
  10. 16
      pubspec.yaml
  11. 17
      test/spectre_test.dart
  12. 11
      test/spectre_tests.dart
  13. 75
      test/spectre_tests.xml

7
.gitignore

@ -0,0 +1,7 @@
# Files and directories created by pub.
.dart_tool/
.packages
# Conventional directory for build output.
build/
.idea/

3
CHANGELOG.md

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

1
README.md

@ -0,0 +1 @@
A simple command-line application.

30
analysis_options.yaml

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

3
lib/spectre.dart

@ -0,0 +1,3 @@
export "src/spectre.dart" show Spectre;
export "src/template.dart" show Template;
export "src/scope.dart" show Scope, Scoper, SimpleScoper, defaultScoper;

44
lib/src/scope.dart

@ -0,0 +1,44 @@
import "template.dart";
enum Scope {
authentication,
identification,
recovery,
}
Template scopeDefaultTemplate(Scope scope) {
switch (scope) {
case Scope.identification:
return Template.name;
case Scope.recovery:
return Template.phrase;
case Scope.authentication:
default:
return Template.long;
}
}
abstract class Scoper {
String scope(Scope scope);
}
class SimpleScoper implements Scoper {
final String _key;
SimpleScoper(this._key);
@override
String scope(Scope scope) {
switch (scope) {
case Scope.identification:
return "$_key.login";
case Scope.recovery:
return "$_key.answer";
case Scope.authentication:
default:
return _key;
}
}
}
var defaultScoper = SimpleScoper("com.lyndir.masterpassword");

84
lib/src/spectre.dart

@ -0,0 +1,84 @@
import 'dart:typed_data';
import 'dart:convert';
import 'package:dart_spectre/src/scope.dart';
import 'package:dart_spectre/src/template.dart';
import "package:pointycastle/export.dart";
final scrypt = Scrypt();
final hmac = HMac(SHA256Digest(), 64);
class Spectre {
final String _name;
final String _secret;
late Scoper _scoper;
late Uint8List _key;
Spectre(this._name, this._secret, [Scoper? scoper]) {
_scoper = scoper ?? defaultScoper;
_key = _userKey();
}
Uint8List _userKey() {
var nameBytes = utf8.encode(_name);
var secretBytes = utf8.encode(_secret);
var keyScope = utf8.encode(_scoper.scope(Scope.authentication));
var nameBytesLen = nameBytes.length;
var keySalt = keyScope +
List.from([
nameBytesLen >> 24,
nameBytesLen >> 16,
nameBytesLen >> 8,
nameBytesLen,
]) +
nameBytes;
scrypt.init(ScryptParameters(32768, 8, 2, 64, Uint8List.fromList(keySalt)));
return scrypt.process(Uint8List.fromList(secretBytes));
}
Uint8List _siteKey(String name, int counter, Scope scope) {
var nameBytes = utf8.encode(name);
var scopeBytes = utf8.encode(_scoper.scope(scope));
var nameBytesLen = nameBytes.length;
var keySalt = scopeBytes +
List.from([
nameBytesLen >> 24,
nameBytesLen >> 16,
nameBytesLen >> 8,
nameBytesLen,
]) +
nameBytes +
List.from([
counter >> 24,
counter >> 16,
counter >> 8,
counter,
]);
hmac.init(KeyParameter(_key));
return hmac.process(Uint8List.fromList(keySalt));
}
String site(String siteName, {Template? templateType, int counter = 1, Scope scope = Scope.authentication}) {
templateType = templateType ?? scopeDefaultTemplate(scope);
var siteKey = _siteKey(siteName, counter, scope);
var templateSet = templates[templateType]!;
var template = templateSet[siteKey[0]%templateSet.length];
var out = StringBuffer();
var idx = 0;
for (var rune in template.runes) {
var chars = characters[String.fromCharCode(rune)]!;
var char = chars[siteKey[++idx]%chars.length];
out.write(char);
}
return out.toString();
}
}

77
lib/src/template.dart

@ -0,0 +1,77 @@
enum Template {
maximum,
long,
medium,
short,
pin,
name,
phrase,
basic,
}
const Map<Template, List<String>> templates = {
Template.maximum: [
"anoxxxxxxxxxxxxxxxxx",
"axxxxxxxxxxxxxxxxxno",
],
Template.long: [
"CvcvnoCvcvCvcv",
"CvcvCvcvnoCvcv",
"CvcvCvcvCvcvno",
"CvccnoCvcvCvcv",
"CvccCvcvnoCvcv",
"CvccCvcvCvcvno",
"CvcvnoCvccCvcv",
"CvcvCvccnoCvcv",
"CvcvCvccCvcvno",
"CvcvnoCvcvCvcc",
"CvcvCvcvnoCvcc",
"CvcvCvcvCvccno",
"CvccnoCvccCvcv",
"CvccCvccnoCvcv",
"CvccCvccCvcvno",
"CvcvnoCvccCvcc",
"CvcvCvccnoCvcc",
"CvcvCvccCvccno",
"CvccnoCvcvCvcc",
"CvccCvcvnoCvcc",
"CvccCvcvCvccno",
],
Template.medium: [
"CvcnoCvc",
"CvcCvcno",
],
Template.short: [
"Cvcn",
],
Template.pin: [
"nnnn",
],
Template.name: [
"cvccvcvcv",
],
Template.phrase: [
"cvcc cvc cvccvcv cvc",
"cvc cvccvcvcv cvcv",
"cv cvccv cvc cvcvccv",
],
Template.basic: [
"aaanaaan",
"aannaaan",
"aaannaaa",
],
};
const Map<String, String> characters = {
"V": "AEIOU",
"C": "BCDFGHJKLMNPQRSTVWXYZ",
"v": "aeiou",
"c": "bcdfghjklmnpqrstvwxyz",
"A": "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
"a": "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
"n": "0123456789",
"o": r"@&%?,=[]_:-+*$#!'^~;()/.",
"x":
r"AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
" ": " ",
};

362
pubspec.lock

@ -0,0 +1,362 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "30.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "2.7.0"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
coverage:
dependency: transitive
description:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
lints:
dependency: "direct dev"
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.11"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
node_preamble:
dependency: transitive
description:
name: node_preamble
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.4.0"
pointycastle:
dependency: "direct main"
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "3.4.0"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
shelf_static:
dependency: transitive
description:
name: shelf_static
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.10"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test:
dependency: "direct dev"
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.19.3"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.7"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.8"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "7.4.0"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
xml:
dependency: "direct dev"
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.3.1"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.14.4 <3.0.0"

16
pubspec.yaml

@ -0,0 +1,16 @@
name: dart_spectre
description: A simple command-line application.
version: 1.0.0
# homepage: https://www.example.com
environment:
sdk: '>=2.14.4 <3.0.0'
dependencies:
pointycastle: ^3.4.0
dev_dependencies:
lints: ^1.0.0
test: ^1.19.3
xml: ^5.3.1

17
test/spectre_test.dart

@ -0,0 +1,17 @@
import 'package:dart_spectre/spectre.dart';
import 'package:test/test.dart';
void main() {
test("Example", () {
var spectre = Spectre("Robert Lee Mitchell", "banana colored duckling");
var pw = spectre.site("masterpasswordapp.com");
expect(pw, equals("Jejr5[RepuSosp"));
});
test("Example_second", () {
var scoper = SimpleScoper("com.jojodev.jolheiser");
var spectre = Spectre("Robert Lee Mitchell", "banana colored duckling", scoper);
var pw = spectre.site("jojodev.com", scope: Scope.identification, templateType: Template.maximum, counter: 2);
expect(pw, equals("Ig^JIcxD!*)TbefJBi6-"));
});
}

11
test/spectre_tests.dart

@ -0,0 +1,11 @@
import 'dart:io';
import 'package:xml/xml.dart';
import 'package:dart_spectre/spectre.dart';
import 'package:test/test.dart';
void main() async {
var tests = await File("spectre_tests.xml").readAsString();
}

75
test/spectre_tests.xml

@ -0,0 +1,75 @@
<tests>
<!-- Default values for all parameters. -->
<case id="default">
<algorithm>-1</algorithm>
<userName>Robert Lee Mitchell</userName>
<userSecret>banana colored duckling</userSecret>
<siteName>spectre.app</siteName>
<resultType>Long</resultType>
<keyCounter>1</keyCounter>
<keyPurpose>Authentication</keyPurpose>
<result><!-- abstract --></result>
</case>
<!-- Algorithm 3 -->
<case id="v3" parent="default">
<algorithm>3</algorithm>
<result>ReqoCenuXonu1?</result>
</case>
<case id="v3_mb_userName" parent="v3">
<userName></userName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>Wewa9$YaqdDaje</result>
</case>
<case id="v3_mb_userSecret" parent="v3">
<userSecret></userSecret>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>TeweBacg0$Tobe</result>
</case>
<case id="v3_mb_siteName" parent="v3">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v3_loginName" parent="v3">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>wesruqori</result>
</case>
<case id="v3_securityAnswer" parent="v3">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>zowp quy roxzuyu qim</result>
</case>
<case id="v3_type_maximum" parent="v3">
<resultType>Maximum</resultType>
<result>FB22U#U*LPFWlWxaxK2.</result>
</case>
<case id="v3_type_medium" parent="v3">
<resultType>Medium</resultType>
<result>ReqMon0)</result>
</case>
<case id="v3_type_basic" parent="v3">
<resultType>Basic</resultType>
<result>FLI88cDK</result>
</case>
<case id="v3_type_short" parent="v3">
<resultType>Short</resultType>
<result>Req8</result>
</case>
<case id="v3_type_pin" parent="v3">
<resultType>PIN</resultType>
<result>3648</result>
</case>
<case id="v3_type_name" parent="v3">
<resultType>Name</resultType>
<result>reqmonajo</result>
</case>
<case id="v3_type_phrase" parent="v3">
<resultType>Phrase</resultType>
<result>re monnu mit jededda</result>
</case>
<case id="v3_counter_ceiling" parent="v3">
<keyCounter>4294967295</keyCounter>
<result>ZurdYoda6:Jogs</result>
</case>
</tests>
Loading…
Cancel
Save