keepassxc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6c60203ef5b9cdcd3cbabce41a9712e62d35793cbc5f5a3cb40070a45acee25d
4
+ data.tar.gz: e23b41982c3bec59afff4a50230fa31e00172c51949749e9ac27d3477a94a434
5
+ SHA512:
6
+ metadata.gz: 8737d87a03efbcb532f225cee2eb22ead55449944fc0c5b53b366b8bf922a4fb8197834e4d520061e0bfca1bda19b634de87bedda13df56fb789ad4a8edc213c
7
+ data.tar.gz: a2be3cf5b98ed7e63c65097882b391dd3faef90d0ee3345b6389575412ce70770bb561d79e37f91bce162fb323682047ab247cea4bb72601776a3a7fd55dfaf7
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # KeypassXC Ruby binding
2
+
3
+ You want to fetch login data as the browsers do. Then this is for you.
4
+ Similar to my https://github.com/Kjarrigan/keepasshttp-ruby repo.
5
+
6
+ ## Work in progress
7
+
8
+ A coworker just asked me wether I have use the keepassxc-cli but it requires
9
+ to enter a password every time which is annoying and then I remembered my
10
+ keepasshttp-ruby binding and it turns out this is no longer the desired way
11
+ for KeepassXC. So why not make the new version work. Yay!
12
+
13
+ ## Links
14
+
15
+ Some things have changed but the rough idea is the same. So I can probably copy
16
+ over quite a bit of context.
17
+
18
+ * [My HTTP Code](https://github.com/Kjarrigan/keepasshttp-ruby/blob/master/lib/keepasshttp.rb)
19
+ * [The official protocal docs](https://github.com/keepassxreboot/keepassxc-browser/blob/develop/keepassxc-protocol.md)
20
+
21
+ ## Basic communication snippet
22
+
23
+ It is already working now! Altough some comfort is still missing, you can already register your client and
24
+ fetch logins. Yay!
25
+
26
+ ```ruby
27
+ load 'test.rb'
28
+
29
+ kpx = KeepassXC.new client_identifier: KEY_FROM_ASSOCIATE_OR_DB, client_name: ID_FROM_ASSOCIATE_OR_DB
30
+ kpx.change_public_keys
31
+ # kpx.associate
32
+ kpx.test_associate
33
+ p kpx.get_logins 'https://github.com'
34
+ ```
35
+
36
+ You can check what clients are already registered in your DB via the GUI like this:
37
+ * Database
38
+ * Database-Settings
39
+ * Browser-Integration
40
+
41
+ Technically you could even re-use the Key/ID from your browser by just copying them from there.
data/exe/keepassxc-rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'keepassxc'
2
+
3
+ name = ARGV[1] || `hostname`.chomp
4
+
5
+ cfg = KeepassXC::KeyStore.find_or_create
6
+ kpx = KeepassXC::Client.new client_name: name
7
+ kpx.change_public_keys
8
+ if cfg.profiles[name]
9
+ kpx.client_identifier = cfg.profiles[name]
10
+ else
11
+ kpx.associate
12
+ cfg.profiles[name] = kpx.client_identifier
13
+ cfg.save
14
+ end
15
+ kpx.test_associate
16
+
17
+ puts kpx.get_logins(ARGV[0]).first['password']
data/keepassxc.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ require_relative 'lib/keepassxc/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'keepassxc'
5
+ spec.version = KeepassXC::VERSION
6
+ spec.authors = ['Holger Arndt']
7
+ spec.email = ['keepassxc-ruby@kjarrigan.de']
8
+
9
+ spec.summary = %q{Ruby bindings for the KeepassXC Browser API}
10
+ spec.description = %q{Ruby bindings for the KeepassXC Browser API}
11
+ spec.homepage = 'https://github.com/Kjarrigan/keepassxc-ruby'
12
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
13
+
14
+ spec.metadata['homepage_uri'] = spec.homepage
15
+ spec.metadata['source_code_uri'] = spec.homepage
16
+ spec.metadata['changelog_uri'] = spec.homepage
17
+
18
+ spec.files = Dir.chdir(__dir__) do
19
+ `git ls-files`.split("\n").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = %w{keepassxc-rb}
23
+ spec.require_paths = ['lib']
24
+ end
data/lib/keepassxc.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'socket'
2
+ require 'json'
3
+
4
+ # TODO, replace this with openssl
5
+ require 'rbnacl'
6
+
7
+ module KeepassXC
8
+ class Error < StandardError; end
9
+
10
+ autoload :Client, 'keepassxc/client'
11
+ autoload :Helper, 'keepassxc/helper'
12
+ autoload :KeyStore, 'keepassxc/key_store'
13
+ autoload :VERSION, 'keepassxc/version'
14
+ end
@@ -0,0 +1,100 @@
1
+ require_relative 'helper'
2
+
3
+ module KeepassXC
4
+ class Client
5
+ include Helper
6
+
7
+ attr_accessor :client_id, :client_identifier, :client_name
8
+
9
+ def initialize(client_identifier: nil, client_name: nil)
10
+ @client_id = generate_nonce
11
+ user_id = `id -u`.chomp.to_i
12
+ @sock = UNIXSocket.new("/run/user/#{user_id}/org.keepassxc.KeePassXC.BrowserServer")
13
+ @private_key = RbNaCl::PrivateKey.generate
14
+ @client_identifier = client_identifier || to_b64(@private_key.public_key)
15
+ @client_name = client_name
16
+ end
17
+
18
+ def generate_nonce
19
+ to_b64 RbNaCl::Random.random_bytes
20
+ end
21
+
22
+ def change_public_keys
23
+ resp = send_msg(
24
+ action: 'change-public-keys',
25
+ publicKey: to_b64(@private_key.public_key),
26
+ nonce: generate_nonce,
27
+ clientID: @client_id
28
+ )
29
+ @session = RbNaCl::SimpleBox.from_keypair(resp['publicKey'].unpack1('m*'), @private_key)
30
+
31
+ resp
32
+ end
33
+
34
+ def associate
35
+ resp = send_encrypted_msg(
36
+ 'action' => 'associate',
37
+ 'key' => to_b64(@private_key.public_key),
38
+ 'idKey' => @client_identifier
39
+ )
40
+ @client_name = resp['id']
41
+
42
+ resp
43
+ end
44
+
45
+ def test_associate
46
+ send_encrypted_msg(
47
+ 'action' => 'test-associate',
48
+ 'key' => @client_identifier,
49
+ 'id' => @client_name
50
+ )
51
+ end
52
+
53
+ def get_logins(url)
54
+ send_encrypted_msg(
55
+ 'action' => 'get-logins',
56
+ 'url' => url,
57
+ 'keys' => [
58
+ {
59
+ 'id' => @client_name,
60
+ 'key' => @client_identifier
61
+ }
62
+ ]
63
+ )['entries']
64
+ end
65
+
66
+ def send_encrypted_msg(msg)
67
+ nonce, enc = encrypt(msg)
68
+
69
+ send_msg(
70
+ action: msg['action'],
71
+ message: to_b64(enc),
72
+ nonce: to_b64(nonce),
73
+ clientID: client_id
74
+ )
75
+ end
76
+
77
+ def encrypt(msg)
78
+ crypt = @session.box(JSON.dump(msg.transform_keys(&:to_s)))
79
+ nonce = crypt.slice!(0, RbNaCl::SecretBox.nonce_bytes)
80
+
81
+ [nonce, crypt]
82
+ end
83
+
84
+ def decrypt(msg, nonce:)
85
+ @session.decrypt(from_b64(nonce) + from_b64(msg))
86
+ end
87
+
88
+ def send_msg(msg)
89
+ @sock.send(JSON.dump(msg.transform_keys(&:to_s)), 0)
90
+ json = @sock.recvfrom(4096).first
91
+ resp = JSON.parse(json)
92
+
93
+ raise Error, resp['error'] if resp.key?('error')
94
+ resp = JSON.parse(decrypt(resp['message'], nonce: resp['nonce'])) if resp.key?('message')
95
+ binding.irb if resp['success'] != 'true'
96
+
97
+ resp
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,11 @@
1
+ module KeepassXC
2
+ module Helper
3
+ def to_b64(string)
4
+ [string].pack('m*').chomp
5
+ end
6
+
7
+ def from_b64(string)
8
+ string.unpack1('m*')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+
4
+ module KeepassXC
5
+ class KeyStore
6
+ attr_reader :path
7
+
8
+ DEFAULT_PATH = File.join(Dir.home, '.config', 'keepassxc-rb')
9
+ def initialize(path: DEFAULT_PATH)
10
+ @path = path
11
+ end
12
+
13
+ def create
14
+ unless exist?
15
+ FileUtils.mkdir_p File.dirname(path)
16
+ File.write(path, JSON.dump(profiles: {}))
17
+ end
18
+ File.chmod(0600, path) unless secure?
19
+ end
20
+
21
+ def self.find_or_create(path: DEFAULT_PATH)
22
+ storage = new(path: path)
23
+ storage.create
24
+ storage
25
+ end
26
+
27
+ def raw
28
+ @raw ||= JSON.load_file(path)
29
+ rescue Errno::ENOENT
30
+ @raw = {}
31
+ end
32
+
33
+ def profiles
34
+ raw['profiles']
35
+ end
36
+
37
+ def save
38
+ create
39
+ File.write(path, JSON.dump(raw))
40
+ end
41
+
42
+ def secure?
43
+ File.stat(path).mode == 0600
44
+ end
45
+
46
+ def exist?
47
+ File.exist?(path)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module KeepassXC
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keepassxc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Holger Arndt
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-04-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ruby bindings for the KeepassXC Browser API
14
+ email:
15
+ - keepassxc-ruby@kjarrigan.de
16
+ executables:
17
+ - keepassxc-rb
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - exe/keepassxc-rb
23
+ - keepassxc.gemspec
24
+ - lib/keepassxc.rb
25
+ - lib/keepassxc/client.rb
26
+ - lib/keepassxc/helper.rb
27
+ - lib/keepassxc/key_store.rb
28
+ - lib/keepassxc/version.rb
29
+ homepage: https://github.com/Kjarrigan/keepassxc-ruby
30
+ licenses: []
31
+ metadata:
32
+ homepage_uri: https://github.com/Kjarrigan/keepassxc-ruby
33
+ source_code_uri: https://github.com/Kjarrigan/keepassxc-ruby
34
+ changelog_uri: https://github.com/Kjarrigan/keepassxc-ruby
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.7.0
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.1.2
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Ruby bindings for the KeepassXC Browser API
54
+ test_files: []