diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2016-10-20 23:54:50 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2016-10-20 23:54:50 +0900 |
commit | 4881e4a8b5cdefdbbc0a259959e4967bab1d81b7 (patch) | |
tree | 63b7569e1a932b03c68bca821d15f5fc485c8c71 | |
parent | bc1a67dfa9975426cf239aa3cca970f55c273966 (diff) | |
parent | 41944f14ad191a7853c567c3807a711fa2c24226 (diff) | |
download | ruby-openssl-history-merged.tar.gz |
Merge remote-tracking branch 'ossl/master' into mergedmerged
* ossl/master: (406 commits)
Make OpenSSL.debug more verbose
pkey: fix possible memory leak in ossl_pkey_new()
pkey: remove unused ossl_pkey_new_from_file() function
test/test_pkey_*: refine sign/verify tests
pkey: fix possible memory leak in PKey#verify
ssl: correct return type of SSL_SESSION_get_{time,timeout}()
pkey: add missing return value check in PKey#{sign,verify}
pkey: tighten buffer size for signature
test/test_hmac: use static test vectors
test/test_cipher: use static test vectors
Add link to the documentation on README.md
cipher: always define Cipher#authenticated?
cipher: fix documentation regarding default IV
Avoid memory leak on rb_str_new()
test/test_digest: compare with literals
cipher: don't set dummy encryption key in Cipher#initialize
test/test_ssl: suppress syntax warning
pkey: allow specifying conversion form in EC::Point#to_bn
fid typos [ci skip]
pkey: make PKey#verify check the existence of the public key
...
43 files changed, 1835 insertions, 824 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1be0883 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/doc/ +/pkg/ +/tmp/ +/html/ +*.bundle +*.so +*.o +ext/openssl/mkmf.log +ext/openssl/Makefile +ext/openssl/extconf.h diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..30d967c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +language: c +sudo: required +group: edge +dist: trusty +services: + - docker +before_install: + - sudo apt-get -qq update +install: + - sudo sh -c "curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose" + - sudo chmod +x /usr/local/bin/docker-compose +script: + - docker -v + - docker-compose -v + - docker-compose build --no-cache + - docker-compose run test +matrix: + fast_finish: true + include: + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=openssl-1.0.0 OSSL_MDEBUG=1 + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=openssl-1.0.1 OSSL_MDEBUG=1 + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=openssl-1.0.2 OSSL_MDEBUG=1 + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=openssl-1.1.0 OSSL_MDEBUG=1 + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=libressl-2.1 + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=libressl-2.2 + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=libressl-2.3 + - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=libressl-2.4 + allow_failures: @@ -0,0 +1,22 @@ +Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..523c555 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,130 @@ +# Contributing to Ruby OpenSSL + +Thank you for your interest in contributing to Ruby OpenSSL! + +This documentation provides an overview how you can contribute. + +## Bugs and feature requests + +Bugs and feature requests are tracked on [GitHub]. + +If you think you found a bug, file a ticket on GitHub. Please DO NOT report +security issues here, there is a separate procedure which is described on +["Security at ruby-lang.org"](https://www.ruby-lang.org/en/security/). + +When reporting a bug, please make sure you include the version of Ruby, the +version of openssl gem, the version of the OpenSSL library, along with a sample +file that illustrates the problem or link to repository or gem that is +associated with the bug. + +There is a number of unresolved issues and feature requests for openssl that +need review. Before submitting a new ticket, it is recommended to check +[known issues] and [bugs.ruby-lang.org], the previous issue tracker. + +## Submitting patches + +Patches are also very welcome! + +Please submit a [pull request] with your changes. + +Make sure that your branch does: + +* Have good commit messages +* Follow Ruby's coding style ([DeveloperHowTo]) +* Pass the test suite successfully (see "Testing") +* Add an entry to [History.md] if necessary + +## Testing + +We have a test suite! + +Test cases are located under the +[`test/`](https://github.com/ruby/openssl/tree/master/test) directory. + +You can run it with the following three commands: + +``` +$ gem install rake-compiler test-unit +$ rake compile +$ rake test +``` + +### Docker + +You can also use Docker Compose to run tests. It can be used to check that your +changes work correctly with various supported versions of Ruby and OpenSSL. + +First, you need to install [Docker](https://www.docker.com/products/docker) and +[Docker Compose](https://www.docker.com/products/docker-compose) on your +computer. + +If you're on MacOS or Windows, we recommended to use the official [Docker +Toolbox](https://www.docker.com/products/docker-toolbox). On Linux, follow the +instructions for your package manager. For further information, please check +the [official documentation](https://docs.docker.com/). + +Once you have Docker and Docker Compose, running the following commands will +build the container and execute the openssl tests. In this example, we will use +Ruby version 2.3 with OpenSSL version 1.0.2. + +``` +$ docker-compose build +$ export RUBY_VERSION=ruby-2.3 +$ export OPENSSL_VERSION=openssl-1.0.2 +$ docker-compose run test + +# You may want an interactive shell for dubugging +$ docker-compose run debug +``` + +All possible values for `RUBY_VERSION` and `OPENSSL_VERSION` can be found in +[`.travis.yml`](https://github.com/ruby/openssl/tree/master/.travis.yml). + +**NOTE**: these commands must be run from the openssl repository root, in order +to use the +[`docker-compose.yml`](https://github.com/ruby/openssl/blob/master/docker-compose.yml) +file we have provided. + +This Docker image is built using the +[Dockerfile](https://github.com/ruby/openssl/tree/master/tool/ruby-openssl-docker) +provided in the repository. + + +## Relation with Ruby source tree + +After Ruby 2.3, `ext/openssl` was converted into a "default gem", a library +which ships with standard Ruby builds but can be upgraded via RubyGems. This +means the development of this gem has migrated to a [separate +repository][GitHub] and will be released independently. + +The version included in the Ruby source tree (trunk branch) is synchronized with +the latest release. + +## Release policy + +Bug fixes (including security fixes) will be made only for the version series +included in a stable Ruby release. + +## Security + +If you discovered a security issue, please send us in private, using the +security issue handling procedure for Ruby core. + +You can either use [HackerOne] or send an email to security@ruby-lang.org. + +Please see [Security] page on ruby-lang.org website for details. + +Reported problems will be published after a fix is released. + +_Thanks for your contributions!_ + + _\- The Ruby OpenSSL team_ + +[GitHub]: https://github.com/ruby/openssl +[known issues]: https://github.com/ruby/openssl/issues +[bugs.ruby-lang.org]: https://bugs.ruby-lang.org/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=o&f%5B%5D=assigned_to_id&op%5Bassigned_to_id%5D=%3D&v%5Bassigned_to_id%5D%5B%5D=7150&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=&t%5B%5D= +[DeveloperHowTo]: https://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto +[HackerOne]: https://hackerone.com/ruby +[Security]: https://www.ruby-lang.org/en/security/ +[pull request]: https://github.com/ruby/openssl/compare +[History.md]: https://github.com/ruby/openssl/tree/master/History.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ab7f09e --- /dev/null +++ b/Dockerfile @@ -0,0 +1 @@ +FROM zzak/ruby-openssl-docker:2.0 diff --git a/History.md b/History.md new file mode 100644 index 0000000..bd392da --- /dev/null +++ b/History.md @@ -0,0 +1,137 @@ +Version 2.0.0 +============= + +This is the first release of openssl gem, formerly a standard library of Ruby, +ext/openssl. This is the successor of the version included in Ruby 2.3. + +Compatibility notes +------------------- + +* Support for OpenSSL version 0.9.6 and 0.9.7 is completely removed. openssl gem + still works with OpenSSL 0.9.8, but users are strongly encouraged to upgrade + to at least 1.0.1, as OpenSSL < 1.0.1 will not receive any security fixes from + the OpenSSL development team. + +Supported platforms +------------------- + +* OpenSSL 1.0.0, 1.0.1, 1.0.2, 1.1.0 +* OpenSSL < 0.9.8 is no longer supported. +* LibreSSL 2.1, 2.2, 2.3, 2.4 +* Ruby 2.3, 2.4 + +Notable changes +--------------- + +* Add support for OpenSSL 1.1.0. [[Feature #12324]](https://bugs.ruby-lang.org/issues/12324) +* Add support for LibreSSL + +* OpenSSL::Cipher + + - OpenSSL::Cipher#key= and #iv= reject too long inputs. They used to truncate + silently. [[Bug #12561]](https://bugs.ruby-lang.org/issues/12561) + + - OpenSSL::Cipher#iv_len= is added. It allows changing IV (nonce) length if + using AEAD ciphers. + [[Bug #8667]](https://bugs.ruby-lang.org/issues/8667), + [[Bug #10420]](https://bugs.ruby-lang.org/issues/10420), + [[GH ruby/ruby#569]](https://github.com/ruby/ruby/pull/569), + [[GH ruby/openssl#58]](https://github.com/ruby/openssl/pull/58) + + - OpenSSL::Cipher#auth_tag_len= is added. This sets the authentication tag + length to be generated by an AEAD cipher. + +* OpenSSL::OCSP + + - Accessor methods are added to OpenSSL::OCSP::CertificateId. + [[Feature #7181]](https://bugs.ruby-lang.org/issues/7181) + + - OpenSSL::OCSP::Request and BasicResponse can be signed with non-SHA-1 hash + algorithm. [[Feature #11552]](https://bugs.ruby-lang.org/issues/11552) + + - OpenSSL::OCSP::CertificateId and BasicResponse can be encoded into DER. + + - A new class OpenSSL::OCSP::SingleResponse is added for convenience. + + - OpenSSL::OCSP::BasicResponse#add_status accepts absolute times. They used to + accept only relative seconds from the current time. + +* OpenSSL::PKey + + - OpenSSL::PKey::EC follows the general PKey interface. + [[Bug #6567]](https://bugs.ruby-lang.org/issues/6567) + + - OpenSSL::PKey.read raises OpenSSL::PKey::PKeyError instead of ArgumentError + for consistency with OpenSSL::PKey::{DH,DSA,RSA,EC}#new. + [[Bug #11774]](https://bugs.ruby-lang.org/issues/11774), + [[GH ruby/openssl#55]](https://github.com/ruby/openssl/pull/55) + + - OpenSSL::PKey::EC::Group retrieved by OpenSSL::PKey::EC#group is no longer + linked with the EC key. Modifications to the EC::Group have no effect on the + key. [[GH ruby/openssl#71]](https://github.com/ruby/openssl/pull/71) + + - OpenSSL::PKey::EC::Point#to_bn allows specifying the point conversion form + by the optional argument. + +* OpenSSL::SSL + + - OpenSSL::SSL::SSLSocket#tmp_key is added. A client can call it after the + connection is established to retrieve the ephemeral key. + [[GH ruby/ruby#1318]](https://github.com/ruby/ruby/pull/1318) + + - The automatic ephemeral ECDH curve selection is enabled by default when + built with OpenSSL >= 1.0.2 or LibreSSL. + + - OpenSSL::SSL::SSLContext#security_level= is added. You can set the "security + level" of the SSL context. This is effective only when built with OpenSSL + 1.1.0. + + - A new option 'verify_hostname' is added to OpenSSL::SSL::SSLContext. When it + is enabled, and the SNI hostname is also set, the hostname verification on + the server certificate is automatically performed. It is now enabled by + OpenSSL::SSL::Context#set_params. + [[GH ruby/openssl#60]](https://github.com/ruby/openssl/pull/60) + +Removals +-------- + +* OpenSSL::Engine + + - OpenSSL::Engine.cleanup does nothing when built with OpenSSL 1.1.0. + +* OpenSSL::SSL + + - OpenSSL::PKey::DH::DEFAULT_512 is removed. Hence servers no longer use + 512-bit DH group by default. It is considered too weak nowadays. + [[Bug #11968]](https://bugs.ruby-lang.org/issues/11968), + [[GH ruby/ruby#1196]](https://github.com/ruby/ruby/pull/1196) + + - RC4 cipher suites are removed from OpenSSL::SSL::SSLContext::DEFAULT_PARAMS. + RC4 is now considered to be weak. + [[GH ruby/openssl#50]](https://github.com/ruby/openssl/pull/50) + +Deprecations +------------ + +* OpenSSL::PKey + + - OpenSSL::PKey::RSA#n=, #e=, #d=, #p=, #q=, #dmp1=, #dmq1=, #iqmp=, + OpenSSL::PKey::DSA#p=, #q=, #g=, #priv_key=, #pub_key=, + OpenSSL::PKey::DH#p=, #g=, #priv_key= and #pub_key= are deprecated. They are + disabled when built with OpenSSL 1.1.0, due to its API change. Instead, + OpenSSL::PKey::RSA#set_key, #set_factors, #set_crt_params, + OpenSSL::PKey::DSA#set_pqg, #set_key, OpenSSL::PKey::DH#set_pqg and #set_key + are added. + +* OpenSSL::Random + + - OpenSSL::Random.pseudo_bytes is deprecated, and not defined when built with + OpenSSL 1.1.0. Use OpenSSL::Random.random_bytes instead. + +* OpenSSL::SSL + + - OpenSSL::SSL::SSLContext#tmp_ecdh_callback is deprecated, as the underlying + API SSL_CTX_set_tmp_ecdh_callback() is removed in OpenSSL 1.1.0. It was + first added in Ruby 2.3.0. To specify the curve to be used in ephemeral + ECDH, use OpenSSL::SSL::SSLContext#ecdh_curves=. The automatic curve + selection is also now enabled by default when built with a capable OpenSSL. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..426810a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,56 @@ +Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>. +You can redistribute it and/or modify it under either the terms of the +2-clause BSDL (see the file BSDL), or the conditions below: + + 1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + + 2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b) use the modified software only within your corporation or + organization. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a) distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b) accompany the distribution with the machine-readable source of + the software. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under these terms. + + For the list of those files and their copying conditions, see the + file LEGAL. + + 5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + + 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..42dd242 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# OpenSSL + +[![Build Status](https://travis-ci.org/ruby/openssl.svg?branch=master)](https://travis-ci.org/ruby/openssl) + +OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the +OpenSSL library. + +## Installation + +The openssl gem is available at [rubygems.org](https://rubygems.org/gems/openssl). +You can install with: + +``` +gem install openssl +``` + +You may need to specify the path where OpenSSL is installed. + +``` +gem install openssl -- --with-openssl-dir=/opt/openssl +``` + +Alternatively, you can install the gem with `bundler`: + +```ruby +# Gemfile +gem 'openssl' +# or specify git master +gem 'openssl', github: 'ruby/openssl' +``` + +After doing `bundle install`, you should have the gem installed in your bundle. + +## Usage + +Once installed, you can require "openssl" in your application. + +```ruby +require "openssl" +``` + +**NOTE**: If you are using Ruby 2.3 (and not Bundler), you **must** activate +the gem version of openssl, otherwise the default gem packaged with the Ruby +installation will be used: + +```ruby +gem "openssl" +require "openssl" +``` + +## Documentation + +See https://ruby.github.io/openssl/. + +## Contributing + +Please read our [CONTRIBUTING.md] for instructions. + +## Security + +Security issues should be reported to ruby-core by following the process +described on ["Security at ruby-lang.org"](https://www.ruby-lang.org/en/security/). + + +[CONTRIBUTING.md]: https://github.com/ruby/openssl/tree/master/CONTRIBUTING.md diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..f3e7e3f --- /dev/null +++ b/Rakefile @@ -0,0 +1,55 @@ +gem 'rake-compiler' + +require 'rake' +require 'rake/extensiontask' +require 'rake/testtask' +require 'rdoc/task' + +Rake::ExtensionTask.new('openssl') + +Rake::TestTask.new do |t| + t.libs << 'test' + t.warning = true +end + +RDoc::Task.new do |rdoc| + rdoc.main = "README.md" + rdoc.rdoc_files.include("*.md", "lib/**/*.rb", "ext/**/*.c") +end + +task :test => :debug +task :debug do + ruby "-I./lib -ropenssl -ve'puts OpenSSL::OPENSSL_VERSION, OpenSSL::OPENSSL_LIBRARY_VERSION'" +end + +namespace :sync do + task :from_ruby do + sh "./tool/sync-with-trunk" + end + + task :to_ruby do + trunk_path = ENV.fetch("RUBY_TRUNK_PATH", "../ruby") + + rsync = "rsync -av --delete" + excludes = %w{Makefile extconf.h mkmf.log depend *.o *.so *.bundle} + excludes.each { |name| rsync << " --exclude #{name}" } + + paths = [ + ["ext/openssl/", "ext/openssl/"], + ["test/utils.rb", "test/openssl/"], + ["test/ut_eof.rb", "test/openssl/"], + ["test/test_*", "test/openssl/"], + ["lib/", "ext/openssl/lib/"], + ["sample/", "sample/openssl/"], + ] + paths.each do |src, dst| + sh "#{rsync} #{src} #{trunk_path}/#{dst}" + end + + gemspec_file = File.expand_path("../openssl.gemspec", __FILE__) + gemspec = eval(File.read(gemspec_file), binding, gemspec_file) + File.write("#{trunk_path}/ext/openssl/openssl.gemspec", gemspec.to_ruby) + + puts "Don't forget to update ext/openssl/depend" + end +end diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4289654 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +compile: &defaults + build: . + environment: + RUBY_VERSION: + OPENSSL_VERSION: + MDEBUG: + command: rake compile +test: + <<: *defaults + command: rake compile test +debug: + <<: *defaults + command: /bin/bash diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index a9000f2..0723878 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -370,12 +370,28 @@ void ossl_clear_error(void) { if (dOSSL == Qtrue) { - long e; - while ((e = ERR_get_error())) { - rb_warn("error on stack: %s", ERR_error_string(e, NULL)); + unsigned long e; + const char *file, *data, *errstr; + int line, flags; + + while ((e = ERR_get_error_line_data(&file, &line, &data, &flags))) { + errstr = ERR_error_string(e, NULL); + if (!errstr) + errstr = "(null)"; + + if (flags & ERR_TXT_STRING) { + if (!data) + data = "(null)"; + rb_warn("error on stack: %s (%s)", errstr, data); + } + else { + rb_warn("error on stack: %s", errstr); + } } } - ERR_clear_error(); + else { + ERR_clear_error(); + } } /* diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 85b1f02..2b8a85f 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -110,16 +110,11 @@ asn1str_to_str(const ASN1_STRING *str) /* * ASN1_INTEGER conversions - * TODO: Make a decision what's the right way to do this. */ -#define DO_IT_VIA_RUBY 0 VALUE asn1integer_to_num(const ASN1_INTEGER *ai) { BIGNUM *bn; -#if DO_IT_VIA_RUBY - char *txt; -#endif VALUE num; if (!ai) { @@ -133,43 +128,12 @@ asn1integer_to_num(const ASN1_INTEGER *ai) if (!bn) ossl_raise(eOSSLError, NULL); -#if DO_IT_VIA_RUBY - if (!(txt = BN_bn2dec(bn))) { - BN_free(bn); - ossl_raise(eOSSLError, NULL); - } - num = rb_cstr_to_inum(txt, 10, Qtrue); - OPENSSL_free(txt); -#else num = ossl_bn_new(bn); -#endif BN_free(bn); return num; } -#if DO_IT_VIA_RUBY -ASN1_INTEGER * -num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) -{ - BIGNUM *bn = NULL; - - if (RTEST(rb_obj_is_kind_of(obj, cBN))) { - bn = GetBNPtr(obj); - } else { - obj = rb_String(obj); - if (!BN_dec2bn(&bn, StringValueCStr(obj))) { - ossl_raise(eOSSLError, NULL); - } - } - if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) { - BN_free(bn); - ossl_raise(eOSSLError, NULL); - } - BN_free(bn); - return ai; -} -#else ASN1_INTEGER * num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) { @@ -185,7 +149,6 @@ num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) return ai; } -#endif /********/ /* @@ -225,8 +188,8 @@ VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ -static ID sIMPLICIT, sEXPLICIT; -static ID sUNIVERSAL, sAPPLICATION, sCONTEXT_SPECIFIC, sPRIVATE; +static VALUE sym_IMPLICIT, sym_EXPLICIT; +static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINFINITE_LENGTH, sivUNUSED_BITS; /* @@ -364,13 +327,12 @@ decode_bool(unsigned char* der, long length) { const unsigned char *p = der; - assert(length == 3); - if (*p++ != 1) - ossl_raise(eASN1Error, "not a boolean"); - if (*p++ != 1) - ossl_raise(eASN1Error, "length is not 1"); + if (length != 3) + ossl_raise(eASN1Error, "invalid length for BOOLEAN"); + if (p[0] != 1 || p[1] != 1) + ossl_raise(eASN1Error, "invalid BOOLEAN"); - return *p ? Qtrue : Qfalse; + return p[2] ? Qtrue : Qfalse; } static VALUE @@ -632,17 +594,14 @@ ossl_asn1_default_tag(VALUE obj) VALUE tmp_class, tag; tmp_class = CLASS_OF(obj); - while (tmp_class) { + while (!NIL_P(tmp_class)) { tag = rb_hash_lookup(class_tag_map, tmp_class); - if (tag != Qnil) { - return NUM2INT(tag); - } - tmp_class = rb_class_superclass(tmp_class); + if (tag != Qnil) + return NUM2INT(tag); + tmp_class = rb_class_superclass(tmp_class); } ossl_raise(eASN1Error, "universal tag for %"PRIsVALUE" not found", rb_obj_class(obj)); - - return -1; /* dummy */ } static int @@ -661,59 +620,45 @@ static int ossl_asn1_is_explicit(VALUE obj) { VALUE s; - int ret = -1; s = ossl_asn1_get_tagging(obj); - if(NIL_P(s)) return 0; - else if(SYMBOL_P(s)){ - if (SYM2ID(s) == sIMPLICIT) - ret = 0; - else if (SYM2ID(s) == sEXPLICIT) - ret = 1; - } - if(ret < 0){ + if (NIL_P(s) || s == sym_IMPLICIT) + return 0; + else if (s == sym_EXPLICIT) + return 1; + else ossl_raise(eASN1Error, "invalid tag default"); - } - - return ret; } static int ossl_asn1_tag_class(VALUE obj) { VALUE s; - int ret = -1; s = ossl_asn1_get_tag_class(obj); - if(NIL_P(s)) ret = V_ASN1_UNIVERSAL; - else if(SYMBOL_P(s)){ - if (SYM2ID(s) == sUNIVERSAL) - ret = V_ASN1_UNIVERSAL; - else if (SYM2ID(s) == sAPPLICATION) - ret = V_ASN1_APPLICATION; - else if (SYM2ID(s) == sCONTEXT_SPECIFIC) - ret = V_ASN1_CONTEXT_SPECIFIC; - else if (SYM2ID(s) == sPRIVATE) - ret = V_ASN1_PRIVATE; - } - if(ret < 0){ + if (NIL_P(s) || s == sym_UNIVERSAL) + return V_ASN1_UNIVERSAL; + else if (s == sym_APPLICATION) + return V_ASN1_APPLICATION; + else if (s == sym_CONTEXT_SPECIFIC) + return V_ASN1_CONTEXT_SPECIFIC; + else if (s == sym_PRIVATE) + return V_ASN1_PRIVATE; + else ossl_raise(eASN1Error, "invalid tag class"); - } - - return ret; } static VALUE ossl_asn1_class2sym(int tc) { if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) - return ID2SYM(sPRIVATE); + return sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) - return ID2SYM(sCONTEXT_SPECIFIC); + return sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) - return ID2SYM(sAPPLICATION); + return sym_APPLICATION; else - return ID2SYM(sUNIVERSAL); + return sym_UNIVERSAL; } /* @@ -737,7 +682,7 @@ ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class) { if(!SYMBOL_P(tag_class)) ossl_raise(eASN1Error, "invalid tag class"); - if((SYM2ID(tag_class) == sUNIVERSAL) && NUM2INT(tag) > 31) + if (tag_class == sym_UNIVERSAL && NUM2INT(tag) > 31) ossl_raise(eASN1Error, "tag number for Universal too large"); ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); @@ -816,7 +761,7 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, p = *pp; - if(tc == sUNIVERSAL && tag < ossl_asn1_info_size) { + if(tc == sym_UNIVERSAL && tag < ossl_asn1_info_size) { switch(tag){ case V_ASN1_EOC: value = decode_eoc(p, hlen+length); @@ -858,13 +803,14 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, *pp += hlen + length; *num_read = hlen + length; - if (tc == sUNIVERSAL && tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { + if (tc == sym_UNIVERSAL && + tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { VALUE klass = *ossl_asn1_info[tag].klass; VALUE args[4]; args[0] = value; args[1] = INT2NUM(tag); args[2] = Qnil; - args[3] = ID2SYM(tc); + args[3] = tc; asn1data = rb_obj_alloc(klass); ossl_asn1_initialize(4, args, asn1data); if(tag == V_ASN1_BIT_STRING){ @@ -873,7 +819,7 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, } else { asn1data = rb_obj_alloc(cASN1Data); - ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), ID2SYM(tc)); + ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc); } return asn1data; @@ -902,12 +848,12 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, if (infinite && NUM2INT(ossl_asn1_get_tag(value)) == V_ASN1_EOC && - SYM2ID(ossl_asn1_get_tag_class(value)) == sUNIVERSAL) { + ossl_asn1_get_tag_class(value) == sym_UNIVERSAL) { break; } } - if (tc == sUNIVERSAL) { + if (tc == sym_UNIVERSAL) { VALUE args[4]; int not_sequence_or_set; @@ -929,12 +875,12 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, args[0] = ary; args[1] = INT2NUM(tag); args[2] = Qnil; - args[3] = ID2SYM(tc); + args[3] = tc; ossl_asn1_initialize(4, args, asn1data); } else { asn1data = rb_obj_alloc(cASN1Data); - ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), ID2SYM(tc)); + ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); } if (infinite) @@ -964,13 +910,13 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, if(j & 0x80) ossl_raise(eASN1Error, NULL); if(len > length) ossl_raise(eASN1Error, "value is too short"); if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) - tag_class = sPRIVATE; + tag_class = sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) - tag_class = sCONTEXT_SPECIFIC; + tag_class = sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) - tag_class = sAPPLICATION; + tag_class = sym_APPLICATION; else - tag_class = sUNIVERSAL; + tag_class = sym_UNIVERSAL; hlen = p - start; @@ -1162,19 +1108,19 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) ossl_raise(eASN1Error, "invalid tagging method"); if(NIL_P(tag_class)) { if (NIL_P(tagging)) - tag_class = ID2SYM(sUNIVERSAL); + tag_class = sym_UNIVERSAL; else - tag_class = ID2SYM(sCONTEXT_SPECIFIC); + tag_class = sym_CONTEXT_SPECIFIC; } if(!SYMBOL_P(tag_class)) ossl_raise(eASN1Error, "invalid tag class"); - if(!NIL_P(tagging) && SYM2ID(tagging) == sIMPLICIT && NUM2INT(tag) > 31) + if (tagging == sym_IMPLICIT && NUM2INT(tag) > 31) ossl_raise(eASN1Error, "tag number for Universal too large"); } else{ tag = INT2NUM(ossl_asn1_default_tag(self)); tagging = Qnil; - tag_class = ID2SYM(sUNIVERSAL); + tag_class = sym_UNIVERSAL; } ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); @@ -1190,7 +1136,7 @@ ossl_asn1eoc_initialize(VALUE self) { VALUE tag, tagging, tag_class, value; tag = INT2NUM(ossl_asn1_default_tag(self)); tagging = Qnil; - tag_class = ID2SYM(sUNIVERSAL); + tag_class = sym_UNIVERSAL; value = rb_str_new("", 0); ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); @@ -1264,8 +1210,8 @@ ossl_asn1cons_to_der(VALUE self) if (inf_length == Qtrue) { VALUE ary, example; constructed = 2; - if (CLASS_OF(self) == cASN1Sequence || - CLASS_OF(self) == cASN1Set) { + if (rb_obj_class(self) == cASN1Sequence || + rb_obj_class(self) == cASN1Set) { tag = ossl_asn1_default_tag(self); } else { /* must be a constructive encoding of a primitive value */ @@ -1294,7 +1240,7 @@ ossl_asn1cons_to_der(VALUE self) } } else { - if (CLASS_OF(self) == cASN1Constructive) + if (rb_obj_class(self) == cASN1Constructive) ossl_raise(eASN1Error, "Constructive shall only be used with infinite length"); tag = ossl_asn1_default_tag(self); } @@ -1476,12 +1422,12 @@ Init_ossl_asn1(void) eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif - sUNIVERSAL = rb_intern("UNIVERSAL"); - sCONTEXT_SPECIFIC = rb_intern("CONTEXT_SPECIFIC"); - sAPPLICATION = rb_intern("APPLICATION"); - sPRIVATE = rb_intern("PRIVATE"); - sEXPLICIT = rb_intern("EXPLICIT"); - sIMPLICIT = rb_intern("IMPLICIT"); + sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL")); + sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC")); + sym_APPLICATION = ID2SYM(rb_intern_const("APPLICATION")); + sym_PRIVATE = ID2SYM(rb_intern_const("PRIVATE")); + sym_EXPLICIT = ID2SYM(rb_intern_const("EXPLICIT")); + sym_IMPLICIT = ID2SYM(rb_intern_const("IMPLICIT")); sivVALUE = rb_intern("@value"); sivTAG = rb_intern("@tag"); diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 19a868c..eaf6254 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -380,7 +380,7 @@ BIGNUM_BOOL1(is_odd) BIGNUM *bn, *result; \ VALUE obj; \ GetBN(self, bn); \ - obj = NewBN(CLASS_OF(self)); \ + obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ @@ -406,7 +406,7 @@ BIGNUM_1c(sqr) BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ VALUE obj; \ GetBN(self, bn1); \ - obj = NewBN(CLASS_OF(self)); \ + obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ @@ -439,7 +439,7 @@ BIGNUM_2(sub) BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ VALUE obj; \ GetBN(self, bn1); \ - obj = NewBN(CLASS_OF(self)); \ + obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ @@ -504,12 +504,13 @@ static VALUE ossl_bn_div(VALUE self, VALUE other) { BIGNUM *bn1, *bn2 = GetBNPtr(other), *r1, *r2; - VALUE obj1, obj2; + VALUE klass, obj1, obj2; GetBN(self, bn1); - obj1 = NewBN(CLASS_OF(self)); - obj2 = NewBN(CLASS_OF(self)); + klass = rb_obj_class(self); + obj1 = NewBN(klass); + obj2 = NewBN(klass); if (!(r1 = BN_new())) { ossl_raise(eBNError, NULL); } @@ -536,7 +537,7 @@ ossl_bn_div(VALUE self, VALUE other) BIGNUM *bn3 = GetBNPtr(other2), *result; \ VALUE obj; \ GetBN(self, bn1); \ - obj = NewBN(CLASS_OF(self)); \ + obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ @@ -639,7 +640,7 @@ ossl_bn_is_bit_set(VALUE self, VALUE bit) VALUE obj; \ b = NUM2INT(bits); \ GetBN(self, bn); \ - obj = NewBN(CLASS_OF(self)); \ + obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 57bc3cf..73b667b 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -36,7 +36,7 @@ */ VALUE cCipher; VALUE eCipherError; -static ID id_auth_tag_len; +static ID id_auth_tag_len, id_key_set; static VALUE ossl_cipher_alloc(VALUE klass); static void ossl_cipher_free(void *ptr); @@ -118,7 +118,6 @@ ossl_cipher_initialize(VALUE self, VALUE str) EVP_CIPHER_CTX *ctx; const EVP_CIPHER *cipher; char *name; - unsigned char dummy_key[EVP_MAX_KEY_LENGTH] = { 0 }; name = StringValueCStr(str); GetCipherInit(self, ctx); @@ -129,16 +128,7 @@ ossl_cipher_initialize(VALUE self, VALUE str) if (!(cipher = EVP_get_cipherbyname(name))) { ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str); } - /* - * EVP_CipherInit_ex() allows to specify NULL to key and IV, however some - * ciphers don't handle well (OpenSSL's bug). [Bug #2768] - * - * The EVP which has EVP_CIPH_RAND_KEY flag (such as DES3) allows - * uninitialized key, but other EVPs (such as AES) does not allow it. - * Calling EVP_CipherUpdate() without initializing key causes SEGV so we - * set the data filled with "\0" as the key by default. - */ - if (EVP_CipherInit_ex(ctx, cipher, NULL, dummy_key, NULL, -1) != 1) + if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1) ossl_raise(eCipherError, NULL); return self; @@ -251,6 +241,9 @@ ossl_cipher_init(int argc, VALUE *argv, VALUE self, int mode) ossl_raise(eCipherError, NULL); } + if (p_key) + rb_ivar_set(self, id_key_set, Qtrue); + return self; } @@ -337,6 +330,8 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self) OPENSSL_cleanse(key, sizeof key); OPENSSL_cleanse(iv, sizeof iv); + rb_ivar_set(self, id_key_set, Qtrue); + return Qnil; } @@ -387,6 +382,9 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &data, &str); + if (!RTEST(rb_attr_get(self, id_key_set))) + ossl_raise(eCipherError, "key not set"); + StringValue(data); in = (unsigned char *)RSTRING_PTR(data); if ((in_len = RSTRING_LEN(data)) == 0) @@ -488,6 +486,8 @@ ossl_cipher_set_key(VALUE self, VALUE key) if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1) ossl_raise(eCipherError, NULL); + rb_ivar_set(self, id_key_set, Qtrue); + return key; } @@ -502,9 +502,6 @@ ossl_cipher_set_key(VALUE self, VALUE key) * Cipher#random_iv to create a secure random IV. * * Only call this method after calling Cipher#encrypt or Cipher#decrypt. - * - * If not explicitly set, the OpenSSL default of an all-zeroes ("\\0") IV is - * used. */ static VALUE ossl_cipher_set_iv(VALUE self, VALUE iv) @@ -530,6 +527,27 @@ ossl_cipher_set_iv(VALUE self, VALUE iv) return iv; } +/* + * call-seq: + * cipher.authenticated? -> true | false + * + * Indicated whether this Cipher instance uses an Authenticated Encryption + * mode. + */ +static VALUE +ossl_cipher_is_authenticated(VALUE self) +{ + EVP_CIPHER_CTX *ctx; + + GetCipher(self, ctx); + +#if defined(HAVE_AUTHENTICATED_ENCRYPTION) + return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; +#else + return Qfalse; +#endif +} + #ifdef HAVE_AUTHENTICATED_ENCRYPTION /* * call-seq: @@ -676,23 +694,6 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) } /* - * call-seq: - * cipher.authenticated? -> boolean - * - * Indicated whether this Cipher instance uses an Authenticated Encryption - * mode. - */ -static VALUE -ossl_cipher_is_authenticated(VALUE self) -{ - EVP_CIPHER_CTX *ctx; - - GetCipher(self, ctx); - - return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; -} - -/* * call-seq: * cipher.iv_len = integer -> integer * @@ -726,7 +727,6 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) #define ossl_cipher_get_auth_tag rb_f_notimplement #define ossl_cipher_set_auth_tag rb_f_notimplement #define ossl_cipher_set_auth_tag_len rb_f_notimplement -#define ossl_cipher_is_authenticated rb_f_notimplement #define ossl_cipher_set_iv_length rb_f_notimplement #endif @@ -939,12 +939,10 @@ Init_ossl_cipher(void) * you absolutely need it</b> * * Because of this, you will end up with a mode that explicitly requires - * an IV in any case. Note that for backwards compatibility reasons, - * setting an IV is not explicitly mandated by the Cipher API. If not - * set, OpenSSL itself defaults to an all-zeroes IV ("\\0", not the - * character). Although the IV can be seen as public information, i.e. - * it may be transmitted in public once generated, it should still stay - * unpredictable to prevent certain kinds of attacks. Therefore, ideally + * an IV in any case. Although the IV can be seen as public information, + * i.e. it may be transmitted in public once generated, it should still + * stay unpredictable to prevent certain kinds of attacks. Therefore, + * ideally * * <b>Always create a secure random IV for every encryption of your * Cipher</b> @@ -1082,4 +1080,5 @@ Init_ossl_cipher(void) rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1); id_auth_tag_len = rb_intern_const("auth_tag_len"); + id_key_set = rb_intern_const("key_set"); } diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index 2f7845b..98f6552 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -159,8 +159,6 @@ ossl_spki_print(VALUE self) { NETSCAPE_SPKI *spki; BIO *out; - BUF_MEM *buf; - VALUE str; GetSPKI(self, spki); if (!(out = BIO_new(BIO_s_mem()))) { @@ -170,11 +168,8 @@ ossl_spki_print(VALUE self) BIO_free(out); ossl_raise(eSPKIError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + return ossl_membio2str(out); } /* diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 3c7c5e1..9e6c615 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -70,12 +70,12 @@ const rb_data_type_t ossl_evp_pkey_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; -VALUE -ossl_pkey_new(EVP_PKEY *pkey) +static VALUE +pkey_new0(EVP_PKEY *pkey) { - if (!pkey) { - ossl_raise(ePKeyError, "Cannot make new key from NULL."); - } + if (!pkey) + ossl_raise(ePKeyError, "cannot make new key from NULL"); + switch (EVP_PKEY_base_id(pkey)) { #if !defined(OPENSSL_NO_RSA) case EVP_PKEY_RSA: @@ -96,29 +96,21 @@ ossl_pkey_new(EVP_PKEY *pkey) default: ossl_raise(ePKeyError, "unsupported key type"); } - - UNREACHABLE; } VALUE -ossl_pkey_new_from_file(VALUE filename) +ossl_pkey_new(EVP_PKEY *pkey) { - FILE *fp; - EVP_PKEY *pkey; - - rb_check_safe_obj(filename); - if (!(fp = fopen(StringValueCStr(filename), "r"))) { - ossl_raise(ePKeyError, "%s", strerror(errno)); - } - rb_fd_fix_cloexec(fileno(fp)); + VALUE obj; + int status; - pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL); - fclose(fp); - if (!pkey) { - ossl_raise(ePKeyError, NULL); + obj = rb_protect((VALUE (*)(VALUE))pkey_new0, (VALUE)pkey, &status); + if (status) { + EVP_PKEY_free(pkey); + rb_jump_tag(status); } - return ossl_pkey_new(pkey); + return obj; } /* @@ -166,6 +158,45 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) return ossl_pkey_new(pkey); } +static void +pkey_check_public_key(EVP_PKEY *pkey) +{ + void *ptr; + const BIGNUM *n, *e, *pubkey; + + if (EVP_PKEY_missing_parameters(pkey)) + ossl_raise(ePKeyError, "parameters missing"); + + ptr = EVP_PKEY_get0(pkey); + switch (EVP_PKEY_base_id(pkey)) { + case EVP_PKEY_RSA: + RSA_get0_key(ptr, &n, &e, NULL); + if (n && e) + return; + break; + case EVP_PKEY_DSA: + DSA_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; + case EVP_PKEY_DH: + DH_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; +#if !defined(OPENSSL_NO_EC) + case EVP_PKEY_EC: + if (EC_KEY_get0_public_key(ptr)) + return; + break; +#endif + default: + /* unsupported type; assuming ok */ + return; + } + ossl_raise(ePKeyError, "public key missing"); +} + EVP_PKEY * GetPKeyPtr(VALUE obj) { @@ -264,18 +295,23 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) pkey = GetPrivPKeyPtr(self); md = GetDigestPtr(digest); StringValue(data); - str = rb_str_new(0, EVP_PKEY_size(pkey)+16); + str = rb_str_new(0, EVP_PKEY_size(pkey)); ctx = EVP_MD_CTX_new(); if (!ctx) ossl_raise(ePKeyError, "EVP_MD_CTX_new"); - EVP_SignInit(ctx, md); - EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)); + if (!EVP_SignInit_ex(ctx, md, NULL)) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_SignInit_ex"); + } + if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_SignUpdate"); + } result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey); EVP_MD_CTX_free(ctx); if (!result) - ossl_raise(ePKeyError, NULL); - assert((long)buf_len <= RSTRING_LEN(str)); + ossl_raise(ePKeyError, "EVP_SignFinal"); rb_str_set_len(str, buf_len); return str; @@ -308,19 +344,27 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) EVP_PKEY *pkey; const EVP_MD *md; EVP_MD_CTX *ctx; - int result; + int siglen, result; GetPKey(self, pkey); + pkey_check_public_key(pkey); md = GetDigestPtr(digest); StringValue(sig); + siglen = RSTRING_LENINT(sig); StringValue(data); ctx = EVP_MD_CTX_new(); if (!ctx) ossl_raise(ePKeyError, "EVP_MD_CTX_new"); - EVP_VerifyInit(ctx, md); - EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)); - result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey); + if (!EVP_VerifyInit_ex(ctx, md, NULL)) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_VerifyInit_ex"); + } + if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_VerifyUpdate"); + } + result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey); EVP_MD_CTX_free(ctx); switch (result) { case 0: @@ -329,9 +373,8 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) case 1: return Qtrue; default: - ossl_raise(ePKeyError, NULL); + ossl_raise(ePKeyError, "EVP_VerifyFinal"); } - return Qnil; /* dummy */ } /* diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 218f2eb..e3b723c 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -48,7 +48,6 @@ int ossl_generate_cb_2(int p, int n, BN_GENCB *cb); void ossl_generate_cb_stop(void *ptr); VALUE ossl_pkey_new(EVP_PKEY *); -VALUE ossl_pkey_new_from_file(VALUE); EVP_PKEY *GetPKeyPtr(VALUE); EVP_PKEY *DupPKeyPtr(VALUE); EVP_PKEY *GetPrivPKeyPtr(VALUE); diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 938efe1..dd85b7b 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -460,7 +460,7 @@ ossl_dh_to_public_key(VALUE self) GetDH(self, orig_dh); dh = DHparams_dup(orig_dh); /* err check perfomed by dh_instance */ - obj = dh_instance(CLASS_OF(self), dh); + obj = dh_instance(rb_obj_class(self), dh); if (obj == Qfalse) { DH_free(dh); ossl_raise(eDHError, NULL); diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 3821cd8..8508541 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -491,7 +491,7 @@ ossl_dsa_to_public_key(VALUE self) (i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa)) dsa = DSAPublicKey_dup(EVP_PKEY_get0_DSA(pkey)); #undef DSAPublicKey_dup - obj = dsa_instance(CLASS_OF(self), dsa); + obj = dsa_instance(rb_obj_class(self), dsa); if (obj == Qfalse) { DSA_free(dsa); ossl_raise(eDSAError, NULL); @@ -499,8 +499,6 @@ ossl_dsa_to_public_key(VALUE self) return obj; } -#define ossl_dsa_buf_size(dsa) (DSA_size(dsa) + 16) - /* * call-seq: * dsa.syssign(string) -> aString @@ -535,7 +533,7 @@ ossl_dsa_sign(VALUE self, VALUE data) if (!DSA_PRIVATE(self, dsa)) ossl_raise(eDSAError, "Private DSA key needed!"); StringValue(data); - str = rb_str_new(0, ossl_dsa_buf_size(dsa)); + str = rb_str_new(0, DSA_size(dsa)); if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *)RSTRING_PTR(str), &buf_len, dsa)) { /* type is ignored (0) */ diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index f0a3123..c795c07 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -643,11 +643,10 @@ static VALUE ossl_ec_key_dsa_sign_asn1(VALUE self, VALUE data) if (EC_KEY_get0_private_key(ec) == NULL) ossl_raise(eECError, "Private EC key needed!"); - str = rb_str_new(0, ECDSA_size(ec) + 16); + str = rb_str_new(0, ECDSA_size(ec)); if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1) - ossl_raise(eECError, "ECDSA_sign"); - - rb_str_resize(str, buf_len); + ossl_raise(eECError, "ECDSA_sign"); + rb_str_set_len(str, buf_len); return str; } @@ -1106,6 +1105,22 @@ static VALUE ossl_ec_group_get_point_conversion_form(VALUE self) return ID2SYM(ret); } +static point_conversion_form_t +parse_point_conversion_form_symbol(VALUE sym) +{ + ID id = SYM2ID(sym); + + if (id == ID_uncompressed) + return POINT_CONVERSION_UNCOMPRESSED; + else if (id == ID_compressed) + return POINT_CONVERSION_COMPRESSED; + else if (id == ID_hybrid) + return POINT_CONVERSION_HYBRID; + else + ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE + " (expected :compressed, :uncompressed, or :hybrid)", sym); +} + /* * call-seq: * group.point_conversion_form = form @@ -1125,23 +1140,14 @@ static VALUE ossl_ec_group_get_point_conversion_form(VALUE self) * * See the OpenSSL documentation for EC_GROUP_set_point_conversion_form() */ -static VALUE ossl_ec_group_set_point_conversion_form(VALUE self, VALUE form_v) +static VALUE +ossl_ec_group_set_point_conversion_form(VALUE self, VALUE form_v) { - EC_GROUP *group = NULL; + EC_GROUP *group; point_conversion_form_t form; - ID form_id = SYM2ID(form_v); GetECGroup(self, group); - - if (form_id == ID_uncompressed) { - form = POINT_CONVERSION_UNCOMPRESSED; - } else if (form_id == ID_compressed) { - form = POINT_CONVERSION_COMPRESSED; - } else if (form_id == ID_hybrid) { - form = POINT_CONVERSION_HYBRID; - } else { - ossl_raise(rb_eArgError, "form must be :compressed, :uncompressed, or :hybrid"); - } + form = parse_point_conversion_form_symbol(form_v); EC_GROUP_set_point_conversion_form(group, form); @@ -1549,22 +1555,30 @@ static VALUE ossl_ec_point_set_to_infinity(VALUE self) /* * call-seq: - * point.to_bn => OpenSSL::BN + * point.to_bn(conversion_form = nil) => OpenSSL::BN + * + * Convert the EC point into an octet string and store in an OpenSSL::BN. If + * +conversion_form+ is given, the point data is converted using the specified + * form. If not given, the default form set in the EC::Group object is used. * - * See the OpenSSL documentation for EC_POINT_point2bn() + * See also EC::Point#point_conversion_form=. */ -static VALUE ossl_ec_point_to_bn(VALUE self) +static VALUE +ossl_ec_point_to_bn(int argc, VALUE *argv, VALUE self) { EC_POINT *point; - VALUE bn_obj; + VALUE form_obj, bn_obj; const EC_GROUP *group; point_conversion_form_t form; BIGNUM *bn; GetECPoint(self, point); GetECPointGroup(self, group); - - form = EC_GROUP_get_point_conversion_form(group); + rb_scan_args(argc, argv, "01", &form_obj); + if (NIL_P(form_obj)) + form = EC_GROUP_get_point_conversion_form(group); + else + form = parse_point_conversion_form_symbol(form_obj); bn_obj = rb_obj_alloc(cBN); bn = GetBNPtr(bn_obj); @@ -1793,7 +1807,7 @@ void Init_ossl_ec(void) rb_define_method(cEC_POINT, "set_to_infinity!", ossl_ec_point_set_to_infinity, 0); /* all the other methods */ - rb_define_method(cEC_POINT, "to_bn", ossl_ec_point_to_bn, 0); + rb_define_method(cEC_POINT, "to_bn", ossl_ec_point_to_bn, -1); rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1); id_i_group = rb_intern("@group"); diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 17a7494..cea228d 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -404,8 +404,6 @@ ossl_rsa_to_der(VALUE self) return str; } -#define ossl_rsa_buf_size(rsa) (RSA_size(rsa)+16) - /* * call-seq: * rsa.public_encrypt(string) => String @@ -429,7 +427,7 @@ ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); - str = rb_str_new(0, ossl_rsa_buf_size(rsa)); + str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_public_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); @@ -461,7 +459,7 @@ ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); - str = rb_str_new(0, ossl_rsa_buf_size(rsa)); + str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_public_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); @@ -495,7 +493,7 @@ ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); - str = rb_str_new(0, ossl_rsa_buf_size(rsa)); + str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_private_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); @@ -529,7 +527,7 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); - str = rb_str_new(0, ossl_rsa_buf_size(rsa)); + str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_private_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); @@ -620,7 +618,7 @@ ossl_rsa_to_public_key(VALUE self) GetPKeyRSA(self, pkey); /* err check performed by rsa_instance */ rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey)); - obj = rsa_instance(CLASS_OF(self), rsa); + obj = rsa_instance(rb_obj_class(self), rsa); if (obj == Qfalse) { RSA_free(rsa); ossl_raise(eRSAError, NULL); diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index fb7c0fb..f3da25b 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -141,19 +141,18 @@ static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2) * * Returns the time at which the session was established. */ -static VALUE ossl_ssl_session_get_time(VALUE self) +static VALUE +ossl_ssl_session_get_time(VALUE self) { - SSL_SESSION *ctx; - time_t t; - - GetSSLSession(self, ctx); + SSL_SESSION *ctx; + long t; - t = SSL_SESSION_get_time(ctx); + GetSSLSession(self, ctx); + t = SSL_SESSION_get_time(ctx); + if (t == 0) + return Qnil; - if (t == 0) - return Qnil; - - return rb_funcall(rb_cTime, rb_intern("at"), 1, TIMET2NUM(t)); + return rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(t)); } /* @@ -164,16 +163,16 @@ static VALUE ossl_ssl_session_get_time(VALUE self) * established time. * */ -static VALUE ossl_ssl_session_get_timeout(VALUE self) +static VALUE +ossl_ssl_session_get_timeout(VALUE self) { - SSL_SESSION *ctx; - time_t t; + SSL_SESSION *ctx; + long t; - GetSSLSession(self, ctx); - - t = SSL_SESSION_get_timeout(ctx); + GetSSLSession(self, ctx); + t = SSL_SESSION_get_timeout(ctx); - return TIMET2NUM(t); + return LONG2NUM(t); } /* @@ -270,9 +269,6 @@ static VALUE ossl_ssl_session_to_pem(VALUE self) { SSL_SESSION *ctx; BIO *out; - BUF_MEM *buf; - VALUE str; - int i; GetSSLSession(self, ctx); @@ -280,16 +276,13 @@ static VALUE ossl_ssl_session_to_pem(VALUE self) ossl_raise(eSSLSession, "BIO_s_mem()"); } - if (!(i=PEM_write_bio_SSL_SESSION(out, ctx))) { + if (!PEM_write_bio_SSL_SESSION(out, ctx)) { BIO_free(out); ossl_raise(eSSLSession, "SSL_SESSION_print()"); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + return ossl_membio2str(out); } @@ -303,8 +296,6 @@ static VALUE ossl_ssl_session_to_text(VALUE self) { SSL_SESSION *ctx; BIO *out; - BUF_MEM *buf; - VALUE str; GetSSLSession(self, ctx); @@ -317,11 +308,7 @@ static VALUE ossl_ssl_session_to_text(VALUE self) ossl_raise(eSSLSession, "SSL_SESSION_print()"); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - - return str; + return ossl_membio2str(out); } diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c index 0ff5d2f..f9819f5 100644 --- a/ext/openssl/ossl_x509crl.c +++ b/ext/openssl/ossl_x509crl.c @@ -182,8 +182,6 @@ ossl_x509crl_get_signature_algorithm(VALUE self) X509_CRL *crl; const X509_ALGOR *alg; BIO *out; - BUF_MEM *buf; - VALUE str; GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { @@ -194,10 +192,8 @@ ossl_x509crl_get_signature_algorithm(VALUE self) BIO_free(out); ossl_raise(eX509CRLError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + + return ossl_membio2str(out); } static VALUE @@ -388,8 +384,6 @@ ossl_x509crl_to_der(VALUE self) { X509_CRL *crl; BIO *out; - BUF_MEM *buf; - VALUE str; GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { @@ -399,11 +393,8 @@ ossl_x509crl_to_der(VALUE self) BIO_free(out); ossl_raise(eX509CRLError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + return ossl_membio2str(out); } static VALUE @@ -411,8 +402,6 @@ ossl_x509crl_to_pem(VALUE self) { X509_CRL *crl; BIO *out; - BUF_MEM *buf; - VALUE str; GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { @@ -422,11 +411,8 @@ ossl_x509crl_to_pem(VALUE self) BIO_free(out); ossl_raise(eX509CRLError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + return ossl_membio2str(out); } static VALUE @@ -434,8 +420,6 @@ ossl_x509crl_to_text(VALUE self) { X509_CRL *crl; BIO *out; - BUF_MEM *buf; - VALUE str; GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { @@ -445,11 +429,8 @@ ossl_x509crl_to_text(VALUE self) BIO_free(out); ossl_raise(eX509CRLError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + return ossl_membio2str(out); } /* diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c index abbdc3b..4523e0d 100644 --- a/ext/openssl/ossl_x509name.c +++ b/ext/openssl/ossl_x509name.c @@ -372,12 +372,10 @@ ossl_x509name_cmp(VALUE self, VALUE other) static VALUE ossl_x509name_eql(VALUE self, VALUE other) { - int result; - - if(CLASS_OF(other) != cX509Name) return Qfalse; - result = ossl_x509name_cmp0(self, other); + if (!rb_obj_is_kind_of(other, cX509Name)) + return Qfalse; - return (result == 0) ? Qtrue : Qfalse; + return ossl_x509name_cmp0(self, other) ? Qtrue : Qfalse; } /* diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c index d261997..220d2f4 100644 --- a/ext/openssl/ossl_x509req.c +++ b/ext/openssl/ossl_x509req.c @@ -160,8 +160,6 @@ ossl_x509req_to_pem(VALUE self) { X509_REQ *req; BIO *out; - BUF_MEM *buf; - VALUE str; GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { @@ -171,11 +169,8 @@ ossl_x509req_to_pem(VALUE self) BIO_free(out); ossl_raise(eX509ReqError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + return ossl_membio2str(out); } static VALUE @@ -203,8 +198,6 @@ ossl_x509req_to_text(VALUE self) { X509_REQ *req; BIO *out; - BUF_MEM *buf; - VALUE str; GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { @@ -214,11 +207,8 @@ ossl_x509req_to_text(VALUE self) BIO_free(out); ossl_raise(eX509ReqError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + return ossl_membio2str(out); } #if 0 @@ -304,8 +294,6 @@ ossl_x509req_get_signature_algorithm(VALUE self) X509_REQ *req; const X509_ALGOR *alg; BIO *out; - BUF_MEM *buf; - VALUE str; GetX509Req(self, req); @@ -317,10 +305,8 @@ ossl_x509req_get_signature_algorithm(VALUE self) BIO_free(out); ossl_raise(eX509ReqError, NULL); } - BIO_get_mem_ptr(out, &buf); - str = rb_str_new(buf->data, buf->length); - BIO_free(out); - return str; + + return ossl_membio2str(out); } static VALUE diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb index 190f504..f40a451 100644 --- a/lib/openssl/ssl.rb +++ b/lib/openssl/ssl.rb @@ -16,8 +16,7 @@ require "io/nonblock" module OpenSSL module SSL class SSLContext - # :nodoc: - DEFAULT_PARAMS = { + DEFAULT_PARAMS = { # :nodoc: :ssl_version => "SSLv23", :verify_mode => OpenSSL::SSL::VERIFY_PEER, :verify_hostname => true, @@ -68,8 +67,7 @@ module OpenSSL ) end - # :nodoc: - DEFAULT_CERT_STORE = OpenSSL::X509::Store.new + DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc: DEFAULT_CERT_STORE.set_default_paths DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL @@ -84,14 +82,12 @@ module OpenSSL attr_accessor :tmp_dh_callback - if ExtConfig::HAVE_TLSEXT_HOST_NAME - # A callback invoked at connect time to distinguish between multiple - # server names. - # - # The callback is invoked with an SSLSocket and a server name. The - # callback must return an SSLContext for the server name or nil. - attr_accessor :servername_cb - end + # A callback invoked at connect time to distinguish between multiple + # server names. + # + # The callback is invoked with an SSLSocket and a server name. The + # callback must return an SSLContext for the server name or nil. + attr_accessor :servername_cb if ExtConfig::HAVE_TLSEXT_HOST_NAME # call-seq: # SSLContext.new => ctx diff --git a/openssl.gemspec b/openssl.gemspec new file mode 100644 index 0000000..3080cb0 --- /dev/null +++ b/openssl.gemspec @@ -0,0 +1,24 @@ +Gem::Specification.new do |spec| + spec.name = "openssl" + spec.version = "2.0.0.beta.2" + spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] + spec.email = ["ruby-core@ruby-lang.org"] + spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} + spec.description = %q{It wraps the OpenSSL library.} + spec.homepage = "https://www.ruby-lang.org/" + spec.license = "Ruby" + + spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md", "BSDL", "LICENSE.txt"] + spec.require_paths = ["lib"] + spec.extensions = ["ext/openssl/extconf.rb"] + + spec.extra_rdoc_files = Dir["*.md"] + spec.rdoc_options = ["--main", "README.md"] + + spec.required_ruby_version = ">= 2.3.0" + + spec.add_development_dependency "rake", "~> 10.3" + spec.add_development_dependency "rake-compiler", "~> 0.9" + spec.add_development_dependency "test-unit", "~> 3.0" + spec.add_development_dependency "rdoc", "~> 4.2" +end diff --git a/test/envutil.rb b/test/envutil.rb new file mode 100644 index 0000000..da30410 --- /dev/null +++ b/test/envutil.rb @@ -0,0 +1,311 @@ +# -*- coding: us-ascii -*- +require "timeout" +require "rbconfig" + +module EnvUtil + def rubybin + ENV["RUBY"] || RbConfig.ruby + end + module_function :rubybin + + LANG_ENVS = %w"LANG LC_ALL LC_CTYPE" + + def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false, + encoding: nil, timeout: 10, reprieve: 1, + stdout_filter: nil, stderr_filter: nil, + rubybin: EnvUtil.rubybin, + **opt) + in_c, in_p = IO.pipe + out_p, out_c = IO.pipe if capture_stdout + err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout + opt[:in] = in_c + opt[:out] = out_c if capture_stdout + opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr + if encoding + out_p.set_encoding(encoding) if out_p + err_p.set_encoding(encoding) if err_p + end + c = "C" + child_env = {} + LANG_ENVS.each {|lc| child_env[lc] = c} + if Array === args and Hash === args.first + child_env.update(args.shift) + end + args = [args] if args.kind_of?(String) + pid = spawn(child_env, rubybin, *args, **opt) + in_c.close + out_c.close if capture_stdout + err_c.close if capture_stderr && capture_stderr != :merge_to_stdout + if block_given? + return yield in_p, out_p, err_p, pid + else + th_stdout = Thread.new { out_p.read } if capture_stdout + th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout + in_p.write stdin_data.to_str unless stdin_data.empty? + in_p.close + if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout)) + stdout = th_stdout.value if capture_stdout + stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout + else + signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM + case pgroup = opt[:pgroup] + when 0, true + pgroup = -pid + when nil, false + pgroup = pid + end + begin + Process.kill signal, pgroup + Timeout.timeout((reprieve unless signal == :KILL)) do + Process.wait(pid) + end + rescue Errno::ESRCH + break + rescue Timeout::Error + raise if signal == :KILL + signal = :KILL + else + break + end while true + bt = caller_locations + raise Timeout::Error, "execution of #{bt.shift.label} expired", bt.map(&:to_s) + end + out_p.close if capture_stdout + err_p.close if capture_stderr && capture_stderr != :merge_to_stdout + Process.wait pid + status = $? + stdout = stdout_filter.call(stdout) if stdout_filter + stderr = stderr_filter.call(stderr) if stderr_filter + return stdout, stderr, status + end + ensure + [th_stdout, th_stderr].each do |th| + th.kill if th + end + [in_c, in_p, out_c, out_p, err_c, err_p].each do |io| + io.close if io && !io.closed? + end + [th_stdout, th_stderr].each do |th| + th.join if th + end + end + module_function :invoke_ruby + + def suppress_warning + verbose, $VERBOSE = $VERBOSE, nil + yield + ensure + $VERBOSE = verbose + end + module_function :suppress_warning + + if /darwin/ =~ RUBY_PLATFORM + DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports") + DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S' + def self.diagnostic_reports(signame, cmd, pid, now) + return unless %w[ABRT QUIT SEGV ILL].include?(signame) + cmd = File.basename(cmd) + path = DIAGNOSTIC_REPORTS_PATH + timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT + pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash" + first = true + 30.times do + first ? (first = false) : sleep(0.1) + Dir.glob(pat) do |name| + log = File.read(name) rescue next + if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log + File.unlink(name) + File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil + return log + end + end + end + nil + end + else + def self.diagnostic_reports(signame, cmd, pid, now) + end + end +end + +module Test + module Unit + module Assertions + FailDesc = proc do |status, message = "", out = ""| + pid = status.pid + now = Time.now + faildesc = proc do + if signo = status.termsig + signame = Signal.signame(signo) + sigdesc = "signal #{signo}" + end + log = EnvUtil.diagnostic_reports(signame, EnvUtil.rubybin, pid, now) + if signame + sigdesc = "SIG#{signame} (#{sigdesc})" + end + if status.coredump? + sigdesc << " (core dumped)" + end + full_message = '' + if message and !message.empty? + full_message << message << "\n" + end + full_message << "pid #{pid} killed by #{sigdesc}" + if out and !out.empty? + full_message << "\n#{out.gsub(/^/, '| ')}" + full_message << "\n" if /\n\z/ !~ full_message + end + if log + full_message << "\n#{log.gsub(/^/, '| ')}" + end + full_message + end + faildesc + end + + ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV") + + def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) + unless file and line + loc, = caller_locations(1,1) + file ||= loc.path + line ||= loc.lineno + end + line -= 6 # lines until src + src = <<eom +# -*- coding: #{src.encoding}; -*- + require 'test/unit';include Test::Unit::Assertions + END { + puts [Marshal.dump($!)].pack('m')#, "assertions=\#{self._assertions}" + exit + } + def pend(msg = nil) $stdout.syswrite [Marshal.dump(msg.to_s)].pack("m"); exit! 0 end +#{src} + class Test::Unit::Runner + @@stop_auto_run = true + end +eom + args = args.dup + args.insert((Hash === args.first ? 1 : 0), "--disable=gems", *$:.map {|l| "-I#{l}"}) + stdout, stderr, status = EnvUtil.invoke_ruby(args, src, true, true, **opt) + abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig)) + assert(!abort, FailDesc[status, nil, stderr]) + #self._assertions += stdout[/^assertions=(\d+)/, 1].to_i + begin + res = Marshal.load(stdout.unpack("m")[0]) + rescue => marshal_error + ignore_stderr = nil + end + if res.is_a?(String) + pend res + elsif res + if bt = res.backtrace + bt.each do |l| + l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} + end + bt.concat(caller) + else + res.set_backtrace(caller) + end + raise res + end + + # really is it succeed? + unless ignore_stderr + # the body of assert_separately must not output anything to detect error + assert_equal("", stderr, "assert_separately failed with error message") + end + assert_equal(0, status, "assert_separately failed: '#{stderr}'") + raise marshal_error if marshal_error + end + + def message msg = nil, ending = ".", &default + proc { + msg = msg.call.chomp(".") if Proc === msg + custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? + "#{custom_message}#{default.call}#{ending}" + } + end + + # threads should respond to shift method. + # Array can be used. + def assert_join_threads(threads, message = nil) + errs = [] + values = [] + while th = threads.shift + begin + values << th.value + rescue Exception + errs << [th, $!] + end + end + if !errs.empty? + msg = "exceptions on #{errs.length} threads:\n" + + errs.map {|t, err| + "#{t.inspect}:\n" + + err.backtrace.map.with_index {|line, i| + if i == 0 + "#{line}: #{err.message} (#{err.class})" + else + "\tfrom #{line}" + end + }.join("\n") + }.join("\n---\n") + if message + msg = "#{message}\n#{msg}" + end + raise Test::Unit::AssertionFailedError, msg + end + values + end + + # :call-seq: + # assert_raise_with_message(exception, expected, msg = nil, &block) + # + #Tests if the given block raises an exception with the expected + #message. + # + # assert_raise_with_message(RuntimeError, "foo") do + # nil #Fails, no Exceptions are raised + # end + # + # assert_raise_with_message(RuntimeError, "foo") do + # raise ArgumentError, "foo" #Fails, different Exception is raised + # end + # + # assert_raise_with_message(RuntimeError, "foo") do + # raise "bar" #Fails, RuntimeError is raised but the message differs + # end + # + # assert_raise_with_message(RuntimeError, "foo") do + # raise "foo" #Raises RuntimeError with the message, so assertion succeeds + # end + def assert_raise_with_message(exception, expected, msg = nil, &block) + case expected + when String + assert = :assert_equal + when Regexp + assert = :assert_match + else + raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}" + end + + ex = m = nil + ex = assert_raise(exception, msg || "Exception(#{exception}) with message matches to #{expected.inspect}") do + yield + end + m = ex.message + msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"} + + if assert == :assert_equal + assert_equal(expected, m, msg) + else + msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp m}" } + assert expected =~ m, msg + block.binding.eval("proc{|_|$~=_}").call($~) + end + ex + end + end + end +end diff --git a/test/test_asn1.rb b/test/test_asn1.rb index f226da5..612c59a 100644 --- a/test/test_asn1.rb +++ b/test/test_asn1.rb @@ -243,14 +243,9 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm end def test_primitive_cannot_set_infinite_length - begin - prim = OpenSSL::ASN1::Integer.new(50) - assert_equal(false, prim.infinite_length) - prim.infinite_length = true - flunk('Could set infinite length on primitive value') - rescue NoMethodError - #ok - end + prim = OpenSSL::ASN1::Integer.new(50) + assert_equal false, prim.infinite_length + assert_not_respond_to prim, :infinite_length= end def test_decode_all @@ -289,12 +284,8 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm def test_create_inf_length_primitive expected = %w{ 24 80 04 01 61 00 00 } raw = [expected.join('')].pack('H*') - val = OpenSSL::ASN1::OctetString.new('a') - cons = OpenSSL::ASN1::Constructive.new([val, - OpenSSL::ASN1::EndOfContent.new], - OpenSSL::ASN1::OCTET_STRING, - nil, - :UNIVERSAL) + content = [OpenSSL::ASN1::OctetString.new("a"), OpenSSL::ASN1::EndOfContent.new] + cons = OpenSSL::ASN1::Constructive.new(content, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.infinite_length = true assert_equal(nil, cons.tagging) assert_equal(raw, cons.to_der) @@ -306,10 +297,7 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm def test_cons_without_inf_length_forbidden assert_raise(OpenSSL::ASN1::ASN1Error) do val = OpenSSL::ASN1::OctetString.new('a') - cons = OpenSSL::ASN1::Constructive.new([val], - OpenSSL::ASN1::OCTET_STRING, - nil, - :UNIVERSAL) + cons = OpenSSL::ASN1::Constructive.new([val], OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.to_der end end @@ -317,10 +305,7 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm def test_cons_without_array_forbidden assert_raise(OpenSSL::ASN1::ASN1Error) do val = OpenSSL::ASN1::OctetString.new('a') - cons = OpenSSL::ASN1::Constructive.new(val, - OpenSSL::ASN1::OCTET_STRING, - nil, - :UNIVERSAL) + cons = OpenSSL::ASN1::Constructive.new(val, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.infinite_length = true cons.to_der end @@ -352,218 +337,158 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm end def test_seq_infinite_length - begin - content = [ OpenSSL::ASN1::Null.new(nil), - OpenSSL::ASN1::EndOfContent.new ] - cons = OpenSSL::ASN1::Sequence.new(content) - cons.infinite_length = true - expected = %w{ 30 80 05 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content = [ OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::EndOfContent.new ] + cons = OpenSSL::ASN1::Sequence.new(content) + cons.infinite_length = true + expected = %w{ 30 80 05 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_set_infinite_length - begin - content = [ OpenSSL::ASN1::Null.new(nil), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Set.new(content) - cons.infinite_length = true - expected = %w{ 31 80 05 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content = [ OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Set.new(content) + cons.infinite_length = true + expected = %w{ 31 80 05 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_octet_string_infinite_length - begin - octets = [ OpenSSL::ASN1::OctetString.new('aaa'), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new( - octets, - OpenSSL::ASN1::OCTET_STRING, - nil, - :UNIVERSAL) - cons.infinite_length = true - expected = %w{ 24 80 04 03 61 61 61 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + octets = [ OpenSSL::ASN1::OctetString.new('aaa'), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new(octets, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) + cons.infinite_length = true + expected = %w{ 24 80 04 03 61 61 61 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_prim_explicit_tagging - begin - oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) - expected = %w{ A0 03 04 01 61 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, oct_str.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) + expected = %w{ A0 03 04 01 61 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, oct_str.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_prim_explicit_tagging_tag_class - begin - oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) - oct_str2 = OpenSSL::ASN1::OctetString.new( - "a", - 0, - :EXPLICIT, - :CONTEXT_SPECIFIC) - assert_equal(oct_str.to_der, oct_str2.to_der) - end + oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) + oct_str2 = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT, :CONTEXT_SPECIFIC) + assert_equal(oct_str.to_der, oct_str2.to_der) end def test_prim_implicit_tagging - begin - int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) - expected = %w{ 80 01 01 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, int.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) + expected = %w{ 80 01 01 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, int.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_prim_implicit_tagging_tag_class - begin - int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) - int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC); - assert_equal(int.to_der, int2.to_der) - end + int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) + int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC); + assert_equal(int.to_der, int2.to_der) end def test_cons_explicit_tagging - begin - content = [ OpenSSL::ASN1::PrintableString.new('abc') ] - seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) - expected = %w{ A2 07 30 05 13 03 61 62 63 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content = [ OpenSSL::ASN1::PrintableString.new('abc') ] + seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) + expected = %w{ A2 07 30 05 13 03 61 62 63 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_cons_explicit_tagging_inf_length - begin - content = [ OpenSSL::ASN1::PrintableString.new('abc') , - OpenSSL::ASN1::EndOfContent.new() ] - seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) - seq.infinite_length = true - expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content = [ OpenSSL::ASN1::PrintableString.new('abc') , + OpenSSL::ASN1::EndOfContent.new() ] + seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) + seq.infinite_length = true + expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_cons_implicit_tagging - begin - content = [ OpenSSL::ASN1::Null.new(nil) ] - seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) - expected = %w{ A1 02 05 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content = [ OpenSSL::ASN1::Null.new(nil) ] + seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) + expected = %w{ A1 02 05 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_cons_implicit_tagging_inf_length - begin - content = [ OpenSSL::ASN1::Null.new(nil), - OpenSSL::ASN1::EndOfContent.new() ] - seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) - seq.infinite_length = true - expected = %w{ A1 80 05 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content = [ OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::EndOfContent.new() ] + seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) + seq.infinite_length = true + expected = %w{ A1 80 05 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, seq.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_octet_string_infinite_length_explicit_tagging - begin - octets = [ OpenSSL::ASN1::OctetString.new('aaa'), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new( - octets, - 1, - :EXPLICIT) - cons.infinite_length = true - expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + octets = [ OpenSSL::ASN1::OctetString.new('aaa'), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new(octets, 1, :EXPLICIT) + cons.infinite_length = true + expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_octet_string_infinite_length_implicit_tagging - begin - octets = [ OpenSSL::ASN1::OctetString.new('aaa'), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new( - octets, - 0, - :IMPLICIT) - cons.infinite_length = true - expected = %w{ A0 80 04 03 61 61 61 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + octets = [ OpenSSL::ASN1::OctetString.new('aaa'), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new(octets, 0, :IMPLICIT) + cons.infinite_length = true + expected = %w{ A0 80 04 03 61 61 61 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_recursive_octet_string_infinite_length - begin - octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"), - OpenSSL::ASN1::EndOfContent.new() ] - octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"), - OpenSSL::ASN1::EndOfContent.new() ] - container1 = OpenSSL::ASN1::Constructive.new( - octets_sub1, - OpenSSL::ASN1::OCTET_STRING, - nil, - :UNIVERSAL) - container1.infinite_length = true - container2 = OpenSSL::ASN1::Constructive.new( - octets_sub2, - OpenSSL::ASN1::OCTET_STRING, - nil, - :UNIVERSAL) - container2.infinite_length = true - octets3 = OpenSSL::ASN1::OctetString.new("\x03") - - octets = [ container1, container2, octets3, - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new( - octets, - OpenSSL::ASN1::OCTET_STRING, - nil, - :UNIVERSAL) - cons.infinite_length = true - expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"), + OpenSSL::ASN1::EndOfContent.new() ] + octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"), + OpenSSL::ASN1::EndOfContent.new() ] + container1 = OpenSSL::ASN1::Constructive.new(octets_sub1, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) + container1.infinite_length = true + container2 = OpenSSL::ASN1::Constructive.new(octets_sub2, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) + container2.infinite_length = true + octets3 = OpenSSL::ASN1::OctetString.new("\x03") + + octets = [ container1, container2, octets3, + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new(octets, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) + cons.infinite_length = true + expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_bit_string_infinite_length - begin - content = [ OpenSSL::ASN1::BitString.new("\x01"), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new( - content, - OpenSSL::ASN1::BIT_STRING, - nil, - :UNIVERSAL) - cons.infinite_length = true - expected = %w{ 23 80 03 02 00 01 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content = [ OpenSSL::ASN1::BitString.new("\x01"), + OpenSSL::ASN1::EndOfContent.new() ] + cons = OpenSSL::ASN1::Constructive.new(content, OpenSSL::ASN1::BIT_STRING, nil, :UNIVERSAL) + cons.infinite_length = true + expected = %w{ 23 80 03 02 00 01 00 00 } + raw = [expected.join('')].pack('H*') + assert_equal(raw, cons.to_der) + assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_primitive_inf_length @@ -621,4 +546,3 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm end end if defined?(OpenSSL::TestUtils) - diff --git a/test/test_cipher.rb b/test/test_cipher.rb index 74c5394..8954cb6 100644 --- a/test/test_cipher.rb +++ b/test/test_cipher.rb @@ -4,110 +4,131 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestCipher < OpenSSL::TestCase - - @ciphers = OpenSSL::Cipher.ciphers - - class << self - + module Helper def has_cipher?(name) + @ciphers ||= OpenSSL::Cipher.ciphers @ciphers.include?(name) end - - def has_ciphers?(list) - list.all? { |name| has_cipher?(name) } - end - end - - def setup - @c1 = OpenSSL::Cipher.new("DES-EDE3-CBC") - @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC") - @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - @iv = "\0\0\0\0\0\0\0\0" - @hexkey = "0000000000000000000000000000000000000000000000" - @hexiv = "0000000000000000" - @data = "DATA" + include Helper + extend Helper + + def test_encrypt_decrypt + # NIST SP 800-38A F.2.1 + key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*") + iv = ["000102030405060708090a0b0c0d0e0f"].pack("H*") + pt = ["6bc1bee22e409f96e93d7e117393172a" \ + "ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*") + ct = ["7649abac8119b246cee98e9b12e9197d" \ + "5086cb9b507219ee95db113a917678b2"].pack("H*") + cipher = new_encryptor("aes-128-cbc", key: key, iv: iv, padding: 0) + assert_equal ct, cipher.update(pt) << cipher.final + cipher = new_decryptor("aes-128-cbc", key: key, iv: iv, padding: 0) + assert_equal pt, cipher.update(ct) << cipher.final end - def teardown - super - @c1 = @c2 = nil - end - - def test_crypt - @c1.encrypt.pkcs5_keyivgen(@key, @iv) - @c2.encrypt.pkcs5_keyivgen(@key, @iv) - s1 = @c1.update(@data) + @c1.final - s2 = @c2.update(@data) + @c2.final - assert_equal(s1, s2, "encrypt") - - @c1.decrypt.pkcs5_keyivgen(@key, @iv) - @c2.decrypt.pkcs5_keyivgen(@key, @iv) - assert_equal(@data, @c1.update(s1)+@c1.final, "decrypt") - assert_equal(@data, @c2.update(s2)+@c2.final, "decrypt") + def test_pkcs5_keyivgen + pass = "\x00" * 8 + salt = "\x01" * 8 + num = 2048 + pt = "data to be encrypted" + cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt + cipher.pkcs5_keyivgen(pass, salt, num, "MD5") + s1 = cipher.update(pt) << cipher.final + + d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) } + d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) } + key = (d1 + d2)[0, 24] + iv = (d1 + d2)[24, 8] + cipher = new_encryptor("DES-EDE3-CBC", key: key, iv: iv) + s2 = cipher.update(pt) << cipher.final + + assert_equal s1, s2 end def test_info - assert_equal("DES-EDE3-CBC", @c1.name, "name") - assert_equal("DES-EDE3-CBC", @c2.name, "name") - assert_kind_of(Integer, @c1.key_len, "key_len") - assert_kind_of(Integer, @c1.iv_len, "iv_len") + cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt + assert_equal "DES-EDE3-CBC", cipher.name + assert_equal 24, cipher.key_len + assert_equal 8, cipher.iv_len end def test_dup - assert_equal(@c1.name, @c1.dup.name, "dup") - assert_equal(@c1.name, @c1.clone.name, "clone") - @c1.encrypt - @c1.key = @key - @c1.iv = @iv - tmpc = @c1.dup - s1 = @c1.update(@data) + @c1.final - s2 = tmpc.update(@data) + tmpc.final + cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt + assert_equal cipher.name, cipher.dup.name + cipher.encrypt + cipher.random_key + cipher.random_iv + tmpc = cipher.dup + s1 = cipher.update("data") + cipher.final + s2 = tmpc.update("data") + tmpc.final assert_equal(s1, s2, "encrypt dup") end def test_reset - @c1.encrypt - @c1.key = @key - @c1.iv = @iv - s1 = @c1.update(@data) + @c1.final - @c1.reset - s2 = @c1.update(@data) + @c1.final + cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt + cipher.encrypt + cipher.random_key + cipher.random_iv + s1 = cipher.update("data") + cipher.final + cipher.reset + s2 = cipher.update("data") + cipher.final assert_equal(s1, s2, "encrypt reset") end def test_key_iv_set - # default value for DES-EDE3-CBC - assert_equal(24, @c1.key_len) - assert_equal(8, @c1.iv_len) - assert_raise(ArgumentError) { @c1.key = "\x01" * 23 } - @c1.key = "\x01" * 24 - assert_raise(ArgumentError) { @c1.key = "\x01" * 25 } - assert_raise(ArgumentError) { @c1.iv = "\x01" * 7 } - @c1.iv = "\x01" * 8 - assert_raise(ArgumentError) { @c1.iv = "\x01" * 9 } + cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt + assert_raise(ArgumentError) { cipher.key = "\x01" * 23 } + assert_nothing_raised { cipher.key = "\x01" * 24 } + assert_raise(ArgumentError) { cipher.key = "\x01" * 25 } + assert_raise(ArgumentError) { cipher.iv = "\x01" * 7 } + assert_nothing_raised { cipher.iv = "\x01" * 8 } + assert_raise(ArgumentError) { cipher.iv = "\x01" * 9 } + end + + def test_random_key_iv + data = "data" + s1, s2 = 2.times.map do + cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt + cipher.random_key + cipher.iv = "\x01" * 16 + cipher.update(data) << cipher.final + end + assert_not_equal s1, s2 + + s1, s2 = 2.times.map do + cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt + cipher.key = "\x01" * 16 + cipher.random_iv + cipher.update(data) << cipher.final + end + assert_not_equal s1, s2 end def test_empty_data - @c1.encrypt - assert_raise(ArgumentError){ @c1.update("") } + cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt + cipher.random_key + assert_raise(ArgumentError) { cipher.update("") } end def test_initialize - assert_raise(RuntimeError) {@c1.__send__(:initialize, "DES-EDE3-CBC")} - assert_raise(RuntimeError) {OpenSSL::Cipher.allocate.final} + cipher = OpenSSL::Cipher.new("DES-EDE3-CBC") + assert_raise(RuntimeError) { cipher.__send__(:initialize, "DES-EDE3-CBC") } + assert_raise(RuntimeError) { OpenSSL::Cipher.allocate.final } end def test_ctr_if_exists - begin - cipher = OpenSSL::Cipher.new('aes-128-ctr') - cipher.encrypt - cipher.pkcs5_keyivgen('password') - c = cipher.update('hello,world') + cipher.final - cipher.decrypt - cipher.pkcs5_keyivgen('password') - assert_equal('hello,world', cipher.update(c) + cipher.final) - end + # NIST SP 800-38A F.5.1 + key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*") + iv = ["f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"].pack("H*") + pt = ["6bc1bee22e409f96e93d7e117393172a" \ + "ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*") + ct = ["874d6191b620e3261bef6864990db6ce" \ + "9806f66b7970fdff8617187bb9fffdff"].pack("H*") + cipher = new_encryptor("aes-128-ctr", key: key, iv: iv, padding: 0) + assert_equal ct, cipher.update(pt) << cipher.final + cipher = new_decryptor("aes-128-ctr", key: key, iv: iv, padding: 0) + assert_equal pt, cipher.update(ct) << cipher.final end if has_cipher?('aes-128-ctr') def test_ciphers @@ -136,202 +157,159 @@ class OpenSSL::TestCipher < OpenSSL::TestCase } end - def test_AES_crush - 500.times do - assert_nothing_raised("[Bug #2768]") do - # it caused OpenSSL SEGV by uninitialized key - OpenSSL::Cipher::AES128.new("ECB").update "." * 17 - end + def test_update_raise_if_key_not_set + assert_raise(OpenSSL::Cipher::CipherError) do + # it caused OpenSSL SEGV by uninitialized key [Bug #2768] + OpenSSL::Cipher::AES128.new("ECB").update "." * 17 end end - if has_ciphers?(['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm']) - - def test_authenticated + def test_authenticated + if has_cipher?('aes-128-gcm') cipher = OpenSSL::Cipher.new('aes-128-gcm') assert_predicate(cipher, :authenticated?) - cipher = OpenSSL::Cipher.new('aes-128-cbc') - assert_not_predicate(cipher, :authenticated?) - end - - def test_aes_gcm - ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo| - pt = "You should all use Authenticated Encryption!" - cipher, key, iv = new_encryptor(algo) - - cipher.auth_data = "aad" - ct = cipher.update(pt) + cipher.final - tag = cipher.auth_tag - assert_equal(16, tag.size) - - decipher = new_decryptor(algo, key, iv) - decipher.auth_tag = tag - decipher.auth_data = "aad" - - assert_equal(pt, decipher.update(ct) + decipher.final) - end - end - - def test_aes_gcm_short_tag - ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo| - pt = "You should all use Authenticated Encryption!" - cipher, key, iv = new_encryptor(algo) - - cipher.auth_data = "aad" - ct = cipher.update(pt) + cipher.final - tag = cipher.auth_tag(8) - assert_equal(8, tag.size) - - decipher = new_decryptor(algo, key, iv) - decipher.auth_tag = tag - decipher.auth_data = "aad" - - assert_equal(pt, decipher.update(ct) + decipher.final) - end - end - - def test_aes_gcm_wrong_tag - pt = "You should all use Authenticated Encryption!" - cipher, key, iv = new_encryptor('aes-128-gcm') - - cipher.auth_data = "aad" - ct = cipher.update(pt) + cipher.final - tag = cipher.auth_tag - - decipher = new_decryptor('aes-128-gcm', key, iv) - tag.setbyte(-1, (tag.getbyte(-1) + 1) & 0xff) - decipher.auth_tag = tag - decipher.auth_data = "aad" - - assert_raise OpenSSL::Cipher::CipherError do - decipher.update(ct) + decipher.final - end - end - - def test_aes_gcm_wrong_auth_data - pt = "You should all use Authenticated Encryption!" - cipher, key, iv = new_encryptor('aes-128-gcm') - - cipher.auth_data = "aad" - ct = cipher.update(pt) + cipher.final - tag = cipher.auth_tag - - decipher = new_decryptor('aes-128-gcm', key, iv) - decipher.auth_tag = tag - decipher.auth_data = "daa" - - assert_raise OpenSSL::Cipher::CipherError do - decipher.update(ct) + decipher.final - end end + cipher = OpenSSL::Cipher.new('aes-128-cbc') + assert_not_predicate(cipher, :authenticated?) + end - def test_aes_gcm_wrong_ciphertext - pt = "You should all use Authenticated Encryption!" - cipher, key, iv = new_encryptor('aes-128-gcm') + def test_aes_gcm + # GCM spec Appendix B Test Case 4 + key = ["feffe9928665731c6d6a8f9467308308"].pack("H*") + iv = ["cafebabefacedbaddecaf888"].pack("H*") + aad = ["feedfacedeadbeeffeedfacedeadbeef" \ + "abaddad2"].pack("H*") + pt = ["d9313225f88406e5a55909c5aff5269a" \ + "86a7a9531534f7da2e4c303d8a318a72" \ + "1c3c0c95956809532fcf0e2449a6b525" \ + "b16aedf5aa0de657ba637b39"].pack("H*") + ct = ["42831ec2217774244b7221b784d0d49c" \ + "e3aa212f2c02a4e035c17e2329aca12e" \ + "21d514b25466931c7d8f6a5aac84aa05" \ + "1ba30b396a0aac973d58e091"].pack("H*") + tag = ["5bc94fbc3221a5db94fae95ae7121a47"].pack("H*") + + cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad) + assert_equal ct, cipher.update(pt) << cipher.final + assert_equal tag, cipher.auth_tag + cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad, auth_tag: tag) + assert_equal pt, cipher.update(ct) << cipher.final + + # truncated tag is accepted + cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad) + assert_equal ct, cipher.update(pt) << cipher.final + assert_equal tag[0, 8], cipher.auth_tag(8) + cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad, auth_tag: tag[0, 8]) + assert_equal pt, cipher.update(ct) << cipher.final + + # wrong tag is rejected + tag2 = tag.dup + tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff) + cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad, auth_tag: tag2) + cipher.update(ct) + assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } + + # wrong aad is rejected + aad2 = aad[0..-2] << aad[-1].succ + cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad2, auth_tag: tag) + cipher.update(ct) + assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } + + # wrong ciphertext is rejected + ct2 = ct[0..-2] << ct[-1].succ + cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad, auth_tag: tag) + cipher.update(ct2) + assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } + end if has_cipher?("aes-128-gcm") + + def test_aes_gcm_variable_iv_len + # GCM spec Appendix B Test Case 5 + key = ["feffe9928665731c6d6a8f9467308308"].pack("H*") + iv = ["cafebabefacedbad"].pack("H*") + aad = ["feedfacedeadbeeffeedfacedeadbeef" \ + "abaddad2"].pack("H*") + pt = ["d9313225f88406e5a55909c5aff5269a" \ + "86a7a9531534f7da2e4c303d8a318a72" \ + "1c3c0c95956809532fcf0e2449a6b525" \ + "b16aedf5aa0de657ba637b39"].pack("H*") + ct = ["61353b4c2806934a777ff51fa22a4755" \ + "699b2a714fcdc6f83766e5f97b6c7423" \ + "73806900e49f24b22b097544d4896b42" \ + "4989b5e1ebac0f07c23f4598"].pack("H*") + tag = ["3612d2e79e3b0785561be14aaca2fccb"].pack("H*") + + cipher = new_encryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_data: aad) + assert_equal ct, cipher.update(pt) << cipher.final + assert_equal tag, cipher.auth_tag + cipher = new_decryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_data: aad, auth_tag: tag) + assert_equal pt, cipher.update(ct) << cipher.final + end if has_cipher?("aes-128-gcm") - cipher.auth_data = "aad" - ct = cipher.update(pt) + cipher.final - tag = cipher.auth_tag + def test_aes_ocb_tag_len + # RFC 7253 Appendix A; the second sample + key = ["000102030405060708090A0B0C0D0E0F"].pack("H*") + iv = ["BBAA99887766554433221101"].pack("H*") + aad = ["0001020304050607"].pack("H*") + pt = ["0001020304050607"].pack("H*") + ct = ["6820B3657B6F615A"].pack("H*") + tag = ["5725BDA0D3B4EB3A257C9AF1F8F03009"].pack("H*") + + cipher = new_encryptor("aes-128-ocb", key: key, iv: iv, auth_data: aad) + assert_equal ct, cipher.update(pt) << cipher.final + assert_equal tag, cipher.auth_tag + cipher = new_decryptor("aes-128-ocb", key: key, iv: iv, auth_data: aad, auth_tag: tag) + assert_equal pt, cipher.update(ct) << cipher.final + + # RFC 7253 Appendix A; with 96 bits tag length + key = ["0F0E0D0C0B0A09080706050403020100"].pack("H*") + iv = ["BBAA9988776655443322110D"].pack("H*") + aad = ["000102030405060708090A0B0C0D0E0F1011121314151617" \ + "18191A1B1C1D1E1F2021222324252627"].pack("H*") + pt = ["000102030405060708090A0B0C0D0E0F1011121314151617" \ + "18191A1B1C1D1E1F2021222324252627"].pack("H*") + ct = ["1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1" \ + "A0124B0A55BAE884ED93481529C76B6A"].pack("H*") + tag = ["D0C515F4D1CDD4FDAC4F02AA"].pack("H*") + + cipher = new_encryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_data: aad) + assert_equal ct, cipher.update(pt) << cipher.final + assert_equal tag, cipher.auth_tag + cipher = new_decryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_data: aad, auth_tag: tag) + assert_equal pt, cipher.update(ct) << cipher.final - decipher = new_decryptor('aes-128-gcm', key, iv) - decipher.auth_tag = tag - decipher.auth_data = "aad" + end if has_cipher?("aes-128-ocb") - assert_raise OpenSSL::Cipher::CipherError do - decipher.update(ct[0..-2] << ct[-1].succ) + decipher.final - end - end + def test_aes_gcm_key_iv_order_issue + pt = "[ruby/openssl#49]" + cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt + cipher.key = "x" * 16 + cipher.iv = "a" * 12 + ct1 = cipher.update(pt) << cipher.final + tag1 = cipher.auth_tag - def test_aes_gcm_variable_iv_len - pt = "You should all use Authenticated Encryption!" - cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt - cipher.key = "x" * 16 - assert_equal(12, cipher.iv_len) - cipher.iv = "a" * 12 - ct1 = cipher.update(pt) << cipher.final - tag1 = cipher.auth_tag - - cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt - cipher.key = "x" * 16 - cipher.iv_len = 10 - assert_equal(10, cipher.iv_len) - cipher.iv = "a" * 10 - ct2 = cipher.update(pt) << cipher.final - tag2 = cipher.auth_tag - - assert_not_equal ct1, ct2 - assert_not_equal tag1, tag2 - - decipher = OpenSSL::Cipher.new("aes-128-gcm").decrypt - decipher.auth_tag = tag1 - decipher.key = "x" * 16 - decipher.iv_len = 12 - decipher.iv = "a" * 12 - assert_equal(pt, decipher.update(ct1) << decipher.final) - - decipher.reset - decipher.auth_tag = tag2 - assert_raise(OpenSSL::Cipher::CipherError) { - decipher.update(ct2) << decipher.final - } - - decipher.reset - decipher.auth_tag = tag2 - decipher.iv_len = 10 - decipher.iv = "a" * 10 - assert_equal(pt, decipher.update(ct2) << decipher.final) - end + cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt + cipher.iv = "a" * 12 + cipher.key = "x" * 16 + ct2 = cipher.update(pt) << cipher.final + tag2 = cipher.auth_tag - end - - def test_aes_ocb_tag_len - pt = "You should all use Authenticated Encryption!" - cipher = OpenSSL::Cipher.new("aes-128-ocb").encrypt - cipher.auth_tag_len = 14 - cipher.iv_len = 8 - key = cipher.random_key - iv = cipher.random_iv - cipher.auth_data = "aad" - ct = cipher.update(pt) + cipher.final - tag = cipher.auth_tag - assert_equal(14, tag.size) - - decipher = OpenSSL::Cipher.new("aes-128-ocb").decrypt - decipher.auth_tag_len = 14 - decipher.auth_tag = tag - decipher.iv_len = 8 - decipher.key = key - decipher.iv = iv - decipher.auth_data = "aad" - assert_equal(pt, decipher.update(ct) + decipher.final) - - decipher = OpenSSL::Cipher.new("aes-128-ocb").decrypt - decipher.auth_tag_len = 9 - decipher.auth_tag = tag[0, 9] - decipher.iv_len = 8 - decipher.key = key - decipher.iv = iv - decipher.auth_data = "aad" - assert_raise(OpenSSL::Cipher::CipherError) { - decipher.update(ct) + decipher.final - } - end if has_cipher?("aes-128-ocb") + assert_equal ct1, ct2 + assert_equal tag1, tag2 + end if has_cipher?("aes-128-gcm") private - def new_encryptor(algo) - cipher = OpenSSL::Cipher.new(algo) - cipher.encrypt - key = cipher.random_key - iv = cipher.random_iv - [cipher, key, iv] + def new_encryptor(algo, **kwargs) + OpenSSL::Cipher.new(algo).tap do |cipher| + cipher.encrypt + kwargs.each {|k, v| cipher.send(:"#{k}=", v) } + end end - def new_decryptor(algo, key, iv) + def new_decryptor(algo, **kwargs) OpenSSL::Cipher.new(algo).tap do |cipher| cipher.decrypt - cipher.key = key - cipher.iv = iv + kwargs.each {|k, v| cipher.send(:"#{k}=", v) } end end diff --git a/test/test_digest.rb b/test/test_digest.rb index 51fc03b..028889c 100644 --- a/test/test_digest.rb +++ b/test/test_digest.rb @@ -7,27 +7,21 @@ class OpenSSL::TestDigest < OpenSSL::TestCase def setup @d1 = OpenSSL::Digest.new("MD5") @d2 = OpenSSL::Digest::MD5.new - @md = Digest::MD5.new - @data = "DATA" - end - - def teardown - super - @d1 = @d2 = @md = nil end def test_digest - assert_equal(@md.digest, @d1.digest) - assert_equal(@md.hexdigest, @d1.hexdigest) - @d1 << @data - @d2 << @data - @md << @data - assert_equal(@md.digest, @d1.digest) - assert_equal(@md.hexdigest, @d1.hexdigest) - assert_equal(@d1.digest, @d2.digest) - assert_equal(@d1.hexdigest, @d2.hexdigest) - assert_equal(@md.digest, OpenSSL::Digest::MD5.digest(@data)) - assert_equal(@md.hexdigest, OpenSSL::Digest::MD5.hexdigest(@data)) + null_hex = "d41d8cd98f00b204e9800998ecf8427e" + null_bin = [null_hex].pack("H*") + data = "DATA" + hex = "e44f9e348e41cb272efa87387728571b" + bin = [hex].pack("H*") + assert_equal(null_bin, @d1.digest) + assert_equal(null_hex, @d1.hexdigest) + @d1 << data + assert_equal(bin, @d1.digest) + assert_equal(hex, @d1.hexdigest) + assert_equal(bin, OpenSSL::Digest::MD5.digest(data)) + assert_equal(hex, OpenSSL::Digest::MD5.hexdigest(data)) end def test_eql @@ -43,17 +37,17 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def test_dup - @d1.update(@data) + @d1.update("DATA") assert_equal(@d1.name, @d1.dup.name, "dup") assert_equal(@d1.name, @d1.clone.name, "clone") assert_equal(@d1.digest, @d1.clone.digest, "clone .digest") end def test_reset - @d1.update(@data) + @d1.update("DATA") dig1 = @d1.digest @d1.reset - @d1.update(@data) + @d1.update("DATA") dig2 = @d1.digest assert_equal(dig1, dig2, "reset") end diff --git a/test/test_hmac.rb b/test/test_hmac.rb index dd58e4a..dbde97d 100644 --- a/test/test_hmac.rb +++ b/test/test_hmac.rb @@ -1,33 +1,26 @@ -# coding: UTF-8 # frozen_string_literal: false - require_relative 'utils' class OpenSSL::TestHMAC < OpenSSL::TestCase - def setup - @digest = OpenSSL::Digest::MD5 - @key = "KEY" - @data = "DATA" - @h1 = OpenSSL::HMAC.new(@key, @digest.new) - @h2 = OpenSSL::HMAC.new(@key, "MD5") - end - def test_hmac - @h1.update(@data) - @h2.update(@data) - assert_equal(@h1.digest, @h2.digest) - - assert_equal(OpenSSL::HMAC.digest(@digest.new, @key, @data), @h1.digest, "digest") - assert_equal(OpenSSL::HMAC.hexdigest(@digest.new, @key, @data), @h1.hexdigest, "hexdigest") - - assert_equal(OpenSSL::HMAC.digest("MD5", @key, @data), @h2.digest, "digest") - assert_equal(OpenSSL::HMAC.hexdigest("MD5", @key, @data), @h2.hexdigest, "hexdigest") + # RFC 2202 2. Test Cases for HMAC-MD5 + hmac = OpenSSL::HMAC.new(["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*"), "MD5") + hmac.update("Hi There") + assert_equal ["9294727a3638bb1c13f48ef8158bfc9d"].pack("H*"), hmac.digest + assert_equal "9294727a3638bb1c13f48ef8158bfc9d", hmac.hexdigest + + # RFC 4231 4.2. Test Case 1 + hmac = OpenSSL::HMAC.new(["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*"), "SHA224") + hmac.update("Hi There") + assert_equal ["896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22"].pack("H*"), hmac.digest + assert_equal "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22", hmac.hexdigest end def test_dup - @h1.update(@data) - h = @h1.dup - assert_equal(@h1.digest, h.digest, "dup digest") + h1 = OpenSSL::HMAC.new("KEY", "MD5") + h1.update("DATA") + h = h1.dup + assert_equal(h1.digest, h.digest, "dup digest") end def test_binary_update @@ -38,9 +31,10 @@ class OpenSSL::TestHMAC < OpenSSL::TestCase end def test_reset_keep_key - first = @h1.update("test").hexdigest - @h2.reset - second = @h2.update("test").hexdigest + h1 = OpenSSL::HMAC.new("KEY", "MD5") + first = h1.update("test").hexdigest + h1.reset + second = h1.update("test").hexdigest assert_equal first, second end end if defined?(OpenSSL::TestUtils) diff --git a/test/test_pkey_dsa.rb b/test/test_pkey_dsa.rb index d0ba8ec..a4ccd1d 100644 --- a/test/test_pkey_dsa.rb +++ b/test/test_pkey_dsa.rb @@ -36,6 +36,26 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase end end + def test_sign_verify + data = "Sign me!" + if defined?(OpenSSL::Digest::DSS1) + signature = DSA512.sign(OpenSSL::Digest::DSS1.new, data) + assert_equal true, DSA512.verify(OpenSSL::Digest::DSS1.new, signature, data) + end + + return if OpenSSL::OPENSSL_VERSION_NUMBER <= 0x010000000 + signature = DSA512.sign("SHA1", data) + assert_equal true, DSA512.verify("SHA1", signature, data) + + signature0 = (<<~'end;').unpack("m")[0] + MCwCFH5h40plgU5Fh0Z4wvEEpz0eE9SnAhRPbkRB8ggsN/vsSEYMXvJwjGg/ + 6g== + end; + assert_equal true, DSA512.verify("SHA256", signature0, data) + signature1 = signature0.succ + assert_equal false, DSA512.verify("SHA256", signature1, data) + end + def test_sys_sign_verify key = OpenSSL::TestUtils::TEST_KEY_DSA256 data = 'Sign me!' diff --git a/test/test_pkey_ec.rb b/test/test_pkey_ec.rb index 53aa5a1..e281f80 100644 --- a/test/test_pkey_ec.rb +++ b/test/test_pkey_ec.rb @@ -73,6 +73,20 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase assert_raise(OpenSSL::PKey::ECError) { key2.check_key } end + def test_sign_verify + data = "Sign me!" + signature = P256.sign("SHA1", data) + assert_equal true, P256.verify("SHA1", signature, data) + + signature0 = (<<~'end;').unpack("m")[0] + MEQCIEOTY/hD7eI8a0qlzxkIt8LLZ8uwiaSfVbjX2dPAvN11AiAQdCYx56Fq + QdBp1B4sxJoA8jvODMMklMyBKVmudboA6A== + end; + assert_equal true, P256.verify("SHA256", signature0, data) + signature1 = signature0.succ + assert_equal false, P256.verify("SHA256", signature1, data) + end + def test_dsa_sign_verify data1 = "foo" data2 = "bar" @@ -244,6 +258,10 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase raise end + assert_equal 0x040603.to_bn, point.to_bn(:uncompressed) + assert_equal 0x0306.to_bn, point.to_bn(:compressed) + assert_equal 0x070603.to_bn, point.to_bn(:hybrid) + assert_equal 0x040603.to_bn, point.to_bn assert_equal true, point.on_curve? point.invert! # 8.5 diff --git a/test/test_pkey_rsa.rb b/test/test_pkey_rsa.rb index e211faa..b24f1d5 100644 --- a/test/test_pkey_rsa.rb +++ b/test/test_pkey_rsa.rb @@ -70,6 +70,21 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase end end + def test_sign_verify + data = "Sign me!" + signature = RSA1024.sign("SHA1", data) + assert_equal true, RSA1024.verify("SHA1", signature, data) + + signature0 = (<<~'end;').unpack("m")[0] + oLCgbprPvfhM4pjFQiDTFeWI9Sk+Og7Nh9TmIZ/xSxf2CGXQrptlwo7NQ28+ + WA6YQo8jPH4hSuyWIM4Gz4qRYiYRkl5TDMUYob94zm8Si1HxEiS9354tzvqS + zS8MLW2BtNPuTubMxTItHGTnOzo9sUg0LAHVFt8kHG2NfKAw/gQ= + end; + assert_equal true, RSA1024.verify("SHA256", signature0, data) + signature1 = signature0.succ + assert_equal false, RSA1024.verify("SHA256", signature1, data) + end + def test_digest_state_irrelevant_sign key = RSA1024 digest1 = OpenSSL::Digest::SHA1.new @@ -93,6 +108,13 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase assert(key.verify(digest2, sig, data)) end + def test_verify_empty_rsa + rsa = OpenSSL::PKey::RSA.new + assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") { + rsa.verify("SHA1", "a", "b") + } + end + def test_RSAPrivateKey asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(0), diff --git a/test/test_ssl.rb b/test/test_ssl.rb index 8b4c090..7f0b939 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -656,7 +656,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.hostname = "foo.example.com" ssl.connect - assert_match /^ADH-/, ssl.cipher[0], "the context returned by servername_cb is used" + assert_match (/^ADH-/), ssl.cipher[0], "the context returned by servername_cb is used" assert_predicate ctx3, :frozen? ensure sock.close @@ -667,7 +667,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.hostname = "bar.example.com" ssl.connect - assert_not_match /^A(EC)?DH-/, ssl.cipher[0], "the original context is used" + assert_not_match (/^A(EC)?DH-/), ssl.cipher[0], "the original context is used" ensure sock.close end diff --git a/test/test_x509name.rb b/test/test_x509name.rb index 250f1d0..78da4df 100644 --- a/test/test_x509name.rb +++ b/test/test_x509name.rb @@ -345,13 +345,13 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase def test_hash dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" name = OpenSSL::X509::Name.parse(dn) - d = Digest::MD5.digest(name.to_der) + d = OpenSSL::Digest::MD5.digest(name.to_der) expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24 assert_equal(expected, name_hash(name)) # dn = "/DC=org/DC=ruby-lang/CN=baz.ruby-lang.org" name = OpenSSL::X509::Name.parse(dn) - d = Digest::MD5.digest(name.to_der) + d = OpenSSL::Digest::MD5.digest(name.to_der) expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24 assert_equal(expected, name_hash(name)) end diff --git a/test/utils.rb b/test/utils.rb index 6f3a3c6..0016f5c 100644 --- a/test/utils.rb +++ b/test/utils.rb @@ -10,7 +10,6 @@ rescue LoadError end require "test/unit" -require "digest/md5" require 'tempfile' require "rbconfig" require "socket" diff --git a/tool/ruby-openssl-docker/Dockerfile b/tool/ruby-openssl-docker/Dockerfile new file mode 100644 index 0000000..4ecc393 --- /dev/null +++ b/tool/ruby-openssl-docker/Dockerfile @@ -0,0 +1,92 @@ +FROM ubuntu:14.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + autoconf \ + bison \ + build-essential \ + bzip2 \ + ca-certificates \ + cpio \ + curl \ + file \ + git \ + gzip \ + libreadline-dev \ + make \ + patch \ + pkg-config \ + sed \ + xz-utils \ + zlib1g-dev + +# Supported OpenSSL versions: 1.0.0, 1.0.1, 1.0.2, 1.1.0 +RUN mkdir -p /build/openssl +RUN curl -s https://www.openssl.org/source/openssl-1.0.0t.tar.gz | tar -C /build/openssl -xzf - && \ + cd /build/openssl/openssl-1.0.0t && \ + ./Configure \ + --openssldir=/opt/openssl/openssl-1.0.0 \ + shared debug-linux-x86_64 && \ + make && make install_sw + +RUN curl -s https://www.openssl.org/source/openssl-1.0.1t.tar.gz | tar -C /build/openssl -xzf - && \ + cd /build/openssl/openssl-1.0.1t && \ + ./Configure \ + --openssldir=/opt/openssl/openssl-1.0.1 \ + shared debug-linux-x86_64 && \ + make && make install_sw + +RUN curl -s https://www.openssl.org/source/openssl-1.0.2h.tar.gz | tar -C /build/openssl -xzf - && \ + cd /build/openssl/openssl-1.0.2h && \ + ./Configure \ + --openssldir=/opt/openssl/openssl-1.0.2 \ + shared debug-linux-x86_64 && \ + make && make install_sw + +RUN curl -s https://www.openssl.org/source/openssl-1.1.0.tar.gz | tar -C /build/openssl -xzf - && \ + cd /build/openssl/openssl-1.1.0 && \ + ./Configure \ + --prefix=/opt/openssl/openssl-1.1.0 \ + enable-crypto-mdebug enable-crypto-mdebug-backtrace \ + debug-linux-x86_64 && \ + make && make install_sw + +# Supported libressl versions: 2.1, 2.2, 2.3, 2.4 +RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.1.10.tar.gz | tar -C /build/openssl -xzf - +RUN cd /build/openssl/libressl-2.1.10 && \ + ./configure \ + --prefix=/opt/openssl/libressl-2.1 && \ + make && make install + +RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.2.9.tar.gz | tar -C /build/openssl -xzf - +RUN cd /build/openssl/libressl-2.2.9 && \ + ./configure \ + --prefix=/opt/openssl/libressl-2.2 && \ + make && make install + +RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.3.7.tar.gz | tar -C /build/openssl -xzf - +RUN cd /build/openssl/libressl-2.3.7 && \ + ./configure \ + --prefix=/opt/openssl/libressl-2.3 && \ + make && make install + +RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.4.2.tar.gz | tar -C /build/openssl -xzf - +RUN cd /build/openssl/libressl-2.4.2 && \ + ./configure \ + --prefix=/opt/openssl/libressl-2.4 && \ + make && make install + +# Supported Ruby versions: 2.3 +RUN mkdir -p /build/ruby +RUN curl -s https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz | tar -C /build/ruby -xzf - && \ + cd /build/ruby/ruby-2.3.1 && \ + autoconf && ./configure \ + --without-openssl \ + --prefix=/opt/ruby/ruby-2.3 \ + --disable-install-doc && \ + make && make install + +ONBUILD ADD . /home/openssl/code +ONBUILD WORKDIR /home/openssl/code + +COPY init.sh /home/openssl/init.sh +ENTRYPOINT ["/home/openssl/init.sh"] diff --git a/tool/ruby-openssl-docker/README.md b/tool/ruby-openssl-docker/README.md new file mode 100644 index 0000000..5211dd7 --- /dev/null +++ b/tool/ruby-openssl-docker/README.md @@ -0,0 +1,5 @@ +# ruby-openssl-docker + +Supports OpenSSL 1.0.0t, 1.0.1q, 1.0.2e and LibreSSL 2.1.9, 2.2.5 and 2.3.1. + +Currently only Ruby 2.3.0 is supported. diff --git a/tool/ruby-openssl-docker/init.sh b/tool/ruby-openssl-docker/init.sh new file mode 100755 index 0000000..3fa271c --- /dev/null +++ b/tool/ruby-openssl-docker/init.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +if [[ "$RUBY_VERSION" = "" ]] +then + RUBY_VERSION=ruby-2.3 +fi + +if [[ "$OPENSSL_VERSION" = "" ]] +then + OPENSSL_VERSION=openssl-1.0.2 +fi + +echo "Using Ruby ${RUBY_VERSION} with OpenSSL ${OPENSSL_VERSION}." +export PATH="/opt/ruby/${RUBY_VERSION}/bin:$PATH" +export LD_LIBRARY_PATH="/opt/openssl/${OPENSSL_VERSION}/lib" +export PKG_CONFIG_PATH="/opt/openssl/${OPENSSL_VERSION}/lib/pkgconfig" + +ruby -e ' + newsource = Gem::Source.new("http://rubygems.org") + Gem.sources.replace([newsource]) + Gem.configuration.write + + spec = eval(File.read("openssl.gemspec")) + spec.development_dependencies.each do |dep| + Gem.install(dep.name, dep.requirement, force: true) + end +' + +exec $* diff --git a/tool/sync-with-trunk b/tool/sync-with-trunk new file mode 100755 index 0000000..699508c --- /dev/null +++ b/tool/sync-with-trunk @@ -0,0 +1,105 @@ +#!/bin/sh +set -e + +# Pick changes from Ruby trunk and apply on this repository. +# Note that Git >= 2.5 is required. + +sha1_to_rev() { git show -s --format=format:%B $1 | tail -n1 | grep -Po '(?<=@)[0-9]+'; } +rev_to_sha1() { git log --format=format:%H -n1 --grep '^git-svn-id: .*@'$2' ' $1; } + +echo "#### Step 0. Fetch Ruby trunk" +git remote | grep '^ruby$' >/dev/null || git remote add ruby https://github.com/ruby/ruby.git +git fetch ruby + +BRANCH_EXTRACT=sync/extract +echo "#### Step 1. Sync '$BRANCH_EXTRACT' with 'ruby/trunk'" +[ "$(git branch --list $BRANCH_EXTRACT)" ] || git branch $BRANCH_EXTRACT ruby/trunk +[ ! -d tmp/sync-extract ] && git worktree add tmp/sync-extract $BRANCH_EXTRACT +( + cd tmp/sync-extract + git checkout $BRANCH_EXTRACT + + if [ $(git rev-parse HEAD) = $(git rev-parse ruby/trunk) ]; then + filter_range= + else + old_head=$(git rev-parse HEAD) + echo "Updating '$BRANCH_EXTRACT'... HEAD was $old_head." + graftpoint="$(rev_to_sha1 ruby/trunk $(sha1_to_rev $old_head)) $old_head" + grep "^$graftpoint$" $(git rev-parse --git-common-dir)/info/grafts >/dev/null 2>&1 || + echo "$graftpoint" >> $(git rev-parse --git-common-dir)/info/grafts + git reset -q --hard ruby/trunk + filter_range=$old_head..$BRANCH_EXTRACT + fi + + echo "## Remove unrelated commits" + git filter-branch -f --prune-empty --index-filter ' + git rm --cached -qr --ignore-unmatch . && + git reset -q $GIT_COMMIT -- ext/openssl test/openssl sample/openssl && + git rm --cached -q --ignore-unmatch ext/openssl/depend ext/openssl/openssl.gemspec + ' -- $filter_range ||: + + echo "## Adjust path" + git filter-branch -f --prune-empty --index-filter ' + git ls-files --stage | \ + sed "s:\ttest/openssl:\ttest:" | \ + sed "s:\text/openssl/lib:\tlib:" | \ + sed "s:\text/openssl/sample:\tsample/openssl:" | \ + sed "s:\tsample/openssl:\tsample:" | \ + GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && + mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" + : + ' -- $filter_range ||: + + echo "## Fix author/committer email" + git filter-branch -f --env-filter ' + export GIT_AUTHOR_EMAIL=${GIT_AUTHOR_EMAIL/b2dd03c8-39d4-4d8f-98ff-823fe69b080e/ruby-lang.org} + export GIT_COMMITTER_EMAIL=${GIT_COMMITTER_EMAIL/b2dd03c8-39d4-4d8f-98ff-823fe69b080e/ruby-lang.org} + ' -- $filter_range ||: + + [ "$graftpoint" ] && + sed -ie "/^$graftpoint$/d" $(git rev-parse --git-common-dir)/info/grafts +) + +LASY_SYNC_COMMIT=$(git log --format=format:%H -n1 --grep '^Sync-with-trunk: r') +LAST_SYNC_REV=$(git show --format=format:%B $LASY_SYNC_COMMIT | grep -Po '(?<=^Sync-with-trunk: r)[0-9]+$' | tail -n1) +NEXT_SYNC_REV=$(sha1_to_rev $BRANCH_EXTRACT) + +[ "$LAST_SYNC_REV" = "$NEXT_SYNC_REV" ] && ( + echo "No changes since last sync; aborting." + exit 1 +) + +BRANCH_MERGE=sync/merge-r$NEXT_SYNC_REV +echo "#### Step 2. Rebase '$BRANCH_EXTRACT' on the last sync commit" +[ "$(git branch --list $BRANCH_MERGE)" ] || git branch $BRANCH_MERGE $BRANCH_EXTRACT +[ ! -d tmp/sync-merge ] && git worktree add tmp/sync-merge $BRANCH_MERGE +( + cd tmp/sync-merge + git checkout $BRANCH_MERGE + git reset -q --hard $BRANCH_EXTRACT + git rebase --onto $LASY_SYNC_COMMIT $(rev_to_sha1 $BRANCH_EXTRACT $LAST_SYNC_REV) $BRANCH_MERGE +) + +echo "#### Step 3. Merge '$BRANCH_MERGE' into '$(git rev-parse --abbrev-ref HEAD)'" +commit_message=$( + commits=$(git log --oneline --format='%H %<(61,trunc)%s' $LASY_SYNC_COMMIT..$BRANCH_MERGE) + echo "Merge changes from Ruby trunk r$LAST_SYNC_REV..r$NEXT_SYNC_REV" + echo "" + echo "* ruby-trunk r$LAST_SYNC_REV..r$NEXT_SYNC_REV: ($(echo "$commits" | wc -l) commits)" + echo "$commits" | while read line; do + sha1=$(echo "$line" | cut -f1 -d' ') + cmsg=$(echo "$line" | cut -f2- -d' ') + echo " (r$(sha1_to_rev $sha1)) $cmsg" + done + + echo "" + echo "Sync-with-trunk: r$NEXT_SYNC_REV" +) +if git merge --no-ff --no-commit $BRANCH_MERGE; then + git commit -m "$commit_message" +else + echo "Merge failed; fix conflict and commit with message:" + echo "" + echo "$commit_message" + exit 1 +fi diff --git a/tool/update-gh-pages b/tool/update-gh-pages new file mode 100755 index 0000000..8eb546a --- /dev/null +++ b/tool/update-gh-pages @@ -0,0 +1,22 @@ +#!/bin/sh +set -e + +# Generates RDoc HTML and update gh-pages branch. + +HEAD_DESCRIPTION=$(git describe --dirty --always --abbrev=12) + +[ -d html ] && + rm -r html +rake rdoc + +[ ! -d tmp/gh-pages ] && + git worktree add tmp/gh-pages gh-pages +( + cd tmp/gh-pages + + git rm -r . + cp -r ../../html/* . + rm created.rid js/*.gz # to avoid unnecessary change :x + git add . + git commit -m "Sync with $HEAD_DESCRIPTION" +) |