Skip to content

Commit 09122c5

Browse files
committed
Merge branch 'maint-3.0' into maint-3.1
* maint-3.0: pkcs7: raise PKCS7Error for PKCS7 without content in PKCS7.read_smime pkcs7: raise ArgumentError for PKCS7 with no content in PKCS7.new cipher: fix buffer overflow in Cipher#update ssl: allow failure on test_connect_certificate_verify_failed_exception_message .github/workflows/test.yml: synchronize with master Only CSR version 1 (encoded as 0) is allowed by PKIX standards test_asn1.rb: Remove the assertions of the time string format without second. test/openssl/test_asn1.rb: skip failing tests on LibreSSL 3.6.0 Use EVP_Digest{Sign,Verify} when available Fix performance regression in do_write(s)
2 parents c263cd4 + 3b71ccf commit 09122c5

File tree

9 files changed

+127
-40
lines changed

9 files changed

+127
-40
lines changed

.github/workflows/test.yml

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ name: CI
33
on: [push, pull_request]
44

55
jobs:
6+
ruby-versions:
7+
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
8+
with:
9+
engine: cruby-truffleruby
10+
min_version: 2.6
611
test:
12+
needs: ruby-versions
713
name: >-
814
${{ matrix.os }} ${{ matrix.ruby }}
915
runs-on: ${{ matrix.os }}
@@ -12,17 +18,21 @@ jobs:
1218
matrix:
1319
# ubuntu-22.04 uses OpenSSL 3.0, ubuntu-20.04 uses OpenSSL 1.1.1
1420
os: [ ubuntu-22.04, ubuntu-20.04, macos-latest, windows-latest ]
15-
ruby: [ head, "3.1", "3.0", "2.7", "2.6" ]
21+
ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
1622
exclude:
1723
# uses non-standard MSYS2 OpenSSL 3 package
1824
- { os: windows-latest, ruby: head }
25+
- { os: windows-latest, ruby: truffleruby }
26+
- { os: windows-latest, ruby: truffleruby-head }
27+
- { os: macos-latest, ruby: truffleruby }
28+
- { os: ubuntu-20.04, ruby: truffleruby }
1929
include:
2030
- { os: windows-latest, ruby: ucrt }
2131
- { os: windows-latest, ruby: mswin }
2232

2333
steps:
2434
- name: repo checkout
25-
uses: actions/checkout@v3
35+
uses: actions/checkout@v4
2636

2737
- name: load ruby
2838
uses: ruby/setup-ruby@v1
@@ -32,48 +42,73 @@ jobs:
3242
- name: depends
3343
run: bundle install
3444

45+
# Enable the verbose option in mkmf.rb to print the compiling commands.
46+
- name: enable mkmf verbose
47+
run: echo "MAKEFLAGS=V=1" >> $GITHUB_ENV
48+
if: runner.os == 'Linux' || runner.os == 'macOS'
49+
50+
- name: set flags to check compiler warnings.
51+
run: echo "RUBY_OPENSSL_EXTCFLAGS=-Werror" >> $GITHUB_ENV
52+
if: ${{ !matrix.skip-warnings }}
53+
3554
- name: compile
36-
run: rake compile -- --enable-debug
55+
run: rake compile
3756

3857
- name: test
39-
run: rake test TESTOPTS="-v --no-show-detail-immediately" OSSL_MDEBUG=1
58+
run: rake test TESTOPTS="-v --no-show-detail-immediately"
4059
timeout-minutes: 5
4160

4261
test-openssls:
4362
name: >-
44-
${{ matrix.openssl }}
63+
${{ matrix.openssl }} ${{ matrix.name-extra || '' }}
4564
runs-on: ${{ matrix.os }}
4665
strategy:
4766
fail-fast: false
4867
matrix:
4968
os: [ ubuntu-latest ]
5069
ruby: [ "3.0" ]
5170
openssl:
71+
# https://www.openssl.org/source/
5272
- openssl-1.0.2u # EOL
5373
- openssl-1.1.0l # EOL
54-
- openssl-1.1.1s
55-
- openssl-3.0.7
74+
- openssl-1.1.1w # EOL
75+
- openssl-3.0.13
76+
- openssl-3.1.5
77+
- openssl-3.2.1
78+
- openssl-3.3.0
79+
# http://www.libressl.org/releases.html
5680
- libressl-3.1.5 # EOL
5781
- libressl-3.2.7 # EOL
5882
- libressl-3.3.6 # EOL
5983
- libressl-3.4.3 # EOL
60-
- libressl-3.5.3
61-
- libressl-3.6.1
62-
- libressl-3.7.0 # Development release
84+
- libressl-3.5.3 # EOL
85+
- libressl-3.6.3 # EOL
86+
- libressl-3.7.3 # EOL
87+
- libressl-3.8.4
88+
- libressl-3.9.1
6389
steps:
6490
- name: repo checkout
65-
uses: actions/checkout@v3
91+
uses: actions/checkout@v4
6692

