noise-ruby 0.10.0 → 0.11.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/.dockerignore +8 -0
- data/.github/workflows/ruby.yml +56 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +29 -7
- data/.ruby-version +1 -1
- data/Dockerfile +29 -0
- data/Gemfile +2 -7
- data/README.md +6 -0
- data/lib/noise/connection/base.rb +21 -16
- data/lib/noise/exceptions/decrypt_error.rb +1 -0
- data/lib/noise/exceptions/encrypt_error.rb +1 -0
- data/lib/noise/exceptions/invalid_public_key_error.rb +3 -0
- data/lib/noise/exceptions/missing_dependency_error.rb +8 -0
- data/lib/noise/exceptions.rb +1 -0
- data/lib/noise/functions/cipher/aes_gcm.rb +19 -8
- data/lib/noise/functions/cipher/cha_cha_poly.rb +5 -4
- data/lib/noise/functions/dh/ed448.rb +7 -6
- data/lib/noise/functions/dh/secp256k1.rb +6 -5
- data/lib/noise/functions/hash/blake2s.rb +2 -0
- data/lib/noise/functions/hash/blake3.rb +8 -6
- data/lib/noise/functions/hash.rb +10 -6
- data/lib/noise/pattern.rb +153 -30
- data/lib/noise/protocol.rb +23 -21
- data/lib/noise/state/cipher_state.rb +7 -1
- data/lib/noise/state/handshake_state.rb +97 -70
- data/lib/noise/state/symmetric_state.rb +6 -3
- data/lib/noise/utils/string.rb +20 -6
- data/lib/noise/version.rb +1 -1
- data/lib/noise.rb +27 -1
- data/noise.gemspec +16 -6
- metadata +87 -30
- data/.travis.yml +0 -5
- data/lib/noise/utils/hash.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 876220b34ef23fb45d18cb2dd0dfd7627b5bb202e51f57af9c0cd5a62c211626
|
|
4
|
+
data.tar.gz: 662c47506e70adf05928da7b8c5e3fb48503e0043242f4127459297e29dba476
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8db525a248cd3a66f1597736745d70470ef109a3e7c7acd9758fda79c5c45aa78350d833c38a4d6d3d2ae9f8a06dd3b6b7f89a6c4928c12ea3dfe899efca4906
|
|
7
|
+
data.tar.gz: 420bd4dbeff8a66570f9d71ec219375aafeb9e5a45d76b1f23f6d9dfe47776b73da860b918f585214983224fea488370dc423f27dbd7c8905c95f33d8a4dce13
|
data/.dockerignore
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
+
|
|
8
|
+
name: Ruby
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [ master ]
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [ master ]
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
strategy:
|
|
20
|
+
matrix:
|
|
21
|
+
ruby:
|
|
22
|
+
- '3.3'
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout code
|
|
25
|
+
uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
|
28
|
+
uses: ruby/setup-ruby@v1
|
|
29
|
+
with:
|
|
30
|
+
ruby-version: ${{ matrix.ruby }}
|
|
31
|
+
bundler-cache: true
|
|
32
|
+
- name: Lint code for consistent style
|
|
33
|
+
run: bundle exec rubocop -f github
|
|
34
|
+
|
|
35
|
+
test:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
strategy:
|
|
38
|
+
matrix:
|
|
39
|
+
ruby:
|
|
40
|
+
- '3.0'
|
|
41
|
+
- '3.1'
|
|
42
|
+
- '3.2'
|
|
43
|
+
- '3.3'
|
|
44
|
+
- '3.4'
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout code
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
|
50
|
+
uses: ruby/setup-ruby@v1
|
|
51
|
+
with:
|
|
52
|
+
ruby-version: ${{ matrix.ruby }}
|
|
53
|
+
bundler-cache: true
|
|
54
|
+
- name: Run tests
|
|
55
|
+
run: bundle exec rspec spec
|
|
56
|
+
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
require:
|
|
2
|
+
- rubocop-rspec
|
|
3
|
+
Metrics:
|
|
2
4
|
Enabled: false
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
Layout/LineLength:
|
|
5
7
|
Max: 120
|
|
6
8
|
|
|
7
|
-
Metrics/MethodLength:
|
|
8
|
-
Max: 30
|
|
9
|
-
|
|
10
9
|
Style/Documentation:
|
|
11
10
|
Enabled: false
|
|
12
11
|
|
|
@@ -20,7 +19,30 @@ Style/WordArray:
|
|
|
20
19
|
MinSize: 100
|
|
21
20
|
|
|
22
21
|
AllCops:
|
|
23
|
-
TargetRubyVersion:
|
|
22
|
+
TargetRubyVersion: 3.0
|
|
23
|
+
SuggestExtensions: false
|
|
24
24
|
|
|
25
|
-
Naming/
|
|
25
|
+
Naming/MethodParameterName:
|
|
26
26
|
MinNameLength: 1
|
|
27
|
+
|
|
28
|
+
RSpec:
|
|
29
|
+
Exclude:
|
|
30
|
+
- "spec/factories/*"
|
|
31
|
+
RSpec/AnyInstance:
|
|
32
|
+
Enabled: false
|
|
33
|
+
RSpec/ContextWording:
|
|
34
|
+
Enabled: false
|
|
35
|
+
RSpec/ExampleLength:
|
|
36
|
+
Enabled: false
|
|
37
|
+
RSpec/MultipleExpectations:
|
|
38
|
+
Enabled: false
|
|
39
|
+
RSpec/NamedSubject:
|
|
40
|
+
Enabled: false
|
|
41
|
+
RSpec/NestedGroups:
|
|
42
|
+
Enabled: false
|
|
43
|
+
RSpec/MultipleMemoizedHelpers:
|
|
44
|
+
Enabled: false
|
|
45
|
+
RSpec/SpecFilePathFormat:
|
|
46
|
+
Enabled: false
|
|
47
|
+
RSpec/IndexedLet:
|
|
48
|
+
Enabled: false
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3.3.5
|
data/Dockerfile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# check=skip=FromPlatformFlagConstDisallowed
|
|
2
|
+
# The test suite loads the x86_64 libsecp256k1 / libgoldilocks bundled in spec/lib, and
|
|
3
|
+
# spec/spec_helper.rb points at them unconditionally, so this image has to be linux/amd64
|
|
4
|
+
# even on an arm64 host. It mirrors the CI runner (ubuntu x86_64).
|
|
5
|
+
FROM --platform=linux/amd64 ruby:3.3
|
|
6
|
+
|
|
7
|
+
# libsodium: dlopen'd by rbnacl, which lib/noise.rb requires unconditionally.
|
|
8
|
+
# cargo: the blake3 gem is a Rust extension and is built at install time.
|
|
9
|
+
# git: noise.gemspec calls `git ls-files` while the gemspec is evaluated.
|
|
10
|
+
RUN apt-get update -qq \
|
|
11
|
+
&& apt-get install -y --no-install-recommends build-essential git libsodium23 cargo \
|
|
12
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
13
|
+
|
|
14
|
+
WORKDIR /work
|
|
15
|
+
|
|
16
|
+
# Install the gems from the dependency definitions alone so the layer stays cached when only
|
|
17
|
+
# the source changes. Gems live in BUNDLE_PATH (/usr/local/bundle), outside /work, so mounting
|
|
18
|
+
# the working tree over /work at run time does not hide them.
|
|
19
|
+
#
|
|
20
|
+
# Gemfile.lock is gitignored, hence the glob: it is copied when the host has one so the image
|
|
21
|
+
# installs the exact versions the mounted lock will ask for at run time, and skipped otherwise.
|
|
22
|
+
COPY Gemfile* noise.gemspec ./
|
|
23
|
+
COPY lib/noise/version.rb lib/noise/version.rb
|
|
24
|
+
RUN bundle install
|
|
25
|
+
|
|
26
|
+
# Run with the working tree mounted:
|
|
27
|
+
# docker run --rm -v "$PWD":/work noise-dev
|
|
28
|
+
# docker run --rm -v "$PWD":/work noise-dev bundle exec rubocop
|
|
29
|
+
CMD ["bundle", "exec", "rspec"]
|
data/Gemfile
CHANGED
|
@@ -4,11 +4,6 @@ source 'https://rubygems.org'
|
|
|
4
4
|
|
|
5
5
|
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
6
6
|
|
|
7
|
-
# Specify your gem's dependencies in noise.gemspec
|
|
7
|
+
# Specify your gem's dependencies in noise.gemspec. The optional ed448, secp256k1-ruby and blake3
|
|
8
|
+
# backends are development dependencies there, so the test suite covers all of them.
|
|
8
9
|
gemspec
|
|
9
|
-
|
|
10
|
-
# Use secp256k1 as ecdh function
|
|
11
|
-
# gem 'secp256k1-ruby'
|
|
12
|
-
|
|
13
|
-
# Use blake3 as hash function
|
|
14
|
-
# gem 'blake3'
|
data/README.md
CHANGED
|
@@ -59,6 +59,12 @@ After installing, define an environment variable as follows:
|
|
|
59
59
|
|
|
60
60
|
$ export LIBGOLDILOCKS=/usr/local/lib/libgoldilocks.so
|
|
61
61
|
|
|
62
|
+
and, add this line to your Gemfile:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
gem 'ed448'
|
|
66
|
+
```
|
|
67
|
+
|
|
62
68
|
If you use Secp256k1, you must install [libsecp256k1](https://github.com/bitcoin-core/secp256k1).
|
|
63
69
|
|
|
64
70
|
$ git clone https://github.com/bitcoin-core/secp256k1
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
module Noise
|
|
4
4
|
module Connection
|
|
5
5
|
class Base
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# The Noise spec caps a handshake or transport message at 65535 bytes.
|
|
7
|
+
MAX_MESSAGE_LENGTH = 65_535
|
|
8
|
+
|
|
9
|
+
attr_reader :protocol, :handshake_started, :handshake_finished, :handshake_hash, :handshake_state,
|
|
10
|
+
:cipher_state_encrypt, :cipher_state_decrypt, :cipher_state_handshake, :s, :rs
|
|
8
11
|
attr_accessor :psks, :prologue
|
|
9
|
-
attr_reader :s, :rs
|
|
10
12
|
|
|
11
13
|
def initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil })
|
|
12
14
|
@protocol = Protocol.create(name)
|
|
@@ -27,11 +29,15 @@ module Noise
|
|
|
27
29
|
@handshake_started = true
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
# Restarts the handshake with a fallback pattern, carrying over the keys of the aborted one.
|
|
33
|
+
#
|
|
34
|
+
# The roles swap here: the party that wrote the aborted message now reads, and the one that
|
|
35
|
+
# failed to read it now writes. Both sides are already in that state, so @next_message is
|
|
36
|
+
# deliberately left as it is rather than reset through initialize_next_message.
|
|
30
37
|
def fallback(fallback_name)
|
|
31
38
|
@protocol = Protocol.create(fallback_name)
|
|
32
39
|
@handshake_started = false
|
|
33
40
|
@handshake_finished = false
|
|
34
|
-
# initialize_next_message
|
|
35
41
|
@local_keypairs = { e: @handshake_state.e, s: @handshake_state.s }
|
|
36
42
|
@remote_keys = { re: @handshake_state.re, rs: @handshake_state.rs }
|
|
37
43
|
start_handshake
|
|
@@ -40,7 +46,6 @@ module Noise
|
|
|
40
46
|
def initialise_handshake_state
|
|
41
47
|
@handshake_state = Noise::State::HandshakeState.new(
|
|
42
48
|
self,
|
|
43
|
-
protocol,
|
|
44
49
|
initiator?,
|
|
45
50
|
@prologue,
|
|
46
51
|
@local_keypairs,
|
|
@@ -56,10 +61,13 @@ module Noise
|
|
|
56
61
|
raise Noise::Exceptions::NoiseHandshakeError if @next_message != :write
|
|
57
62
|
raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished
|
|
58
63
|
|
|
64
|
+
if @handshake_state.expected_message_length(payload.bytesize) > MAX_MESSAGE_LENGTH
|
|
65
|
+
raise Noise::Exceptions::NoiseHandshakeError, 'Message exceeds the maximum length.'
|
|
66
|
+
end
|
|
67
|
+
|
|
59
68
|
@next_message = :read
|
|
60
69
|
buffer = +''
|
|
61
|
-
|
|
62
|
-
@handshake_finished = true if result
|
|
70
|
+
@handshake_finished = @handshake_state.write_message(payload, buffer)
|
|
63
71
|
buffer
|
|
64
72
|
end
|
|
65
73
|
|
|
@@ -68,11 +76,12 @@ module Noise
|
|
|
68
76
|
raise Noise::Exceptions::NoiseHandshakeError unless @handshake_started
|
|
69
77
|
raise Noise::Exceptions::NoiseHandshakeError if @next_message != :read
|
|
70
78
|
raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished
|
|
79
|
+
raise Noise::Exceptions::NoiseHandshakeError, 'Message exceeds the maximum length.' if
|
|
80
|
+
data.bytesize > MAX_MESSAGE_LENGTH
|
|
71
81
|
|
|
72
82
|
@next_message = :write
|
|
73
83
|
buffer = +''
|
|
74
|
-
|
|
75
|
-
@handshake_finished = true if result
|
|
84
|
+
@handshake_finished = @handshake_state.read_message(data, buffer)
|
|
76
85
|
buffer
|
|
77
86
|
end
|
|
78
87
|
|
|
@@ -94,23 +103,19 @@ module Noise
|
|
|
94
103
|
raise Noise::Exceptions::NoisePSKError if @protocol.pattern.psk_count != @psks.count
|
|
95
104
|
end
|
|
96
105
|
|
|
97
|
-
def
|
|
106
|
+
def missing_keypairs?
|
|
98
107
|
keypairs = @local_keypairs.merge(@remote_keys)
|
|
99
108
|
@protocol.pattern.required_keypairs(initiator?).any? { |keypair| !keypairs[keypair] }
|
|
100
109
|
end
|
|
101
110
|
|
|
102
111
|
def validate
|
|
103
|
-
validate_psk! if
|
|
112
|
+
validate_psk! if @protocol.psk?
|
|
104
113
|
|
|
105
|
-
raise Noise::Exceptions::NoiseValidationError if
|
|
114
|
+
raise Noise::Exceptions::NoiseValidationError if missing_keypairs?
|
|
106
115
|
|
|
107
116
|
true
|
|
108
117
|
end
|
|
109
118
|
|
|
110
|
-
def psk_handshake?
|
|
111
|
-
@protocol.is_psk_handshake
|
|
112
|
-
end
|
|
113
|
-
|
|
114
119
|
def handshake_done(_c1, _c2)
|
|
115
120
|
@handshake_hash = @symmetric_state.handshake_hash
|
|
116
121
|
@s = @handshake_state.s
|
data/lib/noise/exceptions.rb
CHANGED
|
@@ -6,6 +6,7 @@ module Noise
|
|
|
6
6
|
autoload :EncryptError, 'noise/exceptions/encrypt_error'
|
|
7
7
|
autoload :InvalidPublicKeyError, 'noise/exceptions/invalid_public_key_error'
|
|
8
8
|
autoload :MaxNonceError, 'noise/exceptions/max_nonce_error'
|
|
9
|
+
autoload :MissingDependencyError, 'noise/exceptions/missing_dependency_error'
|
|
9
10
|
autoload :ProtocolNameError, 'noise/exceptions/protocol_name_error'
|
|
10
11
|
autoload :NoiseHandshakeError, 'noise/exceptions/noise_handshake_error'
|
|
11
12
|
autoload :NoiseValidationError, 'noise/exceptions/noise_validation_error'
|
|
@@ -7,28 +7,29 @@ module Noise
|
|
|
7
7
|
MAX_NONCE = 2**64 - 1
|
|
8
8
|
|
|
9
9
|
def encrypt(k, n, ad, plaintext)
|
|
10
|
-
cipher = OpenSSL::Cipher
|
|
10
|
+
cipher = OpenSSL::Cipher.new('aes-256-gcm').encrypt
|
|
11
11
|
cipher.key = k
|
|
12
12
|
cipher.iv = nonce_to_bytes(n)
|
|
13
13
|
cipher.auth_data = ad
|
|
14
|
-
|
|
14
|
+
update(cipher, plaintext) + cipher.final + cipher.auth_tag
|
|
15
15
|
rescue OpenSSL::Cipher::CipherError => e
|
|
16
|
-
raise Noise::Exceptions::EncryptError.
|
|
16
|
+
raise Noise::Exceptions::EncryptError, "Encrypt failed. #{e.message}", e.backtrace
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def decrypt(k, n, ad, ciphertext)
|
|
20
|
-
cipher = OpenSSL::Cipher
|
|
20
|
+
cipher = OpenSSL::Cipher.new('aes-256-gcm').decrypt
|
|
21
21
|
cipher.key = k
|
|
22
22
|
cipher.iv = nonce_to_bytes(n)
|
|
23
23
|
cipher.auth_data = ad
|
|
24
|
-
cipher.auth_tag = ciphertext[-16
|
|
25
|
-
|
|
24
|
+
cipher.auth_tag = ciphertext[-16..]
|
|
25
|
+
update(cipher, ciphertext[0...-16]) + cipher.final
|
|
26
26
|
rescue OpenSSL::Cipher::CipherError => e
|
|
27
|
-
raise Noise::Exceptions::DecryptError.
|
|
27
|
+
raise Noise::Exceptions::DecryptError, "Decrpyt failed. #{e.message}", e.backtrace
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# 4 zero bytes followed by n as a big-endian 64 bit integer.
|
|
30
31
|
def nonce_to_bytes(n)
|
|
31
|
-
"\x00" * 4 +
|
|
32
|
+
"\x00" * 4 + [n].pack('Q>')
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# Returns a new 32-byte cipher key as a pseudorandom function of k.
|
|
@@ -40,6 +41,16 @@ module Noise
|
|
|
40
41
|
def rekey(k)
|
|
41
42
|
encrypt(k, MAX_NONCE, '', "\x00" * 32)[0...32]
|
|
42
43
|
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# A zero-length payload is normal in a Noise message, but the openssl gem shipped with
|
|
48
|
+
# Ruby 3.0 raises ArgumentError('data must not be empty') instead of returning ''.
|
|
49
|
+
def update(cipher, data)
|
|
50
|
+
return String.new if data.empty?
|
|
51
|
+
|
|
52
|
+
cipher.update(data)
|
|
53
|
+
end
|
|
43
54
|
end
|
|
44
55
|
end
|
|
45
56
|
end
|
|
@@ -10,18 +10,19 @@ module Noise
|
|
|
10
10
|
cipher = RbNaCl::AEAD::ChaCha20Poly1305IETF.new(String.new(k).force_encoding('ASCII-8BIT'))
|
|
11
11
|
cipher.encrypt(nonce_to_bytes(n), plaintext, ad)
|
|
12
12
|
rescue ::RbNaCl::CryptoError => e
|
|
13
|
-
raise Noise::Exceptions::EncryptError.
|
|
13
|
+
raise Noise::Exceptions::EncryptError, "Encrypt failed. #{e.message}", e.backtrace
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def decrypt(k, n, ad, ciphertext)
|
|
17
17
|
cipher = RbNaCl::AEAD::ChaCha20Poly1305IETF.new(String.new(k).force_encoding('ASCII-8BIT'))
|
|
18
18
|
cipher.decrypt(nonce_to_bytes(n), ciphertext, ad)
|
|
19
19
|
rescue ::RbNaCl::CryptoError => e
|
|
20
|
-
raise Noise::Exceptions::DecryptError.
|
|
20
|
+
raise Noise::Exceptions::DecryptError, "Decrpyt failed. #{e.message}", e.backtrace
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# 4 zero bytes followed by n as a little-endian 64 bit integer.
|
|
23
24
|
def nonce_to_bytes(n)
|
|
24
|
-
"\x00" * 4 +
|
|
25
|
+
"\x00" * 4 + [n].pack('Q<')
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
# Returns a new 32-byte cipher key as a pseudorandom function of k.
|
|
@@ -31,7 +32,7 @@ module Noise
|
|
|
31
32
|
# zerolen is a zero-length byte sequence, and zeros is a sequence of
|
|
32
33
|
# 32 bytes filled with zeros.
|
|
33
34
|
def rekey(k)
|
|
34
|
-
encrypt(k, MAX_NONCE, '', "\x00" * 32)[0
|
|
35
|
+
encrypt(k, MAX_NONCE, '', "\x00" * 32)[0...32]
|
|
35
36
|
end
|
|
36
37
|
end
|
|
37
38
|
end
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require 'ed448'
|
|
5
|
-
Ed448.init
|
|
6
|
-
rescue LoadError
|
|
7
|
-
end
|
|
3
|
+
Noise.require_optional('ed448') { Ed448.init }
|
|
8
4
|
|
|
9
5
|
module Noise
|
|
10
6
|
module Functions
|
|
11
7
|
module DH
|
|
12
8
|
class ED448
|
|
13
|
-
|
|
9
|
+
# Ed448::X448::X448_PRIVATE_BYTES, spelled out so this file loads without the gem.
|
|
10
|
+
DHLEN = 56
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
Noise.optional_dependency!('ed448')
|
|
14
|
+
end
|
|
14
15
|
|
|
15
16
|
def generate_keypair
|
|
16
17
|
private_key = SecureRandom.random_bytes(DHLEN)
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require 'secp256k1'
|
|
5
|
-
rescue LoadError
|
|
6
|
-
end
|
|
3
|
+
Noise.require_optional 'secp256k1'
|
|
7
4
|
|
|
8
5
|
module Noise
|
|
9
6
|
module Functions
|
|
10
7
|
module DH
|
|
11
8
|
class Secp256k1
|
|
9
|
+
def initialize
|
|
10
|
+
Noise.optional_dependency!('secp256k1')
|
|
11
|
+
end
|
|
12
|
+
|
|
12
13
|
def generate_keypair
|
|
13
14
|
group = ECDSA::Group::Secp256k1
|
|
14
15
|
private_key = 1 + SecureRandom.random_number(group.order - 1)
|
|
@@ -22,7 +23,7 @@ module Noise
|
|
|
22
23
|
def dh(private_key, public_key)
|
|
23
24
|
key = ::Secp256k1::PublicKey.new(pubkey: public_key, raw: true)
|
|
24
25
|
key.ecdh(private_key)
|
|
25
|
-
rescue ::Secp256k1::AssertError
|
|
26
|
+
rescue ::Secp256k1::AssertError
|
|
26
27
|
raise Noise::Exceptions::InvalidPublicKeyError, public_key
|
|
27
28
|
end
|
|
28
29
|
|
|
@@ -60,6 +60,7 @@ module Noise
|
|
|
60
60
|
# @return context
|
|
61
61
|
def init(out_len, key)
|
|
62
62
|
raise ArgumentError if out_len.zero? || out_len > 32
|
|
63
|
+
|
|
63
64
|
h = IV.dup
|
|
64
65
|
h[0] ^= 0x01010000 ^ (key.size << 8) ^ out_len
|
|
65
66
|
t = 0
|
|
@@ -168,6 +169,7 @@ module Noise
|
|
|
168
169
|
|
|
169
170
|
class Context
|
|
170
171
|
attr_accessor :b, :h, :t, :c, :out_len
|
|
172
|
+
|
|
171
173
|
def initialize(b, h, t, c, out_len)
|
|
172
174
|
@b = b
|
|
173
175
|
@h = h
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require 'blake3'
|
|
5
|
-
rescue LoadError
|
|
6
|
-
end
|
|
3
|
+
Noise.require_optional 'blake3'
|
|
7
4
|
|
|
8
5
|
module Noise
|
|
9
6
|
module Functions
|
|
10
7
|
module Hash
|
|
11
8
|
class Blake3
|
|
12
|
-
HASHLEN =
|
|
13
|
-
BLOCKLEN =
|
|
9
|
+
HASHLEN = 32
|
|
10
|
+
BLOCKLEN = 64
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
Noise.optional_dependency!('blake3')
|
|
14
|
+
end
|
|
15
|
+
|
|
14
16
|
def hash(data)
|
|
15
17
|
::Blake3.digest(data)
|
|
16
18
|
end
|
data/lib/noise/functions/hash.rb
CHANGED
|
@@ -10,14 +10,17 @@ module Noise
|
|
|
10
10
|
autoload :Blake3, 'noise/functions/hash/blake3'
|
|
11
11
|
|
|
12
12
|
def self.hmac_hash(key, data, digest)
|
|
13
|
-
|
|
13
|
+
case digest
|
|
14
|
+
when /SHA/
|
|
14
15
|
OpenSSL::HMAC.digest(OpenSSL::Digest.new(digest), key, data)
|
|
15
|
-
|
|
16
|
+
when /BLAKE2b/
|
|
16
17
|
Noise::Functions::Hash::Blake2bHMAC.new(key).update(data).digest
|
|
17
|
-
|
|
18
|
+
when /BLAKE2s/
|
|
18
19
|
Noise::Functions::Hash::Blake2sHMAC.new(key).update(data).digest
|
|
19
|
-
|
|
20
|
+
when /BLAKE3/
|
|
20
21
|
Noise::Functions::Hash::Blake3HMAC.new(key).update(data).digest
|
|
22
|
+
else
|
|
23
|
+
raise Noise::Exceptions::ProtocolNameError, "Unsupported hash function: #{digest}"
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
|
|
@@ -30,9 +33,10 @@ module Noise
|
|
|
30
33
|
def self.hkdf(chaining_key, input_key_material, num_outputs, digest)
|
|
31
34
|
temp_key = hmac_hash(chaining_key, input_key_material, digest)
|
|
32
35
|
output1 = hmac_hash(temp_key, "\x01", digest)
|
|
33
|
-
output2 = hmac_hash(temp_key, output1
|
|
36
|
+
output2 = hmac_hash(temp_key, "#{output1}\u0002", digest)
|
|
34
37
|
return [output1, output2] if num_outputs == 2
|
|
35
|
-
|
|
38
|
+
|
|
39
|
+
output3 = hmac_hash(temp_key, "#{output2}\u0003", digest)
|
|
36
40
|
[output1, output2, output3]
|
|
37
41
|
end
|
|
38
42
|
end
|