noise-ruby 0.13.0 → 0.14.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/Dockerfile +3 -3
- data/README.md +14 -5
- data/lib/noise/functions/cipher/cha_cha_poly.rb +31 -6
- data/lib/noise/functions/dh/ed25519.rb +27 -15
- data/lib/noise/functions/hash/blake2b.rb +24 -2
- data/lib/noise/functions/hash/blake3.rb +4 -4
- data/lib/noise/functions/hash/sha256.rb +1 -1
- data/lib/noise/functions/hash/sha512.rb +1 -1
- data/lib/noise/version.rb +1 -1
- data/lib/noise.rb +0 -1
- data/noise.gemspec +7 -9
- metadata +2 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a6817736b9a72ca54f30ceb3238c9962fbd1f4a09092e9e0f783fec5a1035e9f
|
|
4
|
+
data.tar.gz: de1d7510423907f0b73fa4f4115916bb2044195dc4dbbf8acb1f83cb484f2e76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3235949265d26680b60447ad79e7c2ef02824233343a948b3b6805cd519c5e3f1ff5b573abb2ab1feec11eb7e51523448e47ce787ed81bc0399e8ab86e2b8368
|
|
7
|
+
data.tar.gz: 29d18a35a9d052d117e2ce0422f2a165eedf85d8f53af2d4b77263af500a257d194237cfa274b6ee40d432fbb8366490ed30fffafdaffa7713d70593c4104559
|
data/Dockerfile
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
# architecture and needs no emulation.
|
|
4
4
|
FROM ruby:4.0
|
|
5
5
|
|
|
6
|
-
# libsodium: dlopen'd by rbnacl, which lib/noise.rb requires unconditionally.
|
|
7
|
-
# cargo: the blake3 gem is a Rust extension and is built at install time.
|
|
8
6
|
# git: noise.gemspec calls `git ls-files` while the gemspec is evaluated.
|
|
7
|
+
# No Rust toolchain: the blake3-rb gem ships a precompiled binary for this platform.
|
|
8
|
+
# No libsodium: every function is computed by OpenSSL or in pure Ruby.
|
|
9
9
|
RUN apt-get update -qq \
|
|
10
|
-
&& apt-get install -y --no-install-recommends build-essential git
|
|
10
|
+
&& apt-get install -y --no-install-recommends build-essential git \
|
|
11
11
|
&& rm -rf /var/lib/apt/lists/*
|
|
12
12
|
|
|
13
13
|
WORKDIR /work
|
data/README.md
CHANGED
|
@@ -33,8 +33,14 @@ Supported Features:
|
|
|
33
33
|
This gem requires Ruby 3.2 or later, and is tested on 3.2, 3.3, 3.4 and 4.0. Ruby 3.0 and 3.1 are
|
|
34
34
|
past end of life and are no longer supported; stay on noise-ruby 0.12.0 or earlier if you need them.
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
Every function except BLAKE3, which is optional and covered below, is computed either by the
|
|
37
|
+
OpenSSL that Ruby is linked against or in pure Ruby, so no system library has to be installed
|
|
38
|
+
separately. OpenSSL 1.1.1 or later provides everything this gem asks of it, and that is what the
|
|
39
|
+
official Ruby packages and the usual version managers (rbenv, rvm, asdf) are built with. Two kinds
|
|
40
|
+
of build fall short: a Ruby linked against LibreSSL has no BLAKE2b, no ChaCha20-Poly1305 and no
|
|
41
|
+
X448, and an OpenSSL restricted to the FIPS provider has neither BLAKE2b nor ChaCha20-Poly1305 nor
|
|
42
|
+
the secp256k1 curve. On such a build the protocol names that use a missing function raise an error,
|
|
43
|
+
and the remaining ones keep working.
|
|
38
44
|
|
|
39
45
|
Add this line to your application's Gemfile:
|
|
40
46
|
|
|
@@ -50,13 +56,16 @@ Or install it yourself as:
|
|
|
50
56
|
|
|
51
57
|
$ gem install noise-ruby
|
|
52
58
|
|
|
53
|
-
If you use BLAKE3,
|
|
54
|
-
And add this line to your Gemfile:
|
|
59
|
+
If you use BLAKE3, add this line to your Gemfile:
|
|
55
60
|
|
|
56
61
|
```
|
|
57
|
-
gem 'blake3'
|
|
62
|
+
gem 'blake3-rb'
|
|
58
63
|
```
|
|
59
64
|
|
|
65
|
+
`blake3-rb` ships precompiled binaries for common Linux, macOS and Windows platforms (x86_64 and
|
|
66
|
+
aarch64), so no Rust or C toolchain is needed to install it there. On any other platform bundler
|
|
67
|
+
falls back to the source gem, which does need a C compiler.
|
|
68
|
+
|
|
60
69
|
## Usage
|
|
61
70
|
|
|
62
71
|
Followings shows handshake protocol with "Noise_NN_25519_ChaChaPoly_BLAKE2b"
|
|
@@ -6,17 +6,32 @@ module Noise
|
|
|
6
6
|
class ChaChaPoly
|
|
7
7
|
MAX_NONCE = 2**64 - 1
|
|
8
8
|
|
|
9
|
+
# The name OpenSSL knows the AEAD construction of RFC 8439 by. It takes a 12 byte nonce and
|
|
10
|
+
# produces a 16 byte authentication tag, which is what the Noise specification requires of
|
|
11
|
+
# the ChaChaPoly cipher functions.
|
|
12
|
+
ALGORITHM = 'chacha20-poly1305'
|
|
13
|
+
|
|
14
|
+
# Length in bytes of the authentication tag that encrypt appends to the ciphertext.
|
|
15
|
+
TAGLEN = 16
|
|
16
|
+
|
|
9
17
|
def encrypt(k, n, ad, plaintext)
|
|
10
|
-
cipher =
|
|
11
|
-
cipher.
|
|
12
|
-
|
|
18
|
+
cipher = OpenSSL::Cipher.new(ALGORITHM).encrypt
|
|
19
|
+
cipher.key = k
|
|
20
|
+
cipher.iv = nonce_to_bytes(n)
|
|
21
|
+
cipher.auth_data = ad
|
|
22
|
+
update(cipher, plaintext) + cipher.final + cipher.auth_tag
|
|
23
|
+
rescue OpenSSL::Cipher::CipherError => e
|
|
13
24
|
raise Noise::Exceptions::EncryptError, "Encrypt failed. #{e.message}", e.backtrace
|
|
14
25
|
end
|
|
15
26
|
|
|
16
27
|
def decrypt(k, n, ad, ciphertext)
|
|
17
|
-
cipher =
|
|
18
|
-
cipher.
|
|
19
|
-
|
|
28
|
+
cipher = OpenSSL::Cipher.new(ALGORITHM).decrypt
|
|
29
|
+
cipher.key = k
|
|
30
|
+
cipher.iv = nonce_to_bytes(n)
|
|
31
|
+
cipher.auth_data = ad
|
|
32
|
+
cipher.auth_tag = ciphertext[-TAGLEN..]
|
|
33
|
+
update(cipher, ciphertext[0...-TAGLEN]) + cipher.final
|
|
34
|
+
rescue OpenSSL::Cipher::CipherError => e
|
|
20
35
|
raise Noise::Exceptions::DecryptError, "Decrpyt failed. #{e.message}", e.backtrace
|
|
21
36
|
end
|
|
22
37
|
|
|
@@ -34,6 +49,16 @@ module Noise
|
|
|
34
49
|
def rekey(k)
|
|
35
50
|
encrypt(k, MAX_NONCE, '', "\x00" * 32)[0...32]
|
|
36
51
|
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# A zero-length payload is normal in a Noise message, but OpenSSL::Cipher#update raises
|
|
56
|
+
# ArgumentError('data must not be empty') instead of returning ''.
|
|
57
|
+
def update(cipher, data)
|
|
58
|
+
return String.new if data.empty?
|
|
59
|
+
|
|
60
|
+
cipher.update(data)
|
|
61
|
+
end
|
|
37
62
|
end
|
|
38
63
|
end
|
|
39
64
|
end
|
|
@@ -3,29 +3,41 @@
|
|
|
3
3
|
module Noise
|
|
4
4
|
module Functions
|
|
5
5
|
module DH
|
|
6
|
+
# The 25519 DH function of the Noise specification, which is X25519 as defined by RFC 7748 and
|
|
7
|
+
# not the Ed25519 signature scheme the class name suggests. The name is kept because it is
|
|
8
|
+
# what Protocol::DH maps '25519' to and what callers reference.
|
|
9
|
+
#
|
|
10
|
+
# OpenSSL implements X25519 and exposes it through the raw key interface, so this function
|
|
11
|
+
# needs no gem and no system library beyond the OpenSSL the other functions already use.
|
|
6
12
|
class ED25519
|
|
7
13
|
DHLEN = 32
|
|
14
|
+
|
|
15
|
+
# The name OpenSSL knows the curve by. Ed25519 is a different algorithm, and asking for it
|
|
16
|
+
# here would produce a signing key rather than one that can derive a shared secret.
|
|
17
|
+
ALGORITHM = 'X25519'
|
|
18
|
+
|
|
8
19
|
def generate_keypair
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
public_key = RbNaCl::GroupElements::Curve25519.base.mult(scalar_as_string)
|
|
12
|
-
Noise::Key.new(ECDSA::Format::IntegerOctetString.encode(private_key, 32), public_key.to_bytes)
|
|
20
|
+
pkey = OpenSSL::PKey.generate_key(ALGORITHM)
|
|
21
|
+
Noise::Key.new(pkey.raw_private_key, pkey.raw_public_key)
|
|
13
22
|
end
|
|
14
23
|
|
|
15
24
|
# Computes the X25519 shared secret for the given remote public key.
|
|
16
25
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
26
|
+
# OpenSSL reports a public key it cannot use as OpenSSL::PKey::PKeyError, both when the key
|
|
27
|
+
# is not DHLEN bytes and when the derivation would produce the all-zero output that RFC 7748
|
|
28
|
+
# requires be rejected. Both are translated to InvalidPublicKeyError so that a caller
|
|
29
|
+
# handling a peer-supplied key rescues the same class for every DH function. The local key
|
|
30
|
+
# is built outside the rescue so that a malformed private key keeps propagating as
|
|
31
|
+
# PKeyError instead of being reported as the peer's fault.
|
|
23
32
|
def dh(private_key, public_key)
|
|
24
33
|
raise Noise::Exceptions::InvalidPublicKeyError, public_key unless public_key.bytesize == DHLEN
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
local = OpenSSL::PKey.new_raw_private_key(ALGORITHM, private_key)
|
|
36
|
+
begin
|
|
37
|
+
local.derive(OpenSSL::PKey.new_raw_public_key(ALGORITHM, public_key))
|
|
38
|
+
rescue OpenSSL::PKey::PKeyError
|
|
39
|
+
raise Noise::Exceptions::InvalidPublicKeyError, public_key
|
|
40
|
+
end
|
|
29
41
|
end
|
|
30
42
|
|
|
31
43
|
def dhlen
|
|
@@ -33,8 +45,8 @@ module Noise
|
|
|
33
45
|
end
|
|
34
46
|
|
|
35
47
|
def self.from_private(private_key)
|
|
36
|
-
|
|
37
|
-
Noise::Key.new(private_key,
|
|
48
|
+
pkey = OpenSSL::PKey.new_raw_private_key(ALGORITHM, private_key)
|
|
49
|
+
Noise::Key.new(private_key, pkey.raw_public_key)
|
|
38
50
|
end
|
|
39
51
|
end
|
|
40
52
|
end
|
|
@@ -6,8 +6,14 @@ module Noise
|
|
|
6
6
|
class Blake2b
|
|
7
7
|
HASHLEN = 64
|
|
8
8
|
BLOCKLEN = 128
|
|
9
|
+
|
|
10
|
+
# The name OpenSSL knows BLAKE2b with a 64 byte output by. OpenSSL only implements the
|
|
11
|
+
# fixed 512 bit output length, which is exactly the HASHLEN the Noise specification gives
|
|
12
|
+
# the BLAKE2b hash function.
|
|
13
|
+
DIGEST_NAME = 'BLAKE2b512'
|
|
14
|
+
|
|
9
15
|
def hash(data)
|
|
10
|
-
|
|
16
|
+
OpenSSL::Digest.digest(DIGEST_NAME, data)
|
|
11
17
|
end
|
|
12
18
|
|
|
13
19
|
def hashlen
|
|
@@ -19,9 +25,25 @@ module Noise
|
|
|
19
25
|
end
|
|
20
26
|
end
|
|
21
27
|
|
|
28
|
+
# Builds an OpenSSL BLAKE2b digest with no argument, which is how HMAC::Base creates the
|
|
29
|
+
# digests it feeds the inner and outer blocks to. OpenSSL defines no OpenSSL::Digest::BLAKE2b512
|
|
30
|
+
# constant, so the algorithm name is bound here instead of being passed in at every call.
|
|
31
|
+
class Blake2bDigester < OpenSSL::Digest
|
|
32
|
+
def initialize
|
|
33
|
+
super(Blake2b::DIGEST_NAME)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# HMAC::Base hashes a key longer than the block size through this class method. The one
|
|
37
|
+
# inherited from OpenSSL::Digest takes the algorithm name as its first argument, which this
|
|
38
|
+
# class has already bound, so it is redefined to take the data alone.
|
|
39
|
+
def self.digest(data)
|
|
40
|
+
new.digest(data)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
22
44
|
class Blake2bHMAC < HMAC::Base
|
|
23
45
|
def initialize(key = nil)
|
|
24
|
-
super(
|
|
46
|
+
super(Blake2bDigester, Blake2b::BLOCKLEN, Blake2b::HASHLEN, key)
|
|
25
47
|
end
|
|
26
48
|
public_class_method :new, :digest, :hexdigest
|
|
27
49
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
Noise.require_optional 'blake3'
|
|
3
|
+
Noise.require_optional 'blake3-rb'
|
|
4
4
|
|
|
5
5
|
module Noise
|
|
6
6
|
module Functions
|
|
@@ -10,11 +10,11 @@ module Noise
|
|
|
10
10
|
BLOCKLEN = 64
|
|
11
11
|
|
|
12
12
|
def initialize
|
|
13
|
-
Noise.optional_dependency!('blake3')
|
|
13
|
+
Noise.optional_dependency!('blake3-rb')
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def hash(data)
|
|
17
|
-
::Blake3.digest(data)
|
|
17
|
+
::Digest::Blake3.digest(data)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def hashlen
|
|
@@ -28,7 +28,7 @@ module Noise
|
|
|
28
28
|
|
|
29
29
|
class Blake3HMAC < HMAC::Base
|
|
30
30
|
def initialize(key = nil)
|
|
31
|
-
super(::Blake3
|
|
31
|
+
super(::Digest::Blake3, Blake3::BLOCKLEN, Blake3::HASHLEN, key)
|
|
32
32
|
end
|
|
33
33
|
public_class_method :new, :digest, :hexdigest
|
|
34
34
|
end
|
data/lib/noise/version.rb
CHANGED
data/lib/noise.rb
CHANGED
data/noise.gemspec
CHANGED
|
@@ -30,17 +30,15 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.add_development_dependency 'simplecov'
|
|
31
31
|
spec.add_development_dependency 'simplecov-json'
|
|
32
32
|
|
|
33
|
-
# Optional backend. BLAKE3 is needed only when it appears in a protocol name,
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
spec.add_development_dependency 'blake3'
|
|
33
|
+
# Optional backend. BLAKE3 is needed only when it appears in a protocol name, so it is not a
|
|
34
|
+
# runtime dependency. Add it to your own Gemfile if you need it; see the README.
|
|
35
|
+
spec.add_development_dependency 'blake3-rb'
|
|
37
36
|
|
|
38
37
|
spec.add_runtime_dependency 'ecdsa'
|
|
39
|
-
# The 448 DH
|
|
40
|
-
# which arrived in openssl 3.0. Every supported interpreter bundles a newer default
|
|
41
|
-
# floor is stated so an older openssl pinned in an application's Gemfile fails to
|
|
42
|
-
# than failing at runtime.
|
|
38
|
+
# The 25519 and 448 DH functions need the raw key API (OpenSSL::PKey.new_raw_private_key and
|
|
39
|
+
# friends), which arrived in openssl 3.0. Every supported interpreter bundles a newer default
|
|
40
|
+
# gem, but the floor is stated so an older openssl pinned in an application's Gemfile fails to
|
|
41
|
+
# resolve rather than failing at runtime.
|
|
43
42
|
spec.add_runtime_dependency 'openssl', '>= 3.0'
|
|
44
|
-
spec.add_runtime_dependency 'rbnacl'
|
|
45
43
|
spec.add_runtime_dependency 'ruby-hmac'
|
|
46
44
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: noise-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hajime Yamaguchi
|
|
@@ -94,7 +94,7 @@ dependencies:
|
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '0'
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
|
-
name: blake3
|
|
97
|
+
name: blake3-rb
|
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - ">="
|
|
@@ -135,20 +135,6 @@ dependencies:
|
|
|
135
135
|
- - ">="
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
137
|
version: '3.0'
|
|
138
|
-
- !ruby/object:Gem::Dependency
|
|
139
|
-
name: rbnacl
|
|
140
|
-
requirement: !ruby/object:Gem::Requirement
|
|
141
|
-
requirements:
|
|
142
|
-
- - ">="
|
|
143
|
-
- !ruby/object:Gem::Version
|
|
144
|
-
version: '0'
|
|
145
|
-
type: :runtime
|
|
146
|
-
prerelease: false
|
|
147
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
-
requirements:
|
|
149
|
-
- - ">="
|
|
150
|
-
- !ruby/object:Gem::Version
|
|
151
|
-
version: '0'
|
|
152
138
|
- !ruby/object:Gem::Dependency
|
|
153
139
|
name: ruby-hmac
|
|
154
140
|
requirement: !ruby/object:Gem::Requirement
|