6793
- name: prepare openssl
6894
run: |
95+
# Enable Bash debugging option temporarily for debugging use.
96+
set -x
6997
mkdir -p tmp/build-openssl && cd tmp/build-openssl
7098
case ${{ matrix.openssl }} in
7199
openssl-*)
72-
curl -OL https://ftp.openssl.org/source/${{ matrix.openssl }}.tar.gz
73-
tar xf ${{ matrix.openssl }}.tar.gz && cd ${{ matrix.openssl }}
100+
if [ -z "${{ matrix.git }}" ]; then
101+
curl -OL https://ftp.openssl.org/source/${{ matrix.openssl }}.tar.gz
102+
tar xf ${{ matrix.openssl }}.tar.gz && cd ${{ matrix.openssl }}
103+
else
104+
git clone -b ${{ matrix.branch }} --depth 1 ${{ matrix.git }} ${{ matrix.openssl }}
105+
cd ${{ matrix.openssl }}
106+
# Log the commit hash.
107+
echo "Git commit: $(git rev-parse HEAD)"
108+
fi
74109
# shared is required for 1.0.x.
75110
./Configure --prefix=$HOME/.openssl/${{ matrix.openssl }} --libdir=lib \
76-
shared linux-x86_64
111+
shared linux-x86_64 ${{ matrix.append-configure }}
77112
make depend
78113
;;
79114
libressl-*)
@@ -96,9 +131,17 @@ jobs:
96131
- name: depends
97132
run: bundle install
98133

134+
- name: enable mkmf verbose
135+
run: echo "MAKEFLAGS=V=1" >> $GITHUB_ENV
136+
if: runner.os == 'Linux' || runner.os == 'macOS'
137+
138+
- name: set flags to check compiler warnings.
139+
run: echo "RUBY_OPENSSL_EXTCFLAGS=-Werror" >> $GITHUB_ENV
140+
if: ${{ !matrix.skip-warnings }}
141+
99142
- name: compile
100-
run: rake compile -- --enable-debug --with-openssl-dir=$HOME/.openssl/${{ matrix.openssl }}
143+
run: rake compile -- --with-openssl-dir=$HOME/.openssl/${{ matrix.openssl }}
101144

102145
- name: test
103-
run: rake test TESTOPTS="-v --no-show-detail-immediately" OSSL_MDEBUG=1
146+
run: rake test TESTOPTS="-v --no-show-detail-immediately"
104147
timeout-minutes: 5

ext/openssl/ossl_cipher.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,11 +386,23 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
386386
in = (unsigned char *)RSTRING_PTR(data);
387387
in_len = RSTRING_LEN(data);
388388
GetCipher(self, ctx);
389-
out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
390-
if (out_len <= 0) {
389+
390+
/*
391+
* As of OpenSSL 3.2, there is no reliable way to determine the required
392+
* output buffer size for arbitrary cipher modes.
393+
* https://github.com/openssl/openssl/issues/22628
394+
*
395+
* in_len+block_size is usually sufficient, but AES key wrap with padding
396+
* ciphers require in_len+15 even though they have a block size of 8 bytes.
397+
*
398+
* Using EVP_MAX_BLOCK_LENGTH (32) as a safe upper bound for ciphers
399+
* currently implemented in OpenSSL, but this can change in the future.
400+
*/
401+
if (in_len > LONG_MAX - EVP_MAX_BLOCK_LENGTH) {
391402
ossl_raise(rb_eRangeError,
392403
"data too big to make output buffer: %ld bytes", in_len);
393404
}
405+
out_len = in_len + EVP_MAX_BLOCK_LENGTH;
394406

395407
if (NIL_P(str)) {
396408
str = rb_str_new(0, out_len);
@@ -401,7 +413,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
401413

402414
if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len))
403415
ossl_raise(eCipherError, NULL);
404-
assert(out_len < RSTRING_LEN(str));
416+
assert(out_len <= RSTRING_LEN(str));
405417
rb_str_set_len(str, out_len);
406418

407419
return str;

ext/openssl/ossl_pkcs7.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@ ossl_pkcs7_s_read_smime(VALUE klass, VALUE arg)
165165
out = NULL;
166166
pkcs7 = SMIME_read_PKCS7(in, &out);
167167
BIO_free(in);
168-
if(!pkcs7) ossl_raise(ePKCS7Error, NULL);
168+
if (!pkcs7)
169+
ossl_raise(ePKCS7Error, "Could not parse the PKCS7");
170+
if (!pkcs7->d.ptr)
171+
ossl_raise(ePKCS7Error, "No content in PKCS7");
172+
169173
data = out ? ossl_membio2str(out) : Qnil;
170174
SetPKCS7(ret, pkcs7);
171175
ossl_pkcs7_set_data(ret, data);
@@ -346,6 +350,8 @@ ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self)
346350
BIO_free(in);
347351
if (!p7)
348352
ossl_raise(rb_eArgError, "Could not parse the PKCS7");
353+
if (!p7->d.ptr)
354+
ossl_raise(rb_eArgError, "No content in PKCS7");
349355

350356
RTYPEDDATA_DATA(self) = p7;
351357
PKCS7_free(p7_orig);

