rbnacl 4.0.2 → 5.0.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 +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGES.md +8 -1
- data/README.md +2 -2
- data/lib/rbnacl/hash.rb +1 -1
- data/lib/rbnacl/hash/blake2b.rb +123 -23
- data/lib/rbnacl/version.rb +1 -1
- data/spec/rbnacl/hash/blake2b_spec.rb +70 -0
- metadata +16 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ff080260ca84ccc368c7c8414627267dffa9348
|
4
|
+
data.tar.gz: 2a3275a27b2ca89e36223529316076b67f895cac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32bd4e83ea75ce57d93dcc7c1b2f3249d7e0010694c03d7d9a73a20e1a310ec6b9b6f40e0f190fb454f64eda528ea4e7eb35367d49d04f5ea7aafad270d26ff2
|
7
|
+
data.tar.gz: 9408f5ae9713e018e29a943bb9ddbb64010e9ace49d4cff37b2769e886f09025edba7776dcfbe81372363051b8d7b68cd7e04a583b8eeda87b3d48a17f39607b
|
data/.rubocop.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
##
|
1
|
+
## 5.0.0 (2017-06-13)
|
2
|
+
|
3
|
+
* [#159](https://github.com/cryptosphere/rbnacl/pull/159)
|
4
|
+
Support the BLAKE2b Initialize-Update-Finalize API.
|
5
|
+
([@fudanchii])
|
6
|
+
|
7
|
+
## 4.0.2 (2017-03-12)
|
2
8
|
|
3
9
|
* [#157](https://github.com/cryptosphere/rbnacl/pull/157)
|
4
10
|
Raise error on degenerate keys (fixes #152).
|
@@ -112,3 +118,4 @@
|
|
112
118
|
[@mwpastore]: https://github.com/mwpastore
|
113
119
|
[@elijh]: https://github.com/elijh
|
114
120
|
[@paragonie-scott]: https://github.com/paragonie-scott
|
121
|
+
[@fudanchii]: https://github.com/fudanchii
|
data/README.md
CHANGED
@@ -6,10 +6,10 @@
|
|
6
6
|
[](https://coveralls.io/r/cryptosphere/rbnacl)
|
7
7
|
[](https://github.com/cryptosphere/rbnacl/blob/master/LICENSE.txt)
|
8
8
|
|
9
|
-
_NOTE: This is the
|
9
|
+
_NOTE: This is the 5.x **stable** branch of RbNaCl. For the 4.x **legacy**
|
10
10
|
branch, please see:_
|
11
11
|
|
12
|
-
https://github.com/cryptosphere/rbnacl/tree/
|
12
|
+
https://github.com/cryptosphere/rbnacl/tree/4-x-stable
|
13
13
|
|
14
14
|
A Ruby binding to the state-of-the-art [Networking and Cryptography][nacl]
|
15
15
|
library by [Daniel J. Bernstein][djb]. This is **NOT** Google Native Client.
|
data/lib/rbnacl/hash.rb
CHANGED
data/lib/rbnacl/hash/blake2b.rb
CHANGED
@@ -28,12 +28,25 @@ module RbNaCl
|
|
28
28
|
:crypto_generichash_blake2b_salt_personal,
|
29
29
|
[:pointer, :size_t, :pointer, :ulong_long, :pointer, :size_t, :pointer, :pointer]
|
30
30
|
|
31
|
+
sodium_function :generichash_blake2b_init,
|
32
|
+
:crypto_generichash_blake2b_init_salt_personal,
|
33
|
+
[:pointer, :pointer, :size_t, :size_t, :pointer, :pointer]
|
34
|
+
|
35
|
+
sodium_function :generichash_blake2b_update,
|
36
|
+
:crypto_generichash_blake2b_update,
|
37
|
+
[:pointer, :pointer, :ulong_long]
|
38
|
+
|
39
|
+
sodium_function :generichash_blake2b_final,
|
40
|
+
:crypto_generichash_blake2b_final,
|
41
|
+
[:pointer, :pointer, :size_t]
|
42
|
+
|
31
43
|
EMPTY_PERSONAL = ("\0" * PERSONALBYTES).freeze
|
32
44
|
EMPTY_SALT = ("\0" * SALTBYTES).freeze
|
33
45
|
|
34
|
-
#
|
46
|
+
# Calculate a Blake2b digest
|
35
47
|
#
|
36
|
-
# @param [
|
48
|
+
# @param [String] message Message to be hashed
|
49
|
+
# @param [Hash] options Blake2b configuration
|
37
50
|
# @option opts [String] :key for Blake2b keyed mode
|
38
51
|
# @option opts [Integer] :digest_size size of output digest in bytes
|
39
52
|
# @option opts [String] :salt Provide a salt to support randomised hashing.
|
@@ -43,39 +56,126 @@ module RbNaCl
|
|
43
56
|
#
|
44
57
|
# @raise [RbNaCl::LengthError] Invalid length specified for one or more options
|
45
58
|
#
|
46
|
-
# @return [
|
47
|
-
def
|
48
|
-
|
59
|
+
# @return [String] Blake2b digest of the string as raw bytes
|
60
|
+
def self.digest(message, options)
|
61
|
+
opts = validate_opts(options)
|
62
|
+
digest = Util.zeros(opts[:digest_size])
|
63
|
+
generichash_blake2b(digest, opts[:digest_size], message, message.bytesize,
|
64
|
+
opts[:key], opts[:key_size], opts[:salt], opts[:personal]) ||
|
65
|
+
raise(CryptoError, "Hashing failed!")
|
66
|
+
digest
|
67
|
+
end
|
49
68
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
69
|
+
# Validate and sanitize values for Blake2b configuration
|
70
|
+
#
|
71
|
+
# @param [Hash] options Blake2b configuration
|
72
|
+
# @option opts [String] :key for Blake2b keyed mode
|
73
|
+
# @option opts [Integer] :digest_size size of output digest in bytes
|
74
|
+
# @option opts [String] :salt Provide a salt to support randomised hashing.
|
75
|
+
# This is mixed into the parameters block to start the hashing.
|
76
|
+
# @option opts [Personal] :personal Provide personalisation string to allow pinning a hash for a particular purpose.
|
77
|
+
# This is mixed into the parameters block to start the hashing
|
78
|
+
#
|
79
|
+
# @raise [RbNaCl::LengthError] Invalid length specified for one or more options
|
80
|
+
#
|
81
|
+
# @return [Hash] opts Configuration hash with sanitized values
|
82
|
+
def self.validate_opts(opts)
|
83
|
+
key = opts.fetch(:key, nil)
|
84
|
+
if key
|
85
|
+
key_size = key.bytesize
|
86
|
+
raise LengthError, "key too short" if key_size < KEYBYTES_MIN
|
87
|
+
raise LengthError, "key too long" if key_size > KEYBYTES_MAX
|
54
88
|
else
|
55
|
-
|
89
|
+
key_size = 0
|
56
90
|
end
|
91
|
+
opts[:key_size] = key_size
|
57
92
|
|
58
|
-
|
59
|
-
raise LengthError, "digest size too short" if
|
60
|
-
raise LengthError, "digest size too long" if
|
93
|
+
digest_size = opts.fetch(:digest_size, BYTES_MAX)
|
94
|
+
raise LengthError, "digest size too short" if digest_size < BYTES_MIN
|
95
|
+
raise LengthError, "digest size too long" if digest_size > BYTES_MAX
|
96
|
+
opts[:digest_size] = digest_size
|
61
97
|
|
62
|
-
|
63
|
-
|
98
|
+
personal = opts.fetch(:personal, EMPTY_PERSONAL)
|
99
|
+
opts[:personal] = Util.zero_pad(PERSONALBYTES, personal)
|
64
100
|
|
65
|
-
|
66
|
-
|
101
|
+
salt = opts.fetch(:salt, EMPTY_SALT)
|
102
|
+
opts[:salt] = Util.zero_pad(SALTBYTES, salt)
|
103
|
+
opts
|
67
104
|
end
|
68
105
|
|
69
|
-
|
106
|
+
private_class_method :validate_opts
|
107
|
+
|
108
|
+
def self.new(opts = {})
|
109
|
+
opts = validate_opts(opts)
|
110
|
+
super
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create a new Blake2b hash object
|
114
|
+
#
|
115
|
+
# @param [Hash] opts Blake2b configuration
|
116
|
+
# @option opts [String] :key for Blake2b keyed mode
|
117
|
+
# @option opts [Integer] :digest_size size of output digest in bytes
|
118
|
+
# @option opts [String] :salt Provide a salt to support randomised hashing.
|
119
|
+
# This is mixed into the parameters block to start the hashing.
|
120
|
+
# @option opts [Personal] :personal Provide personalisation string to allow pinning a hash for a particular purpose.
|
121
|
+
# This is mixed into the parameters block to start the hashing
|
122
|
+
#
|
123
|
+
# @raise [RbNaCl::LengthError] Invalid length specified for one or more options
|
124
|
+
#
|
125
|
+
# @return [RbNaCl::Hash::Blake2b] A Blake2b hasher object
|
126
|
+
def initialize(opts = {})
|
127
|
+
@key = opts[:key]
|
128
|
+
@key_size = opts[:key_size]
|
129
|
+
@digest_size = opts[:digest_size]
|
130
|
+
@personal = opts[:personal]
|
131
|
+
@salt = opts[:salt]
|
132
|
+
|
133
|
+
@incycle = false
|
134
|
+
@instate = nil
|
135
|
+
end
|
136
|
+
|
137
|
+
# Initialize state for Blake2b hash calculation,
|
138
|
+
# this will be called automatically from #update if needed
|
139
|
+
def reset
|
140
|
+
@instate.release if @instate
|
141
|
+
@instate = State.new
|
142
|
+
self.class.generichash_blake2b_init(@instate.pointer, @key, @key_size, @digest_size, @salt, @personal) ||
|
143
|
+
raise(CryptoError, "Hash init failed!")
|
144
|
+
@incycle = true
|
145
|
+
@digest = nil
|
146
|
+
end
|
147
|
+
|
148
|
+
# Reentrant version of Blake2b digest calculation method
|
70
149
|
#
|
71
150
|
# @param [String] message Message to be hashed
|
151
|
+
def update(message)
|
152
|
+
reset unless @incycle
|
153
|
+
self.class.generichash_blake2b_update(@instate.pointer, message, message.bytesize) ||
|
154
|
+
raise(CryptoError, "Hashing failed!")
|
155
|
+
end
|
156
|
+
alias << update
|
157
|
+
|
158
|
+
# Finalize digest calculation, return cached digest if any
|
72
159
|
#
|
73
160
|
# @return [String] Blake2b digest of the string as raw bytes
|
74
|
-
def digest
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
digest
|
161
|
+
def digest
|
162
|
+
raise(CryptoError, "No message to hash yet!") unless @incycle
|
163
|
+
return @digest if @digest
|
164
|
+
@digest = Util.zeros(@digest_size)
|
165
|
+
self.class.generichash_blake2b_final(@instate.pointer, @digest, @digest_size) ||
|
166
|
+
raise(CryptoError, "Hash finalization failed!")
|
167
|
+
@digest
|
168
|
+
end
|
169
|
+
|
170
|
+
# The crypto_generichash_blake2b_state struct representation
|
171
|
+
# ref: jedisct1/libsodium/src/libsodium/include/sodium/crypto_generichash_blake2b.h#L23
|
172
|
+
class State < FFI::Struct
|
173
|
+
layout :h, [:uint64, 8],
|
174
|
+
:t, [:uint64, 2],
|
175
|
+
:f, [:uint64, 2],
|
176
|
+
:buf, [:uint8, 2 * 128],
|
177
|
+
:buflen, :size_t,
|
178
|
+
:last_node, :uint8
|
79
179
|
end
|
80
180
|
end
|
81
181
|
end
|
data/lib/rbnacl/version.rb
CHANGED
@@ -14,6 +14,29 @@ RSpec.describe RbNaCl::Hash::Blake2b do
|
|
14
14
|
expect(RbNaCl::Hash.blake2b("")).to eq empty_string_hash
|
15
15
|
end
|
16
16
|
|
17
|
+
context "arbitrary length message API" do
|
18
|
+
let(:blake2b) { RbNaCl::Hash::Blake2b.new }
|
19
|
+
|
20
|
+
it "calculates the correct hash for a reference string" do
|
21
|
+
blake2b << reference_string
|
22
|
+
expect(blake2b.digest).to eq reference_string_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
it "calculates the correct hash for an empty string" do
|
26
|
+
blake2b << ""
|
27
|
+
expect(blake2b.digest).to eq empty_string_hash
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raise CryptoError when digest called without reset / message" do
|
31
|
+
expect { blake2b.digest }.to raise_error(RbNaCl::CryptoError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "calculates hash for empty string when digest called directly after reset" do
|
35
|
+
blake2b.reset
|
36
|
+
expect(blake2b.digest).to eq empty_string_hash
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
17
40
|
context "keyed" do
|
18
41
|
let(:reference_string) { vector :blake2b_keyed_message }
|
19
42
|
let(:reference_key) { vector :blake2b_key }
|
@@ -26,6 +49,23 @@ RSpec.describe RbNaCl::Hash::Blake2b do
|
|
26
49
|
it "doesn't accept empty strings as a key" do
|
27
50
|
expect { RbNaCl::Hash.blake2b(reference_string, key: "") }.to raise_error(RbNaCl::LengthError)
|
28
51
|
end
|
52
|
+
|
53
|
+
context "arbitrary length message API" do
|
54
|
+
let(:blake2b) { RbNaCl::Hash::Blake2b.new(key: "") }
|
55
|
+
let(:blake2b_wk) { RbNaCl::Hash::Blake2b.new(key: reference_key) }
|
56
|
+
|
57
|
+
it "calculates keyed hashes correctly" do
|
58
|
+
blake2b_wk << reference_string
|
59
|
+
expect(blake2b_wk.digest).to eq reference_string_hash
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't accept empty strings as a key" do
|
63
|
+
expect do
|
64
|
+
blake2b << reference_string
|
65
|
+
blake2b.digest
|
66
|
+
end.to raise_error(RbNaCl::LengthError)
|
67
|
+
end
|
68
|
+
end
|
29
69
|
end
|
30
70
|
|
31
71
|
context "personalized" do
|
@@ -42,6 +82,21 @@ RSpec.describe RbNaCl::Hash::Blake2b do
|
|
42
82
|
it "calculates personalised hashes correctly with a short personal" do
|
43
83
|
expect(RbNaCl::Hash.blake2b(reference_string, personal: reference_personal_short)).to eq reference_personal_short_hash
|
44
84
|
end
|
85
|
+
|
86
|
+
context "arbitrary length message API" do
|
87
|
+
let(:blake2b) { RbNaCl::Hash::Blake2b.new(personal: reference_personal) }
|
88
|
+
let(:blake2b_sh) { RbNaCl::Hash::Blake2b.new(personal: reference_personal_short) }
|
89
|
+
|
90
|
+
it "calculates personalised hashes correctly" do
|
91
|
+
blake2b << reference_string
|
92
|
+
expect(blake2b.digest).to eq reference_personal_hash
|
93
|
+
end
|
94
|
+
|
95
|
+
it "calculates personalised hashes correctly with a short personal" do
|
96
|
+
blake2b_sh << reference_string
|
97
|
+
expect(blake2b_sh.digest).to eq reference_personal_short_hash
|
98
|
+
end
|
99
|
+
end
|
45
100
|
end
|
46
101
|
|
47
102
|
context "salted" do
|
@@ -58,5 +113,20 @@ RSpec.describe RbNaCl::Hash::Blake2b do
|
|
58
113
|
it "calculates saltised hashes correctly with a short salt" do
|
59
114
|
expect(RbNaCl::Hash.blake2b(reference_string, salt: reference_salt_short)).to eq reference_salt_short_hash
|
60
115
|
end
|
116
|
+
|
117
|
+
context "arbitrary length message API" do
|
118
|
+
let(:blake2b) { RbNaCl::Hash::Blake2b.new(salt: reference_salt) }
|
119
|
+
let(:blake2b_sh) { RbNaCl::Hash::Blake2b.new(salt: reference_salt_short) }
|
120
|
+
|
121
|
+
it "calculates saltised hashes correctly" do
|
122
|
+
blake2b << reference_string
|
123
|
+
expect(blake2b.digest).to eq reference_salt_hash
|
124
|
+
end
|
125
|
+
|
126
|
+
it "calculates saltised hashes correctly with a short salt" do
|
127
|
+
blake2b_sh << reference_string
|
128
|
+
expect(blake2b_sh.digest).to eq reference_salt_short_hash
|
129
|
+
end
|
130
|
+
end
|
61
131
|
end
|
62
132
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbnacl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
@@ -9,34 +9,34 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-06-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - '>='
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '0'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - '>='
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: bundler
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - '>='
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - '>='
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
description: The Networking and Cryptography (NaCl) library provides a high-level
|
@@ -48,13 +48,13 @@ executables: []
|
|
48
48
|
extensions: []
|
49
49
|
extra_rdoc_files: []
|
50
50
|
files:
|
51
|
-
-
|
52
|
-
-
|
53
|
-
-
|
54
|
-
-
|
55
|
-
-
|
56
|
-
-
|
57
|
-
-
|
51
|
+
- .coveralls.yml
|
52
|
+
- .gitignore
|
53
|
+
- .rspec
|
54
|
+
- .rubocop.yml
|
55
|
+
- .ruby-version
|
56
|
+
- .travis.yml
|
57
|
+
- .yardopts
|
58
58
|
- CHANGES.md
|
59
59
|
- Gemfile
|
60
60
|
- Guardfile
|
@@ -139,17 +139,17 @@ require_paths:
|
|
139
139
|
- lib
|
140
140
|
required_ruby_version: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
|
-
- -
|
142
|
+
- - '>='
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: 2.2.6
|
145
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
146
|
requirements:
|
147
|
-
- -
|
147
|
+
- - '>='
|
148
148
|
- !ruby/object:Gem::Version
|
149
149
|
version: '0'
|
150
150
|
requirements: []
|
151
151
|
rubyforge_project:
|
152
|
-
rubygems_version: 2.
|
152
|
+
rubygems_version: 2.0.14.1
|
153
153
|
signing_key:
|
154
154
|
specification_version: 4
|
155
155
|
summary: Ruby binding to the Networking and Cryptography (NaCl) library
|