reedb 0.10.rc1
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/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +38 -0
- data/Rakefile +2 -0
- data/bin/reedbd +62 -0
- data/lib/reedb.rb +739 -0
- data/lib/reedb/config.rb +24 -0
- data/lib/reedb/constants.rb +42 -0
- data/lib/reedb/daemon_wrapper.rb +617 -0
- data/lib/reedb/datafile.rb +268 -0
- data/lib/reedb/errors/daemon_errors.rb +34 -0
- data/lib/reedb/errors/encryption_errors.rb +29 -0
- data/lib/reedb/errors/vault_errors.rb +39 -0
- data/lib/reedb/reevault.rb +547 -0
- data/lib/reedb/security/aes.rb +94 -0
- data/lib/reedb/security/encryption.rb +64 -0
- data/lib/reedb/security/multifish.rb +15 -0
- data/lib/reedb/security/secure_hash.rb +131 -0
- data/lib/reedb/security/twofish.rb +14 -0
- data/lib/reedb/utils/logger.rb +97 -0
- data/lib/reedb/utils/meta_vault.rb +28 -0
- data/lib/reedb/utils/sorting.rb +49 -0
- data/lib/reedb/utils/utilities.rb +121 -0
- data/lib/reedb/utils/uuids.rb +375 -0
- data/lib/reedb/utils/version.rb +101 -0
- data/reedb.gemspec +31 -0
- data/tests/daemons/reedb.rb +27 -0
- data/tests/layout.rb +24 -0
- data/tests/networking/client.rb +14 -0
- data/tests/networking/server.rb +14 -0
- data/tests/play.rb +5 -0
- data/tests/serialisation.rb +49 -0
- data/tests/tests.rb +64 -0
- metadata +209 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
# ====================================================
|
2
|
+
# Copyright 2015 Random Robot Softworks (see @author)
|
3
|
+
# @author: Leander Sabel | www.2rsoftworks.de
|
4
|
+
#
|
5
|
+
# Distributed under the GNU Lesser GPL Version 3
|
6
|
+
# (See accompanying LICENSE file or get a copy at
|
7
|
+
# https://www.gnu.org/licenses/lgpl.html)
|
8
|
+
# ====================================================
|
9
|
+
|
10
|
+
require_relative 'encryption'
|
11
|
+
require 'aes'
|
12
|
+
|
13
|
+
module Reedb
|
14
|
+
class RAES < MCypher
|
15
|
+
def initialize
|
16
|
+
super # => Super constructor
|
17
|
+
end
|
18
|
+
|
19
|
+
# Starts the encryption and loads a key by either generating a new one
|
20
|
+
# or loading an encrypted one from file.
|
21
|
+
#
|
22
|
+
def start_encryption(password, raw_key = nil)
|
23
|
+
if raw_key != nil
|
24
|
+
# => Decrypting key with user password
|
25
|
+
@key = AES.decrypt(raw_key, password)
|
26
|
+
else
|
27
|
+
# => Generating new key and encrypting it with user pw
|
28
|
+
@key = AES.key
|
29
|
+
key_encrypted = AES.encrypt(@key, password)
|
30
|
+
end
|
31
|
+
|
32
|
+
# => At this point @key should be the unencrypted key!
|
33
|
+
@init = true
|
34
|
+
return key_encrypted
|
35
|
+
end
|
36
|
+
|
37
|
+
# Tries to remove the unencryted key from memory as best as possible.
|
38
|
+
# Stops the encryption and prevents further decrypts to occur.
|
39
|
+
def stop_encryption
|
40
|
+
remove_instance_variable(:@key)
|
41
|
+
@init = false
|
42
|
+
end
|
43
|
+
|
44
|
+
# Encrypt the clear text using the encryption key
|
45
|
+
# Returns a base64 encoded string
|
46
|
+
# Throws exceptions
|
47
|
+
#
|
48
|
+
def encrypt(data)
|
49
|
+
begin
|
50
|
+
return AES.encrypt(data, @key) unless @key.nil?
|
51
|
+
rescue Exception => e
|
52
|
+
puts e.message
|
53
|
+
raise EncryptionFailedError.new, "An error was encountered while encrypting data"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Decrypt the cypher text using the encryption key
|
58
|
+
# Returns the original clear text.
|
59
|
+
# Throws exceptions
|
60
|
+
#
|
61
|
+
def decrypt(data)
|
62
|
+
begin
|
63
|
+
return AES.decrypt(data, @key) unless @key.nil?
|
64
|
+
rescue Exception => e
|
65
|
+
puts e.message
|
66
|
+
raise DecryptionFailedError.new, "An error was encountered while decrypting data"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Starts the shift of the main password and creates a new key (cipher) to encrypt with the new pw
|
71
|
+
#
|
72
|
+
def init_shift
|
73
|
+
@tmp_key = AES.key
|
74
|
+
end
|
75
|
+
|
76
|
+
# Change the encryption cipher for a file in the vault.
|
77
|
+
#
|
78
|
+
def shift_cipher(file)
|
79
|
+
temp = AES.decrypt(file, @key) unless @key.nil?
|
80
|
+
return AES.encrypt(temp, @tmp_key)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns new encrypted key
|
84
|
+
#
|
85
|
+
def finalise_shift(fresh)
|
86
|
+
@key = @tmp_key # => Finishing the cipher shift
|
87
|
+
key_encrypted = AES.encrypt(@tmp_key, fresh)
|
88
|
+
remove_instance_variable(:@tmp_key) # => Removing insecure imprint
|
89
|
+
|
90
|
+
return key_encrypted # => To be stored in the new config file!
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# ====================================================
|
2
|
+
# Copyright 2015 Random Robot Softworks (see @author)
|
3
|
+
# @author: Katharina Sabel | www.2rsoftworks.de
|
4
|
+
#
|
5
|
+
# Distributed under the GNU Lesser GPL Version 2.1
|
6
|
+
# (See accompanying LICENSE file or get a copy at
|
7
|
+
# https://www.gnu.org/licenses/lgpl.html)
|
8
|
+
# ====================================================
|
9
|
+
|
10
|
+
require 'digest'
|
11
|
+
|
12
|
+
|
13
|
+
# Master crypt module to be extended by specific cyphers
|
14
|
+
module Reedb
|
15
|
+
class MCypher
|
16
|
+
|
17
|
+
# Attribute reader to get init state of crypt module
|
18
|
+
#
|
19
|
+
attr_reader :init
|
20
|
+
|
21
|
+
def initialize()
|
22
|
+
@init = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_encryption
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop_encryption
|
29
|
+
end
|
30
|
+
|
31
|
+
def encrypt(string)
|
32
|
+
end
|
33
|
+
|
34
|
+
def decrypt(string)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class SecurityUtils
|
39
|
+
|
40
|
+
# => Returns 190 bit tiger hash
|
41
|
+
# DO NOT USE FOR PASSWORD HASHING!
|
42
|
+
# Used to hash file-names in vaults
|
43
|
+
#
|
44
|
+
def self.tiger_hash(string)
|
45
|
+
if Reedb::verbose?
|
46
|
+
DaemonLogger.write("[FIX ME]: t_hash is a broken function!", "warn")
|
47
|
+
end
|
48
|
+
return self.sha512_hash("#{string}")
|
49
|
+
# Digest::Tiger.hexdigest("#{string}")
|
50
|
+
end
|
51
|
+
|
52
|
+
# => Returns 64 byte sha hash.
|
53
|
+
# DO NOT USE FOR PASSWORD HASHING!
|
54
|
+
# Used for integrety checking files
|
55
|
+
#
|
56
|
+
def self.sha512_hash(string)
|
57
|
+
return Digest::SHA256.hexdigest("#{string}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.name_col_hash(string)
|
61
|
+
return "---#{string}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# ====================================================
|
2
|
+
# Copyright 2015 Random Robot Softworks (see @author)
|
3
|
+
# @author: Katharina Sabel | www.2rsoftworks.de
|
4
|
+
#
|
5
|
+
# Distributed under the GNU Lesser GPL Version 2.1
|
6
|
+
# (See accompanying LICENSE file or get a copy at
|
7
|
+
# https://www.gnu.org/licenses/lgpl.html)
|
8
|
+
# ====================================================
|
9
|
+
|
10
|
+
module Reedb
|
11
|
+
class MLE
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
|
2
|
+
# Copyright (c) 2013, Taylor Hornby
|
3
|
+
#
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
14
|
+
# and/or other materials provided with the distribution.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
19
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
20
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
21
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
22
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
23
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
24
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
25
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
26
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
|
28
|
+
require 'securerandom'
|
29
|
+
require 'openssl'
|
30
|
+
require 'base64'
|
31
|
+
|
32
|
+
# Salted password hashing with SHA2.
|
33
|
+
# Authors: @RedragonX (dicesoft.net), havoc AT defuse.ca
|
34
|
+
# @SpaceKookie, spacekookie AT c-base.org
|
35
|
+
#
|
36
|
+
# www: http://crackstation.net/hashing-security.htm
|
37
|
+
module Reedb
|
38
|
+
|
39
|
+
class SecureHash
|
40
|
+
|
41
|
+
# Constants for the hashing process.
|
42
|
+
# Can be changed without breaking existing hashes!
|
43
|
+
PBKDF2_ITERATIONS = 10000
|
44
|
+
SALT_BYTE_SIZE = 32
|
45
|
+
HASH_BYTE_SIZE = 24
|
46
|
+
|
47
|
+
HASH_SECTIONS = 3
|
48
|
+
SECTION_DELIMITER = '::'
|
49
|
+
ITERATIONS_INDEX = 0
|
50
|
+
SALT_INDEX = 1
|
51
|
+
HASH_INDEX = 2
|
52
|
+
|
53
|
+
def self.secure_hash password
|
54
|
+
salt = SecureRandom.base64(SALT_BYTE_SIZE)
|
55
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
56
|
+
len = sha256.digest_length
|
57
|
+
pbkdf2 = OpenSSL::PKCS5.pbkdf2_hmac(
|
58
|
+
password,
|
59
|
+
salt,
|
60
|
+
PBKDF2_ITERATIONS,
|
61
|
+
len,
|
62
|
+
sha256
|
63
|
+
)
|
64
|
+
|
65
|
+
return [PBKDF2_ITERATIONS, salt, Base64.encode64( pbkdf2 )].join( SECTION_DELIMITER )
|
66
|
+
end
|
67
|
+
|
68
|
+
# Checks if a password is correct given a hash of the correct one.
|
69
|
+
# correctHash must be a hash string generated with createHash.
|
70
|
+
def self.validate_pw(password, correctHash)
|
71
|
+
params = correctHash.split(SECTION_DELIMITER)
|
72
|
+
return false if params.length != HASH_SECTIONS
|
73
|
+
|
74
|
+
salt = params[SALT_INDEX]
|
75
|
+
hash = [HASH_INDEX]
|
76
|
+
|
77
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
78
|
+
len = sha256.digest_length
|
79
|
+
|
80
|
+
confirmed = Base64.decode64(params[HASH_INDEX])
|
81
|
+
test_hash = OpenSSL::PKCS5.pbkdf2_hmac(
|
82
|
+
password,
|
83
|
+
salt,
|
84
|
+
PBKDF2_ITERATIONS,
|
85
|
+
len,
|
86
|
+
sha256
|
87
|
+
)
|
88
|
+
|
89
|
+
return confirmed == test_hash
|
90
|
+
end
|
91
|
+
|
92
|
+
# Run tests to ensure the module is functioning properly.
|
93
|
+
# Returns true if all tests succeed, false if not.
|
94
|
+
def self.runSelfTests
|
95
|
+
puts "Sample hashes:"
|
96
|
+
3.times { puts secure_hash("password") }
|
97
|
+
|
98
|
+
puts "\nRunning self tests..."
|
99
|
+
@@allPass = true
|
100
|
+
|
101
|
+
correctPassword = 'aaaaaaaaaa'
|
102
|
+
wrongPassword = 'aaaaaaaaab'
|
103
|
+
hash = secure_hash(correctPassword)
|
104
|
+
|
105
|
+
assert( validate_pw( correctPassword, hash ) == true, "correct password" )
|
106
|
+
assert( validate_pw( wrongPassword, hash ) == false, "wrong password" )
|
107
|
+
|
108
|
+
h1 = hash.split( SECTION_DELIMITER )
|
109
|
+
h2 = secure_hash( correctPassword ).split( SECTION_DELIMITER )
|
110
|
+
assert( h1[HASH_INDEX] != h2[HASH_INDEX], "different hashes" )
|
111
|
+
assert( h1[SALT_INDEX] != h2[SALT_INDEX], "different salt" )
|
112
|
+
|
113
|
+
if @@allPass
|
114
|
+
puts "*** ALL TESTS PASS ***"
|
115
|
+
else
|
116
|
+
puts "*** FAILURES ***"
|
117
|
+
end
|
118
|
+
|
119
|
+
return @@allPass
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.assert( truth, msg )
|
123
|
+
if truth
|
124
|
+
puts "PASS [#{msg}]"
|
125
|
+
else
|
126
|
+
puts "FAIL [#{msg}]"
|
127
|
+
@@allPass = false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# ====================================================
|
2
|
+
# Copyright 2015 Random Robot Softworks (see @author)
|
3
|
+
# @author: Katharina Sabel | www.2rsoftworks.de
|
4
|
+
#
|
5
|
+
# Distributed under the GNU Lesser GPL Version 2.1
|
6
|
+
# (See accompanying LICENSE file or get a copy at
|
7
|
+
# https://www.gnu.org/licenses/lgpl.html)
|
8
|
+
# ====================================================
|
9
|
+
|
10
|
+
module Reedb
|
11
|
+
class Fish
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# ====================================================
|
2
|
+
# Copyright 2015 Lonely Robot (see @author)
|
3
|
+
# @author: Katharina Sabel | www.2rsoftworks.de
|
4
|
+
#
|
5
|
+
# Distributed under the GNU Lesser GPL Version 3
|
6
|
+
# (See accompanying LICENSE file or get a copy at
|
7
|
+
# https://www.gnu.org/licenses/lgpl.html)
|
8
|
+
# ====================================================
|
9
|
+
|
10
|
+
require 'logger'
|
11
|
+
|
12
|
+
module Reedb
|
13
|
+
|
14
|
+
# A logger that gets bound to a vault and logs vault activity in the
|
15
|
+
# vaults logs/ directory.
|
16
|
+
# It's using the Ruby internal logging library to split up logs into date blocks.
|
17
|
+
# (Each day is one log file).
|
18
|
+
#
|
19
|
+
class VaultLogger
|
20
|
+
|
21
|
+
@@logger = nil
|
22
|
+
|
23
|
+
# Sets up a logger on a vault and loads existing logs if they exist.
|
24
|
+
# Logs are limited not in size but only by dates. Vault logs don't contain whitespaces.
|
25
|
+
#
|
26
|
+
def self.setup(path)
|
27
|
+
log_path = "#{path}/logs/#{Reedb::Utilities.get_time(true)}.log"
|
28
|
+
@@logger = Logger.new("#{log_path}")
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Writes something to the current vault log (under #{@path}/logs/)
|
33
|
+
# Possible parameters are:
|
34
|
+
# 'debug':: Throwing every operation at the log.
|
35
|
+
# 'info':: Default logging level for most situations and events.
|
36
|
+
# 'warn':: Logs a warning. Should be event that won't impact stability.
|
37
|
+
# 'error':: Logs an error. Should be recoverable event.
|
38
|
+
# 'fatal':: Logs a fatal crash. Should make the Reepass daemon crash!
|
39
|
+
def self.write(message, level = "")
|
40
|
+
if level == "warn"
|
41
|
+
@@logger.warn(message)
|
42
|
+
|
43
|
+
elsif level == "debug"
|
44
|
+
@@logger.debug(message)
|
45
|
+
|
46
|
+
elsif level == "error"
|
47
|
+
@@logger.error(message)
|
48
|
+
|
49
|
+
elsif level == "fatal"
|
50
|
+
@@logger.fatal(message)
|
51
|
+
|
52
|
+
else
|
53
|
+
@@logger.info(message)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class DaemonLogger
|
59
|
+
|
60
|
+
@@logger = nil
|
61
|
+
|
62
|
+
# Sets up a logger on a vault and loads existing logs if they exist.
|
63
|
+
# Logs are limited not in size but only by dates. Vault logs don't contain whitespaces.
|
64
|
+
#
|
65
|
+
def self.setup(path)
|
66
|
+
log_path = "#{path}/#{Reedb::Utilities.get_time(true)}.log"
|
67
|
+
@@logger = Logger.new("#{log_path}")
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# Writes something to the current vault log (under #{@path}/logs/)
|
72
|
+
# Possible parameters are:
|
73
|
+
# 'debug':: Throwing every operation at the log.
|
74
|
+
# 'info':: Default logging level for most situations and events.
|
75
|
+
# 'warn':: Logs a warning. Should be event that won't impact stability.
|
76
|
+
# 'error':: Logs an error. Should be recoverable event.
|
77
|
+
# 'fatal':: Logs a fatal crash. Should make the Reepass daemon crash!
|
78
|
+
def self.write(message, level = "")
|
79
|
+
if level == "warn"
|
80
|
+
@@logger.warn(message)
|
81
|
+
|
82
|
+
elsif level == "debug"
|
83
|
+
@@logger.debug(message)
|
84
|
+
|
85
|
+
elsif level == "error"
|
86
|
+
@@logger.error(message)
|
87
|
+
|
88
|
+
elsif level == "fatal"
|
89
|
+
@@logger.fatal(message)
|
90
|
+
|
91
|
+
else
|
92
|
+
@@logger.info(message)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end # End of class
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# ====================================================
|
2
|
+
# Copyright 2015 Lonely Robot (see @author)
|
3
|
+
# @author: Katharina Sabel | www.2rsoftworks.de
|
4
|
+
#
|
5
|
+
# Distributed under the GNU Lesser GPL Version 3
|
6
|
+
# (See accompanying LICENSE file or get a copy at
|
7
|
+
# https://www.gnu.org/licenses/lgpl.html)
|
8
|
+
# ====================================================
|
9
|
+
|
10
|
+
module Reedb
|
11
|
+
|
12
|
+
# Handler class to map vaults in the reedb api to a set
|
13
|
+
class MetaVault
|
14
|
+
attr_accessor :name, :path, :size, :uuid
|
15
|
+
|
16
|
+
def initialize(name, path, size, uuid)
|
17
|
+
@name = name
|
18
|
+
@path = path
|
19
|
+
@size = size
|
20
|
+
@uuid = uuid
|
21
|
+
end
|
22
|
+
|
23
|
+
# Used to compare vaults
|
24
|
+
def includes?(name) return (@name == name) end
|
25
|
+
def includes?(name, path) return (@name == name && @path == path) end
|
26
|
+
def to_s() return "'#{@name}'@'#{@path}', size: #{@size}" end
|
27
|
+
end
|
28
|
+
end
|