kstor 0.4.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 +7 -0
- data/README.md +7 -0
- data/bin/kstor +286 -0
- data/bin/kstor-srv +26 -0
- data/lib/kstor/config.rb +66 -0
- data/lib/kstor/controller/authentication.rb +79 -0
- data/lib/kstor/controller/secret.rb +201 -0
- data/lib/kstor/controller/users.rb +62 -0
- data/lib/kstor/controller.rb +80 -0
- data/lib/kstor/crypto/ascii_armor.rb +27 -0
- data/lib/kstor/crypto/keys.rb +116 -0
- data/lib/kstor/crypto.rb +240 -0
- data/lib/kstor/error.rb +85 -0
- data/lib/kstor/log.rb +56 -0
- data/lib/kstor/message.rb +132 -0
- data/lib/kstor/model.rb +437 -0
- data/lib/kstor/server.rb +51 -0
- data/lib/kstor/session.rb +80 -0
- data/lib/kstor/socket_server.rb +113 -0
- data/lib/kstor/sql_connection.rb +74 -0
- data/lib/kstor/store.rb +383 -0
- data/lib/kstor/systemd.rb +25 -0
- data/lib/kstor/version.rb +5 -0
- data/lib/kstor.rb +10 -0
- metadata +141 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/store'
|
4
|
+
require 'kstor/model'
|
5
|
+
require 'kstor/crypto'
|
6
|
+
require 'kstor/log'
|
7
|
+
|
8
|
+
module KStor
|
9
|
+
module Controller
|
10
|
+
# Handle user and group related requests.
|
11
|
+
class User
|
12
|
+
def initialize(store)
|
13
|
+
@store = store
|
14
|
+
end
|
15
|
+
|
16
|
+
def handle_request(user, req)
|
17
|
+
case req.type
|
18
|
+
when /^group-create$/ then handle_group_create(user, req)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def handle_group_create(user, req)
|
25
|
+
unless req.args['name']
|
26
|
+
raise Error.for_code('REQ/MISSINGARG', 'name', req.type)
|
27
|
+
end
|
28
|
+
|
29
|
+
group = group_create(user, req.args['name'])
|
30
|
+
@groups = nil
|
31
|
+
Response.new(
|
32
|
+
'group.created',
|
33
|
+
'group_id' => group.id,
|
34
|
+
'group_name' => group.name,
|
35
|
+
'group_pubk' => group.pubk
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def group_create(user, name)
|
40
|
+
pubk, privk = Crypto.generate_key_pair
|
41
|
+
group_id = @store.group_create(name, pubk)
|
42
|
+
encrypted_privk = Crypto.encrypt_group_privk(
|
43
|
+
user.pubk, privk
|
44
|
+
)
|
45
|
+
Log.debug("encrypted_privk = #{encrypted_privk}")
|
46
|
+
keychain_item_create(user, group_id, pubk, encrypted_privk)
|
47
|
+
|
48
|
+
Model::Group.new(id: group_id, name:, pubk:)
|
49
|
+
end
|
50
|
+
|
51
|
+
def keychain_item_create(user, group_id, pubk, encrypted_privk)
|
52
|
+
item = Model::KeychainItem.new(
|
53
|
+
group_id:,
|
54
|
+
group_pubk: pubk,
|
55
|
+
encrypted_privk:
|
56
|
+
)
|
57
|
+
user.keychain[item.group_id] = item
|
58
|
+
@store.keychain_item_create(user.id, group_id, encrypted_privk)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/error'
|
4
|
+
require 'kstor/log'
|
5
|
+
require 'kstor/store'
|
6
|
+
require 'kstor/message'
|
7
|
+
require 'kstor/controller/authentication'
|
8
|
+
require 'kstor/controller/secret'
|
9
|
+
require 'kstor/controller/users'
|
10
|
+
|
11
|
+
module KStor
|
12
|
+
# Error: user was not allowed to access application.
|
13
|
+
class UserNotAllowed < Error
|
14
|
+
error_code 'AUTH/FORBIDDEN'
|
15
|
+
error_message 'User %s not allowed.'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Error: invalid session ID
|
19
|
+
class InvalidSession < Error
|
20
|
+
error_code 'AUTH/BADSESSION'
|
21
|
+
error_message 'Invalid session ID %s'
|
22
|
+
end
|
23
|
+
|
24
|
+
class MissingLoginPassword < Error
|
25
|
+
error_code 'AUTH/MISSING'
|
26
|
+
error_message 'Missing login and password'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Error: unknown request type.
|
30
|
+
class UnknownRequestType < Error
|
31
|
+
error_code 'REQ/UNKNOWN'
|
32
|
+
error_message 'Unknown request type %s'
|
33
|
+
end
|
34
|
+
|
35
|
+
# Error: missing request argument.
|
36
|
+
class MissingArgument < Error
|
37
|
+
error_code 'REQ/MISSINGARG'
|
38
|
+
error_message 'Missing argument %s for request type %s'
|
39
|
+
end
|
40
|
+
|
41
|
+
module Controller
|
42
|
+
# Request handler.
|
43
|
+
class RequestHandler
|
44
|
+
def initialize(store, session_store)
|
45
|
+
@auth = Controller::Authentication.new(store, session_store)
|
46
|
+
@secret = Controller::Secret.new(store)
|
47
|
+
@user = Controller::User.new(store)
|
48
|
+
@store = store
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_request(req)
|
52
|
+
user, sid = @auth.authenticate(req)
|
53
|
+
controller = controller_from_request_type(req)
|
54
|
+
resp = @store.transaction { controller.handle_request(user, req) }
|
55
|
+
user.lock
|
56
|
+
resp.session_id = sid
|
57
|
+
resp
|
58
|
+
rescue RbNaClError => e
|
59
|
+
Log.exception(e)
|
60
|
+
Error.for_code('CRYPTO/UNSPECIFIED').response
|
61
|
+
rescue Error => e
|
62
|
+
Log.info(e.message)
|
63
|
+
e.response
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def controller_from_request_type(req)
|
69
|
+
case req.type
|
70
|
+
when /^secret-(create|delete|search|unlock|update-(meta|value)?)$/
|
71
|
+
@secret
|
72
|
+
when /^group-create$/
|
73
|
+
@user
|
74
|
+
else
|
75
|
+
raise Error.for_code('REQ/UNKNOWN', req.type)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KStor
|
4
|
+
# Cryptographic functions for KStor.
|
5
|
+
module Crypto
|
6
|
+
# Encode and decode binary data to ASCII.
|
7
|
+
module ASCIIArmor
|
8
|
+
class << self
|
9
|
+
# ASCII-armor a String of bytes.
|
10
|
+
#
|
11
|
+
# @param bytes [String] raw string
|
12
|
+
# @return [String] ASCII-armored string
|
13
|
+
def encode(bytes)
|
14
|
+
Base64.strict_encode64(bytes)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Decode an ASCII-armored string back to raw data.
|
18
|
+
#
|
19
|
+
# @param str [String] ASCII-armored string
|
20
|
+
# @return [String] raw string
|
21
|
+
def decode(str)
|
22
|
+
Base64.strict_decode64(str)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/crypto/ascii_armor'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
module Crypto
|
7
|
+
# Holds together a secret key value and the KDF associated parameters.
|
8
|
+
class SecretKey
|
9
|
+
# The secret key as an ASCII-armored String
|
10
|
+
attr_reader :value
|
11
|
+
# KDF parameters as an ASCII-armored String
|
12
|
+
attr_reader :kdf_params
|
13
|
+
|
14
|
+
# Create a SecretKey instance.
|
15
|
+
#
|
16
|
+
# @param [String] value ASCII-armored secret key derived from passphrase
|
17
|
+
# @param [String] kdf_params ASCII-armored key derivation parameters
|
18
|
+
# @return [SecretKey] the SecretKey object
|
19
|
+
def initialize(value, kdf_params)
|
20
|
+
@value = value
|
21
|
+
@kdf_params = kdf_params
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Holds together a public and private key pair.
|
26
|
+
class KeyPair
|
27
|
+
# ASCII-armored public key
|
28
|
+
attr_reader :pubk
|
29
|
+
# ASCII-armored private key
|
30
|
+
attr_reader :privk
|
31
|
+
|
32
|
+
# Create a KeyPair instance.
|
33
|
+
#
|
34
|
+
# @param [String] pubk ASCII-armored public key
|
35
|
+
# @param [String] privk ASCII-armored private key
|
36
|
+
def initialize(pubk, privk)
|
37
|
+
@pubk = pubk
|
38
|
+
@privk = privk
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Wrapper class for an ASCII-armored value.
|
43
|
+
class ArmoredValue
|
44
|
+
def initialize(value)
|
45
|
+
@value = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_ascii
|
49
|
+
@value
|
50
|
+
end
|
51
|
+
alias to_s to_ascii
|
52
|
+
|
53
|
+
def to_binary
|
54
|
+
ASCIIArmor.decode(@value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.from_binary(bin_str)
|
58
|
+
new(ASCIIArmor.encode(bin_str))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# A Hash.
|
63
|
+
class ArmoredHash < ArmoredValue
|
64
|
+
def self.from_hash(hash)
|
65
|
+
from_binary(hash.to_json)
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_hash
|
69
|
+
JSON.parse(to_binary)
|
70
|
+
end
|
71
|
+
|
72
|
+
def [](key)
|
73
|
+
to_hash[key]
|
74
|
+
end
|
75
|
+
|
76
|
+
def []=(key, val)
|
77
|
+
h = to_hash
|
78
|
+
h[key] = val
|
79
|
+
@value = ASCIIArmor.encode(h.to_json)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# KDF parameters.
|
84
|
+
class KDFParams < ArmoredHash
|
85
|
+
def self.from_hash(hash)
|
86
|
+
hash['salt'] = ASCIIArmor.encode(hash['salt'])
|
87
|
+
hash['opslimit'] = hash['opslimit'].to_s
|
88
|
+
hash['memlimit'] = hash['memlimit'].to_s
|
89
|
+
super(hash)
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_hash
|
93
|
+
hash = super
|
94
|
+
hash['salt'] = ASCIIArmor.decode(hash['salt'])
|
95
|
+
hash['opslimit'] = hash['opslimit'].to_sym
|
96
|
+
hash['memlimit'] = hash['memlimit'].to_sym
|
97
|
+
|
98
|
+
hash
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# A private key.
|
103
|
+
class PrivateKey < ArmoredValue
|
104
|
+
def to_rbnacl
|
105
|
+
RbNaCl::PrivateKey.new(to_binary)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# A public key.
|
110
|
+
class PublicKey < ArmoredValue
|
111
|
+
def to_rbnacl
|
112
|
+
RbNaCl::PublicKey.new(to_binary)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/kstor/crypto.rb
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rbnacl'
|
4
|
+
require 'json'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
require 'kstor/error'
|
8
|
+
require 'kstor/crypto/ascii_armor'
|
9
|
+
require 'kstor/crypto/keys'
|
10
|
+
|
11
|
+
module KStor
|
12
|
+
# Generic crypto error.
|
13
|
+
class CryptoError < Error
|
14
|
+
error_code 'CRYPTO/UNSPECIFIED'
|
15
|
+
error_message 'Cryptographic error.'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Error in key derivation.
|
19
|
+
class RbNaClError < Error
|
20
|
+
error_code 'CRYPTO/RBNACL'
|
21
|
+
error_message 'RbNaCl error: %s'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Cryptographic functions for KStor.
|
25
|
+
#
|
26
|
+
# @version 1.0
|
27
|
+
module Crypto
|
28
|
+
VERSION = 1
|
29
|
+
|
30
|
+
class << self
|
31
|
+
# Derive a secret key suitable for symetric encryption from a passphrase.
|
32
|
+
#
|
33
|
+
# Key derivation function can use previously stored parameters (as an
|
34
|
+
# opaque String) or pass nil to generate random parameters.
|
35
|
+
#
|
36
|
+
# @param passphrase [String] user passphrase as clear text
|
37
|
+
# @param params [KDFParams, nil] KDF parameters;
|
38
|
+
# if nil, use defaults.
|
39
|
+
# @return [SecretKey] secret key and KDF parameters
|
40
|
+
def key_derive(passphrase, params = nil)
|
41
|
+
params ||= key_derive_params_generate
|
42
|
+
Log.debug("crypto: kdf params = #{params.to_hash}")
|
43
|
+
data = RbNaCl::PasswordHash.argon2(
|
44
|
+
passphrase, params['salt'],
|
45
|
+
params['opslimit'], params['memlimit'], params['digest_size']
|
46
|
+
)
|
47
|
+
SecretKey.new(ArmoredValue.from_binary(data), params)
|
48
|
+
rescue RbNaCl::CryptoError => e
|
49
|
+
raise Error.for_code('CRYPTO/RBNACL', e.message)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Check if KDF params match current code in this library.
|
53
|
+
#
|
54
|
+
# If it is obsolete, you should generate a new secret key from the user's
|
55
|
+
# passphrase, and re-encrypt everything that was encrypted with the old
|
56
|
+
# secret key.
|
57
|
+
#
|
58
|
+
# @param params [String] KDF params as an opaque string.
|
59
|
+
# @return [Boolean] false if parameters match current library version.
|
60
|
+
def kdf_params_obsolete?(params)
|
61
|
+
return true if params_str.nil?
|
62
|
+
|
63
|
+
params['_version'] != VERSION
|
64
|
+
rescue RbNaCl::CryptoError => e
|
65
|
+
raise Error.for_code('CRYPTO/RBNACL', e.message)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Generate new key pair.
|
69
|
+
#
|
70
|
+
# @return [Array<PublicKey, PrivateKey>] new key pair
|
71
|
+
def generate_key_pair
|
72
|
+
privk = RbNaCl::PrivateKey.generate
|
73
|
+
pubk = privk.public_key
|
74
|
+
[PublicKey.from_binary(pubk.to_bytes),
|
75
|
+
PrivateKey.from_binary(privk.to_bytes)]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Encrypt user private key.
|
79
|
+
#
|
80
|
+
# @param [SecretKey] secret_key secret key derived from passphrase
|
81
|
+
# @param [PrivateKey] privk private key
|
82
|
+
# @return [ArmoredValue] encrypted user private key
|
83
|
+
def encrypt_user_privk(secret_key, privk)
|
84
|
+
box_secret_encrypt(secret_key, privk.to_binary)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Decrypt user private key.
|
88
|
+
#
|
89
|
+
# @param [SecretKey] secret_key secret key derived from passphrase
|
90
|
+
# @param [ArmoredValue] ciphertext encrypted private key
|
91
|
+
# @return [PrivateKey] user private key
|
92
|
+
def decrypt_user_privk(secret_key, ciphertext)
|
93
|
+
privk_data = box_secret_decrypt(secret_key, ciphertext)
|
94
|
+
PrivateKey.from_binary(privk_data)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Encrypt and sign group private key.
|
98
|
+
#
|
99
|
+
# @param [PublicKey] owner_pubk user public key
|
100
|
+
# @param [PrivateKey] group_privk group private key
|
101
|
+
# @return [ArmoredValue] encrypted group private key
|
102
|
+
def encrypt_group_privk(owner_pubk, group_privk)
|
103
|
+
box_pair_encrypt(owner_pubk, group_privk, group_privk.to_binary)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Decrypt and verify group private key.
|
107
|
+
#
|
108
|
+
# @param [PublicKey] group_pubk group public key to verify signature
|
109
|
+
# @param [PrivateKey] owner_privk user private key
|
110
|
+
# @param [ArmoredValue] encrypted_group_privk encrypted group private key
|
111
|
+
# @return [PrivateKey] group private key
|
112
|
+
def decrypt_group_privk(group_pubk, owner_privk, encrypted_group_privk)
|
113
|
+
PrivateKey.from_binary(
|
114
|
+
box_pair_decrypt(group_pubk, owner_privk, encrypted_group_privk)
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Encrypt and sign secret value.
|
119
|
+
#
|
120
|
+
# @param [PublicKey] group_pubk group public key
|
121
|
+
# @param [PrivateKey] author_privk user private key
|
122
|
+
# @param [String] value secret value
|
123
|
+
# @return [ArmoredValue] ASCII-armored encrypted secret value
|
124
|
+
def encrypt_secret_value(group_pubk, author_privk, value)
|
125
|
+
box_pair_encrypt(group_pubk, author_privk, value)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Decrypt and verify secret value.
|
129
|
+
#
|
130
|
+
# @param [PublicKey] author_pubk user secret key
|
131
|
+
# @param [PrivateKey] group_privk group private key
|
132
|
+
# @param [ArmoredValue] val encrypted secret value
|
133
|
+
# @return [String] original secret value
|
134
|
+
def decrypt_secret_value(author_pubk, group_privk, val)
|
135
|
+
box_pair_decrypt(author_pubk, group_privk, val)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Encrypt and sign secret metadata.
|
139
|
+
#
|
140
|
+
# @param [PublicKey] group_pubk group public key
|
141
|
+
# @param [PrivateKey] author_privk user private key
|
142
|
+
# @param [Hash] metadata_as_hash Hash of keys and values
|
143
|
+
# @return [ArmoredValue] encrypted secret metadata
|
144
|
+
def encrypt_secret_metadata(group_pubk, author_privk, metadata_as_hash)
|
145
|
+
meta = ArmoredHash.from_hash(metadata_as_hash)
|
146
|
+
encrypt_secret_value(group_pubk, author_privk, meta.to_binary)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Decrypt and verify secret metadata.
|
150
|
+
#
|
151
|
+
# @param [PublicKey] author_pubk user public key
|
152
|
+
# @param [PrivateKey] group_privk group private key
|
153
|
+
# @param [ArmoredValue] val encrypted secret metadata
|
154
|
+
# @return [Hash] Hash of keys and values
|
155
|
+
def decrypt_secret_metadata(author_pubk, group_privk, val)
|
156
|
+
bytes = decrypt_secret_value(author_pubk, group_privk, val)
|
157
|
+
ArmoredHash.from_binary(bytes).to_hash
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
# Encrypt raw data with a secret key.
|
163
|
+
#
|
164
|
+
# @param [SecretKey] secret_key secret key
|
165
|
+
# @param [String] bytes raw data to encrypt
|
166
|
+
# @return [ArmoredValue] ciphertext
|
167
|
+
def box_secret_encrypt(secret_key, bytes)
|
168
|
+
ciphertext = make_secret_box(secret_key).encrypt(bytes)
|
169
|
+
ArmoredValue.from_binary(ciphertext)
|
170
|
+
rescue RbNaCl::CryptoError => e
|
171
|
+
raise Error.for_code('CRYPTO/RBNACL', e.message)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Decrypt data with a secret key.
|
175
|
+
#
|
176
|
+
# @param [SecretKey] secret_key secret key
|
177
|
+
# @param [ArmoredValue] val ciphertext to decrypt
|
178
|
+
# @return [String] raw decrypted plaintext
|
179
|
+
def box_secret_decrypt(secret_key, val)
|
180
|
+
make_secret_box(secret_key).decrypt(val.to_binary)
|
181
|
+
rescue RbNaCl::CryptoError => e
|
182
|
+
raise Error.for_code('CRYPTO/RBNACL', e.message)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Encrypt and authenticate data with public-key crypto.
|
186
|
+
#
|
187
|
+
# @param [PublicKey] pubk public key
|
188
|
+
# @param [PrivateKey] privk private key
|
189
|
+
# @param [String] bytes raw data to encrypt
|
190
|
+
# @return [ArmoredValue] ciphertext
|
191
|
+
def box_pair_encrypt(pubk, privk, bytes)
|
192
|
+
ArmoredValue.from_binary(make_pair_box(pubk, privk).encrypt(bytes))
|
193
|
+
rescue RbNaCl::CryptoError => e
|
194
|
+
raise Error.for_code('CRYPTO/RBNACL', e.message)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Decrypt and authentify data with public-key crypto.
|
198
|
+
#
|
199
|
+
# @param [PublicKey] pubk public key
|
200
|
+
# @param [PrivateKey] privk private key
|
201
|
+
# @param [ArmoredValue] val ciphertext to decrypt
|
202
|
+
# @return [String] raw decrypted plaintext
|
203
|
+
def box_pair_decrypt(pubk, privk, val)
|
204
|
+
make_pair_box(pubk, privk).decrypt(val.to_binary)
|
205
|
+
rescue RbNaCl::CryptoError => e
|
206
|
+
raise Error.for_code('CRYPTO/RBNACL', e.message)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Make a SimpleBox for symetric crypto.
|
210
|
+
#
|
211
|
+
# @param secret_key [SecretKey] secret_key secret key
|
212
|
+
# @return [RbNaCl::SimpleBox] the box
|
213
|
+
def make_secret_box(secret_key)
|
214
|
+
RbNaCl::SimpleBox.from_secret_key(secret_key.value.to_binary)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Make a SimpleBox for asymetric cypto.
|
218
|
+
#
|
219
|
+
# @param [PublicKey] pubk public key
|
220
|
+
# @param [PrivateKey] privk private key
|
221
|
+
# @return [RbNaCl::SimpleBox] the box
|
222
|
+
def make_pair_box(pubk, privk)
|
223
|
+
RbNaCl::SimpleBox.from_keypair(pubk.to_binary, privk.to_binary)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Generate new parameters for the Key Derivation Function.
|
227
|
+
#
|
228
|
+
# @return [KDFParams] newly generated KDF parameters
|
229
|
+
def key_derive_params_generate
|
230
|
+
salt = RbNaCl::Random.random_bytes(
|
231
|
+
RbNaCl::PasswordHash::Argon2::SALTBYTES
|
232
|
+
)
|
233
|
+
h = { '_version' => VERSION, 'salt' => salt,
|
234
|
+
'opslimit' => :moderate, 'memlimit' => :moderate,
|
235
|
+
'digest_size' => RbNaCl::SecretBox.key_bytes }
|
236
|
+
KDFParams.from_hash(h)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
data/lib/kstor/error.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/message'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
# Base class of KStor errors.
|
7
|
+
#
|
8
|
+
# Each subclass declares a code and is stored in a global registry.
|
9
|
+
class Error < StandardError
|
10
|
+
class << self
|
11
|
+
attr_reader :code
|
12
|
+
attr_reader :message
|
13
|
+
attr_reader :registry
|
14
|
+
|
15
|
+
def error_code(str)
|
16
|
+
@code = str
|
17
|
+
end
|
18
|
+
|
19
|
+
def error_message(str)
|
20
|
+
@message = str
|
21
|
+
end
|
22
|
+
|
23
|
+
def for_code(code, *args)
|
24
|
+
@registry[code].new(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def list
|
28
|
+
@registry.classes
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.inherited(subclass)
|
33
|
+
super
|
34
|
+
Log.debug("#{subclass} inherits from Error")
|
35
|
+
@registry ||= ErrorRegistry.new
|
36
|
+
if @registry.key?(subclass.code)
|
37
|
+
code = subclass.code
|
38
|
+
klass = @registry[code]
|
39
|
+
raise "duplicate error code #{code} in #{subclass}, " \
|
40
|
+
"already defined in #{klass}"
|
41
|
+
end
|
42
|
+
|
43
|
+
@registry << subclass
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(*args)
|
47
|
+
super(format("ERR/%s #{self.class.message}", self.class.code, *args))
|
48
|
+
end
|
49
|
+
|
50
|
+
def response
|
51
|
+
Response.new('error', 'code' => self.class.code, 'message' => message)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
class ErrorRegistry
|
57
|
+
def initialize
|
58
|
+
@error_classes = []
|
59
|
+
@index = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def classes
|
63
|
+
@error_classes.values
|
64
|
+
end
|
65
|
+
|
66
|
+
def <<(klass)
|
67
|
+
@error_classes << klass
|
68
|
+
@index = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def key?(code)
|
72
|
+
index.key?(code)
|
73
|
+
end
|
74
|
+
|
75
|
+
def [](code)
|
76
|
+
index[code]
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def index
|
82
|
+
@index ||= @error_classes.to_h { |c| [c.code, c] }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/kstor/log.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journald/logger'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
# Central logging to systemd-journald.
|
7
|
+
module Log
|
8
|
+
class << self
|
9
|
+
def exception(exc)
|
10
|
+
logger.exception(exc)
|
11
|
+
end
|
12
|
+
|
13
|
+
def debug(msg)
|
14
|
+
logger.debug(msg)
|
15
|
+
end
|
16
|
+
|
17
|
+
def info(msg)
|
18
|
+
logger.info(msg)
|
19
|
+
end
|
20
|
+
|
21
|
+
def notice(msg)
|
22
|
+
logger.notice(msg)
|
23
|
+
end
|
24
|
+
|
25
|
+
def warn(msg)
|
26
|
+
logger.warn(msg)
|
27
|
+
end
|
28
|
+
|
29
|
+
def error(msg)
|
30
|
+
logger.error(msg)
|
31
|
+
end
|
32
|
+
|
33
|
+
def critical(msg)
|
34
|
+
logger.critical(msg)
|
35
|
+
end
|
36
|
+
|
37
|
+
def alert(msg)
|
38
|
+
logger.alert(msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
def emergency(msg)
|
42
|
+
logger.emergency(msg)
|
43
|
+
end
|
44
|
+
|
45
|
+
def reporting_level=(lvl)
|
46
|
+
logger.min_priority = lvl
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def logger
|
52
|
+
@logger ||= Journald::Logger.new('kstor')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|