lib/openssl/buffering.rb

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,13 +348,18 @@ def do_write(s)
348348
@wbuffer << s
349349
@wbuffer.force_encoding(Encoding::BINARY)
350350
@sync ||= false
351-
if @sync or @wbuffer.size > BLOCK_SIZE
352-
until @wbuffer.empty?
353-
begin
354-
nwrote = syswrite(@wbuffer)
355-
rescue Errno::EAGAIN
356-
retry
351+
buffer_size = @wbuffer.size
352+
if @sync or buffer_size > BLOCK_SIZE
353+
nwrote = 0
354+
begin
355+
while nwrote < buffer_size do
356+
begin
357+
nwrote += syswrite(@wbuffer[nwrote, buffer_size - nwrote])
358+
rescue Errno::EAGAIN
359+
retry
360+
end
357361
end
362+
ensure
358363
@wbuffer[0, nwrote] = ""
359364
end
360365
end

test/openssl/test_asn1.rb

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,6 @@ def test_utctime
406406
rescue OpenSSL::ASN1::ASN1Error
407407
pend "No negative time_t support?"
408408
end
409-
# Seconds is omitted. LibreSSL 3.6.0 requires it
410-
return if libressl?
411-
decode_test B(%w{ 17 0B }) + "1609082343Z".b,
412-
OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 0))
413409
# not implemented
414410
# decode_test B(%w{ 17 11 }) + "500908234339+0930".b,
415411
# OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30"))
@@ -428,10 +424,6 @@ def test_generalizedtime
428424
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29))
429425
encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b,
430426
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39))
431-
# LibreSSL 3.6.0 requires the seconds element
432-
return if libressl?
433-
decode_test B(%w{ 18 0D }) + "201612081934Z".b,
434-
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0))
435427
# not implemented
436428
# decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
437429
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))

test/openssl/test_cipher.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,22 @@ def test_aes_gcm_key_iv_order_issue
331331
assert_equal tag1, tag2
332332
end
333333

334+
def test_aes_keywrap_pad
335+
# RFC 5649 Section 6; The second example
336+
kek = ["5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8"].pack("H*")
337+
key = ["466f7250617369"].pack("H*")
338+
wrap = ["afbeb0f07dfbf5419200f2ccb50bb24f"].pack("H*")
339+
340+
begin
341+
cipher = OpenSSL::Cipher.new("id-aes192-wrap-pad").encrypt
342+
rescue OpenSSL::Cipher::CipherError, RuntimeError
343+
omit "id-aes192-wrap-pad is not supported: #$!"
344+
end
345+
cipher.key = kek
346+
ct = cipher.update(key) << cipher.final
347+
assert_equal wrap, ct
348+
end
349+
334350
def test_non_aead_cipher_set_auth_data
335351
assert_raise(OpenSSL::Cipher::CipherError) {
336352
cipher = OpenSSL::Cipher.new("aes-128-cfb").encrypt

test/openssl/test_pkcs7.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,21 @@ def test_enveloped
155155
assert_equal(data, p7.decrypt(@rsa1024))
156156
end
157157

158+
def test_empty_signed_data_ruby_bug_19974
159+
data = "-----BEGIN PKCS7-----\nMAsGCSqGSIb3DQEHAg==\n-----END PKCS7-----\n"
160+
assert_raise(ArgumentError) { OpenSSL::PKCS7.new(data) }
161+
162+
data = <<END
163+
MIME-Version: 1.0
164+
Content-Disposition: attachment; filename="smime.p7m"
165+
Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
166+
Content-Transfer-Encoding: base64
167+
168+
#{data}
169+
END
170+
assert_raise(OpenSSL::PKCS7::PKCS7Error) { OpenSSL::PKCS7.read_smime(data) }
171+
end
172+
158173
def test_graceful_parsing_failure #[ruby-core:43250]
159174
contents = File.read(__FILE__)
160175
assert_raise(ArgumentError) { OpenSSL::PKCS7.new(contents) }

test/openssl/test_ssl.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,9 @@ def test_verify_hostname_failure_error_code
10431043
end
10441044

10451045
def test_connect_certificate_verify_failed_exception_message
1046+
# Won't fix on the 3.0 branch
1047+
return if openssl?(3, 1, 0)
1048+
10461049
start_server(ignore_listener_error: true) { |port|
10471050
ctx = OpenSSL::SSL::SSLContext.new
10481051
ctx.set_params

test/openssl/test_x509req.rb

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ def test_version
3939
assert_equal(0, req.version)
4040
req = OpenSSL::X509::Request.new(req.to_der)
4141
assert_equal(0, req.version)
42-
43-
req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
44-
assert_equal(1, req.version)
45-
req = OpenSSL::X509::Request.new(req.to_der)
46-
assert_equal(1, req.version)
4742
end
4843

4944
def test_subject
@@ -106,7 +101,7 @@ def test_sign_and_verify_rsa_sha1
106101
assert_equal(false, req.verify(@rsa2048))
107102
assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
108103
assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
109-
req.version = 1
104+
req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBarFooBar")
110105
assert_equal(false, req.verify(@rsa1024))
111106
rescue OpenSSL::X509::RequestError # RHEL 9 disables SHA1
112107
end

0 commit comments

Comments
 (0)