diff options
author | Michal Rokos <m.rokos@sh.cvut.cz> | 2002-06-04 06:44:42 +0000 |
---|---|---|
committer | Michal Rokos <m.rokos@sh.cvut.cz> | 2002-06-04 06:44:42 +0000 |
commit | 55dda5d77323c8100fa87e681893df83b451e131 (patch) | |
tree | cd2cea7862bed69939cb64d20346a3f975b5a9cc | |
download | ruby-openssl-history-55dda5d77323c8100fa87e681893df83b451e131.tar.gz |
Initial revision
92 files changed, 12750 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e5fc022 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,84 @@ +ChangeLog for +'OpenSSL for Ruby 2' project + + ### CHANGE LOG ### + +Mon, 3 Jun 2002 21:14:34 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * digest.c: new methods added to have the same protocol as Ruby's classes + Digest::digest(name, data) + Digest::hexdigest(name, data) + d.clone() + d.==(other) + * digest.rb: rewritten to eval + +Mon, 3 Jun 2002 17:23:10 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * digest.rb: new (holds predefined Digest classes) + * digest.c: redesigned (introduced runtime loading Digest algs from OpenSSL) + * digest.c: ported to Ruby 1.8 interface + * openssl.rb: added require for digest.rb + +Mon, 3 Jun 2002 13:19:34 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * x509.c renamed to x509cert.c + * x509.h: new (moved all bits related to x509 there) + * x509.c: new (moved init from ossl.c for all x509 related classes there) + +Mon, 3 Jun 2002 13:03:08 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * Moved all Error classes under eOSSLError + +Mon, 3 Jun 2002 12:50:57 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * cipher.h: new (moved bits from ossl.h) + * cipher.h: new MACROS: + OSSLCipherValue + OSSLCipherValuePtr + +Mon, 3 Jun 2002 11:27:46 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * digest.h: new file (moved bits from ossl.h there) + * digest.h: new MACROs: + OSSLWrapDigest + OSSLGetDigest + OSSLDigestValue + +Mon, 3 Jun 2002 10:55:44 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * bn.c: added methods mod_add, mod_sub, mod_sqr + +Mon, 3 Jun 2002 10:46:03 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * bn.c: ported to Ruby 1.8 interface (allocate, enable_super) + +Mon, 3 Jun 2002 10:22:17 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * moved from #ifdef, #ifndef to #if defined() + * renamed all Init_[^o] to Init_ossl_ + +Mon, 3 Jun 2002 09:46:43 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * bn.h: new (all .c will have it's .h to lower ossl.h size and increase modularity) + * bn.h: new MACROs introduced: + OSSLWrapBN - creates instance of BN (DOESN'T DUP THE ARG) + OSSLGetBN - gets BIGNUM with check (DOESN'T DUP THE BIGNUM FROM OBJ) + OSSLBNValue - alias to OSSL_Check_Instance(obj, cBN) + OSSLBNValuePtr - alias to ossl_bn_get_BIGNUM (DUPS THE BIGNUM FROM OBJ) + +Mon, 3 Jun 2002 01:17:07 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * dsa.c: removed MS_CALLBACK. + * dh.c: ditto. + * rsa.c: ditto. + * ssl.c: ditto. + * ossl.c: introduced generic error-class: OpenSSLError + * bn.c: initialize moved from Ruby-space to C-space + * bn.c: reordered method defs by 'man bn' + * bn.c: speed up math. ops by 1 global BN_CTX (dropped all local BN_CTXes and BN_CTX_inits) + +Sat, 1 Jun 2002 13:38:03 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * ossl.h: OSSL2 cannot be compiled if Ruby < 1.7.2 and OpenSSL < 0.9.7 + +Sat, 1 Jun 2002 11:49:40 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * Dropped all #if !defined(NO_*) dependences (stayed just OPENSSL_NO_*) + * Dropped all checks for OPENSSL_VERSION_NUMBER + +Sat, 1 Jun 2002 11:25:32 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * openssl.rb: splitted to openssl/bn.rb, openssl/pkey.rb, openssl/ssl.rb, and openssl/x509.rb + * lib/*: added proped descriptions + * bn.rb: simplified BN#initialize (TODO: move it to C-space) + +Sat, 1 Jun 2002 00:40:59 +0200 -- Michal Rokos <m.rokos@sh.cvut.cz> + * Started work on OSSL2 (Starting version = CVS 2002/04/07) + OSSL2 will support only upcomming Ruby 1.8 and OpenSSL 0.9.7. + @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. @@ -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 GPL +(see the file GPL), 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. @@ -0,0 +1,383 @@ +'OpenSSL for Ruby 2' project +Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz> +All rights reserved. +This program is licenced under the same licence as Ruby. +(See the file 'LICENCE'.) + + + $Id$ + + +['OpenSSL for Ruby' team members] + GOTOU Yuuzou <gotoyuzo@notwork.org> - SSL Socket implementation + Michal Rokos <m.rokos@sh.cvut.cz> - The rest (too long to enumerate), maintainer + +[Contributors] + UNKNOWN <oss-ruby@technorama.net> - BN implementation core + Hynek Rostinsky <hynek.rostinsky@foresta.cz> - Windows platform fixes (strptime mainly) + +[Done] (but not fully tested) + = PKey:: RSA,DSA keys - new, load, export + = X509::Certificate - generating new certs, load, looking inside + = X509::CRL - load, new, looking inside + = X509::Name - new, export, to_a, to_h (hash) + = X509::Revoked - new, looking inside (on parameters) + = X509::Store - new, import trusted certs and CRL, verifiing certs + = Digest::... - various hashes + = X509::Request - Cert requests + = X509::Attribute - as X509Request extensions (not tested) + = X509::Extension - to Certs, CRLs... + = X509::ExtensionMaker - for easy creating new Extensions + = Netscape::SPKI - for requests from NetscapeCommunicators + = Cipher::... - various ciphers + = basic PRNG functions (random generator) for OpenSSL module and class Random + = SSLSocket (merged Gotou Yuuzou's SSLsocket-Ruby project) + = PKCS7 (signing&data_verify is working, rest needs some testing) + = HMAC + = OpenSSL config file parser (part) + = BN (safe bignums) + = Diffie-Hellman + +[To-Do] + = check for memory leaking :-)) + = cleaner code + = examples + = RubyUnit to be used! + = API documentation + = comments to sources!!! + = further functionality to existing + = Std. Extensions, Attributes to be made as Classes? + = AttributeFactory? + = add aliases to to_pem as s_dump s_load to support Marshal module + = CipherFactory? + = autogen random IVs for Ciphers + = PKCS12 + = PKCS8 + = ASN.1 ??? + = BIO ??? + = compat tests for RSA/DSA sign/encrypt + +[Requirements] + Ruby >= 1.6.4 + OpenSSL >= 0.9.6b + +[Instalation] + * Unix like systems: + ruby extconf.rb + make + su root -c make install + + * Windows like systems: + add to %PATH%: c:\openssl\bin (where the dlls lays) + ruby extconf.rb --with-openssl-include=c:\openssl\include --with-openssl-lib=c:\openssl\lib + (or ruby extconf.rb --with-openssl-dir=c:\openssl (NOT TESTED)) + nmake + nmake install + + * Developers can use 'ruby extconf.rb --with-debug' (or --enable-debug) + +[Documentation/API] + Sorry, not done. See 'test' folder's examples and grep C sources for rb_define_*method :-)) + +-------------------------------------------------- +=> XXX - XXX is return value +A <=> B - A is an alias to B +[XXX] - argument XXX is optional +A|B - argument can be A or B +bXXX - XXX is true or false +cXXX - XXX is defined as constant +fXXX - XXX is Fixnum +hXXX - XXX is Hash +nXXX - XXX is Number (Fixnum or Bignum) +oXXX - argument.kind_of?(XXX) => true +sXXX - XXX is String +tXXX - XXX is instance of Time +-------------------------------------------------- +Integer + .to_bn() => BN.new + +OpenSSL:: + BN - Doc TODO! + ::new(...) + --- PRIVATE ---------------------- + .initialize(arg, type="dec") + .from_integer(arg, type="dec") + .from_string(arg, type="dec") + .from_bn(arg, dummy=nil) + .from_s_bin(sBIN) + .from_s_mpi(sMPI) + .from_s_dec(sDEC) + .from_s_hex(sHEX) + --- PUBLIC ----------------------- + .to_s(type="dec") => sDEC + .to_s_bin() => sBIN + .to_s_mpi() => sMPI + .to_s_dec() => sDEC + .to_s_hex() => sHEX + + BNError + + Cipher:: + constants: UNSPEC + modes: ECB, CFB, OFB, CBC + types: EDE, EDE3, BIT40, BIT64 + + BlowFish (allowed: ECB, CFB, OFB, CBC) + Cast5 (ECB, CFB, OFB, CBC) + DES (ECB, EDE, EDE3, CFB, CFB:EDE, CFB:EDE3, OFB, OFB:EDE, OFB:EDE3, CBC, CBC:EDE, CBC:EDE3) + Idea (ECB, CFB, OFB, CBC) + RC2 (ECB, CBC, BIT40:CBC, BIT64:CBC, CFB, OFB) + RC4 (nil, UNSPEC, BIT40) + RC5 (ECB, CFB, OFB, CBC) + ::new([cMode|cType] [,cType|cMode]) + ---------------------------------- + .encrypt(sPassword [, sInitVector]) => self + .decrypt(sPassword [, sInitVector]) => self + .update(sData) => s(En|De)crypted + .<< <=> .update + .cipher() => s(En|De)cryptedFinal + + CipherError + + Config + ::new(sFilename) - dispatches .load + ::load(sFilename) + ---------------------------------- + .get_value(sSection|nil, sKey) => sValue + .get_section(sSection) => hSection + + ConfigError + + Digest:: + MD2 + MD4 + MD5 + MDC2 + RIPEMD160 + SHA + SHA1 + DSS + DSS1 + ::new([sData]) + ---------------------------------- + .update(sData) => self + .<< <=> .update + .digest() => sDigestFinal + .hexdigest() => sHEXDigestFinal + .inspect <=> .hexdigest + .to_s <=> .hexdigest + + DigestError + + HMAC + ::new(sKey, oDigest::ANY) => self + ---------------------------------- + .update(sData) => self + .<< <=> .update + .hmac() => sHMACFinal + .hexhmac() => sHEXHMACFinal + .inspect <=> .hexhmac + .to_s <=> .hexhmac + + HMACError + + Netscape:: + SPKI + ::new([sPEM]) + ---------------------------------- + .to_pem() => sPEM + .to_s <=> .to_pem + .to_text() => sHumanReadable + .public_key() => oPKey::ANY + .public_key=(oPKey::ANY) => oPKey::ANY + .sign(oPKey::ANY, oDigest::ANY) => self + .verify(oPKey::ANY) => bResult + .challenge() => sChallenge + .challenge=(sChallenge) => sChallenge + + SPKIError + + PKCS7:: + constants: + type: SIGNED, ENVELOPED, SIGNED_ENVELOPED + + PKCS7 + ::new(cType|sPEM) + ---------------------------------- + .cipher=(oCipher::ANY) => oCipher::ANY + .add_signer(oPKCS7::Signer, oPKey::ANY) => self + .signers() => Array of PKCS7::Signer + .add_recipient(oX509::Certificate) => self + .add_certificate(oX509::Certificate) => self + .add_crl(oX509::CRL) => self + .add_data(sData [, bDetached]) => self + .verify_data(oX509::Store [, sDetachedData]) => bResult, yields PKCS7::Signer + .decode_data(oPKey::ANY, oX509::Certificate) => sData + .to_pem() => sPEM + .to_s <=> .to_pem + + Signer + ::new(oX509::Certificate, oPKey::ANY, oDigest::ANY) + ---------------------------------- + .name() => X509::Name + .serial() => fSerial + .signed_time() => tTime + + PKCS7Error + + PKey:: + PKeyError + + DH + ::new((fLen|sPEM) [, fGenerator=2]) - dispatches .new_from_pem or .generate + ::new_from_pem(sPEM) + ::generate(fLen, fGenerator) - yields |p,n| + ::new_from_fixnum <=> ::generate + ---------------------------------- + .public?() => bResult + .private?() => bResult + .to_text() => sHumanReadable + .export() => sPEM + .to_pem <=> .export + .public_key() => oPKey::ANY + + DSA + ::new([fKeyLen|sPEM [, sPassword]]) - dispatches .new_from_pem or .generate + ::new_from_pem(sPEM [, sPassword]) + ::generate(fKeyLen) - yields |p,n| + ::new_from_fixnum <=> generate + ---------------------------------- + .public?() => bResult + .private?() => bResult + .to_text() => sHumanReadable + .export([oCipher::ANY [, sPassword]]) => sPEM + .to_pem <=> .export + .public_key() => oPKey::DSA + .to_der() => sDER + .sign(oDigest::ANY, sData) => sSig + .sign_digest(sDigest) => sSig + .verify(oDigest::ANY, sData, sSig) => bResult + .verify_digest(sDigest, sSig) => bResult + + DSAError + + RSA + ::new([fKeyLen|sPEM [, sPassword]]) - dispatches .new_from_pem or .generate + ::new_from_pem(sPEM [, sPassword]) + ::generate(fKeyLen) - yields |p,n| + ::new_from_fixnum <=> generate + ---------------------------------- + .public?() => bResult + .private?() => bResult + .to_text() => sHumanReadable + .export([oCipher::ANY [, sPassword]]) => sPEM + .to_pem <=> .export + .public_key() => oPKey::ANY + .public_encrypt(sData) => sEnc + .public_decrypt(sEnrypted) => sData + .private_encrypt(sData) => sEnc + .private_decrypt(sEncrypted) => sData + .to_der() => sDER + .sign(oDigest::ANY, sData) => sSig + .verify(oDigest::ANY, sData, sSig) => bResult + + RSAError + + Random + .seed(sSeed) => sSeed + .load_random_file(sFilename) => bResult + .write_random_file(sFilename) => bResult + .random_bytes(fLen) => sRandom + .egd(sUNIXSocketPath) => bResult + .egd_bytes(sUNIXSocketPath, fLen) => bResult + + RandomError + + SSL:: - Doc TODO! + + Error + + X509:: + Attribute + ::new(arg) - dispatches "new_from_#{arg.type.name.downcase}" + ::new_from_string("oid = value") + ::new_from_array(["oid", "value"]) + ::new_from_hash({"oid"=>"oid", "value"=>"val"}) + ---------------------------------- + + AttributeError + + Certificate - Doc TODO! + .to_s <=> .to_pem + + CertificateError + + CRL - Doc TODO! + .to_s <=> .to_pem + + CRLError + + Extension - Doc TODO! + ::new is DISABLED! + ---------------------------------- + .to_s => string as "oid = critical, value" + .to_a => ary as ["oid", "value", critical], critical as bool + .to_h => hash as {"oid"=>"oid", "value"=>"val", "critical"=>bool} + + ExtensionFactory - Doc TODO! + ::new(...) + ---------------------------------- + .create_extension(*arg) + .create_ext_from_string(str) + .create_ext_from_ary(ary) => X509::Extension, ary as ["oid", "value", critical], critical as bool + .create_ext_from_hash(hash) + + ExtensionError + + Name - Doc TODO! + ::new(arg) dispatches "new_from_#{arg.type.name.downcase}" + ::new_from_string(str) => self, str as "/A=B/C=D/E=F" + ::new_from_array(ary) => self, ary as [["A","B"],["C","D"],["E","F"]] + ::new_from_hash(hash) => self, hash as {"A"=>"B","C"=>"D","E"=>"F"} + ---------------------------------- + .to_s => str as "/A=B/C=D/E=F" + .to_a => ary as [["A","B"],["C","D"],["E","F"]] + .to_h => hash as {"A"=>"B","C"=>"D","E"=>"F"} + + NameError + + Request - Doc TODO! + .to_s <=> .to_pem + + RequestError + + Revoked + ::new() + ---------------------------------- + serial() => nSerial + serial=(nSerial) => nSerial + time() => tRevoked + time=(tRevoked) => tRevoked + extensions() => Ary(X509::Extension) + extensions=(ary) => Ary(X509::Extension) + add_extension(oExtension) => oExtension + + RevokedError + + Store - Doc TODO! + + StoreError +-------------------------------------------------- + +[Examples] + There are some braindead in 'test' directory + +[Note] + All code is under development - API/method names can change + All feedback, bug reports, requests are welcomed! + + + Enjoy! + + Michal <m.rokos@sh.cvut.cz> + @@ -0,0 +1,45 @@ +TODO list for +'OpenSSL for Ruby 2' project + +----------------------------------------------------------------------- +OpenSSL:: + * Move all Errors as child of OpenSSLError + * Implement Ruby 1.8 style of creating instances (see StringIO) + * Detailed object inspection (ie. all params for RSA) + * How to support HW crypto engines? + * Rename ANY classes to uniq ones + * Support more detailed requies (like: require 'openssl/crypto', require 'openssl/ssl')? + * WRITE TEST CASES! + * Use RDoc and write documentation to sources? + * Prune openssl_missing.[ch] + * Add SMIME, PKCS#8 + * Is there any need to implement BIO? (Wrap BIO to Ruby's IO?) + + BN:: + [DONE] Move initialize to Cspace + [DONE] Rethink type= (String or Integer?) [Integer!] + [DONE] Speed up math. ops by the Thread uniq BN_CTX [No - 1 global is OK (Ruby is not thread safe => Don't be afraid of this one)] + [DONE] Convert BN#to_s to Ruby-like behaviour + * introduce BN#pack for MPI, BIN (and call it from BN#to_s and BN#initialize)? + + Cipher:: + * Use Factory (Cipher.new("DES_EDE3_CBC"))? + + Conf:: + * Port it to new (0.9.7) interface + + Digest:: + * Use Factory (Digest.new("SHA1"))? + + HMAC:: + * Move it to Digest module? + + PKey:: + * Make it as class? + * Factory? RSA#initialize -> PKey.new("RSA")? + + SSL:: + * Support more conns via 1 SSL_CTX? + + X509:: + * Rethink X509::Attribute, and X509::Extension diff --git a/examples/01cert.pem b/examples/01cert.pem new file mode 100644 index 0000000..15a8679 --- /dev/null +++ b/examples/01cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMTCCAhmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBKMQswCQYDVQQGEwJDWjEO +MAwGA1UEChMFUm9rb3MxCzAJBgNVBAMTAkNBMR4wHAYJKoZIhvcNAQkBFg9taWNo +YWxAcm9rb3MuY3owHhcNMDEwOTA5MDgzNDExWhcNMDIwOTA5MDgzNDExWjBOMQsw +CQYDVQQGEwJDWjEOMAwGA1UEChMFUm9rb3MxDzANBgNVBAMTBk1pY2hhbDEeMBwG +CSqGSIb3DQEJARYPbWljaGFsQHJva29zLmN6MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJpui4aF44GABGSm5kYglIVjGOfZiaOpx8sgAeWHY/cieHF3fcDCy9 +eUtv1BQeGOruiOuGLUk818TO5Ypsa45lc3mx5Inhusmsm6BJePNIK/pf+vYoskwH +EaGOCg3kMvyylAzsr2nqP7hYm09nSlU96OyHJVssUbUFVaXQ7wBNnQIDAQABo4Gh +MIGeMAkGA1UdEwQCMAAwHQYDVR0OBBYEFHxeY4K03AqIrFcjnWt0xO+SDE8OMHIG +A1UdIwRrMGmAFIsvMx24Ivqj37PFReOHMRnUwQSOoU6kTDBKMQswCQYDVQQGEwJD +WjEOMAwGA1UEChMFUm9rb3MxCzAJBgNVBAMTAkNBMR4wHAYJKoZIhvcNAQkBFg9t +aWNoYWxAcm9rb3MuY3qCAQAwDQYJKoZIhvcNAQEEBQADggEBAAk1AjbFJVir3a1V +k1eL3D4EKpk0/q0iK30L5jCPHKAQUSROVGbxb3t6AgdHR+eEnyaH9jHr/cgqMV4B +ce0mkDUoS3r1NvBIiLIO49Uhs/RVv0YVz2cVK/PSVNLEbWwmSv/oZNJN0bzlfhkk +qIulod3M3Vpr+tw0MCGjGHF7bfJjnvecsinJtlh1dZNElfdq/hZBD0nElKBTI5+J +UyN91hpPMPOCY48CpE7/zDneUHnLJyZ9Dxo32XPBeqTX4shaBaNZ/nlISzoHAR9h +HhcshzQx5WWgdOKnXkpZgqxizacM4Kl4xUpChUz9JhmQDydYC2LpFo8L0zfeLfnW +XuWJtfA= +-----END CERTIFICATE----- diff --git a/examples/01crl.pem b/examples/01crl.pem new file mode 100644 index 0000000..0c9a48f --- /dev/null +++ b/examples/01crl.pem @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBpjCBjzANBgkqhkiG9w0BAQQFADBKMQswCQYDVQQGEwJDWjEOMAwGA1UEChMF +Um9rb3MxCzAJBgNVBAMTAkNBMR4wHAYJKoZIhvcNAQkBFg9taWNoYWxAcm9rb3Mu +Y3oXDTAxMDkyMDIxMTAwMFoXDTAxMTAyMDIxMTAwMFowFDASAgEBFw0wMTA5MjAy +MTA5MDZaMA0GCSqGSIb3DQEBBAUAA4IBAQAHyNipK4xxUoNy0ui38TdRUOcmuN7o +gS0AaI+aEeio4NbRL4+J9qrm0Yf3HfE/gMzAHpBN7Jd5ABeLuRQVeztYCM4d9mtA +CwYcnVIm0s0EzMpM5SSghmKHsUZ5xQOAXuHDjdYXyCgDrTs+xPCFAPabiwhnXAyq +29IoX4V4fo0dyPgMgCZu93a3qdnpEAmdttgfdJrwEXlLIWkxhR88XXVo3ErGMsJ2 +zy2Zjt+Vm5uIHDCGMB0CNUv6zCik/v+9H1VyCmhmz/XfznJu7Fzt6y8Y/tXNLhY9 +i02Wr+IBLWL0PzPy6FOnr6EsH0gLrXO54Xb/RNn4+xDD9P7pe0ZakAgU +-----END X509 CRL----- diff --git a/examples/01key.pem b/examples/01key.pem new file mode 100644 index 0000000..913ed6f --- /dev/null +++ b/examples/01key.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,E6D6ED83B588F034 + +QhTVi7BH3bBdJsxIjfStLVNbGZtrHaAqwCKrgNq8eHmeFh/GQmeh9wAgbVgdeBli +c49UoWug+k7XCaSKO/DQMzePavyEXVirYJGrI5iGARgYOH70gMdJdQfa8K8EZhB7 +zkv0lMCuoMOa/46ItXNS9DKkdr6RRdmPU8C+WCjfgNdQIcAuYZLeiSnvpDCj8a5t +H15ZyNxySzgpTw7OS4Z1PRETHcUWlNOs/vvNqRBtrmypw/P8VFvjXipWFA+hV6t5 +5CEHsMdXIrpb7nJ76OoyCSu9IO7FnftjiP+DuHcdJdTPJ2c7X1vXZ33QJwPqD4ci +e042ZgBfdjQqOthRATX5x+uAdqokbfqDhlt+xUkUvyZsKlfuMpZr4ATqeH5SFcUt +5vx+5oAFU0vvfnEw9PFt/8O3JbQVy/yCle9RSkVdecdpjsLgW/KzHedlc+TiNiac +K7LQqfTv99iLq5tdmQQI8tgWX/vuzTyfAXOHiVo+LnB1m5MWjgjr93Ssrf1zCThu +2EbWEDRbFENh8TRAFri8WCHay4U4/Zk7LzHOumE5sTLcoNfKUsykR/s6PI8W2Z/a +B0uvJ/SUgTiWYEb56oNzOSCJBWdtusjXMskb2GOW8wwGJwS1Vq1sT4suh7NB13dZ +DL56lKRtobdyHTNs8gylf5zgrzfJuChWXRLS0t12gyDVvLcGOXRiF2pdaape9F5V +v8dwkTictvsxFIacBo2/wk/xW9mSlQxpHSjcHLneEV+t4vd21Q5LhjmWlQUG9hS3 +G8e0BD7BKqTcUuBQ8/9bh8VAOvsZjFy26fRMDXuuWV1AABvWyAq3IQ== +-----END RSA PRIVATE KEY----- diff --git a/examples/01pub.pem b/examples/01pub.pem new file mode 100644 index 0000000..d3e9faa --- /dev/null +++ b/examples/01pub.pem @@ -0,0 +1,5 @@ +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMmm6LhoXjgYAEZKbmRiCUhWMY59mJo6nHyyAB5Ydj9yJ4cXd9wMLL15 +S2/UFB4Y6u6I64YtSTzXxM7limxrjmVzebHkieG6yayboEl480gr+l/69iiyTAcR +oY4KDeQy/LKUDOyvaeo/uFibT2dKVT3o7IclWyxRtQVVpdDvAE2dAgMBAAE= +-----END RSA PUBLIC KEY----- diff --git a/examples/01req.pem b/examples/01req.pem new file mode 100644 index 0000000..a3400af --- /dev/null +++ b/examples/01req.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBjTCB9wIBADBOMQswCQYDVQQGEwJDWjEOMAwGA1UEChMFUm9rb3MxDzANBgNV +BAMTBk1pY2hhbDEeMBwGCSqGSIb3DQEJARYPbWljaGFsQHJva29zLmN6MIGfMA0G +CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJpui4aF44GABGSm5kYglIVjGOfZiaOpx8 +sgAeWHY/cieHF3fcDCy9eUtv1BQeGOruiOuGLUk818TO5Ypsa45lc3mx5Inhusms +m6BJePNIK/pf+vYoskwHEaGOCg3kMvyylAzsr2nqP7hYm09nSlU96OyHJVssUbUF +VaXQ7wBNnQIDAQABoAAwDQYJKoZIhvcNAQEEBQADgYEAVovoK9kVmu/sPWQMS/8H +uDbLDWfWRnVOxZlVGnx5+ICryv8BsdYSezcF7+TYAnpypypgX7VzD7OvdO5DyYWN +HnJYpW7M0ZnjgqAQi1dKvM+byj17cew3kyPbeljBf8By4PGvXbQxqzSASUOPTPCl +XYy5y2lEGsY3jpj5IwITtvY= +-----END CERTIFICATE REQUEST----- diff --git a/examples/02cert.pem b/examples/02cert.pem new file mode 100644 index 0000000..2e948e8 --- /dev/null +++ b/examples/02cert.pem @@ -0,0 +1,69 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: md5WithRSAEncryption + Issuer: C=CZ, O=Rokos, CN=CA/Email=michal@rokos.cz + Validity + Not Before: Sep 27 14:51:51 2001 GMT + Not After : Sep 27 14:51:51 2002 GMT + Subject: C=CZ, O=Rokos, CN=Pokus02 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:c1:aa:c0:e9:ac:d1:49:66:21:01:97:13:51:aa: + ff:df:0d:ca:e4:cf:5d:d6:f4:e9:2f:64:89:51:cb: + e8:59:bf:8e:dd:20:21:63:e3:75:a5:ad:35:cb:e5: + da:c6:ee:12:6d:41:f7:75:37:3a:31:94:a0:b3:3f: + c9:69:b6:79:22:ee:03:f0:af:93:fa:21:6f:6c:c5: + af:e6:20:3e:5b:2c:fd:03:c1:70:29:b2:da:17:8e: + d9:4c:5a:2b:30:8b:08:f1:74:90:0d:31:dd:f8:ed: + 06:01:3a:23:39:42:56:e7:59:00:9c:79:b5:27:8a: + 80:b6:6d:90:81:22:d7:0d:59 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 21:FC:47:57:07:F1:9D:E0:F7:7D:43:24:A1:20:04:F2:1E:B9:D6:C9 + X509v3 Authority Key Identifier: + keyid:8B:2F:33:1D:B8:22:FA:A3:DF:B3:C5:45:E3:87:31:19:D4:C1:04:8E + DirName:/C=CZ/O=Rokos/CN=CA/Email=michal@rokos.cz + serial:00 + + Signature Algorithm: md5WithRSAEncryption + 32:d8:78:d3:37:bb:aa:77:4d:ff:a5:e1:1d:57:4b:06:5f:f3: + 25:62:e8:01:5e:25:c8:d9:4f:3e:02:87:0c:98:56:f8:83:7a: + cd:b5:2a:99:80:19:43:32:6b:44:5f:78:00:3c:86:aa:3d:5b: + 51:ac:48:6e:84:c2:41:a1:a1:e4:dc:b0:17:9d:7d:09:b5:2a: + 59:34:df:72:34:6d:8d:80:cf:2a:14:07:41:f1:9c:13:ea:ca: + 66:c6:00:75:fa:be:5a:1b:ec:58:b5:ec:e0:1e:0f:49:12:d4: + f8:01:3e:44:26:6e:f5:fe:f6:56:93:a2:38:26:81:a0:2b:c2: + 54:b7:6a:77:01:cc:5f:7e:98:db:7a:39:15:87:5f:b1:b2:e8: + 7d:19:3d:8b:97:ae:ab:03:a5:76:15:e2:6d:28:e1:a3:4d:a1: + 4f:a0:69:01:0d:03:bf:2f:b6:ec:ae:60:2a:d7:e3:cb:94:4c: + 66:69:e6:8b:4a:50:49:31:c2:3c:e1:d9:bd:ac:bb:11:5a:53: + 10:e4:01:67:5f:16:55:c0:eb:32:15:51:ca:68:a8:3e:5c:51: + c5:09:e2:ac:7f:25:67:8a:47:59:6a:9b:03:52:b8:b8:d8:35: + 77:2d:72:6a:08:fc:b4:8e:9b:4e:29:a3:8d:e0:b5:83:cf:5c: + 6b:c5:33:69 +-----BEGIN CERTIFICATE----- +MIIDEjCCAfqgAwIBAgIBAzANBgkqhkiG9w0BAQQFADBKMQswCQYDVQQGEwJDWjEO +MAwGA1UEChMFUm9rb3MxCzAJBgNVBAMTAkNBMR4wHAYJKoZIhvcNAQkBFg9taWNo +YWxAcm9rb3MuY3owHhcNMDEwOTI3MTQ1MTUxWhcNMDIwOTI3MTQ1MTUxWjAvMQsw +CQYDVQQGEwJDWjEOMAwGA1UEChMFUm9rb3MxEDAOBgNVBAMTB1Bva3VzMDIwgZ8w +DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMGqwOms0UlmIQGXE1Gq/98NyuTPXdb0 +6S9kiVHL6Fm/jt0gIWPjdaWtNcvl2sbuEm1B93U3OjGUoLM/yWm2eSLuA/Cvk/oh +b2zFr+YgPlss/QPBcCmy2heO2UxaKzCLCPF0kA0x3fjtBgE6IzlCVudZAJx5tSeK +gLZtkIEi1w1ZAgMBAAGjgaEwgZ4wCQYDVR0TBAIwADAdBgNVHQ4EFgQUIfxHVwfx +neD3fUMkoSAE8h651skwcgYDVR0jBGswaYAUiy8zHbgi+qPfs8VF44cxGdTBBI6h +TqRMMEoxCzAJBgNVBAYTAkNaMQ4wDAYDVQQKEwVSb2tvczELMAkGA1UEAxMCQ0Ex +HjAcBgkqhkiG9w0BCQEWD21pY2hhbEByb2tvcy5jeoIBADANBgkqhkiG9w0BAQQF +AAOCAQEAMth40ze7qndN/6XhHVdLBl/zJWLoAV4lyNlPPgKHDJhW+IN6zbUqmYAZ +QzJrRF94ADyGqj1bUaxIboTCQaGh5NywF519CbUqWTTfcjRtjYDPKhQHQfGcE+rK +ZsYAdfq+WhvsWLXs4B4PSRLU+AE+RCZu9f72VpOiOCaBoCvCVLdqdwHMX36Y23o5 +FYdfsbLofRk9i5euqwOldhXibSjho02hT6BpAQ0Dvy+27K5gKtfjy5RMZmnmi0pQ +STHCPOHZvay7EVpTEOQBZ18WVcDrMhVRymioPlxRxQnirH8lZ4pHWWqbA1K4uNg1 +dy1yagj8tI6bTimjjeC1g89ca8UzaQ== +-----END CERTIFICATE----- diff --git a/examples/02key.pem b/examples/02key.pem new file mode 100644 index 0000000..e2e8e08 --- /dev/null +++ b/examples/02key.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,C4F9A886D458E50C + +tdvJ2y/Eu67lWI+E6UJHdEKSkmW/h2QQ5gQYtqKgQ0bavN4r6HOB7mH1PVLo+HyD +Q/aeOlWXQe0O5FbmufZ6OSZjyKuHoSyiFCwz9uuU9vAzd8SwT7dXG3ci1bKuvcP+ +0ApbTsdbL6CSHBqTw9gc7IUwUzVEa8d0Cz4skGuR4GqI3zTD/mEqmV9OBM9cFH56 +Wc+YF9BVRw4Yfl4go8wrhukYR+ixxdf5fuRCgdtho77kzm7nOhywEErAE0k44aC0 +lYN+Te9ukzlTe8m2vvd8wZQTL2WUsqN9PNP23oFDQgChPYfQ/FRt4y2Cym9kUK+J +aW+AADdtO7fgDOqXPlpFoc/e2HdCF2Ayv3zER+llIcoxgH+NC0vBW7KMTpsaFeoz +BUowS7t/uYtn+cxOmLUDhPi7cIH/Azjef6XC+puA7oraa6AlmAB7LCTSm1Kptxu4 +LEZrICj1kSnJBsOjcwiMd3Z2Hm9ORxVbj40T6iaG1/waqtdUZRzFUlXABAD/8MTc +RanKi9cZw7nwiTYWtZ8NRhvcCddcxQLrODoCygSeYBB7icbh9XO1hpTwpJNmz+mn +YZVsWkGPN4EUJBKHsgtzuKV3gizZfTfX6mZvE25gxobs+NJuOM8Awekp95N+zYta +Yp44ex/OaQhHNb9M/zwpR0z2K5BE9p+JkfRs3SwWJ5BZzJJcrkF6h0dUBbjQ/1p0 +NHERUPhsLBUEa6ayLcdopf1MqBnyrJljaKrc+8BCneVuwQVxW+ZqFxcPF4qYtrnf +6z3vF4cC8M6FAc0mIEkS4lFB5M4SDHm+Txgof7l6MLyxZAF+QzLdDA== +-----END RSA PRIVATE KEY----- diff --git a/examples/02req.pem b/examples/02req.pem new file mode 100644 index 0000000..3fa9be4 --- /dev/null +++ b/examples/02req.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBbjCB2AIBADAvMQswCQYDVQQGEwJDWjEOMAwGA1UEChMFUm9rb3MxEDAOBgNV +BAMTB1Bva3VzMDIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMGqwOms0Ulm +IQGXE1Gq/98NyuTPXdb06S9kiVHL6Fm/jt0gIWPjdaWtNcvl2sbuEm1B93U3OjGU +oLM/yWm2eSLuA/Cvk/ohb2zFr+YgPlss/QPBcCmy2heO2UxaKzCLCPF0kA0x3fjt +BgE6IzlCVudZAJx5tSeKgLZtkIEi1w1ZAgMBAAGgADANBgkqhkiG9w0BAQQFAAOB +gQCtNmh1fd1M/pm1ybiTdWh2iI8GT01Azff5D5Hxk/WbuZS0U/v0auycrEaBj1w0 +hncaYnN8+fdSACbOBN5efni7FiClvx7COuJ3+qJmB/Cnv4j5ielyydUhkeRQ81Gq +EooiyAXhDzVcCfjO8c5Gk2WkAfuQWf9h/7ZSRlVv72OcWw== +-----END CERTIFICATE REQUEST----- diff --git a/examples/0cert.pem b/examples/0cert.pem new file mode 100644 index 0000000..cb383c7 --- /dev/null +++ b/examples/0cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIBADANBgkqhkiG9w0BAQUFADAtMQswCQYDVQQGEwJDWjEN +MAsGA1UEChMEUnVieTEPMA0GA1UEAxMGUnVieUNBMB4XDTAxMTEwODExNTkxMloX +DTAzMTEwODExNTkxMlowLTELMAkGA1UEBhMCQ1oxDTALBgNVBAoTBFJ1YnkxDzAN +BgNVBAMTBlJ1YnlDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOV8 +Vc1b83eGNdpfM4IFbEY8h+nx8XU9QMJ5EnWiOzSqrOZtG/R6rFP/jxDS+rDgh2FF +lzkdrRpElTVgCePEddrhSMj//8IprCN79sykiTILNAi0Wq9YHUCzADX+ViC0hkyS +rUs4kD7qr3psfp1imARD2IE2gVhs5+fuyZlNGg1xXjsEfRMX6Uf57q7NBh3edjWh +EfyKku7l51Z6X1r8tVPguyeeoZsnOPpQGKutchEskBwHjGwu0xBj9NJFKrIr9GBT +IsKL4iTG31ReZnLQrtUHlCmOQnfId3HBk+zzD3Z8db47KaUgaEFpyitl9l+1HzyU +3CjJzRmJZrYXycE3eYUCAwEAAaOBuTCBtjAPBgNVHRMECDAGAQH/AgEAMC0GCWCG +SAGG+EIBDQQgFh5HZW5lcmF0ZWQgYnkgT3BlblNTTCBmb3IgUnVieS4wHQYDVR0O +BBYEFFhFcoVGe0cAv4c8QexFYb1YqCtoMFUGA1UdIwROMEyAFFhFcoVGe0cAv4c8 +QexFYb1YqCtooTGkLzAtMQswCQYDVQQGEwJDWjENMAsGA1UEChMEUnVieTEPMA0G +A1UEAxMGUnVieUNBggEAMA0GCSqGSIb3DQEBBQUAA4IBAQDQhltRNlB8KyhgyXNZ +qYTupPvsoj1NDbXQT1nyNirOjGSqRYxcOshL2vfhgIESs5jQdcR+B41Oj3N+MEkI +rPyXIcbI1MSjB/W0pHVDpxf/++cZ7OXUhhF0f25+ZO9qnWl4mdKH3XNWJM4lnzGn +Q8UDs9aJr8FBh8puo4u27I844K6GoIWyAzAwyyDmoaCRpkt5YSJua07OVsVTUdpj +5z9rNOjmymuA82VpKvzsxFev7z4UragXRBX92OCRIzIE5Ne8zGLFTjKGt5us7D79 +bYqjOv2ax0bOj8UgTw0Fyl5mKao00uTz4nZyjhxuE7qUeaFNnoFbzQL6isZRBsST +1eph +-----END CERTIFICATE----- diff --git a/examples/0key.pem b/examples/0key.pem new file mode 100644 index 0000000..0ef2deb --- /dev/null +++ b/examples/0key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,26F5AE5B49B3A1FF + +cOkuSZ5jeT5T+vmiBUeBIonGBzmO3HzYMmRN2HoKKLbYHnpcaH/84mA876cZIGDB +Fpu36e1Q5W/IzEO1oWdav0gG6jG8fGIZnZBsyYwkDWayg+p+vGgEUAj6TAnwvK/f +qjAsMfbBJVIhvsMekmuz490A5LZ3tA1zgZBpl6i358JXbDidp1YreRsa2KYD2hSM +dNBNWvRksm8n9/px8U6fgKYlywV267FtmJ9e0dsMLt3gtni2WYU4uLhZfS2RvgwP +a5mwo1DcepbZ4UohCCjWPCy3UYU43A/7ugHaE2+XcWi0gh+pLOTKL7dP84PKL1Az +u3h4fiszYfkLwodUHbODIilAAU+orQv89jPJK9jjlmVjQc0jRMowkfuBvzqwLPGY +JwIjKuIeTR+uh6MEp/m4aBUiNyxfPJZ4lAgJB7mU/9aKPD3qpWvniDg12NWBeniQ +VfHI3qWvR6J2s7idBgEk+H2GeFsIXONpWfbHuqoDITjm80407ms03EbwItMynn51 +sTjY2fLTCkQik5sgnuqikNczi7EnFga7JVkbLa+ltGfBSyqAZIlNpbapr+vEtefl +ucW+S4fJ9duwFg+N/syBIUeMji2EJHNhGa7jt+1JU/Cf8ise/5oets+C/bzAMwyy +jMH4QP4rwoAhGNf7gpw4MYLek2n4LX2F4AN2cTgWj4yt94+GWKgLf5cuvlNG7SfI +Ha5akGWDaG5pfmTdfLM71iDs3S8KXNLQ2gHNYlVP2hvc9rMEZo9P8iGnxIxNKskr +C62x47FGmgsr0ShSTNTG58dqFgaVLb0On53LbD6tsgnYdtPoqC8/Z/agqK0mNIwb +lLb/g0LLAoxqV6gOUNFZ92FEXZ9iUue1YFkHHaLfSe/xeKVoSO2XqaxIhsNMp5AK +cER7/qkvxmb+BII+X5cFSjt/tOcjkCE3FXkyP8PVxlFYF6mY4BuGpKBl7wPFS8kf +Mjp+9kZvXBJm3Awfn0M7ZXF4dkMOyRdjbOfhHkqREvw2Xty1+Sa3RarUM3w3jU1v +DUg3vg5uRs0rVUZeK+HQHI7z3PaLVKMoESoDfSCI7/7kiZaBpS5hGBmMz26Aa+Lg +5CmwM+LMqRE7BcrRo8wiXMbkbh1xCeOqfcQBopii6ZC1xtQeXP3+j19BzA0dlnHV +N01UD3tuQ8cfji79atEX03hIzpLB9q75yCs+C3SDdEJzft/S53jFQjuG1SWpOxDa +xo6lKXjX71X1UUh1gAmSKMkQnrfMzw1FTHtxetwaK5s6LFmmqPgi1lYEYai3zVP/ +rwG2M/aSlvsaC7GfJnpKdfRONXiGEwggDLKodt6sRvtZQ5uddUQnyvBxDyZpV9K6 +JF0/FQXsYwgjFJq7krHIwr4ExvQxb3s2GRtKokwDd+WH6f53oFRgOpWpsWLBYyS3 +uoYSRL8AcTLIEIsco8dFLCwTXmubo5wMhpsjCS0vp4QJ//fo9zLKiOG+nn5p9Yfa +ZN5R+rEUjAchjP/W4poH/tFLIRXQmIKN6K5fTfY30EFnA08l02GpSAW1wzgaa/08 +NTY5VdXGW+vaWT+CNmZ1TB///aqcC50JztiitNecF1oKnm8Ng80QMXh/sEoLWmm2 +-----END RSA PRIVATE KEY----- diff --git a/examples/1cert.pem b/examples/1cert.pem new file mode 100644 index 0000000..34cfc3b --- /dev/null +++ b/examples/1cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDTCCAfWgAwIBAgIBATANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJDWjEN +MAsGA1UEChMEUnVieTETMBEGA1UEAxMKUkEgT2ZmaWNlcjAeFw0wMTExMDgxMjA0 +NDNaFw0wMjExMDgxMjA0NDNaMDExCzAJBgNVBAYTAkNaMQ0wCwYDVQQKEwRSdWJ5 +MRMwEQYDVQQDEwpSQSBPZmZpY2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDmjdOjVhXIhr9kYvAiad8nJwfjtnEGluXRs1YMFOfOLi5bquu7eUL8ADiN3Fry +FKePiJppBscynd/nhaCnh24ideGEAUgFxNUiF5ER2P0q+UEJKqdIttVC3Alh8A+j +84ORre3P2uStpzyGZLBd2AfdEMaYYW+JtA8e6iNug5qrtQIDAQABo4GzMIGwMAkG +A1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkdlbmVyYXRlZCBieSBPcGVuU1NMIGZv +ciBSdWJ5LjAdBgNVHQ4EFgQUZGTsYtG/ZlqNyjRiH3X3KQpZtQswVQYDVR0jBE4w +TIAUWEVyhUZ7RwC/hzxB7EVhvVioK2ihMaQvMC0xCzAJBgNVBAYTAkNaMQ0wCwYD +VQQKEwRSdWJ5MQ8wDQYDVQQDEwZSdWJ5Q0GCAQAwDQYJKoZIhvcNAQEFBQADggEB +AD6K5cVA25rvighcsqPG530gOpOqmfiguJogEJrjugGwXD3n6Cgpu8znNhN2v9bX +v0h/7KisJAP/b0d2jyqEey+us1aZqtGNxAVk5dZ7zwU310fIOV19cujFSsF6LBZP +KlPy2qQHrGvWHtJBk5STn5VdTWEeEqv5wWNmBqYCxToaGg+lsF8U4OHX6QJiPr9Y +iOYPEqgYqpr9cJvu4PIHjXN0SSrwLcxln/wudP97J51cOoGb884zHWwwnzQVcJde +JOg4A+FI3SM4ilsd0SSoOUgy41kIKhej3VP/VS+iFdU5y2gpu9BqgVbwYy+d7hGO +vlPYILTKftShjnc1FxTAkx0= +-----END CERTIFICATE----- diff --git a/examples/1key.pem b/examples/1key.pem new file mode 100644 index 0000000..b82fc7f --- /dev/null +++ b/examples/1key.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,5A6AD2396EEAFD8D + +bIPHC+et8QaPX8BS9xdRJeKOB+bIN3Sa1YEyEBR/nvANheD7YkgH3JOchCOL/EnC +cFzQZH04SZqjh9Ir7X1QORYk2YV5rpaEHrjC3W2TPXDH3ED+SrS55sJuKue6V6Gs +uXQYIOzC22AFs4taUCzilY0mEu2dnua61ueOivimX8mj88Sm9Td7UHPvGArC+t+P +1FeD3wSV4Wt5z5IzrDjCfEA2FOz2GTz41vYF4YmqKOv/xPMBRUCOGvzvGPfnly2C +KYH8ty6jAauQ/jTQJJi7P7oPurtK3v9nHEc3xNo07+uzkKKw1Ur8UFh3xiAHl7KE +EKPUtc05KwD/NKcYEWbwiltFt/BSJIoyhAmR7J3siM5XvJ+saqhE8ycx3HG/I5DX +8Juv8jK7XELawGQoUeHO8KWQT/uEmf9xmRS2a1KMy2NWwqUQgC/kO23c2mhKTUZI +AppciHwExHeY1lXWyq+A0skbuYA531o3S8L28mk55VYSecz8LqFjeuMha774mDVi +QG8gfURS4tliLV9ZT72p5D+rCOSYXUMeSgyZa+mZoIVbqbE8UClmZJvFOfa5MRLe +tqHXk5STM6qQBD28mDeHKHLGFP7g0SnfXlHY1wQiSSZEOjY9RsoZFRcdC3Q4f7vY +Uz5ji0DDytnXyHIoFO/sejf4QacOWcDJoYigkeUrX6WZR11d6wCRMvT1ekuViOM3 +KXZdQ+ziLnUSZVELxngqpK6Ao32QfuFYEDufKkayz8WvByMCuyLwSgrqZVdC8JmP +9CSHO4zl97lQV5DOi9pen00fGL7PpyJhH1o0Ef320xByAR6InrbMTQ== +-----END RSA PRIVATE KEY----- diff --git a/examples/c/hash.c b/examples/c/hash.c new file mode 100644 index 0000000..76f2fa4 --- /dev/null +++ b/examples/c/hash.c @@ -0,0 +1,51 @@ +/* + * $Id$ + * RubySSL project + * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. (You can find the licence + * in LICENCE.txt file.) + */ +#include <openssl/ssl.h> + +int main(int argc, char *argv[]) +{ + BIO *in = NULL, *out = NULL; + X509 *x509 = NULL; + ASN1_BIT_STRING *key = NULL; + ASN1_OCTET_STRING *digest = NULL; + unsigned char dig[EVP_MAX_MD_SIZE]; + EVP_MD_CTX md; + unsigned int dig_len; + char *txt = NULL; + + in = BIO_new_file("./01cert.pem", "r"); + out = BIO_new(BIO_s_file()); + BIO_set_fp(out, stdout, BIO_NOCLOSE|BIO_FP_TEXT); + + x509 = PEM_read_bio_X509(in, NULL, NULL, NULL); + key = x509->cert_info->key->public_key; + + ASN1_STRING_print(out, key); + BIO_printf(out, "\n===\n"); + + EVP_DigestInit(&md, EVP_sha1()); + EVP_DigestUpdate(&md, key->data, key->length); + EVP_DigestFinal(&md, dig, &dig_len); + + txt = hex_to_string(dig, dig_len); + BIO_printf(out, "%s\n===\n", txt); + return 0; +} +//i2v_ ... as STACK_OF(CONF_VALUE) for easy printing + diff --git a/examples/c/key.c b/examples/c/key.c new file mode 100644 index 0000000..5f6cbf3 --- /dev/null +++ b/examples/c/key.c @@ -0,0 +1,84 @@ +/* + * $Id$ + * RubySSL project + * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. (You can find the licence + * in LICENCE.txt file.) + */ +#include <openssl/ssl.h> + +int main(int argc, char *argv[]) +{ + RSA *rsa = NULL; + BIO *in = NULL, *out = NULL; + + OpenSSL_add_all_algorithms(); + + if (!(in = BIO_new(BIO_s_file()))) { + printf("BIO in err\n"); + return 1; + } + //if (BIO_read_filename(in, "./01key.pem") <= 0) { + if (BIO_read_filename(in, "./01rsapub.pem") <= 0) { + printf("BIO_read err\n"); + return 2; + } + //if (!(rsa = PEM_read_bio_RSAPrivateKey(in, NULL, NULL, "pejs8nek"))) { + if (!(rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL))) { + printf("PEM read err\n"); + BIO_free(in); + return 3; + } + BIO_free(in); + if(rsa->n) printf("n=Yes, "); else printf("n=NO, "); + if(rsa->e) printf("e=Yes, "); else printf("e=NO, "); + if(rsa->d) printf("d=Yes, "); else printf("d=NO, "); + if(rsa->p) printf("p=Yes, "); else printf("p=NO, "); + if(rsa->q) printf("q=Yes, "); else printf("q=NO, "); + if(rsa->dmp1) printf("dmp1=Yes, "); else printf("dmp1=NO, "); + if(rsa->dmq1) printf("dmq1=Yes, "); else printf("dmq1=NO, "); + if(rsa->iqmp) printf("iqmp=Yes\n"); else printf("iqmp=NO\n"); + +/* + if (!(out = BIO_new(BIO_s_file()))) { + printf("BIO out err\n"); + return 4; + } + if (BIO_write_filename(out, "./01rsapriv.pem") <= 0) { + printf("BIO write err\n"); + return 5; + } + if (!PEM_write_bio_RSAPrivateKey(out, rsa, EVP_des_ede3_cbc(), NULL, 0, NULL, "alfa")) { + printf("Private err\n"); + return 6; + } + BIO_free(out); + + if (!(out = BIO_new(BIO_s_file()))) { + printf("BIO out err\n"); + return 7; + } + if (BIO_write_filename(out, "./01rsapub.pem") <= 0) { + printf("BIO write err\n"); + return 8; + } + if (!PEM_write_bio_RSAPublicKey(out, rsa)) { + printf("Private err\n"); + return 9; + } + BIO_free(out); +*/ + return 0; +} + diff --git a/examples/cacert.pem b/examples/cacert.pem new file mode 100644 index 0000000..5b592ed --- /dev/null +++ b/examples/cacert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtDCCApygAwIBAgIBADANBgkqhkiG9w0BAQQFADBKMQswCQYDVQQGEwJDWjEO +MAwGA1UEChMFUm9rb3MxCzAJBgNVBAMTAkNBMR4wHAYJKoZIhvcNAQkBFg9taWNo +YWxAcm9rb3MuY3owHhcNMDEwOTA5MDcyMzU0WhcNMDMwOTA5MDcyMzU0WjBKMQsw +CQYDVQQGEwJDWjEOMAwGA1UEChMFUm9rb3MxCzAJBgNVBAMTAkNBMR4wHAYJKoZI +hvcNAQkBFg9taWNoYWxAcm9rb3MuY3owggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQC728A7LcXmMBjNNWLM+F2cr+/yjF2z5E72ZWE7MoFmArNTD9Kpzij+ +BoQr2Epj8FOpYebPPpy3V4WiaQXiG9w59pljHAAuAKnSdtiuWP7HNk/aCGSH19x9 +VjD2HpjqPwv1BiJ2XO/TVi5p6xO9PA/oYsj3GhkfQ3EUyvdeUJo3Fze2LC9hj6ED +Yv4f15KaCGNL1DDYz1XDJtQh12Wt9l4i6GO8KHVIzZxQ4mnatfRiK7XnbaLqM3nC +Z8Qi+MQ7GTycBmSgOAmg0vzZ+8GEZRDRfg02nbhju1P+gbiYA62HM0TI8Zr9Ol/e +iWMBs6PSQ3C2dAcHPxGTXEM99SHlgTWPAgMBAAGjgaQwgaEwHQYDVR0OBBYEFIsv +Mx24Ivqj37PFReOHMRnUwQSOMHIGA1UdIwRrMGmAFIsvMx24Ivqj37PFReOHMRnU +wQSOoU6kTDBKMQswCQYDVQQGEwJDWjEOMAwGA1UEChMFUm9rb3MxCzAJBgNVBAMT +AkNBMR4wHAYJKoZIhvcNAQkBFg9taWNoYWxAcm9rb3MuY3qCAQAwDAYDVR0TBAUw +AwEB/zANBgkqhkiG9w0BAQQFAAOCAQEANZ0WpcFk/fKcYIAK2C9dFVgnHqbP6+xa +NH23epZ/29ONjVVZa4IW1OS3miufJDTGqSWncp8hLe18ux/AyJkGXcjYe0A4g/jn +qARZe7zGlE9Plg6VMGYJk6AJyTf1m8a+0f9Ek9MfUkblltE4zHSFsVEBezmav9n1 +WjjWgc3WA9amagkCx1P/M5r36noclHd7L6O+S4BXMJaOzk10ADahINQ89QmGdeCU +o2i09vtcupCwkepDCVhGU41Yp7ihjt4XJWePFXPf7Y1mf9Hhkq5cCIJVVRqT0YUL +FQrQrZweM8eGF/xouqGWCZQnTIdhDcpRIIlhAQy8MSoJJgxPR5raKw== +-----END CERTIFICATE----- diff --git a/examples/config.cnf b/examples/config.cnf new file mode 100644 index 0000000..2c3fcda --- /dev/null +++ b/examples/config.cnf @@ -0,0 +1,244 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca' and 'req'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = md5 # which md to use. +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (eg, YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 60 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer:always + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always diff --git a/examples/data b/examples/data new file mode 100644 index 0000000..4effa19 --- /dev/null +++ b/examples/data @@ -0,0 +1 @@ +hello! diff --git a/examples/gen_ca_cert.rb b/examples/gen_ca_cert.rb new file mode 100755 index 0000000..798a323 --- /dev/null +++ b/examples/gen_ca_cert.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include X509 +include PKey + +p key = RSA.new(2048) +p new = Certificate.new +name = [['C', 'CZ'],['O','Ruby'],['CN','RubyCA']] +p new.subject = Name.new(name) +p new.issuer = Name.new(name) +p new.not_before = Time.now +p new.not_after = Time.now + (2*365*24*60*60) +p new.public_key = key +p new.serial = 0 +p new.version = 2 +ef = ExtensionFactory.new +ef.subject_certificate = new +p ext1 = ef.create_extension("basicConstraints","CA:TRUE,pathlen:0") +p ext2 = ef.create_extension("nsComment","Generated by OpenSSL for Ruby.") +p ext3 = ef.create_extension("subjectKeyIdentifier", "hash") +new.extensions = [ext1, ext2, ext3] +ef.issuer_certificate = new # we needed subjectKeyInfo inside, now we have it +p ext4 = ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") +p new.add_extension(ext4) +p new.sign(key, Digest::SHA1.new) + +f = File.new("./#{new.serial}cert.pem","w") +f.write new.to_pem +f.close + +puts "Enter Password:" +p pass = gets.chop! + +f = File.new("./#{new.serial}key.pem", "w") +f.write key.export(Cipher::DES.new(Cipher::EDE3, Cipher::CBC), pass) +f.close + diff --git a/examples/gen_cert.rb b/examples/gen_cert.rb new file mode 100755 index 0000000..27bd2af --- /dev/null +++ b/examples/gen_cert.rb @@ -0,0 +1,41 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include X509 +include PKey + +p ca = Certificate.new(File.open("./0cert.pem").read) +p ca_key = RSA.new(File.open("./0key.pem").read) + +p key = RSA.new(1024) +p new = Certificate.new +name = [['C', 'CZ'],['O','Ruby'],['CN','RA Officer']] +p new.subject = Name.new(name) +p new.issuer = Name.new(name) +p new.not_before = Time.now +p new.not_after = Time.now + (365*24*60*60) +p new.public_key = key +p new.serial = 1 +p new.version = 2 +ef = ExtensionFactory.new +ef.subject_certificate = new +ef.issuer_certificate = ca +p ext1 = ef.create_extension("basicConstraints","CA:FALSE") +p ext2 = ef.create_extension("nsComment","Generated by OpenSSL for Ruby.") +p ext3 = ef.create_extension("subjectKeyIdentifier", "hash") +p ext4 = ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") +new.extensions = [ext1, ext2, ext3, ext4] +p new.sign(ca_key, Digest::SHA1.new) + +f = File.new("./#{new.serial}cert.pem","w") +f.write new.to_pem +f.close + +puts "Enter Password:" +p pass = gets.chop! + +f = File.new("./#{new.serial}key.pem", "w") +f.write key.export(Cipher::DES.new(Cipher::EDE3, Cipher::CBC), pass) +f.close + diff --git a/examples/key_hash.rb b/examples/key_hash.rb new file mode 100755 index 0000000..5af095c --- /dev/null +++ b/examples/key_hash.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL + +x509 = X509::Certificate.new(File.open("./01cert.pem").read) +key = x509.public_key +p d = Digest::SHA1.new +p d << key.to_der + +#x509 = X509::Certificate.new +#rsa = PKey::RSA.new(1024) +#x509.public_key = rsa +#rsa = x509.public_key +#d2 = Digest::SHA1.new +#p d2 << rsa.to_der + diff --git a/examples/ossl_cipher.rb b/examples/ossl_cipher.rb new file mode 100755 index 0000000..6a54dce --- /dev/null +++ b/examples/ossl_cipher.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include Cipher + +p des = DES.new(EDE3, CBC) #Des3 CBC mode +p "ENCRYPT" +p des.encrypt("key")#, "iv12345678") +p cipher = des.update("abcdefghijklmnopqrstuvwxyz") +p cipher += des.cipher +p "DECRYPT" +p des.decrypt("key") #, "iv12345678") +p des.update(cipher) + des.cipher + diff --git a/examples/ossl_config.rb b/examples/ossl_config.rb new file mode 100755 index 0000000..e7f28b2 --- /dev/null +++ b/examples/ossl_config.rb @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL + +p config = Config.load("./config.cnf") + +p string = config.get_value("req", "x509_extensions") +p string = config.get_value("req", "default_bits") +p number = config.get_value("req", "default_bits").to_i +p string = config.get_value("req", "distinguished_name") +p config.get_section("req") + +## +#DISABLED! +#p sect = config.get_section(string) +#p ConfigSection.new + diff --git a/examples/ossl_digest.rb b/examples/ossl_digest.rb new file mode 100755 index 0000000..0ea7e0e --- /dev/null +++ b/examples/ossl_digest.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require 'digest/sha1' +require 'digest/md5' +require 'openssl' + +str = "This is only bullshit! :-))" +md5 = Digest::MD5.new(str) +md5a = OpenSSL::Digest::MD5.new(str) +p md5.digest == md5a.digest +p md5.hexdigest == md5a.hexdigest + +sha1 = OpenSSL::Digest::SHA1.new(str*2) +sha1a = Digest::SHA1.new(str*2) +p sha1.digest == sha1a.digest +p sha1.hexdigest == sha1a.hexdigest + diff --git a/examples/ossl_pkey.rb b/examples/ossl_pkey.rb new file mode 100755 index 0000000..1876ba4 --- /dev/null +++ b/examples/ossl_pkey.rb @@ -0,0 +1,97 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include PKey +include Cipher +include Digest + +puts "==RSA==" +p rsa = PKey::RSA.new(512) {|p, n| #the same as in OpenSSL + if (p==0) then putc "." #BN_generate_prime + elsif (p==1) then putc "+" #BN_generate_prime + elsif (p==2) then putc "*" #searching good prime, n = #of try, but also data from BN_generate_prime + elsif (p==3) then putc "\n" #found good prime, n==0 - p, n==1 - q, but also data from BN_generate_prime + else putc "*" #BN_generate_prime + end +} + +puts ".......=sign'n'verify" +txt = <<END +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 GPL +(see the file GPL), 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) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided. + + d) make other distribution arrangements with the author. + + 3. You may distribute the software in object code or executable + form, provided that you do at least ONE of the following: + + a) distribute the executables 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 executables 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. +END +p sig = rsa.sign(SHA1.new, txt) +p rsa.verify(SHA1.new, sig, txt) +puts ".......=encrypt'n'decrypt" +txt2 = "Hello out there!" +p enc = rsa.public_encrypt(txt2) +p rsa.private_decrypt(enc) + +puts "==DSA==" +p dsa = PKey::DSA.new(512) {|p, n| #the same as in OpenSSL + if (p==0) then putc "." + elsif (p==1) then putc "+" + elsif (p==2) then putc "*" #(2,1)=>found q + elsif (p==3) then putc "\n" #(3,1)=>generated g + else putc "*" + end +} +puts ".......=sign'n'verify" +p sig = dsa.sign(DSS.new, txt) +p dsa.verify(DSS.new, sig, txt) + diff --git a/examples/ossl_rsa.rb b/examples/ossl_rsa.rb new file mode 100755 index 0000000..266cece --- /dev/null +++ b/examples/ossl_rsa.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include PKey +include Cipher +#p RSA.new(1024) +p priv = RSA.new(File.open("./01key.pem").read, "pejs8nek") +p priv.private? +p pub = RSA.new(File.open("./01pub.pem").read) +p pub.private? +puts exp = priv.export(DES.new(EDE3, CBC), "password") +p priv2 = RSA.new(exp, "password") +p priv.to_text == priv2.to_text +#puts priv.to_pem +#puts pub.to_text +#puts priv.to_text +#puts pub.export + diff --git a/examples/ossl_x509.rb b/examples/ossl_x509.rb new file mode 100755 index 0000000..6d6261e --- /dev/null +++ b/examples/ossl_x509.rb @@ -0,0 +1,76 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include X509 +include PKey + +p x509 = Certificate.new(File.open("./01cert.pem").read) +#puts x509.to_pem +#p x509.serial +#puts "Version = #{x509.version}" +#p Name.new +#p subject = x509.subject +#p subject.to_s +#p issuer = x509.issuer +#p issuer.to_pem +#p ary = issuer.to_a +#p issuer.to_h +#ary[3] = ["Email", "bow@wow.com"] +#p x509.issuer = ary +#p x509.not_before +#p x509.not_before = Time.now +#p x509.not_after +#p k = x509.public_key +#p k.private? +#puts k.to_text +#p priv = RSA.new(File.open("./01key.pem").read, "pejs8nek") +#p priv.private? +#p x509.public_key = priv +#puts x509.public_key.to_text +#p x509.issuer.to_s +#p x509.sign(priv,MD5.new) +#p x509.issuer.to_s +#puts x509.to_text +#x509.extensions.each_with_index {|e, i| p e.to_a} +#puts "----end----" + +p key = RSA.new(1024) +p new = Certificate.new +name = [['O','Ruby'],['OU','Test'],['CN','test001'],['C','CZ']] +#p n = Name.new(name) +#p n.to_h +#p n.to_a +#p n.to_s +#exit +p new.subject = Name.new(name) +p new.issuer = Name.new(name) +p new.not_before = Time.now +p new.not_after = Time.now + (60*60*24*365) +p new.public_key = key #x509.public_key +p new.serial = 999999999 +p new.version = 2 +#p new.extensions #each_with_index {|e, i| p e.to_a} +maker = ExtensionFactory.new(nil, new) #only subject +p ext1 = maker.create_extension(["basicConstraints","CA:FALSE,pathlen:5"]) +#p ext1.to_a +#p ext1.to_h +#p ext1.to_s +#exit +p ext2 = maker.create_extension(["nsComment","Generated by OpenSSL for Ruby."]) +###p digest = Digest::SHA1.new(new.public_key.to_der) +###p ext3 = maker.create_extension(["subjectKeyIdentifier", digest.hexdigest]) +p ext3 = maker.create_extension(["subjectKeyIdentifier", "hash"]) +new.extensions = [ext1, ext2, ext3] +maker.issuer_certificate = new # we needed subjectKeyInfo inside, now we have it +p ext4 = maker.create_extension(["authorityKeyIdentifier", "keyid:always,issuer:always"]) +#puts ext1.to_s +p new.add_extension(ext4) +p new.sign(key, Digest::MD5.new) +puts "===TEXT===" +puts new.to_text +puts "===PEM===" +puts new.to_pem +puts "===DER===" +p new.to_der + diff --git a/examples/ossl_x509crl.rb b/examples/ossl_x509crl.rb new file mode 100755 index 0000000..7e19ac0 --- /dev/null +++ b/examples/ossl_x509crl.rb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include X509 +include PKey + +p ca = Certificate.new(File.open("./cacert.pem").read) +p key = ca.public_key +p crl = CRL.new(File.open("./01crl.pem").read) +puts crl.to_text +p crl.issuer.to_s +p crl.verify key +p crl.verify RSA.new(1024) +crl.revoked.each {|rev| p rev.time} + diff --git a/examples/ossl_x509req.rb b/examples/ossl_x509req.rb new file mode 100755 index 0000000..215888e --- /dev/null +++ b/examples/ossl_x509req.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include X509 +include PKey + +p req = Request.new +p req = Request.new(File.open("./01req.pem").read) +p pkey = RSA.new(File.open("./02key.pem").read, "alfa") +p k2 = Certificate.new(File.open("./02cert.pem").read).public_key +#puts req.to_pem +#p req.methods.sort +p key = req.public_key +p req.verify key +p req.verify pkey +p req.verify k2 +p req.public_key = k2 +p req.sign(pkey, Digest::MD5.new) +p req.verify key +p req.verify pkey +p req.verify k2 +puts req.to_text diff --git a/examples/ossl_x509store.rb b/examples/ossl_x509store.rb new file mode 100755 index 0000000..d70e85d --- /dev/null +++ b/examples/ossl_x509store.rb @@ -0,0 +1,76 @@ +#!/usr/bin/ruby -w + +require 'openssl' +include OpenSSL +include X509 + +verify_cb = Proc.new {|ok, x509_store| + puts "\t\t====begin Verify====" + puts "\t\tOK = #{ok}" + puts "\t\tchecking #{x509_store.cert.subject.to_s}" + puts "\t\tstatus = #{x509_store.verify_status} - that is \"#{x509_store.verify_message}\"" + puts "\t\t==== end Verify====" + #raise "SOME ERROR!" # Cert will be rejected + #false # Cert will be rejected + #true # Cert is OK + ok # just throw 'ok' through +} + +p ca = Certificate.new(File.open("./cacert.pem").read) +puts "CA = #{ca.subject.to_s}, serial = #{ca.serial}" +cakey = ca.public_key + +p cert = Certificate.new(File.open("./01cert.pem").read) +puts "Cert = #{cert.subject.to_s}, serial = #{cert.serial}" +key = cert.public_key + +p crl = CRL.new(File.open("./01crl.pem").read) +print "Is CRL signed by CA?..." +if crl.verify cakey + puts "Yes - OK!" +else + puts "NO - Strange... Let's stop." + exit +end + +puts "In CRL there are serials:" +crl.revoked.each {|revoked| + puts "> #{revoked.serial} - revoked at #{revoked.time}" +} + +p store = Store.new + +## +# Uncomment to see what is checked... +store.verify_callback = verify_cb + +store.add_trusted ca + +puts "====================" +puts "Is CERT OK?..." +if store.verify cert + puts "Yes - we didn't add CRL to store!" + puts "\t\t(status = #{store.verify_status} - that is \"#{store.verify_message}\")" +else + puts "NO - HEY, this is error!" + puts "\t\t(status = #{store.verify_status} - that is \"#{store.verify_message}\")" +end + +puts "Let's add CRL..." + store.add_crl crl #CRL does NOT have affect on validity in current OpenSSL <= 0.9.6c !!! + +puts "====================" +puts "Is CERT still OK?..." +if store.verify cert + puts "Yes - HEY, this is bug! OpenSSL <= 0.9.6c doesn't care about CRL in Store :-((((" + puts "\t\t(status = #{store.verify_status} - that is \"#{store.verify_message}\")" +else + puts "No - now it works!" + puts "\t\t(status = #{store.verify_status} - that is \"#{store.verify_message}\")" +end + +puts "Trusted certs:" +store.chain.each_with_index {|cert, i| + puts "> #{i} --- #{cert.subject.to_s}" +} + diff --git a/examples/pkcs7.rb b/examples/pkcs7.rb new file mode 100755 index 0000000..8bd479b --- /dev/null +++ b/examples/pkcs7.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby +require 'openssl' + +include OpenSSL +include PKey +include X509 +include PKCS7 + +data = File.open(ARGV[0]).read + +str = File.open('./server.pem').read +cert = Certificate.new(str) +key = RSA.new(str) + +p7 = PKCS7.new(SIGNED) +signer = Signer.new(cert, key, Digest::SHA1.new) +p7.add_signer(signer, key) +p7.add_certificate(cert) +p7.add_data(data, true) #...(data, (detached=false)) +puts (str = p7.to_pem) + +p store = Store.new +p store.set_default_paths +p store.load_locations("../../certs") + +ver_cb = Proc.new {|ok, store| + puts "HERE!" + true +} +p store.verify_callback = ver_cb + +p p7 = PKCS7.new(str) +p p7.verify_data(store, data) {|signer| + puts "GOT IT!" + p signer.name.to_s + p signer.serial + p signer.signed_time +} + diff --git a/examples/server.pem b/examples/server.pem new file mode 100644 index 0000000..750aac2 --- /dev/null +++ b/examples/server.pem @@ -0,0 +1,24 @@ +issuer= /C=AU/ST=Queensland/O=CryptSoft Pty Ltd/CN=Test CA (1024 bit) +subject=/C=AU/ST=Queensland/O=CryptSoft Pty Ltd/CN=Server test cert (512 bit) +-----BEGIN CERTIFICATE----- +MIIB6TCCAVICAQAwDQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV +BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD +VQQDExJUZXN0IENBICgxMDI0IGJpdCkwHhcNOTcwNjA5MTM1NzQ2WhcNOTgwNjA5 +MTM1NzQ2WjBjMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFuZDEaMBgG +A1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxIzAhBgNVBAMTGlNlcnZlciB0ZXN0IGNl +cnQgKDUxMiBiaXQpMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+zw4Qnlf8SMVIP +Fe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/7tdkuD8Ey2// +Kv7+ue0CAwEAATANBgkqhkiG9w0BAQQFAAOBgQB4TMR2CvacKE9wAsu9jyCX8YiW +mgCM+YoP6kt4Zkj2z5IRfm7WrycKsnpnOR+tGeqAjkCeZ6/36o9l91RvPnN1VJ/i +xQv2df0KFeMr00IkDdTNAdIWqFkSsZTAY2QAdgenb7MB1joejquYzO2DQIO7+wpH +irObpESxAZLySCmPPg== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD +TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu +OA4NACqoiFqyblo7yc2tM4h4xMbC3Yx5UKMN9ZkCtX0gzrz6DyF47bdKcWBzNWCj +gQIhANEoojVt7hq+SQ6MCN6FTAysGgQf56Q3TYoJMoWvdiXVAiEAw3e3rc+VJpOz +rHuDo6bgpjUAAXM+v3fcpsfZSNO6V7kCIQCtbVjanpUwvZkMI9by02oUk9taki3b +PzPfAfNPYAbCJQIhAJXNQDWyqwn/lGmR11cqY2y9nZ1+5w3yHGatLrcDnQHxAiEA +vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU= +-----END RSA PRIVATE KEY----- diff --git a/examples/spki.pem b/examples/spki.pem new file mode 100644 index 0000000..ff3c03a --- /dev/null +++ b/examples/spki.pem @@ -0,0 +1 @@ +MIIBPjCBqDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuCjEDvjiNHcgya0+bU+NR7i6detQlZljdXmqx3UsQN+YmAkYPE4DIVtndRg/ofObSEV9VNgujKtwJdew111omp0tPT6gsT5VQJsceERgvJyv01J4oL3njJUKfMtet41hrUANSTDz/YAcKfiJn8fQYH+LgMEM64rWTPMXm+AFA0sCAwEAARYEcGVwYTANBgkqhkiG9w0BAQQFAAOBgQChk2Xq+RrdJWGnpQIaBkgy9Dw6pv39dfBklavXwU/Aapty3N7j+sM+j0nnkABgYpPB5/TZTX/L7RIUQHBbn3dk9GbGuj5R46AQVrKdEOQBPz96S61enXgPp6xJQqglAw5PARr3/5HOSTDz2cDvjqdDG/END21XaHiOtuRSyyZCLw==
\ No newline at end of file diff --git a/examples/spki.rb b/examples/spki.rb new file mode 100644 index 0000000..f97a163 --- /dev/null +++ b/examples/spki.rb @@ -0,0 +1,7 @@ +require "openssl" +include OpenSSL + +puts txt = "MIICUDCCATgwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUIhGCK7sr\r\nO+jHy7S1ZllFCEPzhlneTnjUnjZWuZEVu7c14NUhJzpNXg//6sCoiy5cQPaIYFIs\r\nded/PosTNfJVPX6El+bWk/2Elf5iVYcScRpf+RkUBR6T3WAMFPCajx3JFonhqhny\r\n5bSXU41h7/oLpnQkeeo76ujKoxjV6vl+y36jCeUAI+dzrWLznUswWVnWvdNt/z1h\r\npWILYtCKexLsz+aOqA6NdGTVDb8r+iDorU2KGL4BJjMXGr/LutYQjeVVXZTuaeN+\r\nxa75TVMcSEzvVQm8Dk1u3C3r3hm9I9zKnpta5NqiToR/fA85Qw5YhjEZMWT/Rj+7\r\nB5LBp5NcX35vAgMBAAEWEGNoYWxsZW5nZSBzdHJpbmcwDQYJKoZIhvcNAQEEBQAD\r\nggEBABdXwDZ9yDyyC5xw8rN/+/xAZSYa8xn4gsUEg4P/mM22WZaqh/NXroXUcU5F\r\nQBeGTYlT//wVlobLeES64Mk/FaCIXrZrLRAxb5QUYIupH2MifRU5XWriYcc6pp7S\r\nD1N+U6MOUFPMziqLf2AYqXBxuky1KhFeXuL6t9j1IadEY9UgTbUQ9Joyt50PoacM\r\ncc2i22GGdpowx7mrB0hnkmYmZ5CgQkrxNM2m4TCuuQwVIyaGgED5Xpa29QWaPhkM\r\njqjHBL4FOmPgYtaIFiFihQziYj5WYOtSEcIcEs/mHPx0lrY9V0fzp2yMGz+AQ3XF\r\nylBqpB33EBqXn/NGzHgWfdU1vEM=" +txt.gsub!(/(\r|\n)/,"") +puts Netscape::SPKI.new(txt).to_s + diff --git a/examples/spki2cert.rb b/examples/spki2cert.rb new file mode 100755 index 0000000..a59796d --- /dev/null +++ b/examples/spki2cert.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby + +require 'openssl' +include OpenSSL +include X509 +include PKey + +p ca = Certificate.new(File.open("./0cert.pem").read) +p ca_key = RSA.new(File.open("./0key.pem").read) + +p spki = Netscape::SPKI.new(File.open("./spki.pem").read) +p key = spki.public_key +p new = Certificate.new + +p dn = File.open("./spki_dn.txt").read +dn = dn[1..dn.size] +name = [] +dn.split("/").each {|i| name << i.split("=")} +p new.subject = Name.new(name) + +p new.issuer = ca.subject +p new.not_before = Time.now +p new.not_after = Time.now + (365*24*60*60) +p new.public_key = key +p new.serial = 2 +p new.version = 2 +ef = ExtensionFactory.new +ef.subject_certificate = new +ef.issuer_certificate = ca +p ext1 = ef.create_extension("basicConstraints","CA:FALSE") +p ext2 = ef.create_extension("nsComment","Generated by OpenSSL for Ruby.") +p ext3 = ef.create_extension("subjectKeyIdentifier", "hash") +p ext4 = ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") +new.extensions = [ext1, ext2, ext3, ext4] +p new.sign(ca_key, Digest::SHA1.new) + +f = File.new("./spki_cert.pem","w") +f.write new.to_pem +f.close + diff --git a/examples/spki_cert.pem b/examples/spki_cert.pem new file mode 100644 index 0000000..3bb4396 --- /dev/null +++ b/examples/spki_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHDCCAgSgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAtMQswCQYDVQQGEwJDWjEN +MAsGA1UEChMEUnVieTEPMA0GA1UEAxMGUnVieUNBMB4XDTAxMTExMDA4MjEyN1oX +DTAyMTExMDA4MjEyN1owRDENMAsGA1UEAxMEcGVwYTEXMBUGA1UEChMOUnVieSBj +b21tdW5pdHkxDTALBgNVBAsTBFRlc3QxCzAJBgNVBAYTAkNaMIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQC4KMQO+OI0dyDJrT5tT41HuLp161CVmWN1earHdSxA +35iYCRg8TgMhW2d1GD+h85tIRX1U2C6Mq3Al17DXXWianS09PqCxPlVAmxx4RGC8 +nK/TUnigveeMlQp8y163jWGtQA1JMPP9gBwp+Imfx9Bgf4uAwQzritZM8xeb4AUD +SwIDAQABo4GzMIGwMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkdlbmVyYXRl +ZCBieSBPcGVuU1NMIGZvciBSdWJ5LjAdBgNVHQ4EFgQUu3y24OVXsOp/7leyLT/f +rW2B/l8wVQYDVR0jBE4wTIAUWEVyhUZ7RwC/hzxB7EVhvVioK2ihMaQvMC0xCzAJ +BgNVBAYTAkNaMQ0wCwYDVQQKEwRSdWJ5MQ8wDQYDVQQDEwZSdWJ5Q0GCAQAwDQYJ +KoZIhvcNAQEFBQADggEBABJ26j+AMhSyEIAqgCym911MKJ0s+Athb82NbNN23/6e +adr2O0gLvDKCqJTkbNvpHuyjDheaA6udYaCSay+gbj62IkuNp+qLf2l/J5Tqk32H +w0Q6nE58wiATx0q4Y9MgOBJ97rI/dnL6cBeR7nFwy8YHmBBTpyuQf/NXRR6g0e1k +XVAw39R2R4YgEVMLQZvuc/ZV76DECL8Id3rS8ZRzJNGE97zTxM6GFilhFjuvkPyP +74kErX3Y9dk3hVq5KWcbdhtd6hGUwZCUgO/zPW2lx/0U0Iv4EkhE3N8z81zXjy5D +WV4mZ8b8tDHJchrStL04xqrPxBNkyXRqK9M13Tn22qM= +-----END CERTIFICATE----- diff --git a/examples/spki_dn.txt b/examples/spki_dn.txt new file mode 100644 index 0000000..44fcbfb --- /dev/null +++ b/examples/spki_dn.txt @@ -0,0 +1 @@ +/CN=pepa/O=Ruby community/OU=Test/C=CZ
\ No newline at end of file diff --git a/examples/ssl/cli.rb b/examples/ssl/cli.rb new file mode 100755 index 0000000..82f26db --- /dev/null +++ b/examples/ssl/cli.rb @@ -0,0 +1,41 @@ +#!/usr/bin/env ruby + +require 'socket' +require 'openssl' +require 'getopts' +begin require 'verify_cb'; rescue LoadError; end + +include OpenSSL +include SSL + +getopts "v", "C:", "p:2000", "c:", "k:" + +host = ARGV[0] || "localhost" + +p rsa = PKey::RSA.new(File.open($OPT_k).read) if $OPT_k && FileTest::file?($OPT_k) +p cert = X509::Certificate.new(File.open($OPT_c).read) if $OPT_c && FileTest::file?($OPT_c) + +s = TCPSocket.new(host, $OPT_p) +STDERR.print "connect to #{s.peeraddr[3]}.\n" + +ssl = SSLSocket.new(s, cert, rsa) +###ssl.ca_cert = X509::Certificate.new(File.open($OPT_C).read) if $OPT_C && FileTest::file?($OPT_C) +ssl.ca_file = $OPT_C if $OPT_C && FileTest::file?($OPT_C) +ssl.ca_path = $OPT_C if $OPT_C && FileTest::directory?($OPT_C) +ssl.verify_mode = VERIFY_PEER if $OPT_v +ssl.verify_callback = VerifyCallbackProc if defined? VerifyCallbackProc +STDERR.print "SSLSocket initialized.\n" + +ssl.connect +STDERR.print "SSLSocket connected.\n" +STDERR.print ssl.peer_cert.to_str, "\n" if ssl.peer_cert + +i = 0 +while line = gets + i += 1 + ssl.puts "#{i}: #{line.chop}" +end + +ssl.close +s.close + diff --git a/examples/ssl/login.rb b/examples/ssl/login.rb new file mode 100755 index 0000000..0835ef0 --- /dev/null +++ b/examples/ssl/login.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby + +require 'net/telnets' +require 'getopts' +require 'etc' +begin require 'verify_cb'; rescue LoadError; end + +getopts 'v', 'C:', 'c:', 'k:' + +options = {} +# ordinary options. +options['Host'] = ARGV[0] || "localhost" +options['Port'] = ARGV[1] || "telnets" +options['Prompt'] = /[$%>#] \z/n + +# for SSL/TLS +options['Cert'] = $OPT_c +options['Key'] = $OPT_k +options['CAFile'] = $OPT_C if $OPT_C && File::file?($OPT_C) +options['CAPath'] = $OPT_C if $OPT_C && File::directory?($OPT_C) +options['VerifyMode'] = SSL::VERIFY_PEER if $OPT_v +options['VerifyCallback'] = VerifyCallbackProc if defined? VerifyCallbackProc + +# getting Password. +username = Etc::getlogin || Etc::getpwuid[0] +system "stty -echo" +print "Passwd for #{username}@#{options['Host']}: " +passwd = $stdin.gets.chomp +print "\n" +system "stty echo" + +t = Net::Telnet.new(options) +t.login(username, passwd) +prompt = t.ssl? ? "Telnets: " : "Telnet: " +while $stdout.write(prompt) && line = $stdin.gets + line.chomp! + t.cmd(line){|c| print c } +end +t.close diff --git a/examples/ssl/svr.rb b/examples/ssl/svr.rb new file mode 100755 index 0000000..0494342 --- /dev/null +++ b/examples/ssl/svr.rb @@ -0,0 +1,91 @@ +#!/usr/bin/env ruby + +require 'socket' +require 'openssl' +require 'getopts' +begin require 'verify_cb'; rescue LoadError; end + +include OpenSSL +include SSL + +getopts "v", "C:", "p:2000", "c:", "k:" + +p [ $OPT_p, $OPT_k, $OPT_c ] + + if $OPT_k + p rsa = PKey::RSA.new(File.open($OPT_k).read) + else + p rsa = PKey::RSA.new(512){|p, n| + case p + when 0; putc "." # BN_generate_prime + when 1; putc "+" # BN_generate_prime + when 2; putc "*" # searching good prime, n = #of try, + # but also data from BN_generate_prime + when 3; putc "\n" # found good prime, n==0 - p, n==1 - q, + # but also data from BN_generate_prime + else; putc "*" # BN_generate_prime + end + } + end + + if $OPT_c + p cert = X509::Certificate.new(File.open($OPT_c).read) + else + cert = X509::Certificate.new + cert.version = 2 + cert.serial = 0 + name = X509::Name.new([["C","CZ"],["O","Ruby"],["CN","Test"]]) + cert.subject = name + cert.issuer = name + cert.not_before = Time.now + cert.not_after = Time.now + (365*24*60*60) + cert.public_key = rsa.public_key + ef = X509::ExtensionFactory.new(nil,cert) + cert.extensions = [ + ef.create_extension("basicConstraints","CA:FALSE"), + ef.create_extension("subjectKeyIdentifier", "hash") + ] + ef.issuer_certificate = cert + cert.add_extension ef.create_extension("authorityKeyIdentifier", + "keyid:always,issuer:always") + cert.add_extension ef.create_extension("nsComment", + "Generated by OpenSSL for Ruby!") + cert.sign(rsa, Digest::SHA1.new) + puts cert.to_str + end + +ns = TCPServer.new($OPT_p) +loop do + begin + s = ns.accept + STDERR.print "connect from #{s.peeraddr[3]}.\n" + + ssl = SSLSocket.new(s, cert, rsa) + ###ssl.ca_cert = X509::Certificate.new(File.open($OPT_C).read) if $OPT_C && FileTest::file?($OPT_C) + ssl.ca_file = $OPT_C if $OPT_C && FileTest::file?($OPT_C) + ssl.ca_path = $OPT_C if $OPT_C && FileTest::directory?($OPT_C) + ssl.verify_mode = VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT if $OPT_v + ssl.verify_callback = VerifyCallbackProc if defined? VerifyCallbackProc + STDERR.print "SSLSocket initialized.\n" + + ssl.accept + STDERR.print "SSLSocket accepted.\n" + STDERR.print ssl.peer_cert.inspect, "\n" if ssl.peer_cert + rescue + ssl.close + s.close + print $!, "\n" + next + end + + Thread.start{ + puts "Thread started" + while line = ssl.gets + p line + end + STDERR.print "connection closed.\n" + ssl.close + s.close + } +end + diff --git a/examples/ssl/verify_cb.rb b/examples/ssl/verify_cb.rb new file mode 100644 index 0000000..3196973 --- /dev/null +++ b/examples/ssl/verify_cb.rb @@ -0,0 +1,21 @@ +VerifyCallbackProc = Proc.new{ |ok, x509_store_ctx| + code = x509_store_ctx.verify_status + msg = x509_store_ctx.verify_message + depth = x509_store_ctx.verify_depth + x509 = x509_store_ctx.cert + + if $OPT_v + STDERR.print <<-_eof_ + ------verify callback start------ + ok,code,depth = #{ok},#{code}:#{msg},#{depth} + x509 = #{x509.to_str} + -------verify callback end------- + _eof_ + if !ok + STDERR.print "Couldn't verify peer. Do you want to progerss? [y]: " + ok = true unless /^n/i =~ STDIN.gets() + end + end + ok +} + diff --git a/examples/ssl/wget.rb b/examples/ssl/wget.rb new file mode 100755 index 0000000..e71f55a --- /dev/null +++ b/examples/ssl/wget.rb @@ -0,0 +1,88 @@ +#!/usr/bin/env ruby + +require 'socket' +require 'getopts' +require 'openssl' +begin require 'verify_cb'; rescue LoadError; end + +include OpenSSL +include SSL + +STDOUT.sync = true +STDERR.sync = true + +getopts "v", "c:" + +scheme = host = port = path = nil +p_scheme = p_host = p_port = nil + +# parse request URI. +uri = ARGV[0] +if %r!(https?)://(.*?)(?::(\d+))?(/.*)! =~ uri + scheme = $1 + host = $2 + port = $3 ? $3.to_i : Socket.getservbyname(scheme) + path = $4 || "/" +else + STDERR.print "Invalid URI.\n" + exit 2 +end + +# parse HTTP_PROXY environment variable. +if proxy = ENV['HTTP_PROXY'] + if %r!(http)://(.*?)(?::(\d+))?(/.*)! =~ proxy + p_scheme = $1 + p_host = $2 + p_port = $3 ? $3.to_i : Socket.getservbyname(p_scheme) + else + STDERR.print "Invalid HTTP_PROXY.\n" + exit 2 + end +end + +# Connect to server. +to = proxy ? [ p_host, p_port ] : [ host, port ] +sock = TCPSocket.new(to[0], to[1]) + +# If scheme is ``https'' we are going to initiate SSL session. +if scheme == "https" + # If the peer is a proxy server, send CONNECT method to + # be switched to being a tunnel. + if proxy + sock.write "CONNECT #{host}:#{port} HTTP/1.0\r\n\r\n" + while line = sock.gets + STDERR.print line + break if line == "\r\n" + end + end + + # start SSL session. + sock = SSLSocket.new(sock) + ##sock.ca_cert = X509::Certificate.new(File.open($OPT_c).read) if $OPT_c && FileTest.file?($OPT_c) + sock.ca_file = $OPT_c if $OPT_c && FileTest.file?($OPT_c) + sock.ca_path = $OPT_c if $OPT_c && FileTest.directory?($OPT_c) + # verify server. + sock.verify_mode = VERIFY_PEER if $OPT_v + sock.verify_callback = VerifyCallbackProc if defined? VerifyCallbackProc + + sock.connect # start ssl session. + STDERR.puts "SSLSocket connected." + STDERR.puts cert.to_str if cert = sock.peer_cert +end + +# I expect most servers accept the absoluteURI in requests. +sock.write "GET #{scheme}://#{host}:#{port}#{path} HTTP/1.0\r\n" +sock.write "Connection: close\r\n" +sock.write "\r\n" + +while line = sock.gets + STDERR.print line + break if line == "\r\n" +end + +while data = sock.read(100) + print data +end + +sock.close + diff --git a/examples/ssl/wget2.rb b/examples/ssl/wget2.rb new file mode 100644 index 0000000..4d6a36b --- /dev/null +++ b/examples/ssl/wget2.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby + +require 'net/https' +require 'getopts' +begin require 'verify_cb'; rescue LoadError; end + +getopts 'v', 'p:' + +uri = ARGV[0] +if %r!(https?)://(.*?)(?::(\d+))?(/.*)! =~ uri + scheme = $1 + host = $2 + port = $3 ? $3.to_i : Socket.getservbyname(scheme) + path = $4 || "/" +else + STDERR.print "Invalid URI.\n" + exit 2 +end + +# parse HTTP_PROXY environment variable. +if proxy = ENV['HTTP_PROXY'] + if %r!(http)://(.*?)(?::(\d+))?(/.*)! =~ proxy + p_scheme = $1 + p_host = $2 + p_port = $3 ? $3.to_i : Socket.getservbyname(p_scheme) + else + STDERR.print "Invalid HTTP_PROXY.\n" + exit 2 + end +end + +h = Net::HTTP.new(host, port, p_host, p_port) +h.set_pipe($stderr) if $DEBUG +if scheme == "https" + h.use_ssl = true + h.verify_mode = SSL::VERIFY_PEER if $OPT_v + h.verify_callback = VerifyCallbackProc if defined? VerifyCallbackProc +end +h.get2(path){ |resp| + STDERR.puts h.peer_cert.inspect if h.peer_cert + print resp.body +} + diff --git a/extconf.rb b/extconf.rb new file mode 100644 index 0000000..f89c5da --- /dev/null +++ b/extconf.rb @@ -0,0 +1,49 @@ +=begin += $RCSfile$ -- Generator for Makefile + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +require "mkmf" + +if RUBY_PLATFORM =~ /mswin32/ + CRYPTOLIB="libeay32" + SSLLIB="ssleay32" +else + CRYPTOLIB="crypto" + SSLLIB="ssl" +end + +dir_config("openssl") + +have_func("strptime", "time.h") + +## +# Adds -Wall -DOSSL_DEBUG for compilation +# Use as --with-debug or --enable-debug +# +if with_config("debug") or enable_config("debug") + $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG" + $CPPFLAGS += " " + "-Wall" unless $CPPFLAGS.split.include? "-Wall" +end + + +if have_header("openssl/crypto.h") and + have_library(CRYPTOLIB, nil) and + have_library(SSLLIB, nil) #"SSLv23_method") + create_makefile("openssl") + puts "Done." +else + puts "Makefile wasn't created." +end + diff --git a/lib/net/https.rb b/lib/net/https.rb new file mode 100644 index 0000000..c8bf329 --- /dev/null +++ b/lib/net/https.rb @@ -0,0 +1,169 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net::HTTP. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Requirements + This program requires Net 1.2.0 or higher version. + You can get it from RAA or Ruby's CVS repository. + += Version + $Id$ + + 2001/11/06: Contiributed to Ruby/OpenSSL project. + +== class Net::HTTP + +== Example + +Simple HTTP client is here: + + require 'net/http' + host, port, path = "localhost", 80, "/" + if %r!http://(.*?)(?::(\d+))?(/.*)! =~ ARGV[0] + host = $1 + port = $2.to_i if $2 + path = $3 + end + h = Net::HTTP.new(host, port) + h.get2(path){ |resp| print resp.body } + +It can be replaced by follow one: + + require 'net/https' + host, port, path = "localhost", 80, "/" + if %r!(https?)://(.*?)(?::(\d+))?(/.*)! =~ ARGV[0] + scheme = $1 + host = $2 + port = $3 ? $3.to_i : ((scheme == "http") ? 80 : 443) + path = $4 + end + h = Net::HTTP.new(host, port) + h.use_ssl = true if scheme == "https" # enable SSL/TLS + h.get2(path){ |resp| print resp.body } + +=== Instance Methods + +: use_ssl + returns ture if use SSL/TLS with HTTP. + +: use_ssl=((|true_or_false|)) + sets use_ssl. + +: peer_cert + return the X.509 certificates the server presented. + +: key=((|key|)) + Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + (This method is appeared in Michal Rokos's OpenSSL extention.) + +: key_file=((|path|)) + Sets a private key file to use in PEM format. + +: cert=((|cert|)) + Sets an OpenSSL::X509::Certificate object as client certificate. + (This method is appeared in Michal Rokos's OpenSSL extention.) + +: cert_file=((|path|)) + Sets pathname of a X.509 certification file in PEM format. + +: ca_cert=((|cert|)) + Sets an OpenSSL::X509::Certificate object as specific CA certifacate. + (This method is appeared in Michal Rokos's OpenSSL extention.) + +: ca_file=((|path|)) + Sets path of a CA certification file in PEM format. + The file can contrain several CA certificats. + +: ca_path=((|path|)) + Sets path of a CA certification directory containing certifications + in PEM format. + +: verify_mode=((|mode|)) + Sets the flags for server the certification verification at + begining of SSL/TLS session. + +: verify_callback=((|proc|)) + Sets the verify callback for the server certification verification. + +: verify_depth=((|num|)) + Sets the maximum depth for the certificate chain verification. +=end + +require 'net/protocols' +require 'net/http' + +module Net + class HTTP + protocol_param :socket_type, ::Net::NetPrivate::SSLSocket + + attr_accessor :use_ssl + attr_writer :key, :cert, :key_file, :cert_file + attr_writer :ca_file, :ca_path, :timeout + attr_writer :verify_mode, :verify_callback, :verify_depth + attr_reader :peer_cert + + class Conn < ::Net::NetPrivate::HTTPRequest + REQUEST_HAS_BODY=false + RESPONSE_HAS_BODY=false + METHOD="connect" + + def initialize + super nil, nil + end + + def exec( sock, addr, port, ver ) + @socket = sock + request addr, port, ver + @response = get_response(sock) + @response + end + + def request( addr, port, ver ) + @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', addr, port, ver) + @socket.writeline '' + end + end + + def on_connect + if use_ssl + if proxy? + resp = Conn.new.exec(@socket, @address, @port, "1.0") + if resp.code != '200' + raise resp.message + end + end + @socket.key = @key if @key + @socket.key_file = @key_file if @key_file + @socket.cert = @cert if @cert + @socket.cert_file = @cert_file if @cert_file + @socket.ca_file = @ca_file + @socket.ca_path = @ca_path + @socket.verify_mode = @verify_mode + @socket.verify_callback = @verify_callback + @socket.verify_depth = @verify_depth + @socket.timeout = @timeout + @socket.ssl_connect + @peer_cert = socket.peer_cert + end + end + + module ProxyMod + def edit_path( path ) + if use_ssl + 'https://' + addr_port + path + else + 'http://' + addr_port + path + end + end + end + + end +end diff --git a/lib/net/protocols.rb b/lib/net/protocols.rb new file mode 100644 index 0000000..7cd634d --- /dev/null +++ b/lib/net/protocols.rb @@ -0,0 +1,61 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Requirements + This program requires Net 1.2.0 or higher version. + You can get it from RAA or Ruby's CVS repository. + += Version + $Id$ + + 2001/11/06: Contiributed to Ruby/OpenSSL project. +=end + +require 'net/protocol' +require 'forwardable' +require 'openssl' + +module Net + module NetPrivate + + class SSLSocket < Socket + extend Forwardable + + def_delegators(:@socket, + :key=, :cert=, :key_file=, :cert_file=, + :ca_file=, :ca_path=, + :verify_mode=, :verify_callback=, :verify_depth=, + :timeout=) + + def initialize(addr, port, otime = nil, rtime = nil, pipe = nil) + super + @raw_socket = @socket + @socket = OpenSSL::SSL::SSLSocket.new(@raw_socket) + end + + def reopen(tout=nil) + super + @raw_socket = @socket + @socket = OpenSSL::SSL::SSLSocket.new(@raw_socket) + end + + def close + super + @raw_socket.close + end + + def peer_cert; @socket.peer_cert; end + def ssl_connect; @socket.connect; end + + end + end +end diff --git a/lib/net/telnets.rb b/lib/net/telnets.rb new file mode 100644 index 0000000..c7ecbd7 --- /dev/null +++ b/lib/net/telnets.rb @@ -0,0 +1,250 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net::Telnet. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ + + 2001/11/06: Contiributed to Ruby/OpenSSL project. + +== class Net::Telnet + +This class will initiate SSL/TLS session automaticaly if the server +sent OPT_STARTTLS. Some options are added for SSL/TLS. + + host = Net::Telnet::new({ + "Host" => "localhost", + "Port" => "telnets", + ## follows are new options. + 'CertFile' => "user.crt", + 'KeyFile' => "user.key", + 'CAFile' => "/some/where/certs/casert.pem", + 'CAPath' => "/some/where/caserts", + 'VerifyMode' => SSL::VERIFY_PEER, + 'VerifyCallback' => verify_proc + }) + +Or, the new options ('Cert', 'Key' and 'CACert') are available from +Michal Rokos's OpenSSL module. + + cert_data = File.open("user.crt"){|io| io.read } + pkey_data = File.open("user.key"){|io| io.read } + cacert_data = File.open("your_ca.pem"){|io| io.read } + host = Net::Telnet::new({ + "Host" => "localhost", + "Port" => "telnets", + 'Cert' => OpenSSL::X509::Certificate.new(cert_data) + 'Key' => OpenSSL::PKey::RSA.new(pkey_data) + 'CACert' => OpenSSL::X509::Certificate.new(cacert_data) + 'CAFile' => "/some/where/certs/casert.pem", + 'CAPath' => "/some/where/caserts", + 'VerifyMode' => SSL::VERIFY_PEER, + 'VerifyCallback' => verify_proc + }) + +This class is expected to be a superset of usual Net::Telnet. +=end + +require "net/telnet" +require "openssl" + +module Net + class Telnet + attr_reader :ssl + + OPT_STARTTLS = 46.chr # "\056" # "\x2e" # Start TLS + TLS_FOLLOWS = 1.chr # "\001" # "\x01" # FOLLOWS (for STARTTLS) + + alias preprocess_orig preprocess + + def ssl?; @ssl; end + + def preprocess(string) + # combine CR+NULL into CR + string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"] + + # combine EOL into "\n" + string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"] + + string.gsub(/#{IAC}( + [#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]| + [#{DO}#{DONT}#{WILL}#{WONT}][#{OPT_BINARY}-#{OPT_EXOPL}]| + #{SB}[#{OPT_BINARY}-#{OPT_EXOPL}] + (#{IAC}#{IAC}|[^#{IAC}])+#{IAC}#{SE} + )/xno) do + if IAC == $1 # handle escaped IAC characters + IAC + elsif AYT == $1 # respond to "IAC AYT" (are you there) + self.write("nobody here but us pigeons" + EOL) + '' + elsif DO[0] == $1[0] # respond to "IAC DO x" + if OPT_BINARY[0] == $1[1] + @telnet_option["BINARY"] = true + self.write(IAC + WILL + OPT_BINARY) + elsif OPT_STARTTLS[0] == $1[1] + self.write(IAC + WILL + OPT_STARTTLS) + self.write(IAC + SB + OPT_STARTTLS + TLS_FOLLOWS + IAC + SE) + else + self.write(IAC + WONT + $1[1..1]) + end + '' + elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x" + self.write(IAC + WONT + $1[1..1]) + '' + elsif WILL[0] == $1[0] # respond to "IAC WILL x" + if OPT_BINARY[0] == $1[1] + self.write(IAC + DO + OPT_BINARY) + elsif OPT_ECHO[0] == $1[1] + self.write(IAC + DO + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = true + self.write(IAC + DO + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif WONT[0] == $1[0] # respond to "IAC WON'T x" + if OPT_ECHO[0] == $1[1] + self.write(IAC + DONT + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = false + self.write(IAC + DONT + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif SB[0] == $1[0] # respond to "IAC SB xxx IAC SE" + if OPT_STARTTLS[0] == $1[1] && TLS_FOLLOWS[0] == $2[0] + @sock = OpenSSL::SSL::SSLSocket.new(@sock) + @sock.cert_file = @options['CertFile'] + @sock.cert = @options['Cert'] unless @sock.cert + @sock.key_file = @options['KeyFile'] + @sock.key = @options['Key'] unless @sock.key + @sock.ca_cert = @options['CACert'] + @sock.ca_file = @options['CAFile'] + @sock.ca_path = @options['CAPath'] + @sock.timeout = @options['Timeout'] + @sock.verify_mode = @options['VerifyMode'] + @sock.verify_callback = @options['VerifyCallback'] + @sock.verify_depth = @options['VerifyDepth'] + @sock.connect + @ssl = true + end + '' + else + '' + end + end + end # preprocess + + alias waitfor_org waitfor + + def waitfor(options) + time_out = @options["Timeout"] + waittime = @options["Waittime"] + + if options.kind_of?(Hash) + prompt = if options.has_key?("Match") + options["Match"] + elsif options.has_key?("Prompt") + options["Prompt"] + elsif options.has_key?("String") + Regexp.new( Regexp.quote(options["String"]) ) + end + time_out = options["Timeout"] if options.has_key?("Timeout") + waittime = options["Waittime"] if options.has_key?("Waittime") + else + prompt = options + end + + if time_out == false + time_out = nil + end + + line = '' + buf = '' + @rest = '' unless @rest + + until(prompt === line and not IO::select([@sock], nil, nil, waittime)) + unless IO::select([@sock], nil, nil, time_out) + raise TimeoutError, "timed-out; wait for the next data" + end + begin + c = @rest + @sock.sysread(1024 * 1024) + @dumplog.log_dump('<', c) if @options.has_key?("Dump_log") + if @options["Telnetmode"] + pos = 0 + catch(:next){ + while true + case c[pos] + when IAC[0] + case c[pos+1] + when DO[0], DONT[0], WILL[0], WONT[0] + throw :next unless c[pos+2] + pos += 3 + when SB[0] + ret = detect_sub_negotiation(c, pos) + throw :next unless ret + pos = ret + when nil + throw :next + else + pos += 2 + end + when nil + throw :next + else + pos += 1 + end + end + } + + buf = preprocess(c[0...pos]) + @rest = c[pos..-1] + end + @log.print(buf) if @options.has_key?("Output_log") + line.concat(buf) + yield buf if block_given? + rescue EOFError # End of file reached + if line == '' + line = nil + yield nil if block_given? + end + break + end + end + line + end + + private + + def detect_sub_negotiation(data, pos) + return nil if data.length < pos+6 # IAC SB x param IAC SE + pos += 3 + while true + case data[pos] + when IAC[0] + if data[pos+1] == SE[0] + pos += 2 + return pos + else + pos += 2 + end + when nil + return nil + else + pos += 1 + end + end + end + + end +end diff --git a/lib/openssl.rb b/lib/openssl.rb new file mode 100644 index 0000000..c3d4212 --- /dev/null +++ b/lib/openssl.rb @@ -0,0 +1,24 @@ +=begin += $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +require 'openssl.so' + +require 'openssl/bn' +require 'openssl/digest' +require 'openssl/pkey' +require 'openssl/ssl' +require 'openssl/x509' + diff --git a/lib/openssl/bn.rb b/lib/openssl/bn.rb new file mode 100644 index 0000000..f2ea761 --- /dev/null +++ b/lib/openssl/bn.rb @@ -0,0 +1,39 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for BN + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + +class BN + def to_i + to_s.to_i + end +end # BN + +end # OpenSSL + +## +# Add double dispatch to Integer +# +class Integer + def to_bn + OpenSSL::BN::new(self) + end +end # Integer + diff --git a/lib/openssl/buffering.rb b/lib/openssl/buffering.rb new file mode 100644 index 0000000..a490772 --- /dev/null +++ b/lib/openssl/buffering.rb @@ -0,0 +1,185 @@ +=begin += $RCSfile$ -- Buffering mix-in module. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +module Buffering + include Enumerable + attr_accessor :sync + BLOCK_SIZE = 1024 + + # + # for reading. + # + private + + def fill_rbuff + @rbuffer = "" unless defined? @rbuffer + begin + if self.respond_to?(:to_io) + IO.select([self.to_io], nil, nil) + end + @rbuffer << self.sysread(BLOCK_SIZE) + rescue EOFError + @eof = true + end + end + + def consume_rbuff(size=nil) + if @rbuffer.size == 0 + @eof = nil + nil + else + size = @rbuffer.size unless size + ret = @rbuffer[0, size] + @rbuffer[0, size] = "" + ret + end + end + + public + + def read(size=nil) + fill_rbuff unless defined? @rbuffer + until @eof + break if size && size <= @rbuffer.size + fill_rbuff + end + consume_rbuff(size) + end + + def gets(eol=$/) + fill_rbuff unless defined? @rbuffer + idx = @rbuffer.index(eol) + until @eof + break if idx + fill_rbuff + idx = @rbuffer.index(eol) + end + if eol.is_a?(Regexp) + size = idx ? idx+$&.size : nil + else + size = idx ? idx+eol.size : nil + end + consume_rbuff(size) + end + + def each(eol=$/) + while line = self.gets(eol?) + yield line + end + end + alias each_line each + + def readlines(eol=$/) + ary = [] + while line = self.gets(eol) + ary << line + end + ary + end + + def readline(eol=$/) + raise EOFErorr if eof? + gets(eol) + end + + def getc + c = read(1) + c ? c.to_i : nil + end + + def each_byte + while c = getc + yield(c) + end + end + + def readchar + raise EOFErorr if eof? + getc + end + + def ungetc(c) + @buffer[0,0] = c.chr + end + + def eof? + @eof && @rbuffer.size == 0 + end + alias eof eof? + + # + # for writing. + # + private + + def do_write(s) + @wbuffer = "" unless defined? @wbuffer + @wbuffer << s + if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/) + remain = idx ? idx + $/.size : @wbuffer.length + nwritten = 0 + while remain > 0 + nwrote = syswrite(@wbuffer[nwritten,remain]) + remain -= nwrote + nwritten += nwrote + end + @wbuffer = "" + end + end + + public + + def write(s) + do_write(s) + s.length + end + + def << (s) + do_write(s) + self + end + + def puts(*args) + s = "" + args.each{ |arg| s << arg.to_s + $/ } + do_write(s) + nil + end + + def print(*args) + s = "" + args.each{ |arg| s << arg.to_s } + do_write(s) + nil + end + + def printf(s, *args) + do_write(s % args) + nil + end + + def flush + osync = @sync + @sync = true + do_write "" + @sync = osync + end + + def close + flush + sysclose + end +end diff --git a/lib/openssl/digest.rb b/lib/openssl/digest.rb new file mode 100644 index 0000000..2cfb8a7 --- /dev/null +++ b/lib/openssl/digest.rb @@ -0,0 +1,42 @@ +=begin += $RCSfile$ -- Ruby-space predefined Digest subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL +module Digest + +["DSS", "DSS1", "MD2", "MD4", "MD5", "MDC2", "RIPEMD160", "SHA", "SHA1"].each do |digest| + eval(<<-EOD) + class #{digest} < Digest + def initialize() + super(\"#{digest}\") + end + def #{digest}::digest(data) + super(\"#{digest}\", data) + end + def #{digest}::hexdigest(data) + super(\"#{digest}\", data) + end + end + EOD +end + +end # Digest +end # OpenSSL + diff --git a/lib/openssl/pkey.rb b/lib/openssl/pkey.rb new file mode 100644 index 0000000..f05878b --- /dev/null +++ b/lib/openssl/pkey.rb @@ -0,0 +1,123 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for PKey and subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL +module PKey + +if defined? DSA + class DSA + def DSA::new(arg, pass=nil) + if arg.kind_of? Fixnum + DSA::generate(arg) {|p,n| + if block_given? then yield [p,n] end + } + else + DSA::new_from_pem(arg, pass) + end + end # DSA::new + # + # DSA::new_from_pem(PEM string, pass) is built-in + # DSA::new_from_fixnum(size) is an alias to DSA::generate(size) + # DSA::generate(size) is built-in; yields p,n + # + def sign(digest, data) + unless private? + raise OpenSSL::PKey::DSAError, "Cannot sign with public key!" + end + unless digest.kind_of? OpenSSL::Digest::ANY + raise TypeError, "digest alg needed! (got #{digest.class})" + end + sign_digest digest.update(data.to_s).digest + end # sign + + def verify(digest, signature, data) + unless digest.kind_of? OpenSSL::Digest::ANY + raise TypeError, "digest alg needed! (got #{digest.class})" + end + unless signature.class == String + raise TypeError, "Signature as String expected (got #{sign.class})" + end + verify_digest(digest.update(data.to_s).digest, signature) + end # verify + end # DSA +end #defined? DSA + +if defined? RSA + class RSA + def RSA::new(arg, pass=nil) + if arg.kind_of? Fixnum + RSA::generate(arg) {|p,n| + if block_given? then yield [p,n] end + } + else + RSA::new_from_pem(arg, pass) + end + end # RSA::new + # + # RSA::new_from_pem(PEM string, pass) is built-in + # RSA::new_from_fixnum(size) is an alias to RSA::generate(size) + # RSA::generate(size) is built-in; yields p,n + # + def sign(digest, data) + unless self.private? + raise OpenSSL::PKey::RSAError, "Cannot sign with public key!" + end + unless digest.kind_of? OpenSSL::Digest::ANY + raise TypeError, "digest alg needed! (got #{digest.class})" + end + private_encrypt digest.update(data.to_s).digest + end # sign + + def verify(digest, signature, data) + unless digest.kind_of? OpenSSL::Digest::ANY + raise TypeError, "digest alg needed! (got #{digest.class})" + end + unless signature.class == String + raise TypeError, "Signature as String expected (got #{sign.class})" + end + md_s = self.public_decrypt signature + md_d = digest.update(data.to_s).digest + md_s == md_d + end # verify + end # RSA +end # defined? RSA + +if defined? DH + class DH + def DH::new(arg, gen = 2) + if arg.kind_of? Fixnum + DH::generate(arg, gen) {|p,n| + if block_given? then yield [p,n] end + } + else + DH::new_from_pem(arg) + end + end # DH::new + # + # DH::new_from_pem(PEM string, pass) is built-in + # DH::new_from_fixnum(size, gen) is an alias to DH::generate(size, gen) + # DH::generate(size, gen) is built-in; yields p,n + # + end # DH +end # defined? DH + +end # PKey +end # OpenSSL + diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb new file mode 100644 index 0000000..2ce8a67 --- /dev/null +++ b/lib/openssl/ssl.rb @@ -0,0 +1,42 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +require 'openssl/buffering' +require 'thread' + +module OpenSSL +module SSL + +class SSLSocket + include Buffering + CallbackMutex = Mutex.new + + def connect + CallbackMutex.synchronize{ __connect } + end + + def accept + CallbackMutex.synchronize{ __accept } + end +end # SSLSocket + +end # SSL +end # OpenSSL + diff --git a/lib/openssl/x509.rb b/lib/openssl/x509.rb new file mode 100644 index 0000000..f64774f --- /dev/null +++ b/lib/openssl/x509.rb @@ -0,0 +1,186 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz> + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL +module X509 + +class Name + def Name::new(arg) + type = arg.class + while type + method = "new_from_#{type.name.downcase}".intern + return Name::send(method, arg) if Name::respond_to? method + type = type.superclass + end + raise TypeError, "Don't how to make new #{self} from #{arg.class}" + ###Name::send("new_from_#{arg.class.name.downcase}", arg) + end + # + # Name::new_from_hash(hash) is built-in method + # + def Name::new_from_string(str) # we're expecting string like "/A=B/C=D/E=F" + hash = Hash::new + key = val = nil # speed optim. + ary = str.split("/") + ary.shift # first item is "" - so skip it + ary.each {|item| + key, val = item.split("=") + hash[key] = val + } + Name::new_from_hash(hash) + ###ary.collect! {|item| item.split("=") } + ###Name::new_from_array(ary) + end + + def Name::new_from_array(ary) # [["A","B"],["C","D"],["E","F"]] + hash = Hash::new + ary.each {|key, val| + hash[key] = val + } + Name::new_from_hash(hash) + end + # + # to_h is built-in method + # + def to_s # "/A=B/C=D/E=F" + hash = self.to_h + str = "" + hash.keys.each do |key| + str += "/" + key + "=" + hash[key] + end + str + end + + def to_a # [["A","B"],["C","D"],["E","F"]] + to_h.to_a + end +end # Name + +class ExtensionFactory + def create_extension(*arg) + if arg.size == 1 then arg = arg[0] end + type = arg.class + while type + method = "create_ext_from_#{type.name.downcase}".intern + return send(method, arg) if respond_to? method + type = type.superclass + end + raise TypeError, "Don't how to create ext from #{arg.class}" + ###send("create_ext_from_#{arg.class.name.downcase}", arg) + end + # + # create_ext_from_array is built-in + # + def create_ext_from_string(str) # "oid = critical, value" + unless str =~ /\s*=\s*/ + raise ArgumentError, "string in format \"oid = value\" expected" + end + ary = [] + ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning + rest = $'.sub(/\s*$/,"") # delete them from the end + if rest =~ /^critical,\s*/ # handle 'critical' option + ary << $' + ary << true + else + ary << rest + end + create_ext_from_array(ary) + end + + def create_ext_from_hash(hash) # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + unless (hash.has_key? "oid" and hash.has_key? "value") + raise ArgumentError, "hash in format {\"oid\"=>..., \"value\"=>...} expected" + end + ary = [] + ary << hash["oid"] + ary << hash["value"] + ary << hash["critical"] if hash.has_key? "critical" + create_ext_from_array(ary) + end +end # ExtensionFactory + +class Extension + # note: Extension.new is UNDEFed! - use ExtensionFactory.create_extension + # + # to_a is built-in + # + def to_s # "oid = critical, value" + ary = self.to_a + str = ary[0] + " = " + str += "critical, " if ary[2] == true + str += ary[1] + end + + def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + ary = self.to_a + {"oid"=>ary[0],"value"=>ary[1],"critical"=>ary[2]} + end + + def oid + self.to_a[0] + end + + def value + self.to_a[1] + end + + def critical? + self.to_a[2] + end +end # Extension + +class Attribute + def Attribute::new(arg) + type = arg.class + while type + method = "new_from_#{type.name.downcase}".intern + return Attribute::send(method, arg) if Attribute::respond_to? method + type = type.superclass + end + raise "Don't how to make new #{self} from #{arg.class}" + ###Attribute::send("new_from_#{arg.class.name.downcase}", arg) + end + # + # Attribute::new_from_array(ary) is built-in method + # + def Attribute::new_from_string(str) # "oid = value" + unless str =~ /\s*=\s*/ + raise ArgumentError, "string in format \"oid = value\" expected" + end + ary = [] + ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning + ary << $'.sub(/\s*$/,"") # delete them from the end + Attribute::new_from_array(ary) + end + + def Attribute::new_from_hash(hash) # {"oid"=>"...", "value"=>"..."} + unless (hash.has_key? "oid" and hash.has_key? "value") + raise ArgumentError, "hash in format {\"oid\"=>..., \"value\"=>...} expected" + end + ary = [] + ary << hash["oid"] + ary << hash["value"] + Attribute::new_from_array(ary) + end +end # Attribute + +end # X509 +end # OpenSSL + diff --git a/missing/strptime.c b/missing/strptime.c new file mode 100644 index 0000000..5b90259 --- /dev/null +++ b/missing/strptime.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 1994 Powerdog Industries. All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgement: + * This product includes software developed by Powerdog Industries. + * 4. The name of Powerdog Industries may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``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 POWERDOG INDUSTRIES 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. + */ +#include <stdio.h> +#include <stdlib.h> +/* #include <unistd.h> */ +#include <time.h> +#include <ctype.h> +#include <string.h> + +#ifdef NT +#define strncasecmp _strnicmp +#else +#ifndef HAVE_STRNCASECMP +# include "./strncasecmp.c" +#endif +#endif + +/* +#if !defined(WIN32) +# include "common/config.h" +#endif +*/ + +#define asizeof(a) (sizeof (a) / sizeof ((a)[0])) + +#ifndef sun +struct dtconv { + char *abbrev_month_names[12]; + char *month_names[12]; + char *abbrev_weekday_names[7]; + char *weekday_names[7]; + char *time_format; + char *sdate_format; + char *dtime_format; + char *am_string; + char *pm_string; + char *ldate_format; +}; +#endif + +static struct dtconv En_US = { + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }, + { "January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December" }, + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, + { "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" }, + "%H:%M:%S", + "%m/%d/%y", + "%a %b %e %T %Z %Y", + "AM", + "PM", + "%A, %B, %e, %Y" +}; + +#ifdef SUNOS4 +extern int strncasecmp(); +#endif + +char * +strptime(char *buf, char *fmt, struct tm *tm) +{ + char c, + *ptr; + int i, j, + len; + ptr = fmt; + while (*ptr != 0) { + if (*buf == 0) + break; + + c = *ptr++; + + if (c != '%') { + if (isspace(c)) + while (*buf != 0 && isspace(*buf)) + buf++; + else if (c != *buf++) + return 0; + continue; + } + + c = *ptr++; + switch (c) { + case 0: + case '%': + if (*buf++ != '%') + return 0; + break; + + case 'C': + buf = strptime(buf, En_US.ldate_format, tm); + if (buf == 0) + return 0; + break; + + case 'c': + buf = strptime(buf, "%x %X", tm); + if (buf == 0) + return 0; + break; + + case 'D': + buf = strptime(buf, "%m/%d/%y", tm); + if (buf == 0) + return 0; + break; + + case 'R': + buf = strptime(buf, "%H:%M", tm); + if (buf == 0) + return 0; + break; + + case 'r': + buf = strptime(buf, "%I:%M:%S %p", tm); + if (buf == 0) + return 0; + break; + + case 'T': + buf = strptime(buf, "%H:%M:%S", tm); + if (buf == 0) + return 0; + break; + + case 'X': + buf = strptime(buf, En_US.time_format, tm); + if (buf == 0) + return 0; + break; + + case 'x': + buf = strptime(buf, En_US.sdate_format, tm); + if (buf == 0) + return 0; + break; + + case 'j': + if (!isdigit(*buf)) + return 0; + + for (i = 0; *buf != 0 && isdigit(*buf); buf++) { + i *= 10; + i += *buf - '0'; + } + if (i > 365) + return 0; + + tm->tm_yday = i; + break; + + case 'M': + case 'S': + if (*buf == 0 || isspace(*buf)) + break; + + if (!isdigit(*buf)) + return 0; + + for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) { + i *= 10; + i += *buf - '0'; + } + if (i > 59) + return 0; + + if (c == 'M') + tm->tm_min = i; + else + tm->tm_sec = i; + + if (*buf != 0 && isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) + ptr++; + break; + + case 'H': + case 'I': + case 'k': + case 'l': + if (!isdigit(*buf)) + return 0; + + for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) { + i *= 10; + i += *buf - '0'; + } + if (c == 'H' || c == 'k') { + if (i > 23) + return 0; + } else if (i > 11) + return 0; + + tm->tm_hour = i; + + if (*buf != 0 && isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) + ptr++; + break; + + case 'p': + len = strlen(En_US.am_string); + if (strncasecmp(buf, En_US.am_string, len) == 0) { + if (tm->tm_hour > 12) + return 0; + if (tm->tm_hour == 12) + tm->tm_hour = 0; + buf += len; + break; + } + + len = strlen(En_US.pm_string); + if (strncasecmp(buf, En_US.pm_string, len) == 0) { + if (tm->tm_hour > 12) + return 0; + if (tm->tm_hour != 12) + tm->tm_hour += 12; + buf += len; + break; + } + + return 0; + + case 'A': + case 'a': + for (i = 0; i < asizeof(En_US.weekday_names); i++) { + len = strlen(En_US.weekday_names[i]); + if (strncasecmp(buf, + En_US.weekday_names[i], + len) == 0) + break; + + len = strlen(En_US.abbrev_weekday_names[i]); + if (strncasecmp(buf, + En_US.abbrev_weekday_names[i], + len) == 0) + break; + } + if (i == asizeof(En_US.weekday_names)) + return 0; + + tm->tm_wday = i; + buf += len; + break; + + case 'd': + case 'e': + if (!isdigit(*buf)) + return 0; + + for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) { + i *= 10; + i += *buf - '0'; + } + if (i > 31) + return 0; + + tm->tm_mday = i; + + if (*buf != 0 && isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) + ptr++; + break; + + case 'B': + case 'b': + case 'h': + for (i = 0; i < asizeof(En_US.month_names); i++) { + len = strlen(En_US.month_names[i]); + if (strncasecmp(buf, + En_US.month_names[i], + len) == 0) + break; + + len = strlen(En_US.abbrev_month_names[i]); + if (strncasecmp(buf, + En_US.abbrev_month_names[i], + len) == 0) + break; + } + if (i == asizeof(En_US.month_names)) + return 0; + + tm->tm_mon = i; + buf += len; + break; + + case 'm': + if (!isdigit(*buf)) + return 0; + + for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) { + i *= 10; + i += *buf - '0'; + } + if (i < 1 || i > 12) + return 0; + + tm->tm_mon = i - 1; + + if (*buf != 0 && isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) + ptr++; + break; + + case 'Y': + case 'y': + if (*buf == 0 || isspace(*buf)) + break; + + if (!isdigit(*buf)) + return 0; + + for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<((c=='Y')?4:2); j++,buf++) { + i *= 10; + i += *buf - '0'; + } + + if (c == 'Y') + i -= 1900; + else if (i < 69) /*c=='y', 00-68 is for 20xx, the rest is for 19xx*/ + i += 100; + + if (i < 0) + return 0; + + tm->tm_year = i; + + if (*buf != 0 && isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) + ptr++; + break; + } + } + + return buf; +} + diff --git a/openssl_missing.c b/openssl_missing.c new file mode 100644 index 0000000..4aeef13 --- /dev/null +++ b/openssl_missing.c @@ -0,0 +1,30 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_HMAC) + +#include <string.h> +#include <openssl/hmac.h> + +/* to hmac.[ch] */ +int +HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in) +{ + if (in == NULL) { + /* HMACerr(HMAC_CTX_COPY,HMAC_R_INPUT_NOT_INITIALIZED); */ + return 0; + } + memcpy(out, in, sizeof(HMAC_CTX)); + + return 1; +} + +#endif /* NO_HMAC */ + diff --git a/openssl_missing.h b/openssl_missing.h new file mode 100644 index 0000000..e2af05f --- /dev/null +++ b/openssl_missing.h @@ -0,0 +1,59 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_OPENSSL_MISSING_H_) +#define _OSSL_OPENSSL_MISSING_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * These functions are not included in headers of OPENSSL <= 0.9.6b + */ + +/* to pem.h */ +#if !defined(OPENSSL_NO_DSA) +# define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_DSAPublicKey,PEM_STRING_DSA_PUBLIC,bp,(char **)x,cb,u) +# define PEM_write_bio_DSAPublicKey(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_DSAPublicKey,\ + PEM_STRING_DSA_PUBLIC,\ + bp,(char *)x, NULL, NULL, 0, NULL, NULL) +#endif /* NO_DSA */ + +/* to x509.h */ +#if !defined(OPENSSL_NO_DSA) +# define DSAPrivateKey_dup(dsa) (DSA *)ASN1_dup((int (*)())i2d_DSAPrivateKey, \ + (char *(*)())d2i_DSAPrivateKey,(char *)dsa) +# define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup((int (*)())i2d_DSAPublicKey, \ + (char *(*)())d2i_DSAPublicKey,(char *)dsa) +#endif /* NO_DSA */ + +# define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((int (*)())i2d_X509_REVOKED, \ + (char *(*)())d2i_X509_REVOKED, (char *)rev) + +/* to pkcs7.h */ +#define PKCS7_SIGNER_INFO_dup(si) (PKCS7_SIGNER_INFO *)ASN1_dup((int (*)())i2d_PKCS7_SIGNER_INFO, \ + (char *(*)())d2i_PKCS7_SIGNER_INFO, (char *)si) +#define PKCS7_RECIP_INFO_dup(ri) (PKCS7_RECIP_INFO *)ASN1_dup((int (*)())i2d_PKCS7_RECIP_INFO, \ + (char *(*)())d2i_PKCS7_RECIP_INFO, (char *)ri) + +/* to hmac.[ch] */ +#if !defined(OPENSSL_NO_HMAC) +int HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in); +#endif /* NO_HMAC */ + +#if defined(__cplusplus) +} +#endif + +#endif /* _OSSL_OPENSSL_MISSING_H_ */ + @@ -0,0 +1,170 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +/* + * Surpress dumb warning about implicit declaration of strptime on Linux + */ +#if defined(__linux__) || defined(linux) +# define _GNU_SOURCE +#endif + +#include "ossl.h" + +/* + * On Windows platform there is no strptime function + * implementation in strptime.c + */ +#if !defined(HAVE_STRPTIME) +# include "./missing/strptime.c" +#endif + +/* + * Check Types + */ +void +ossl_check_kind(VALUE obj, VALUE klass) +{ + if (rb_obj_is_kind_of(obj, klass) == Qfalse) { + rb_raise(rb_eTypeError, "wrong argument (%s)! (Expected kind of %s)",\ + rb_class2name(CLASS_OF(obj)), rb_class2name(klass)); + } +} + +void +ossl_check_instance(VALUE obj, VALUE klass) +{ + if (rb_obj_is_instance_of(obj, klass) == Qfalse) { + rb_raise(rb_eTypeError, "wrong argument (%s)! (Expected instance of %s)",\ + rb_class2name(CLASS_OF(obj)), rb_class2name(klass)); + } +} + +/* + * DATE conversion + */ +VALUE +asn1time_to_time(ASN1_UTCTIME *time) +{ + struct tm tm; + + switch(time->type) { + case V_ASN1_UTCTIME: + if (!strptime(time->data, "%y%m%d%H%M%SZ", &tm)) { + rb_raise(rb_eTypeError, "bad UTCTIME format"); + } + break; + case V_ASN1_GENERALIZEDTIME: + if (!strptime(time->data, "%Y%m%d%H%M%SZ", &tm)) { + rb_raise(rb_eTypeError, "bad GENERALIZEDTIME format" ); + } + break; + default: + rb_raise(rb_eTypeError, "unknown time format"); + } + /*return rb_time_new(mktime(gmtime(mktime(&tm))), 0); * Is this correct? */ + return rb_time_new(mktime(&tm), 0); /* or this one? */ +} + +/* + * This function is not exported in Ruby's *.h + */ +extern struct timeval rb_time_timeval(VALUE time); + +time_t +time_to_time_t(VALUE time) +{ + struct timeval t; + + t = rb_time_timeval(time); + + return t.tv_sec; +} + +/* + * Modules + */ +VALUE mOSSL; +VALUE mDigest; +VALUE mNetscape; +VALUE mPKCS7; +VALUE mPKey; +VALUE mRandom; +VALUE mSSL; + +/* + * OpenSSLError < StandardError + */ +VALUE eOSSLError; + +/* + * OSSL library init + */ +void +Init_openssl() +{ +#if defined(OSSL_DEBUG) + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif + + /* + * Init all digests, ciphers + */ + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* + * Universe of Modules + */ + mOSSL = rb_define_module("OpenSSL"); + mNetscape = rb_define_module_under(mOSSL, "Netscape"); + mPKCS7 = rb_define_module_under(mOSSL, "PKCS7"); + mPKey = rb_define_module_under(mOSSL, "PKey"); + mRandom = rb_define_module_under(mOSSL, "Random"); + mSSL = rb_define_module_under(mOSSL, "SSL"); + + /* + * Constants + */ + rb_define_const(mOSSL, "VERSION", rb_str_new2(OSSL_VERSION)); + rb_define_const(mOSSL, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); + + /* + * Generic error, + * common for all classes under OpenSSL module + */ + eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); + + /* + * Init components + */ + Init_ossl_bn(); + Init_ossl_cipher(); + Init_ossl_config(mOSSL); + Init_ossl_digest(); + Init_ossl_hmac(mOSSL); + Init_ossl_pkcs7(mPKCS7); + Init_ossl_pkey(mPKey); + Init_ossl_rand(mRandom); + Init_ossl_spki(mNetscape); + Init_ossl_ssl(mSSL); + Init_ossl_x509(); +} + +#if defined(OSSL_DEBUG) +/* + * Check if all symbols are OK with 'make LDSHARED=gcc all' + */ +int +main(int argc, char *argv[], char *env[]) +{ + return 0; +} +#endif /* OSSL_DEBUG */ + @@ -0,0 +1,220 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_H_) +#define _OSSL_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(NT) +# define OpenFile WINAPI_OpenFile +#endif +#include <errno.h> +#include <openssl/err.h> +#include <openssl/asn1_mac.h> +#include <openssl/x509v3.h> +#include <openssl/ssl.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> +#include <openssl/bn_lcl.h> +#if defined(NT) +# undef OpenFile +#endif + +/* + * OpenSSL has defined RFILE and Ruby has defined RFILE - so undef it! + */ +#if defined(RFILE) /*&& !defined(OSSL_DEBUG)*/ +# undef RFILE +#endif +#include <ruby.h> + +/* + * Check the Ruby version and OpenSSL + * The only supported are: + * Ruby >= 1.7 + * OpenSSL >= 0.9.7 + */ +#include <version.h> +#include <openssl/opensslv.h> + +#if (OPENSSL_VERSION_NUMBER < 0x00907000L) && (RUBY_VERSION_CODE < 172) +# error ! OSSL2 needs Ruby >= 1.7.2 and OpenSSL >= 0.9.7 its run. +#endif + +/* + * Common Module + */ +extern VALUE mOSSL; +extern VALUE mNetscape; +extern VALUE mPKCS7; +extern VALUE mPKey; +extern VALUE mRandom; +extern VALUE mSSL; + +/* + * Common Error Class + */ +VALUE eOSSLError; + +/* + * Classes + */ +extern VALUE cSPKI; +extern VALUE eSPKIError; +extern VALUE eRandomError; +extern VALUE cSSLSocket; +extern VALUE eSSLError; +/* PKey */ +extern ID id_private_q; +extern VALUE cPKey; +extern VALUE ePKeyError; +extern VALUE cRSA; +extern VALUE eRSAError; +extern VALUE cDSA; +extern VALUE eDSAError; +extern VALUE cDH; +extern VALUE eDHError; +/* PKCS7 */ +extern VALUE cPKCS7; +extern VALUE cPKCS7SignerInfo; +extern VALUE ePKCS7Error; +/* HMAC */ +extern VALUE cHMAC; +extern VALUE eHMACError; +/* Conf */ +extern VALUE cConfig; +extern VALUE eConfigError; + +/* + * CheckTypes + */ +#define OSSL_Check_Kind(obj, klass) ossl_check_kind(obj, klass) +void ossl_check_kind(VALUE, VALUE); +#define OSSL_Check_Instance(obj, klass) ossl_check_instance(obj, klass) +#define OSSL_Check_Type(obj, klass) OSSL_Check_Instance(obj, klass) +void ossl_check_instance(VALUE, VALUE); + +/* + * DATE conversion + */ +VALUE asn1time_to_time(ASN1_UTCTIME *); +time_t time_to_time_t(VALUE); + +/* + * ERRor messages + */ +#define OSSL_ErrMsg() \ + ERR_error_string(ERR_get_error(), NULL) + +#if defined(OSSL_DEBUG) +# define OSSL_Raise(klass,text) \ + rb_raise(klass, "%s%s [in '%s', ('%s':%d)]", \ + text, OSSL_ErrMsg(), __func__, __FILE__, __LINE__) +# define OSSL_Warn(text) \ + rb_warn("%s%s [in '%s', ('%s':%d)]", \ + text, OSSL_ErrMsg(), __func__, __FILE__, __LINE__) +# define OSSL_Warning(text) \ + rb_warning("%s%s [in '%s', ('%s':%d)]", \ + text, OSSL_ErrMsg(), __func__, __FILE__, __LINE__) +#else /* OSSL_DEBUG */ +# define OSSL_Raise(klass,text) \ + rb_raise(klass, "%s%s", text, OSSL_ErrMsg()) +# define OSSL_Warn(text) \ + rb_warn("%s%s", text, OSSL_ErrMsg()) +# define OSSL_Warning(text) \ + rb_warning("%s%s", text, OSSL_ErrMsg()) +#endif /* OSSL_DEBUG */ + +/* + * Config + */ +void Init_ossl_config(VALUE); + +/* + * Netscape SPKI + */ +void Init_ossl_spki(VALUE); + +/* + * RAND - module methods only + */ +void Init_ossl_rand(VALUE); + +/* + * PKey + */ +VALUE ossl_pkey_new(EVP_PKEY *); +VALUE ossl_pkey_new_from_file(VALUE); +EVP_PKEY *ossl_pkey_get_EVP_PKEY(VALUE); +void Init_ossl_pkey(VALUE); + +/* + * RSA + */ +#if !defined(OPENSSL_NO_RSA) +VALUE ossl_rsa_new(RSA *); +RSA *ossl_rsa_get_RSA(VALUE); +EVP_PKEY *ossl_rsa_get_EVP_PKEY(VALUE); +#endif /* NO_RSA */ +void Init_ossl_rsa(VALUE, VALUE, VALUE); + +/* + * DSA + */ +#if !defined(OPENSSL_NO_DSA) +VALUE ossl_dsa_new(DSA *); +DSA *ossl_dsa_get_DSA(VALUE); +EVP_PKEY *ossl_dsa_get_EVP_PKEY(VALUE); +#endif /* NO_DSA */ +void Init_ossl_dsa(VALUE, VALUE, VALUE); + +/* + * DH + */ +#if !defined(OPENSSL_NO_DH) +VALUE ossl_dh_new(DH *); +DH *ossl_dh_get_DH(VALUE); +EVP_PKEY *ossl_dh_get_EVP_PKEY(VALUE); +#endif /* NO_DH */ +void Init_ossl_dh(VALUE, VALUE, VALUE); + +/* + * SSL + */ +void Init_ossl_ssl(VALUE); + +/* + * PKCS7 + */ +VALUE ossl_pkcs7si_new(PKCS7_SIGNER_INFO *); +PKCS7_SIGNER_INFO *ossl_pkcs7si_get_PKCS7_SIGNER_INFO(VALUE); +void Init_ossl_pkcs7(VALUE); + +/* + * HMAC + */ +void Init_ossl_hmac(VALUE); + +#include "openssl_missing.h" +#include "ossl_bn.h" +#include "ossl_cipher.h" +#include "ossl_digest.h" +#include "ossl_version.h" +#include "ossl_x509.h" + +#if defined(__cplusplus) +} +#endif + +#endif /* _OSSL_H_ */ + diff --git a/ossl_bn.c b/ossl_bn.c new file mode 100644 index 0000000..ffe282e --- /dev/null +++ b/ossl_bn.c @@ -0,0 +1,732 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 UNKNOWN <oss-ruby@technorama.net> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +/* modified by Michal Rokos <m.rokos@sh.cvut.cz> */ +#include "ossl.h" + +#define WrapBN OSSLWrapBN +#define GetBN(obj, bn) do { \ + Data_Get_Struct(obj, BIGNUM, bn); \ + if (!bn) { \ + rb_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ +} while (0) + +/* + * BN_CTX - is used in more difficult math. ops + * (Why just 1? Because Ruby itself isn't thread safe, we don't need to care about threads) + */ +static BN_CTX *ossl_bn_ctx; + +/* + * Classes + */ +VALUE cBN; +VALUE eBNError; + +/* + * Public + */ +VALUE +ossl_bn_new(BIGNUM *bn) +{ + BIGNUM *new = NULL; + VALUE obj; + + if (!bn) { + new = BN_new(); + } else { + new = BN_dup(bn); + } + + if (!new) { + OSSL_Raise(eBNError, ""); + } + WrapBN(obj, new); + return obj; +} + +BIGNUM * +ossl_bn_get_BIGNUM(VALUE obj) +{ + BIGNUM *bn = NULL, *new; + + OSSLGetBN(obj, bn); + + if (!(new = BN_dup(bn))) { + OSSL_Raise(eBNError, ""); + } + return new; +} + +/* + * Private + */ +static VALUE +ossl_bn_s_allocate(VALUE klass) +{ + return ossl_bn_new(NULL); +} + +static VALUE +ossl_bn_initialize(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn = NULL; + VALUE str, bs; + int base = 10; + + GetBN(self, bn); + + rb_call_super(0, 0); + + if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) { + base = NUM2INT(bs); + } + + if (rb_obj_is_instance_of(str, cBN)) { + BIGNUM *other; + + GetBN(str, other); + if (!BN_copy(bn, other)) { + OSSL_Raise(eBNError, ""); + } + } else { + str = rb_String(str); + + switch (base) { + /* + * MPI: + if (!BN_mpi2bn(RSTRING(str)->ptr, RSTRING(str)->len, bn)) { + OSSL_Raise(eBNError, ""); + } + break; + case 2: + if (!BN_bin2bn(RSTRING(str)->ptr, RSTRING(str)->len, bn)) { + OSSL_Raise(eBNError, ""); + } + break; + */ + case 10: + if (!BN_dec2bn(&bn, StringValuePtr(str))) { + OSSL_Raise(eBNError, ""); + } + break; + case 16: + if (!BN_hex2bn(&bn, StringValuePtr(str))) { + OSSL_Raise(eBNError, ""); + } + break; + default: + rb_raise(rb_eArgError, "illegal radix %d", base); + } + } + return self; +} + +static VALUE +ossl_bn_to_s(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn = NULL; + VALUE str, bs; + int base = 10; + char *buf = NULL; + + GetBN(self, bn); + + if (rb_scan_args(argc, argv, "01", &bs) == 1) { + base = NUM2INT(bs); + } + + switch (base) { + /* + * MPI: { + int len = BN_bn2mpi(bn, NULL); + if (!(buf = OPENSSL_malloc(len))) { + OSSL_Raise(eBNError, "Cannot allocate mem for BN"); + } + if (BN_bn2mpi(bn, buf) != len) { + OPENSSL_free(buf); + OSSL_Raise(eBNError, ""); + } + } + case 2: { + int len = BN_num_bytes(bn); + if (!(buf = OPENSSL_malloc(len))) { + OSSL_Raise(eBNError, "Cannot allocate mem for BN"); + } + if (BN_bn2bin(bn, buf) != len) { + OPENSSL_free(buf); + OSSL_Raise(eBNError, ""); + } + buf[len-1] = '\0'; + } + break; + */ + case 10: + if (!(buf = BN_bn2dec(bn))) { + OSSL_Raise(eBNError, ""); + } + break; + case 16: + if (!(buf = BN_bn2hex(bn))) { + OSSL_Raise(eBNError, ""); + } + break; + default: + rb_raise(rb_eArgError, "illegal radix %d", base); + } + str = rb_str_new2(buf); + OPENSSL_free(buf); + + return str; +} + +#define BIGNUM_BOOL1(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn = NULL; \ + \ + GetBN(self, bn); \ + \ + if (BN_##func(bn) == 1) { \ + return Qtrue; \ + } \ + return Qfalse; \ + } +BIGNUM_BOOL1(is_zero); +BIGNUM_BOOL1(is_one); +BIGNUM_BOOL1(is_odd); + +#define BIGNUM_1c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn = NULL; \ + BIGNUM *result = NULL; \ + VALUE obj; \ + \ + GetBN(self, bn); \ + \ + if (!(result = BN_new())) { \ + OSSL_Raise(eBNError, ""); \ + } \ + if (BN_##func(result, bn, ossl_bn_ctx) != 1) { \ + BN_free(result); \ + OSSL_Raise(eBNError, ""); \ + } \ + WrapBN(obj, result); \ + \ + return obj; \ + } +BIGNUM_1c(sqr); + +#define BIGNUM_2(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1 = NULL, *bn2 = NULL; \ + BIGNUM *result = NULL; \ + VALUE obj; \ + \ + GetBN(self, bn1); \ + \ + OSSLGetBN(other, bn2); \ + \ + if (!(result = BN_new())) { \ + OSSL_Raise(eBNError, ""); \ + } \ + if (BN_##func(result, bn1, bn2) != 1) { \ + BN_free(result); \ + OSSL_Raise(eBNError, ""); \ + } \ + WrapBN(obj, result); \ + \ + return obj; \ + } +BIGNUM_2(add); +BIGNUM_2(sub); + +#define BIGNUM_2c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1 = NULL, *bn2 = NULL; \ + BIGNUM *result = NULL; \ + VALUE obj; \ + \ + GetBN(self, bn1); \ + \ + OSSLGetBN(other, bn2); \ + \ + if (!(result = BN_new())) { \ + OSSL_Raise(eBNError, ""); \ + } \ + if (BN_##func(result, bn1, bn2, ossl_bn_ctx) != 1) { \ + BN_free(result); \ + OSSL_Raise(eBNError, ""); \ + } \ + WrapBN(obj, result); \ + \ + return obj; \ + } +BIGNUM_2c(mul); +BIGNUM_2c(mod); +BIGNUM_2c(exp); +BIGNUM_2c(gcd); +BIGNUM_2c(mod_sqr); + +static VALUE +ossl_bn_div(VALUE self, VALUE other) +{ + BIGNUM *bn1 = NULL, *bn2 = NULL; + BIGNUM *r1 = NULL, *r2 = NULL; + VALUE obj1, obj2; + + GetBN(self, bn1); + + OSSLGetBN(other, bn2); + + if (!(r1 = BN_new())) { + OSSL_Raise(eBNError, ""); + } + if (!(r2 = BN_new())) { + BN_free(r1); + OSSL_Raise(eBNError, ""); + } + + if (BN_div(r1, r2, bn1, bn2, ossl_bn_ctx) != 1) { + BN_free(r1); + BN_free(r2); + OSSL_Raise(eBNError, ""); + } + WrapBN(obj1, r1); + WrapBN(obj2, r2); + + return rb_ary_new3(2, obj1, obj2); +} + +static VALUE +ossl_bn_mod_inverse(VALUE self, VALUE other) +{ + BIGNUM *bn1 = NULL, *bn2 = NULL; + BIGNUM *result = NULL; + VALUE obj; + + GetBN(self, bn1); + + OSSLGetBN(other, bn2); + + if (!(result = BN_new())) { + OSSL_Raise(eBNError, ""); + } + if (!BN_mod_inverse(result, bn1, bn2, ossl_bn_ctx)) { + BN_free(result); + OSSL_Raise(eBNError, ""); + } + WrapBN(obj, result); + + return obj; +} + +#define BIGNUM_3c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \ + { \ + BIGNUM *bn1 = NULL, *bn2 = NULL, *bn3 = NULL; \ + BIGNUM *result = NULL; \ + VALUE obj; \ + \ + GetBN(self, bn1); \ + \ + OSSLGetBN(other1, bn2); \ + OSSLGetBN(other2, bn3); \ + \ + if (!(result = BN_new())) { \ + OSSL_Raise(eBNError, ""); \ + } \ + if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) != 1) { \ + BN_free(result); \ + OSSL_Raise(eBNError, ""); \ + } \ + WrapBN(obj, result); \ + \ + return obj; \ + } +BIGNUM_3c(mod_add); +BIGNUM_3c(mod_sub); +BIGNUM_3c(mod_mul); +BIGNUM_3c(mod_exp); + +#define BIGNUM_BIT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bit) \ + { \ + BIGNUM *bn = NULL; \ + \ + GetBN(self, bn); \ + \ + if (BN_##func(bn, NUM2INT(bit)) != 1) { \ + OSSL_Raise(eBNError, ""); \ + } \ + return self; \ + } +BIGNUM_BIT(set_bit); +BIGNUM_BIT(clear_bit); +BIGNUM_BIT(mask_bits); + +static VALUE +ossl_bn_is_bit_set(VALUE self, VALUE bit) +{ + BIGNUM *bn = NULL; + + GetBN(self, bn); + + if (BN_is_bit_set(bn, NUM2INT(bit)) == 1) { + return Qtrue; + } + return Qfalse; +} + +#define BIGNUM_SHIFT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bits) \ + { \ + BIGNUM *bn = NULL; \ + BIGNUM *result = NULL; \ + VALUE obj; \ + \ + GetBN(self, bn); \ + \ + if (!(result = BN_new())) { \ + OSSL_Raise(eBNError, ""); \ + } \ + if (BN_##func(result, bn, NUM2INT(bits)) != 1) { \ + BN_free(result); \ + OSSL_Raise(eBNError, ""); \ + } \ + WrapBN(obj, result); \ + \ + return obj; \ + } +BIGNUM_SHIFT(lshift); +BIGNUM_SHIFT(rshift); + +#define BIGNUM_RAND(func) \ + static VALUE \ + ossl_bn_s_##func(VALUE klass, VALUE bits, VALUE top, VALUE bottom) \ + { \ + BIGNUM *result = NULL; \ + VALUE obj; \ + \ + if (!(result = BN_new())) { \ + OSSL_Raise(eBNError, ""); \ + } \ + if (!BN_##func(result, NUM2INT(bits), NUM2INT(top), NUM2INT(bottom))) { \ + BN_free(result); \ + OSSL_Raise(eBNError, ""); \ + } \ + WrapBN(obj, result); \ + \ + return obj; \ + } +BIGNUM_RAND(rand); +BIGNUM_RAND(pseudo_rand); + +#define BIGNUM_RAND_RANGE(func) \ + static VALUE \ + ossl_bn_s_##func##_range(VALUE klass, VALUE range) \ + { \ + BIGNUM *bn = NULL; \ + BIGNUM *result = NULL; \ + VALUE obj; \ + \ + OSSLGetBN(range, bn); \ + \ + if (!(result = BN_new())) { \ + OSSL_Raise(eBNError, ""); \ + } \ + if (!BN_##func##_range(result, bn)) { \ + BN_free(result); \ + OSSL_Raise(eBNError, ""); \ + } \ + WrapBN(obj, result); \ + \ + return obj; \ + } +BIGNUM_RAND_RANGE(rand); +BIGNUM_RAND_RANGE(pseudo_rand); + +static VALUE +ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass) +{ + BIGNUM *result = NULL, *add = NULL, *rem = NULL; + int safe = 1; + VALUE vnum, vsafe, vadd, vrem, obj; + + rb_scan_args(argc, argv, "13", &vnum, &vsafe, &vadd, &vrem); + + if (vsafe == Qfalse) { + safe = 0; + } + if (!NIL_P(vadd)) { + if (NIL_P(vrem)) { + rb_raise(rb_eArgError, "if ADD is specified, REM must be also given"); + } + OSSLGetBN(vadd, add); + OSSLGetBN(vrem, rem); + } + + if (!(result = BN_new())) { + OSSL_Raise(eBNError, ""); + } + if (!BN_generate_prime(result, NUM2INT(vnum), safe, add, rem, NULL, NULL)) { + BN_free(result); + OSSL_Raise(eBNError, ""); + } + WrapBN(obj, result); + + return obj; +} + +#define BIGNUM_NUM(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn = NULL; \ + \ + GetBN(self, bn); \ + \ + return INT2FIX(BN_##func(bn)); \ + } +BIGNUM_NUM(num_bytes); +BIGNUM_NUM(num_bits); + +static VALUE +ossl_bn_dup(VALUE self) +{ + BIGNUM *bn = NULL; + + GetBN(self, bn); + + return ossl_bn_new(bn); +} + +static VALUE +ossl_bn_copy(VALUE self, VALUE other) +{ + BIGNUM *bn1 = NULL, *bn2 = NULL; + + GetBN(self, bn1); + + OSSLGetBN(other, bn2); + + if (!BN_copy(bn1, bn2)) { + OSSL_Raise(eBNError, ""); + } + return self; +} + +#define BIGNUM_CMP(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1 = NULL, *bn2 = NULL; \ + \ + GetBN(self, bn1); \ + \ + OSSLGetBN(other, bn2); \ + \ + return INT2FIX(BN_##func(bn1, bn2)); \ + } +BIGNUM_CMP(cmp); +BIGNUM_CMP(ucmp); + +static VALUE +ossl_bn_eql(VALUE self, VALUE other) +{ + if (ossl_bn_cmp(self, other) == INT2FIX(0)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE +ossl_bn_is_prime(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn = NULL; + VALUE vchecks; + int checks = BN_prime_checks; + + GetBN(self, bn); + + if (rb_scan_args(argc, argv, "01", &vchecks) == 0) { + checks = NUM2INT(vchecks); + } + + switch (BN_is_prime(bn, checks, NULL, ossl_bn_ctx, NULL)) { + case 1: + return Qtrue; + case 0: + return Qfalse; + default: + OSSL_Raise(eBNError, ""); + } + + /* not reachable */ + return Qnil; +} + +static VALUE +ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn = NULL; + VALUE vchecks, vtrivdiv; + int checks = BN_prime_checks, do_trial_division = 1; + + GetBN(self, bn); + + rb_scan_args(argc, argv, "02", &vchecks, &vtrivdiv); + + + if (!NIL_P(vchecks)) { + checks = NUM2INT(vchecks); + } + /* handle true/false */ + if (vtrivdiv == Qfalse) { + do_trial_division = 0; + } + switch (BN_is_prime_fasttest(bn, checks, NULL, ossl_bn_ctx, NULL, do_trial_division)) { + case 1: + return Qtrue; + case 0: + return Qfalse; + default: + OSSL_Raise(eBNError, ""); + } + + /* not reachable */ + return Qnil; +} + +/* + * INIT + * (NOTE: ordering of methods is the same as in 'man bn') + */ +void +Init_ossl_bn() +{ + if (!(ossl_bn_ctx = BN_CTX_new())) { + OSSL_Raise(rb_eRuntimeError, "Cannot init BN_CTX"); + } + + eBNError = rb_define_class_under(mOSSL, "BNError", eOSSLError); + + cBN = rb_define_class_under(mOSSL, "BN", rb_cObject); + + rb_define_singleton_method(cBN, "allocate", ossl_bn_s_allocate, 0); + rb_define_method(cBN, "initialize", ossl_bn_initialize, -1); + rb_enable_super(cBN, "initialize"); + + rb_define_method(cBN, "copy", ossl_bn_copy, 1); + rb_define_method(cBN, "dup", ossl_bn_dup, 0); + + /* swap (=coerce?) */ + + rb_define_method(cBN, "num_bytes", ossl_bn_num_bytes, 0); + rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0); + /* num_bits_word */ + + rb_define_method(cBN, "+", ossl_bn_add, 1); + rb_define_method(cBN, "-", ossl_bn_sub, 1); + rb_define_method(cBN, "*", ossl_bn_mul, 1); + rb_define_method(cBN, "sqr", ossl_bn_sqr, 0); + rb_define_method(cBN, "/", ossl_bn_div, 1); + rb_define_method(cBN, "%", ossl_bn_mod, 1); + /* nnmod */ + + rb_define_method(cBN, "mod_add", ossl_bn_mod_add, 1); + rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 1); + rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 1); + rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1); + rb_define_method(cBN, "**", ossl_bn_exp, 1); + rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 1); + rb_define_method(cBN, "gcd", ossl_bn_gcd, 1); + + /* add_word + * sub_word + * mul_word + * div_word + * mod_word */ + + rb_define_method(cBN, "cmp", ossl_bn_cmp, 1); + rb_define_alias(cBN, "<=>", "cmp"); + rb_define_method(cBN, "ucmp", ossl_bn_ucmp, 1); + rb_define_method(cBN, "eql?", ossl_bn_eql, 1); + rb_define_alias(cBN, "==", "eql?"); + rb_define_alias(cBN, "===", "eql?"); + rb_define_method(cBN, "zero?", ossl_bn_is_zero, 0); + rb_define_method(cBN, "one?", ossl_bn_is_one, 0); + /* is_word */ + rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0); + + /* zero + * one + * value_one - DON'T IMPL. + * set_word + * get_word */ + + rb_define_singleton_method(cBN, "rand", ossl_bn_s_rand, 3); + rb_define_singleton_method(cBN, "pseudo_rand", ossl_bn_s_pseudo_rand, 3); + rb_define_singleton_method(cBN, "rand_range", ossl_bn_s_rand_range, 1); + rb_define_singleton_method(cBN, "pseudo_rand_range", ossl_bn_s_pseudo_rand_range, 1); + + rb_define_singleton_method(cBN, "generate_prime", ossl_bn_s_generate_prime, -1); + rb_define_method(cBN, "prime?", ossl_bn_is_prime, -1); + + rb_define_method(cBN, "set_bit!", ossl_bn_set_bit, 1); + rb_define_method(cBN, "clear_bit!", ossl_bn_clear_bit, 1); + rb_define_method(cBN, "bit_set?", ossl_bn_is_bit_set, 1); + rb_define_method(cBN, "mask_bits!", ossl_bn_mask_bits, 1); + rb_define_method(cBN, "<<", ossl_bn_lshift, 1); + /* lshift1 - DON'T IMPL. */ + rb_define_method(cBN, ">>", ossl_bn_rshift, 1); + /* rshift1 - DON'T IMPL. */ + + /* bn2bin + * bin2bn + * bn2hex + * bn2dec + * hex2bn + * dec2bn - all these are implemented in ossl_bn_initialize, and ossl_bn_to_s + * print - NOT IMPL. + * print_fp - NOT IMPL. + * bn2mpi + * mpi2bn */ + rb_define_method(cBN, "to_s", ossl_bn_to_s, -1); + /* + * TODO: + * But how to: from_bin, from_mpi? PACK? + * to_bin + * to_mpi + */ + + rb_define_method(cBN, "mod_inverse", ossl_bn_mod_inverse, 1); + + /* RECiProcal + * MONTgomery */ + + /* + * TODO: + * Where to belong these? + */ + rb_define_method(cBN, "prime_fasttest?", ossl_bn_is_prime_fasttest, -1); +} + diff --git a/ossl_bn.h b/ossl_bn.h new file mode 100644 index 0000000..74be2a5 --- /dev/null +++ b/ossl_bn.h @@ -0,0 +1,40 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_BN_H_) +#define _OSSL_BN_H_ + +extern VALUE cBN; +extern VALUE eBNError; + +#define OSSLWrapBN(obj, bn) do { \ + if (!bn) { \ + rb_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(cBN, 0, BN_clear_free, bn); \ +} while (0) + +#define OSSLGetBN(obj, bn) do { \ + OSSL_Check_Instance(obj, cBN); \ + Data_Get_Struct(obj, BIGNUM, bn); \ + if (!bn) { \ + rb_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ +} while (0) + +#define OSSLBNValue(obj) OSSL_Check_Instance((obj), cBN) +#define OSSLBNValuePtr(obj) ossl_bn_get_BIGNUM((obj)) + +VALUE ossl_bn_new(BIGNUM *); +BIGNUM *ossl_bn_get_BIGNUM(VALUE); +void Init_ossl_bn(void); + +#endif /* _OSS_BN_H_ */ + diff --git a/ossl_cipher.c b/ossl_cipher.c new file mode 100644 index 0000000..38e0d7c --- /dev/null +++ b/ossl_cipher.c @@ -0,0 +1,701 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define MakeCipher(obj, klass, ciphp) obj = Data_Make_Struct(klass, ossl_cipher, 0, ossl_cipher_free, ciphp) +#define GetCipher(obj, ciphp) Data_Get_Struct(obj, ossl_cipher, ciphp) + +/* + * Constants + */ +/* BASIC TYPES */ +#define UNSPEC 0x0000 +#define ECB 0x1000 +#define CFB 0x2000 +#define OFB 0x4000 +#define CBC 0x8000 +#define EDE 0x0001 +#define EDE3 0x0002 +#define BIT40 0x0028 /*== 40*/ +#define BIT64 0x0040 /*== 64*/ +#define BIT128 0x0080 /*== 128*/ +#define BIT192 0x00C0 /*== 192*/ +#define BIT256 0x0100 /*== 256*/ + +/* + * Classes + */ +VALUE mCipher; +VALUE cCipher; +VALUE eCipherError; +VALUE cDES, cRC4, cIdea, cRC2, cBlowFish, cCast5, cRC5, cAES; + +/* + * Struct + */ +typedef struct ossl_cipher_st { + int nid; + EVP_CIPHER_CTX *ctx; +} ossl_cipher; + +static void +ossl_cipher_free(ossl_cipher *ciphp) +{ + if (ciphp) { + if (ciphp->ctx) OPENSSL_free(ciphp->ctx); + ciphp->ctx = NULL; + free(ciphp); + } +} + +/* + * PUBLIC + */ +int +ossl_cipher_get_NID(VALUE obj) +{ + ossl_cipher *ciphp = NULL; + + OSSL_Check_Type(obj, cCipher); + + GetCipher(obj, ciphp); + + return ciphp->nid; /*EVP_CIPHER_CTX_nid(ciphp->ctx);*/ +} + +const EVP_CIPHER * +ossl_cipher_get_EVP_CIPHER(VALUE obj) +{ + ossl_cipher *ciphp = NULL; + + OSSL_Check_Type(obj, cCipher); + + GetCipher(obj, ciphp); + + return EVP_get_cipherbynid(ciphp->nid); /*EVP_CIPHER_CTX_cipher(ciphp->ctx);*/ +} + +/* + * PRIVATE + */ +static VALUE +ossl_cipher_s_new(int argc, VALUE *argv, VALUE klass) +{ + ossl_cipher *ciphp = NULL; + VALUE obj; + + if (klass == cCipher) + rb_raise(rb_eNotImpError, "cannot do Cipher::Cipher.new - it is an abstract class"); + + MakeCipher(obj, klass, ciphp); + + if (!(ciphp->ctx = OPENSSL_malloc(sizeof(EVP_CIPHER_CTX)))) { + OSSL_Raise(eCipherError, ""); + } + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + const EVP_CIPHER *cipher = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH]; + VALUE pass, init_v; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "11", &pass, &init_v); + + pass = rb_String(pass); + + if (NIL_P(init_v)) { + /* + * TODO: + * random IV generation! + */ + memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv)); + /* + RAND_add(data,i,0); where from take data? + if (RAND_pseudo_bytes(iv, 8) < 0) { + OSSL_Raise(eCipherError, ""); + } + */ + } else { + init_v = rb_obj_as_string(init_v); + if (EVP_MAX_IV_LENGTH > RSTRING(init_v)->len) { + memset(iv, 0, EVP_MAX_IV_LENGTH); + memcpy(iv, RSTRING(init_v)->ptr, RSTRING(init_v)->len); + } else + memcpy(iv, RSTRING(init_v)->ptr, sizeof(iv)); + } + EVP_CIPHER_CTX_init(ciphp->ctx); + + cipher = EVP_get_cipherbynid(ciphp->nid); + EVP_BytesToKey(cipher, EVP_md5(), iv, RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL); + + if (!EVP_EncryptInit(ciphp->ctx, cipher, key, iv)) { + OSSL_Raise(eCipherError, ""); + } + return self; +} + +static VALUE +ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + const EVP_CIPHER *cipher = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH]; + VALUE pass, init_v; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "11", &pass, &init_v); + + pass = rb_String(pass); + + if (NIL_P(init_v)) { + /* + * TODO: + * random IV generation! + */ + memcpy(iv, "OpenSSL for Ruby rulez!", EVP_MAX_IV_LENGTH); + } else { + init_v = rb_obj_as_string(init_v); + if (EVP_MAX_IV_LENGTH > RSTRING(init_v)->len) { + memset(iv, 0, EVP_MAX_IV_LENGTH); + memcpy(iv, RSTRING(init_v)->ptr, RSTRING(init_v)->len); + } else + memcpy(iv, RSTRING(init_v)->ptr, EVP_MAX_IV_LENGTH); + } + EVP_CIPHER_CTX_init(ciphp->ctx); + + cipher = EVP_get_cipherbynid(ciphp->nid); + + /*if (!load_iv((unsigned char **)&header,&(cipher->iv[0]),8)) return(0); * cipher = CIPHER_INFO */ + + EVP_BytesToKey(cipher, EVP_md5(), iv, RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL); + + if (!EVP_DecryptInit(ciphp->ctx, cipher, key, iv)) { + OSSL_Raise(eCipherError, ""); + } + return self; +} + +static VALUE +ossl_cipher_update(VALUE self, VALUE data) +{ + ossl_cipher *ciphp = NULL; + char *in = NULL, *out = NULL; + int in_len = 0, out_len = 0; + VALUE str; + + GetCipher(self, ciphp); + + data = rb_String(data); + in = RSTRING(data)->ptr; + in_len = RSTRING(data)->len; + + if (!(out = OPENSSL_malloc(in_len + EVP_CIPHER_CTX_block_size(ciphp->ctx)))) { + OSSL_Raise(eCipherError, ""); + } + if (!EVP_CipherUpdate(ciphp->ctx, out, &out_len, in, in_len)) { + OPENSSL_free(out); + OSSL_Raise(eCipherError, ""); + } + + str = rb_str_new(out, out_len); + OPENSSL_free(out); + + return str; +} + +static VALUE +ossl_cipher_cipher(VALUE self) +{ + ossl_cipher *ciphp = NULL; + + char *out = NULL; + int out_len = 0; + VALUE str; + + GetCipher(self, ciphp); + + if (!(out = OPENSSL_malloc(EVP_CIPHER_CTX_block_size(ciphp->ctx)))) { + OSSL_Raise(eCipherError, ""); + } + if (!EVP_CipherFinal(ciphp->ctx, out, &out_len)) { + OPENSSL_free(out); + OSSL_Raise(eCipherError, ""); + } + str = rb_str_new(out, out_len); + OPENSSL_free(out); + + return str; +} + +/* + * DES + */ +static VALUE +ossl_des_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + if (rb_scan_args(argc, argv, "11", &mode, &type) == 2) + spec = FIX2INT(mode) + FIX2INT(type); + else + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_des_ecb; + break; +/* + * NO LONGER SUPPORTED IN OPENSSL + case EDE: + nid = NID_des_ede; + break; + case EDE3: + nid = NID_des_ede3; + break; + */ + case CFB: + nid = NID_des_cfb64; + break; + case EDE+CFB: + nid = NID_des_ede_cfb64; + break; + case EDE3+CFB: + nid = NID_des_ede3_cfb64; + break; + case OFB: + nid = NID_des_ofb64; + break; + case EDE+OFB: + nid = NID_des_ede_ofb64; + break; + case EDE3+OFB: + nid = NID_des_ede3_ofb64; + break; + case CBC: + nid = NID_des_cbc; + break; + case EDE+CBC: + nid = NID_des_ede_cbc; + break; + case EDE3+CBC: + nid = NID_des_ede3_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} + +/* + * RC4 + */ +static VALUE +ossl_rc4_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode; + + GetCipher(self, ciphp); + + if (rb_scan_args(argc, argv, "01", &mode) == 1) + spec = FIX2INT(mode); + + switch (spec) { + case UNSPEC: + nid = NID_rc4; + break; + case BIT40: + nid = NID_rc4_40; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} + +/* + * Idea + */ +static VALUE +ossl_idea_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_idea_ecb; + break; + case CFB: + nid = NID_idea_cfb64; + break; + case OFB: + nid = NID_idea_ofb64; + break; + case CBC: + nid = NID_idea_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} + +/* + * RC2 + */ +static VALUE +ossl_rc2_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + if (rb_scan_args(argc, argv, "11", &mode, &type) == 2) + spec = FIX2INT(mode) + FIX2INT(type); + else + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_rc2_ecb; + break; + case CBC: + nid = NID_rc2_cbc; + break; + case BIT40+CBC: + nid = NID_rc2_40_cbc; + break; + case BIT64+CBC: + nid = NID_rc2_64_cbc; + break; + case CFB: + nid = NID_rc2_cfb64; + break; + case OFB: + nid = NID_rc2_ofb64; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} + +/* + * BlowFish + */ +static VALUE +ossl_bf_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_bf_ecb; + break; + case CFB: + nid = NID_bf_cfb64; + break; + case OFB: + nid = NID_bf_ofb64; + break; + case CBC: + nid = NID_bf_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} + +/* + * Cast5 + */ +static VALUE +ossl_cast5_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_cast5_ecb; + break; + case CFB: + nid = NID_cast5_cfb64; + break; + case OFB: + nid = NID_cast5_ofb64; + break; + case CBC: + nid = NID_cast5_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} + +/* + * RC5 + */ +static VALUE +ossl_rc5_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "10", &mode); + spec = FIX2INT(mode); + + switch (spec) { + case ECB: + nid = NID_rc5_ecb; + break; + case CFB: + nid = NID_rc5_cfb64; + break; + case OFB: + nid = NID_rc5_ofb64; + break; + case CBC: + nid = NID_rc5_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} + +#if OPENSSL_VERSION_NUMBER >= 0x00907000L /* DEV version of OpenSSL has AES */ +/* + * AES + */ +static VALUE +ossl_aes_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_cipher *ciphp = NULL; + int spec = 0, nid = 0; + VALUE mode, type; + + GetCipher(self, ciphp); + + rb_scan_args(argc, argv, "20", &mode, &type); + spec = FIX2INT(mode) + FIX2INT(type); + + switch (spec) { + case BIT128+ECB: + nid = NID_aes_128_ecb; + break; + /* + case BIT128+CFB: + nid = NID_aes_128_cfb; + break; + case BIT128+OFB: + nid = NID_aes_128_ofb; + break; + */ + case BIT128+CBC: + nid = NID_aes_128_cbc; + break; + case BIT192+ECB: + nid = NID_aes_192_ecb; + break; + /* + case BIT192+CFB: + nid = NID_aes_192_cfb; + break; + case BIT192+OFB: + nid = NID_aes_192_ofb; + break; + */ + case BIT192+CBC: + nid = NID_aes_192_cbc; + break; + case BIT256+ECB: + nid = NID_aes_256_ecb; + break; + /* + case BIT256+CFB: + nid = NID_aes_256_cfb; + break; + case BIT256+OFB: + nid = NID_aes_256_ofb; + break; + */ + case BIT256+CBC: + nid = NID_aes_256_cbc; + break; + default: + rb_raise(rb_eTypeError, "unsupported combination of modes"); + } + ciphp->nid = nid; + + return self; +} +#endif /* OPENSSL_VERSION_NUMBER */ + +/* + * INIT + */ +void +Init_ossl_cipher(void) +{ + mCipher = rb_define_module_under(mOSSL, "Cipher"); + + eCipherError = rb_define_class_under(mOSSL, "CipherError", eOSSLError); + + cCipher = rb_define_class_under(mCipher, "Cipher", rb_cObject); + rb_define_singleton_method(cCipher, "new", ossl_cipher_s_new, -1); + /*"initialize"*/ + rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, -1); + rb_define_method(cCipher, "decrypt", ossl_cipher_decrypt, -1); + rb_define_method(cCipher, "update", ossl_cipher_update, 1); + rb_define_alias(cCipher, "<<", "update"); + rb_define_method(cCipher, "cipher", ossl_cipher_cipher, 0); + +/* + * Constants + */ +#define DefCipherConst(x) rb_define_const(mOSSL, #x, INT2FIX(x)) + + DefCipherConst(ECB); + DefCipherConst(EDE); + DefCipherConst(EDE3); + DefCipherConst(CFB); + DefCipherConst(OFB); + DefCipherConst(CBC); + DefCipherConst(BIT40); + DefCipherConst(BIT64); + DefCipherConst(BIT128); + DefCipherConst(BIT192); + DefCipherConst(BIT256); + +/* + * automation for classes creation and initialize method binding + */ +#define DefCipher(name, func) \ + c##name = rb_define_class_under(mOSSL, #name, cCipher); \ + rb_define_method(c##name, "initialize", ossl_##func##_initialize, -1) + +/* + * create classes and bind initialize method + */ +#if !defined(OPENSSL_NO_DES) + DefCipher(DES, des); +#else +# warning >>> OpenSSL is compiled without DES support <<< + rb_warning("OpenSSL is compiled without DES support"); +#endif /* NO_DES */ + +#if !defined(OPENSSL_NO_RC2) + DefCipher(RC2, rc2); +#else +# warning >>> OpenSSL is compiled without RC2 support <<< + rb_warning("OpenSSL is compiled without RC2 support"); +#endif /* NO_RC2 */ + +#if !defined(OPENSSL_NO_RC4) + DefCipher(RC4, rc4); +#else +# warning >>> OpenSSL is compiled without RC4 support <<< + rb_warning("OpenSSL is compiled without RC4 support"); +#endif /* NO_RC4 */ + +#if !defined(OPENSSL_NO_RC5) + DefCipher(RC5, rc5); +#else +# warning >>> OpenSSL is compiled without RC5 support <<< + rb_warning("OpenSSL is compiled without RC5 support"); +#endif /* NO_RC5 */ + +#if !defined(OPENSSL_NO_BF) + DefCipher(BlowFish, bf); +#else +# warning >>> OpenSSL is compiled without BF support <<< + rb_warning("OpenSSL is compiled without BlowFish support"); +#endif /* NO_BF */ + +#if !defined(OPENSSL_NO_CAST) + DefCipher(Cast5, cast5); +#else +# warning >>> OpenSSL is compiled without CAST support <<< + rb_warning("OpenSSL is compiled without Cast5 support"); +#endif /* NO_CAST */ + +#if !defined(OPENSSL_NO_IDEA) + DefCipher(Idea, idea); +#else +# warning >>> OpenSSL is compiled without IDEA support <<< + rb_warning("OpenSSL is compiled without Idea support"); +#endif /* NO_IDEA */ + +#if !defined(OPENSSL_NO_AES) + DefCipher(AES, aes); +#else +# warning >>> OpenSSL is compiled without AES support <<< + rb_warning("OpenSSL is compiled without AES support"); +#endif /* NO_AES */ + +} /* Init_ossl_cipher */ + diff --git a/ossl_cipher.h b/ossl_cipher.h new file mode 100644 index 0000000..24c7b3d --- /dev/null +++ b/ossl_cipher.h @@ -0,0 +1,27 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_CIPHER_H_) +#define _OSSL_CIPHER_H_ + +extern VALUE mCipher; +extern VALUE cCipher; +extern VALUE eCipherError; +extern VALUE cDES, cRC4, cIdea, cRC2, cBlowFish, cCast5, cRC5, cAES; + +#define OSSLCipherValue(obj) OSSL_Check_Instance((obj), cCipher) +#define OSSLCipherValuePtr(obj) ossl_cipher_get_EVP_CIPHER((obj)) + +int ossl_cipher_get_NID(VALUE); +const EVP_CIPHER *ossl_cipher_get_EVP_CIPHER(VALUE); +void Init_ossl_cipher(void); + +#endif /* _OSSL_DIGEST_H_ */ + diff --git a/ossl_config.c b/ossl_config.c new file mode 100644 index 0000000..109c687 --- /dev/null +++ b/ossl_config.c @@ -0,0 +1,127 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapConfig(obj, conf) obj = Data_Wrap_Struct(cConfig, 0, CONF_free, conf) +#define GetConfig(obj, conf) Data_Get_Struct(obj, LHASH, conf) + +/* + * Classes + */ +VALUE cConfig; +VALUE eConfigError; + +/* + * Public + */ + +/* + * Private + */ +static VALUE +ossl_config_s_load(int argc, VALUE *argv, VALUE klass) +{ + LHASH *conf = NULL; + long err_line = 0; + VALUE obj, path; + + rb_scan_args(argc, argv, "10", &path); + + path = rb_str_to_str(path); + Check_SafeStr(path); + + if (!(conf = CONF_load(NULL, RSTRING(path)->ptr, &err_line))) { + if (err_line <= 0) + rb_raise(eConfigError, "wrong config file %s", RSTRING(path)->ptr); + else + rb_raise(eConfigError, "error on line %ld in config file %s",\ + err_line, RSTRING(path)->ptr); + } + + WrapConfig(obj, conf); + + return obj; +} + +static VALUE +ossl_config_get_value(VALUE self, VALUE section, VALUE item) +{ + LHASH *conf = NULL; + char *sect = NULL, *str = NULL; + + GetConfig(self, conf); + + if (!NIL_P(section)) { + section = rb_String(section); + sect = RSTRING(section)->ptr; + } + item = rb_String(item); + + if (!(str = CONF_get_string(conf, sect, RSTRING(item)->ptr))) { + OSSL_Raise(eConfigError, ""); + } + return rb_str_new2(str); +} + +/* + * Get all numbers as strings - use str.to_i to convert + * long number = CONF_get_number(confp->config, sect, RSTRING(item)->ptr); + */ + +static VALUE +ossl_config_get_section(VALUE self, VALUE section) +{ + LHASH *conf = NULL; + STACK_OF(CONF_VALUE) *sk = NULL; + CONF_VALUE *entry = NULL; + int i, entries = 0; + VALUE hash; + + GetConfig(self, conf); + + section = rb_String(section); + + if (!(sk = CONF_get_section(conf, RSTRING(section)->ptr))) { + OSSL_Raise(eConfigError, ""); + } + + hash = rb_hash_new(); + + if ((entries = sk_CONF_VALUE_num(sk)) < 0) { + rb_warning("# of items in section is < 0?!?"); + return hash; + } + + for (i=0; i<entries; i++) { + entry = sk_CONF_VALUE_value(sk, i); + rb_hash_aset(hash, rb_str_new2(entry->name), rb_str_new2(entry->value)); + } + + return hash; +} + +/* + * INIT + */ +void +Init_ossl_config(VALUE module) +{ + eConfigError = rb_define_class_under(module, "ConfigError", eOSSLError); + + cConfig = rb_define_class_under(module, "Config", rb_cObject); + + rb_define_singleton_method(cConfig, "load", ossl_config_s_load, -1); + rb_define_alias(CLASS_OF(cConfig), "new", "load"); + + rb_define_method(cConfig, "get_value", ossl_config_get_value, 2); + rb_define_method(cConfig, "get_section", ossl_config_get_section, 1); +} + diff --git a/ossl_digest.c b/ossl_digest.c new file mode 100644 index 0000000..0c284c3 --- /dev/null +++ b/ossl_digest.c @@ -0,0 +1,259 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapDigest OSSLWrapDigest +#define GetDigest(obj, ctx) do { \ + Data_Get_Struct(obj, EVP_MD_CTX, ctx); \ + if (!ctx) { \ + rb_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ + } \ +} while (0) + +/* + * Classes + */ +VALUE cDigest; +VALUE eDigestError; + +/* + * Public + */ +int +ossl_digest_get_NID(VALUE obj) +{ + EVP_MD_CTX *ctx = NULL; + + OSSLGetDigest(obj, ctx); + + return EVP_MD_CTX_type(ctx); /*== ctx->digest->type*/ +} + +const EVP_MD * +ossl_digest_get_EVP_MD(VALUE obj) +{ + EVP_MD_CTX *ctx = NULL; + + OSSLGetDigest(obj, ctx); + + return EVP_MD_CTX_md(ctx); /*== ctx->digest*/ +} + +/* + * Private + */ +static VALUE +ossl_digest_s_allocate(VALUE klass) +{ + EVP_MD_CTX *ctx = NULL; + VALUE obj; + + if (!(ctx = OPENSSL_malloc(sizeof(EVP_MD_CTX)))) { + OSSL_Raise(eDigestError, "Cannot allocate memory for a digest's CTX"); + } + WrapDigest(klass, obj, ctx); + + return obj; +} + +static VALUE +ossl_digest_initialize(VALUE self, VALUE str) +{ + EVP_MD_CTX *ctx = NULL; + const EVP_MD *md; + char *md_name = NULL; + + GetDigest(self, ctx); + + md_name = StringValuePtr(str); + + if (!(md = EVP_get_digestbyname(md_name))) { + rb_raise(rb_eRuntimeError, "Unsupported digest algorithm (%s).", md_name); + } + EVP_DigestInit(ctx, md); + + return self; +} + +static VALUE +ossl_digest_update(VALUE self, VALUE data) +{ + EVP_MD_CTX *ctx = NULL; + + GetDigest(self, ctx); + + StringValue(data); + + EVP_DigestUpdate(ctx, RSTRING(data)->ptr, RSTRING(data)->len); + + return self; +} + +static VALUE +ossl_digest_digest(VALUE self) +{ + EVP_MD_CTX *ctx = NULL, final; + char *digest_txt = NULL; + int digest_len = 0; + VALUE digest; + + GetDigest(self, ctx); + + if (!EVP_MD_CTX_copy(&final, ctx)) { + OSSL_Raise(eDigestError, ""); + } + if (!(digest_txt = OPENSSL_malloc(EVP_MD_CTX_size(&final)))) { + OSSL_Raise(eDigestError, "Cannot allocate mem for digest"); + } + EVP_DigestFinal(&final, digest_txt, &digest_len); + + digest = rb_str_new(digest_txt, digest_len); + OPENSSL_free(digest_txt); + + return digest; +} + +static VALUE +ossl_digest_hexdigest(VALUE self) +{ + EVP_MD_CTX *ctx = NULL, final; + static const char hex[]="0123456789abcdef"; + char *digest_txt = NULL, *hexdigest_txt = NULL; + int i,digest_len = 0; + VALUE hexdigest; + + GetDigest(self, ctx); + + if (!EVP_MD_CTX_copy(&final, ctx)) { + OSSL_Raise(eDigestError, ""); + } + if (!(digest_txt = OPENSSL_malloc(EVP_MD_CTX_size(&final)))) { + OSSL_Raise(eDigestError, "Cannot allocate memory for digest"); + } + EVP_DigestFinal(&final, digest_txt, &digest_len); + + if (!(hexdigest_txt = OPENSSL_malloc(2 * digest_len + 1))) { + OPENSSL_free(digest_txt); + OSSL_Raise(eDigestError, "Memory alloc error"); + } + for (i = 0; i < digest_len; i++) { + hexdigest_txt[2 * i] = hex[((unsigned char)digest_txt[i]) >> 4]; + hexdigest_txt[2 * i + 1] = hex[digest_txt[i] & 0x0f]; + } + hexdigest_txt[2 * i] = '\0'; + hexdigest = rb_str_new(hexdigest_txt, 2 * digest_len); + OPENSSL_free(digest_txt); + OPENSSL_free(hexdigest_txt); + + return hexdigest; +} + +static VALUE +ossl_digest_s_digest(VALUE klass, VALUE str, VALUE data) +{ + VALUE obj = rb_class_new_instance(1, &str, cDigest); + + ossl_digest_update(obj, data); + + return ossl_digest_digest(obj); +} + +static VALUE +ossl_digest_s_hexdigest(VALUE klass, VALUE str, VALUE data) +{ + VALUE obj = rb_class_new_instance(1, &str, cDigest); + + ossl_digest_update(obj, data); + + return ossl_digest_hexdigest(obj); +} + +static VALUE +ossl_digest_clone(VALUE self) +{ + EVP_MD_CTX *ctx = NULL, *other; + VALUE obj; + + GetDigest(self, ctx); + + obj = rb_obj_alloc(CLASS_OF(self)); + + GetDigest(obj, other); + + if (!EVP_MD_CTX_copy(other, ctx)) { + OSSL_Raise(eDigestError, ""); + } + + return obj; +} + +static VALUE +ossl_digest_equal(VALUE self, VALUE other) +{ + EVP_MD_CTX *ctx = NULL; + VALUE str1, str2; + + GetDigest(self, ctx); + + if (CLASS_OF(other) == CLASS_OF(self)) { + str2 = ossl_digest_digest(other); + } else { + StringValue(other); + str2 = other; + } + + if (RSTRING(str2)->len == EVP_MD_CTX_size(ctx)) { + str1 = ossl_digest_digest(self); + } else { + str1 = ossl_digest_hexdigest(self); + } + + if (RSTRING(str1)->len == RSTRING(str2)->len && + rb_str_cmp(str1, str2) == 0) { + return Qtrue; + } + + return Qfalse; +} + +/* + * INIT + */ +void +Init_ossl_digest() +{ + mDigest = rb_define_module_under(mOSSL, "Digest"); + + eDigestError = rb_define_class_under(mDigest, "DigestError", eOSSLError); + + cDigest = rb_define_class_under(mDigest, "Digest", rb_cObject); + + rb_define_singleton_method(cDigest, "allocate", ossl_digest_s_allocate, 0); + rb_define_singleton_method(cDigest, "digest", ossl_digest_s_digest, 2); + rb_define_singleton_method(cDigest, "hexdigest", ossl_digest_s_hexdigest, 2); + + rb_define_method(cDigest, "initialize", ossl_digest_initialize, 1); + rb_enable_super(cDigest, "initialize"); + + rb_define_method(cDigest, "clone", ossl_digest_clone, 0); + + rb_define_method(cDigest, "digest", ossl_digest_digest, 0); + rb_define_method(cDigest, "hexdigest", ossl_digest_hexdigest, 0); + rb_define_alias(cDigest, "inspect", "hexdigest"); + rb_define_alias(cDigest, "to_s", "hexdigest"); + + rb_define_method(cDigest, "update", ossl_digest_update, 1); + rb_define_alias(cDigest, "<<", "update"); + + rb_define_method(cDigest, "==", ossl_digest_equal, 1); + +} /* Init_ossl_digest */ + diff --git a/ossl_digest.h b/ossl_digest.h new file mode 100644 index 0000000..aad8e25 --- /dev/null +++ b/ossl_digest.h @@ -0,0 +1,40 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_DIGEST_H_) +#define _OSSL_DIGEST_H_ + +extern VALUE mDigest; +extern VALUE cDigest; +extern VALUE eDigestError; + +#define OSSLWrapDigest(klass, obj, ctx) do { \ + if (!ctx) { \ + rb_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, CRYPTO_free, ctx); \ +} while (0) + +#define OSSLGetDigest(obj, ctx) do { \ + OSSL_Check_Instance(obj, cDigest); \ + Data_Get_Struct(obj, EVP_MD_CTX, ctx); \ + if (!ctx) { \ + rb_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ + } \ +} while (0) + +#define OSSLDigestValue(obj) OSSL_Check_Instance((obj), cDigest) + +int ossl_digest_get_NID(VALUE); +const EVP_MD *ossl_digest_get_EVP_MD(VALUE); +void Init_ossl_digest(void); + +#endif /* _OSSL_DIGEST_H_ */ + diff --git a/ossl_hmac.c b/ossl_hmac.c new file mode 100644 index 0000000..3bbd0d9 --- /dev/null +++ b/ossl_hmac.c @@ -0,0 +1,169 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_HMAC) + +#include "ossl.h" + +#define WrapHMAC(obj, ctx) obj = Data_Wrap_Struct(cHMAC, 0, CRYPTO_free, ctx) +#define GetHMAC(obj, ctx) Data_Get_Struct(obj, HMAC_CTX, ctx) + +/* + * Classes + */ +VALUE cHMAC; +VALUE eHMACError; + +/* + * Public + */ + +/* + * Private + */ +static VALUE +ossl_hmac_s_new(int argc, VALUE *argv, VALUE klass) +{ + HMAC_CTX *ctx = NULL; + VALUE obj; + + if (!(ctx = OPENSSL_malloc(sizeof(HMAC_CTX)))) { + OSSL_Raise(eHMACError, ""); + } + WrapHMAC(obj, ctx); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_hmac_initialize(int argc, VALUE *argv, VALUE self) +{ + HMAC_CTX *ctx = NULL; + const EVP_MD *md = NULL; + VALUE key, digest; + + GetHMAC(self, ctx); + + rb_scan_args(argc, argv, "20", &key, &digest); + + key = rb_String(key); + md = ossl_digest_get_EVP_MD(digest); + + HMAC_Init(ctx, RSTRING(key)->ptr, RSTRING(key)->len, md); + + return self; +} + +static VALUE +ossl_hmac_update(VALUE self, VALUE data) +{ + HMAC_CTX *ctx = NULL; + + GetHMAC(self, ctx); + + data = rb_String(data); + + HMAC_Update(ctx, RSTRING(data)->ptr, RSTRING(data)->len); + + return self; +} + +static VALUE +ossl_hmac_hmac(VALUE self) +{ + HMAC_CTX *ctx = NULL, final; + char *buf = NULL; + int buf_len = 0; + VALUE str; + + GetHMAC(self, ctx); + + if (!HMAC_CTX_copy(&final, ctx)) { + OSSL_Raise(eHMACError, ""); + } + if (!(buf = OPENSSL_malloc(HMAC_size(&final)))) { + OSSL_Raise(eHMACError, "Cannot allocate memory for hmac"); + } + HMAC_Final(&final, buf, &buf_len); + + str = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_hmac_hexhmac(VALUE self) +{ + HMAC_CTX *ctx = NULL, final; + static const char hex[]="0123456789abcdef"; + char *buf = NULL, *hexbuf = NULL; + int i,buf_len = 0; + VALUE str; + + GetHMAC(self, ctx); + + if (!HMAC_CTX_copy(&final, ctx)) { + OSSL_Raise(eHMACError, "Cannot copy HMAC CTX"); + } + if (!(buf = OPENSSL_malloc(HMAC_size(&final)))) { + OSSL_Raise(eHMACError, "Cannot allocate memory for hmac"); + } + HMAC_Final(&final, buf, &buf_len); + + if (!(hexbuf = OPENSSL_malloc(2*buf_len+1))) { + OPENSSL_free(buf); + OSSL_Raise(eHMACError, "Memory alloc error"); + } + for (i = 0; i < buf_len; i++) { + hexbuf[i + i] = hex[((unsigned char)buf[i]) >> 4]; + hexbuf[i + i + 1] = hex[buf[i] & 0x0f]; + } + hexbuf[i + i] = '\0'; + + str = rb_str_new(hexbuf, 2*buf_len); + + OPENSSL_free(buf); + OPENSSL_free(hexbuf); + + return str; +} + +/* + * INIT + */ +void +Init_ossl_hmac(VALUE module) +{ + eHMACError = rb_define_class_under(module, "HMACError", eOSSLError); + + cHMAC = rb_define_class_under(module, "HMAC", rb_cObject); + rb_define_singleton_method(cHMAC, "new", ossl_hmac_s_new, -1); + rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, -1); + rb_define_method(cHMAC, "update", ossl_hmac_update, 1); + rb_define_alias(cHMAC, "<<", "update"); + rb_define_method(cHMAC, "hmac", ossl_hmac_hmac, 0); + rb_define_method(cHMAC, "hexhmac", ossl_hmac_hexhmac, 0); + rb_define_alias(cHMAC, "inspect", "hexhmac"); + rb_define_alias(cHMAC, "to_s", "hexhmac"); +} + +#else /* NO_HMAC */ + +void +Init_ossl_hmac(VALUE module) +{ + rb_warning("HMAC will NOT be avaible: OpenSSL is compiled without HMAC."); +} + +#endif /* NO_HMAC */ + diff --git a/ossl_ns_spki.c b/ossl_ns_spki.c new file mode 100644 index 0000000..f1c0f89 --- /dev/null +++ b/ossl_ns_spki.c @@ -0,0 +1,242 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapSPKI(obj, spki) obj = Data_Wrap_Struct(cSPKI, 0, NETSCAPE_SPKI_free, spki) +#define GetSPKI(obj, spki) Data_Get_Struct(obj, NETSCAPE_SPKI, spki) + +/* + * Classes + */ +VALUE cSPKI; +VALUE eSPKIError; + +/* + * Public functions + */ + +/* + * Private functions + */ +static VALUE +ossl_spki_s_new(int argc, VALUE *argv, VALUE klass) +{ + NETSCAPE_SPKI *spki = NULL; + VALUE obj; + + if (!(spki = NETSCAPE_SPKI_new())) { + OSSL_Raise(eSPKIError, ""); + } + + WrapSPKI(obj, spki); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_spki_initialize(int argc, VALUE *argv, VALUE self) +{ + NETSCAPE_SPKI *spki = NULL; + VALUE buffer; + + if (argc == 0) + return self; + + buffer = rb_String(argv[0]); + + if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING(buffer)->ptr, -1))) { + OSSL_Raise(eSPKIError, ""); + } + + NETSCAPE_SPKI_free(DATA_PTR(self)); + DATA_PTR(self) = spki; + + return self; +} + +static VALUE +ossl_spki_to_pem(VALUE self) +{ + NETSCAPE_SPKI *spki = NULL; + char *data = NULL; + VALUE str; + + GetSPKI(self, spki); + + if (!(data = NETSCAPE_SPKI_b64_encode(spki))) { + OSSL_Raise(eSPKIError, ""); + } + + str = rb_str_new2(data); + OPENSSL_free(data); + + return str; +} + +static VALUE +ossl_spki_to_text(VALUE self) +{ + NETSCAPE_SPKI *spki = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetSPKI(self, spki); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eSPKIError, ""); + } + if (!NETSCAPE_SPKI_print(out, spki)) { + BIO_free(out); + OSSL_Raise(eSPKIError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_spki_get_public_key(VALUE self) +{ + NETSCAPE_SPKI *spki = NULL; + EVP_PKEY *pkey = NULL; + + GetSPKI(self, spki); + + if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { + OSSL_Raise(eSPKIError, ""); + } + + return ossl_pkey_new(pkey); +} + +static VALUE +ossl_spki_set_public_key(VALUE self, VALUE pubk) +{ + NETSCAPE_SPKI *spki = NULL; + EVP_PKEY *pkey = NULL; + + GetSPKI(self, spki); + + pkey = ossl_pkey_get_EVP_PKEY(pubk); + + if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) { + EVP_PKEY_free(pkey); + OSSL_Raise(eSPKIError, ""); + } + + return pubk; +} + +static VALUE +ossl_spki_get_challenge(VALUE self) +{ + NETSCAPE_SPKI *spki = NULL; + + GetSPKI(self, spki); + + if (spki->spkac->challenge->length > 0) + return rb_str_new(spki->spkac->challenge->data, spki->spkac->challenge->length); + + return rb_str_new2(""); +} + +static VALUE +ossl_spki_set_challenge(VALUE self, VALUE str) +{ + NETSCAPE_SPKI *spki = NULL; + + GetSPKI(self, spki); + + str = rb_String(str); + + if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING(str)->ptr, RSTRING(str)->len)) { + OSSL_Raise(eSPKIError, ""); + } + + return str; +} + +static VALUE +ossl_spki_sign(VALUE self, VALUE key, VALUE digest) +{ + NETSCAPE_SPKI *spki = NULL; + EVP_PKEY *pkey = NULL; + const EVP_MD *md = NULL; + + GetSPKI(self, spki); + + md = ossl_digest_get_EVP_MD(digest); + + if (rb_funcall(key, id_private_q, 0, NULL) == Qfalse) { + rb_raise(eSPKIError, "PRIVATE key needed to sign SPKI!"); + } + pkey = ossl_pkey_get_EVP_PKEY(key); + + if (!NETSCAPE_SPKI_sign(spki, pkey, md)) { + EVP_PKEY_free(pkey); + OSSL_Raise(eSPKIError, ""); + } + + return self; +} + +/* + * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' + */ +static VALUE +ossl_spki_verify(VALUE self, VALUE key) +{ + NETSCAPE_SPKI *spki = NULL; + EVP_PKEY *pkey = NULL; + int result = 0; + + GetSPKI(self, spki); + + pkey = ossl_pkey_get_EVP_PKEY(key); + + result = NETSCAPE_SPKI_verify(spki, pkey); + EVP_PKEY_free(pkey); + + if (result < 0) { + OSSL_Raise(eSPKIError, ""); + } else if (result > 0) + return Qtrue; + + return Qfalse; +} + +/* + * NETSCAPE_SPKI init + */ +void +Init_ossl_spki(VALUE module) +{ + eSPKIError = rb_define_class_under(module, "SPKIError", eOSSLError); + + cSPKI = rb_define_class_under(module, "SPKI", rb_cObject); + rb_define_singleton_method(cSPKI, "new", ossl_spki_s_new, -1); + rb_define_method(cSPKI, "initialize", ossl_spki_initialize, -1); + rb_define_method(cSPKI, "to_pem", ossl_spki_to_pem, 0); + rb_define_alias(cSPKI, "to_s", "to_pem"); + rb_define_method(cSPKI, "to_text", ossl_spki_to_text, 0); + rb_define_method(cSPKI, "public_key", ossl_spki_get_public_key, 0); + rb_define_method(cSPKI, "public_key=", ossl_spki_set_public_key, 1); + rb_define_method(cSPKI, "sign", ossl_spki_sign, 2); + rb_define_method(cSPKI, "verify", ossl_spki_verify, 1); + rb_define_method(cSPKI, "challenge", ossl_spki_get_challenge, 0); + rb_define_method(cSPKI, "challenge=", ossl_spki_set_challenge, 1); +} + diff --git a/ossl_pkcs7.c b/ossl_pkcs7.c new file mode 100644 index 0000000..27026f1 --- /dev/null +++ b/ossl_pkcs7.c @@ -0,0 +1,617 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapPKCS7(obj, pkcs7) obj = Data_Wrap_Struct(cPKCS7, 0, PKCS7_free, pkcs7) +#define GetPKCS7(obj, pkcs7) Data_Get_Struct(obj, PKCS7, pkcs7) + +#define WrapPKCS7si(obj, p7si) obj = Data_Wrap_Struct(cPKCS7SignerInfo, 0, PKCS7_SIGNER_INFO_free, p7si) +#define GetPKCS7si(obj, p7si) Data_Get_Struct(obj, PKCS7_SIGNER_INFO, p7si) + +/* + * Constants + */ +#define SIGNED NID_pkcs7_signed +#define ENVELOPED NID_pkcs7_enveloped +#define SIGNED_ENVELOPED NID_pkcs7_signedAndEnveloped +/* + * #define DIGEST NID_digest + * #define ENCRYPTED NID_encrypted + */ + +/* + * Classes + */ +VALUE cPKCS7; +VALUE cPKCS7SignerInfo; +VALUE ePKCS7Error; + +/* + * Public + */ +VALUE +ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si) +{ + PKCS7_SIGNER_INFO *new = NULL; + VALUE obj; + + if (!p7si) + new = PKCS7_SIGNER_INFO_new(); + else new = PKCS7_SIGNER_INFO_dup(p7si); + + if (!new) + OSSL_Raise(ePKCS7Error, ""); + + WrapPKCS7si(obj, new); + + return obj; +} + +PKCS7_SIGNER_INFO * +ossl_pkcs7si_get_PKCS7_SIGNER_INFO(VALUE obj) +{ + PKCS7_SIGNER_INFO *p7si = NULL, *new; + + OSSL_Check_Type(obj, cPKCS7SignerInfo); + + GetPKCS7si(obj, p7si); + + if (!(new = PKCS7_SIGNER_INFO_dup(p7si))) { + OSSL_Raise(ePKCS7Error, ""); + } + return new; +} + +/* + * Private + */ +/* + * WORKS WELL, but we can implement this in Ruby space +static VALUE ossl_pkcs7_s_sign(VALUE klass, VALUE key, VALUE cert, VALUE data) +{ + PKCS7 *pkcs7 = NULL; + EVP_PKEY *pkey = NULL; + X509 *x509 = NULL; + BIO *bio = NULL; + VALUE obj; + + OSSL_Check_Type(key, cPKey); + OSSL_Check_Type(cert, X509Certificate); + data = rb_String(data); + + if (rb_funcall(key, id_private_q, 0, NULL) != Qtrue) { + rb_raise(ePKCS7Error, "private key needed!"); + } + + pkey = ossl_pkey_get_EVP_PKEY(key); + x509 = ossl_x509_get_X509(cert); + + if (!(bio = BIO_new_mem_buf(RSTRING(data)->ptr, RSTRING(data)->len))) { + EVP_PKEY_free(pkey); + X509_free(x509); + OSSL_Raise(ePKCS7Error, ""); + } + if (!(pkcs7 = PKCS7_sign(x509, pkey, NULL, bio, 0))) { + EVP_PKEY_free(pkey); + X509_free(x509); + BIO_free(bio); + OSSL_Raise(ePKCS7Error, ""); + } + EVP_PKEY_free(pkey); + X509_free(x509); + BIO_free(bio); + + WrapPKCS7(obj, pkcs7); + + return obj; +} + */ + +static VALUE +ossl_pkcs7_s_new(int argc, VALUE *argv, VALUE klass) +{ + PKCS7 *pkcs7 = NULL; + VALUE obj; + + if (!(pkcs7 = PKCS7_new())) { + OSSL_Raise(ePKCS7Error, ""); + } + + WrapPKCS7(obj, pkcs7); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) +{ + PKCS7 *pkcs7 = NULL; + BIO *in = NULL; + VALUE arg1; + + rb_scan_args(argc, argv, "10", &arg1); + + switch (TYPE(arg1)) { + case T_FIXNUM: + GetPKCS7(self, pkcs7); + + if(!PKCS7_set_type(pkcs7, FIX2INT(arg1))) { + OSSL_Raise(ePKCS7Error, ""); + } + break; + default: + arg1 = rb_String(arg1); + if (!(in = BIO_new_mem_buf(RSTRING(arg1)->ptr, RSTRING(arg1)->len))) { + OSSL_Raise(ePKCS7Error, ""); + } + if (!PEM_read_bio_PKCS7(in, (PKCS7 **)&DATA_PTR(self), NULL, NULL)) { + BIO_free(in); + OSSL_Raise(ePKCS7Error, ""); + } + BIO_free(in); + } + + return self; +} + +static VALUE +ossl_pkcs7_set_cipher(VALUE self, VALUE cipher) +{ + PKCS7 *pkcs7 = NULL; + + GetPKCS7(self, pkcs7); + + OSSL_Check_Type(cipher, cCipher); + + if (!PKCS7_set_cipher(pkcs7, ossl_cipher_get_EVP_CIPHER(cipher))) { + OSSL_Raise(ePKCS7Error, ""); + } + + return cipher; +} + +static VALUE +ossl_pkcs7_add_signer(VALUE self, VALUE signer, VALUE key) +{ + PKCS7 *pkcs7 = NULL; + PKCS7_SIGNER_INFO *si = NULL; + EVP_PKEY *pkey = NULL; + + GetPKCS7(self, pkcs7); + + OSSL_Check_Type(signer, cPKCS7SignerInfo); + OSSL_Check_Type(key, cPKey); + + if (rb_funcall(key, id_private_q, 0, NULL) != Qtrue) { + rb_raise(ePKCS7Error, "Private key needed!"); + } + + si = ossl_pkcs7si_get_PKCS7_SIGNER_INFO(signer); + pkey = ossl_pkey_get_EVP_PKEY(key); + si->pkey = pkey; + + if (!PKCS7_add_signer(pkcs7, si)) { + PKCS7_SIGNER_INFO_free(si); + OSSL_Raise(ePKCS7Error, "Could not add signer."); + } + + if (PKCS7_type_is_signed(pkcs7)) + PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data)); + + return self; +} + +static VALUE +ossl_pkcs7_get_signer(VALUE self) +{ + PKCS7 *pkcs7 = NULL; + STACK_OF(PKCS7_SIGNER_INFO) *sk = NULL; + PKCS7_SIGNER_INFO *si = NULL; + int num = 0, i; + VALUE ary; + + GetPKCS7(self, pkcs7); + + if (!(sk = PKCS7_get_signer_info(pkcs7))) { + rb_warning("OpenSSL::PKCS7#get_signer_info == NULL!"); + return rb_ary_new(); + } + + if ((num = sk_PKCS7_SIGNER_INFO_num(sk)) < 0) { + rb_raise(ePKCS7Error, "Negative number of signers!"); + } + + ary = rb_ary_new2(num); + + for (i=0; i<num; i++) { + si = sk_PKCS7_SIGNER_INFO_value(sk, i); + rb_ary_push(ary, ossl_pkcs7si_new(si)); + } + + return ary; +} + +static VALUE +ossl_pkcs7_add_recipient(VALUE self, VALUE cert) +{ + PKCS7 *pkcs7 = NULL; + PKCS7_RECIP_INFO *ri = NULL; + X509 *x509 = NULL; + + GetPKCS7(self, pkcs7); + + x509 = ossl_x509_get_X509(cert); + + if (!(ri = PKCS7_RECIP_INFO_new())) { + X509_free(x509); + OSSL_Raise(ePKCS7Error, ""); + } + + + if (!PKCS7_RECIP_INFO_set(ri, x509)) { + X509_free(x509); + PKCS7_RECIP_INFO_free(ri); + OSSL_Raise(ePKCS7Error, ""); + } + X509_free(x509); + + if (!PKCS7_add_recipient_info(pkcs7, ri)) { + PKCS7_RECIP_INFO_free(ri); + OSSL_Raise(ePKCS7Error, ""); + } + + return self; +} + +static VALUE +ossl_pkcs7_add_certificate(VALUE self, VALUE cert) +{ + PKCS7 *pkcs7 = NULL; + X509 *x509 = NULL; + + GetPKCS7(self, pkcs7); + + x509 = ossl_x509_get_X509(cert); + + if (!PKCS7_add_certificate(pkcs7, x509)) { /* DUPs x509 - free it! */ + X509_free(x509); + OSSL_Raise(ePKCS7Error, ""); + } + X509_free(x509); + + return self; +} + +static VALUE +ossl_pkcs7_add_crl(VALUE self, VALUE x509crl) +{ + PKCS7 *pkcs7 = NULL; + X509_CRL *crl = NULL; + + GetPKCS7(self, pkcs7); + + crl = ossl_x509crl_get_X509_CRL(x509crl); + + if (!PKCS7_add_crl(pkcs7, crl)) { /* DUPs crl - free it! */ + X509_CRL_free(crl); + OSSL_Raise(ePKCS7Error, ""); + } + X509_CRL_free(crl); + + return self; +} + +static VALUE +ossl_pkcs7_add_data(int argc, VALUE *argv, VALUE self) +{ + PKCS7 *pkcs7 = NULL; + BIO *bio = NULL; + int i; + VALUE data, detach; + + GetPKCS7(self, pkcs7); + + rb_scan_args(argc, argv, "11", &data, &detach); + + data = rb_String(data); + + PKCS7_content_new(pkcs7, NID_pkcs7_data); + + if (detach == Qtrue) + PKCS7_set_detached(pkcs7, 1); + + if (!(bio=PKCS7_dataInit(pkcs7, NULL))) { + OSSL_Raise(ePKCS7Error, ""); + } + if ((i = BIO_write(bio, RSTRING(data)->ptr, RSTRING(data)->len)) != RSTRING(data)->len) { + BIO_free(bio); + rb_raise(ePKCS7Error, "BIO_wrote %d, but should be %d!", i, RSTRING(data)->len); + } + if (!PKCS7_dataFinal(pkcs7, bio)) { + BIO_free(bio); + OSSL_Raise(ePKCS7Error, ""); + } + BIO_free(bio); + + return self; +} + +static VALUE +ossl_pkcs7_data_verify(int argc, VALUE *argv, VALUE self) +{ + PKCS7 *pkcs7 = NULL; + BIO *bio = NULL, *data = NULL; + char buf[1024*4]; + int i = 0, result; + STACK_OF(PKCS7_SIGNER_INFO) *sk = NULL; + PKCS7_SIGNER_INFO *si = NULL; + X509_STORE *store = NULL; + X509_STORE_CTX ctx; + VALUE x509store, detached; + + GetPKCS7(self, pkcs7); + + if (!PKCS7_type_is_signed(pkcs7)) { + rb_raise(ePKCS7Error, "Wrong content type - PKCS7 is not SIGNED"); + } + + rb_scan_args(argc, argv, "11", &x509store, &detached); + + store = ossl_x509store_get_X509_STORE(x509store); + + if (!NIL_P(detached)) { + detached = rb_String(detached); + if (!(data = BIO_new_mem_buf(RSTRING(detached)->ptr, RSTRING(detached)->len))) { + OSSL_Raise(ePKCS7Error, ""); + } + } + + if (PKCS7_get_detached(pkcs7)) { + if (!data) + rb_raise(ePKCS7Error, "PKCS7 is detached, data needed!"); + + bio = PKCS7_dataInit(pkcs7, data); + } else + bio = PKCS7_dataInit(pkcs7, NULL); + + if (!bio) { + if (data) BIO_free(data); + OSSL_Raise(ePKCS7Error, ""); + } + + /* We have to 'read' from bio to calculate digests etc. */ + for (;;) { + i = BIO_read(bio, buf, sizeof(buf)); + if (i <= 0) break; + } + /*BIO_free(bio); - shall we?*/ + + if (!(sk = PKCS7_get_signer_info(pkcs7))) + rb_raise(ePKCS7Error, "NO SIGNATURES ON THIS DATA"); + + for (i=0; i<sk_PKCS7_SIGNER_INFO_num(sk); i++) { + si = sk_PKCS7_SIGNER_INFO_value(sk, i); + result = PKCS7_dataVerify(store, &ctx, bio, pkcs7, si); + if (result <= 0) { + OSSL_Warning("PKCS7::PKCS7.verify_data():"); + return Qfalse; + } + + /* Yield signer info */ + if (rb_block_given_p()) + rb_yield(ossl_pkcs7si_new(si)); + } + return Qtrue; +} + +static VALUE +ossl_pkcs7_data_decode(VALUE self, VALUE key, VALUE cert) +{ + PKCS7 *pkcs7 = NULL; + EVP_PKEY *pkey = NULL; + X509 *x509 = NULL; + BIO *bio = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetPKCS7(self, pkcs7); + + if(!PKCS7_type_is_enveloped(pkcs7)) { + rb_raise(ePKCS7Error, "Wrong content type - PKCS7 is not ENVELOPED"); + } + + OSSL_Check_Type(key, cPKey); + OSSL_Check_Type(cert, cX509Certificate); + + if (rb_funcall(key, id_private_q, 0, NULL) != Qtrue) { + rb_raise(ePKCS7Error, "private key needed!"); + } + + pkey = ossl_pkey_get_EVP_PKEY(key); + x509 = ossl_x509_get_X509(cert); + + if (!(bio = PKCS7_dataDecode(pkcs7, pkey, NULL, x509))) { + EVP_PKEY_free(pkey); + X509_free(x509); + OSSL_Raise(ePKCS7Error, ""); + } + EVP_PKEY_free(pkey); + X509_free(x509); + + BIO_get_mem_ptr(bio, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(bio); + + return str; +} + +static VALUE +ossl_pkcs7_to_pem(VALUE self) +{ + PKCS7 *pkcs7 = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetPKCS7(self, pkcs7); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(ePKCS7Error, ""); + } + if (!PEM_write_bio_PKCS7(out, pkcs7)) { + BIO_free(out); + OSSL_Raise(ePKCS7Error, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * SIGNER INFO + */ +static VALUE +ossl_pkcs7si_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE obj; + + obj = ossl_pkcs7si_new(NULL); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_pkcs7si_initialize(int argc, VALUE *argv, VALUE self) +{ + PKCS7_SIGNER_INFO *p7si = NULL; + EVP_PKEY *pkey = NULL; + X509 *x509 = NULL; + const EVP_MD *md = NULL; + VALUE key, cert, digest; + + GetPKCS7si(self, p7si); + + rb_scan_args(argc, argv, "30", &cert, &key, &digest); + + OSSL_Check_Type(key, cPKey); + OSSL_Check_Type(cert, cX509Certificate); + md = ossl_digest_get_EVP_MD(digest); + + if (rb_funcall(key, id_private_q, 0, NULL) != Qtrue) { + rb_raise(ePKCS7Error, "private key needed!"); + } + + pkey = ossl_pkey_get_EVP_PKEY(key); + x509 = ossl_x509_get_X509(cert); + + if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, md))) { + EVP_PKEY_free(pkey); + X509_free(x509); + OSSL_Raise(ePKCS7Error, ""); + } + EVP_PKEY_free(pkey); + X509_free(x509); + + return self; +} + +static VALUE +ossl_pkcs7si_get_name(VALUE self) +{ + PKCS7_SIGNER_INFO *p7si = NULL; + + GetPKCS7si(self, p7si); + + return ossl_x509name_new(p7si->issuer_and_serial->issuer); +} + +static VALUE +ossl_pkcs7si_get_serial(VALUE self) +{ + PKCS7_SIGNER_INFO *p7si = NULL; + + GetPKCS7si(self, p7si); + + return INT2NUM(ASN1_INTEGER_get(p7si->issuer_and_serial->serial)); +} + +static VALUE +ossl_pkcs7si_get_signed_time(VALUE self) +{ + PKCS7_SIGNER_INFO *p7si = NULL; + ASN1_TYPE *asn1obj = NULL; + + GetPKCS7si(self, p7si); + + if (!(asn1obj = PKCS7_get_signed_attribute(p7si, NID_pkcs9_signingTime))) { + OSSL_Raise(ePKCS7Error, ""); + } + if (asn1obj->type == V_ASN1_UTCTIME) + return asn1time_to_time(asn1obj->value.utctime); + + /* + * OR + * rb_raise(ePKCS7Error, "..."); + * ? + */ + return Qnil; +} + +/* + * INIT + */ +void +Init_ossl_pkcs7(VALUE module) +{ + ePKCS7Error = rb_define_class_under(module, "PKCS7Error", eOSSLError); + + cPKCS7 = rb_define_class_under(module, "PKCS7", rb_cObject); + /* + * WORKS WELL, but we can implement this in Ruby space + * rb_define_singleton_method(cPKCS7, "sign", ossl_pkcs7_s_sign, 3); + */ + rb_define_singleton_method(cPKCS7, "new", ossl_pkcs7_s_new, -1); + + rb_define_method(cPKCS7, "initialize", ossl_pkcs7_initialize, -1); + rb_define_method(cPKCS7, "add_signer", ossl_pkcs7_add_signer, 2); + rb_define_method(cPKCS7, "signers", ossl_pkcs7_get_signer, 0); + rb_define_method(cPKCS7, "cipher=", ossl_pkcs7_set_cipher, 1); + rb_define_method(cPKCS7, "add_recipient", ossl_pkcs7_add_recipient, 1); + rb_define_method(cPKCS7, "add_certificate", ossl_pkcs7_add_certificate, 1); + rb_define_method(cPKCS7, "add_crl", ossl_pkcs7_add_crl, 1); + rb_define_method(cPKCS7, "add_data", ossl_pkcs7_add_data, -1); + rb_define_method(cPKCS7, "verify_data", ossl_pkcs7_data_verify, -1); + rb_define_method(cPKCS7, "decode_data", ossl_pkcs7_data_decode, 2); + rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0); + rb_define_alias(cPKCS7, "to_s", "to_pem"); + +#define DefPKCS7Const(x) rb_define_const(module, #x, INT2FIX(x)) + + DefPKCS7Const(SIGNED); + DefPKCS7Const(ENVELOPED); + DefPKCS7Const(SIGNED_ENVELOPED); + + cPKCS7SignerInfo = rb_define_class_under(module, "Signer", rb_cObject); + + rb_define_singleton_method(cPKCS7SignerInfo, "new", ossl_pkcs7si_s_new, -1); + + rb_define_method(cPKCS7SignerInfo, "initialize", ossl_pkcs7si_initialize, -1); + rb_define_method(cPKCS7SignerInfo, "name", ossl_pkcs7si_get_name, 0); + rb_define_method(cPKCS7SignerInfo, "serial", ossl_pkcs7si_get_serial, 0); + rb_define_method(cPKCS7SignerInfo, "signed_time", ossl_pkcs7si_get_signed_time, 0); +} + diff --git a/ossl_pkey.c b/ossl_pkey.c new file mode 100644 index 0000000..7252c45 --- /dev/null +++ b/ossl_pkey.c @@ -0,0 +1,137 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" +#include "ossl_pkey.h" + +#define GetPKey(obj, pkeyp) do {\ + Data_Get_Struct(obj, ossl_pkey, pkeyp);\ + if (!pkeyp->get_EVP_PKEY) rb_raise(ePKeyError, "not initialized!");\ +} while (0) + +/* + * Classes + */ +ID id_private_q; +VALUE cPKey; +VALUE ePKeyError; + +/* + * Struct + * see ossl_pkey.h + */ + +/* + * Public + */ +VALUE +ossl_pkey_new(EVP_PKEY *key) +{ + if (!key) + rb_raise(ePKeyError, "Cannot make new key from NULL."); + + switch (key->type) { +#if !defined(OPENSSL_NO_RSA) + case EVP_PKEY_RSA: + return ossl_rsa_new(key->pkey.rsa); +#endif +#if !defined(OPENSSL_NO_DSA) + case EVP_PKEY_DSA: + return ossl_dsa_new(key->pkey.dsa); +#endif +#if !defined(OPENSSL_NO_DH) + case EVP_PKEY_DH: + return ossl_dh_new(key->pkey.dh); +#endif + } + + rb_raise(ePKeyError, "unsupported key type"); + return Qnil; +} + +VALUE +ossl_pkey_new_from_file(VALUE filename) +{ + FILE *fp = NULL; + EVP_PKEY *pkey = NULL; + VALUE obj; + + filename = rb_str_to_str(filename); + Check_SafeStr(filename); + + if ((fp = fopen(RSTRING(filename)->ptr, "r")) == NULL) + rb_raise(ePKeyError, "%s", strerror(errno)); + + /* + * MR: + * How about PublicKeys from file? + * pkey = PEM_read_PublicKey(fp, NULL, NULL, NULL); + * MISSING IN OPENSSL + */ + /* + * Will we handle user passwords? + */ + pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + fclose(fp); + + if (!pkey) + OSSL_Raise(ePKeyError, ""); + + obj = ossl_pkey_new(pkey); + EVP_PKEY_free(pkey); + + return obj; +} + +EVP_PKEY * +ossl_pkey_get_EVP_PKEY(VALUE obj) +{ + ossl_pkey *pkeyp = NULL; + + OSSL_Check_Type(obj, cPKey); + + GetPKey(obj, pkeyp); + + return pkeyp->get_EVP_PKEY(obj); +} + +/* + * Private + */ +static VALUE +ossl_pkey_s_new(int argc, VALUE *argv, VALUE klass) +{ + if (klass == cPKey) + rb_raise(rb_eNotImpError, "cannot do PKey::ANY.new - it is an abstract class"); + + return Qnil; +} + +/* + * INIT + */ +void +Init_ossl_pkey(VALUE module) +{ + id_private_q = rb_intern("private?"); + + ePKeyError = rb_define_class_under(module, "PKeyError", eOSSLError); + + cPKey = rb_define_class_under(module, "ANY", rb_cObject); + rb_define_singleton_method(cPKey, "new", ossl_pkey_s_new, -1); + + /* + * INIT rsa, dsa + */ + Init_ossl_rsa(module, cPKey, ePKeyError); + Init_ossl_dsa(module, cPKey, ePKeyError); + Init_ossl_dh(module, cPKey, ePKeyError); +} + diff --git a/ossl_pkey.h b/ossl_pkey.h new file mode 100644 index 0000000..77e4e43 --- /dev/null +++ b/ossl_pkey.h @@ -0,0 +1,22 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_PKEY_H_) +#define _OSSL_PKEY_H_ + +/* + * Struct + */ +typedef struct ossl_pkey_st { + EVP_PKEY *(*get_EVP_PKEY)(VALUE); +} ossl_pkey; + +#endif + diff --git a/ossl_pkey_dh.c b/ossl_pkey_dh.c new file mode 100644 index 0000000..afe2f16 --- /dev/null +++ b/ossl_pkey_dh.c @@ -0,0 +1,316 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_DH) + +#include "ossl.h" +#include "ossl_pkey.h" + +#define MakeDH(obj, dhp) do {\ + obj = Data_Make_Struct(cDH, ossl_dh, 0, ossl_dh_free, dhp);\ + dhp->pkey.get_EVP_PKEY = ossl_dh_get_EVP_PKEY;\ +} while (0) + +#define GetDH(obj, dhp) do {\ + Data_Get_Struct(obj, ossl_dh, dhp);\ + if (!dhp->dh) rb_raise(eDHError, "not initialized!");\ +} while (0) + +#define DH_PRIVATE(dh) ((dh)->priv_key) + +/* + * Classes + */ +VALUE cDH; +VALUE eDHError; + +/* + * Struct + */ +typedef struct ossl_dh_st { + ossl_pkey pkey; + DH *dh; +} ossl_dh; + +static void +ossl_dh_free(ossl_dh *dhp) +{ + if (dhp) { + if (dhp->dh) DH_free(dhp->dh); + dhp->dh = NULL; + free(dhp); + } +} + +/* + * Public + */ +VALUE +ossl_dh_new(DH *dh) +{ + ossl_dh *dhp = NULL; + DH *new = NULL; + VALUE obj; + + if (!dh) + new = DH_new(); + else new = DHparams_dup(dh); + + if (!new) + OSSL_Raise(eDHError, ""); + + MakeDH(obj, dhp); + dhp->dh = new; + + return obj; +} + +DH * +ossl_dh_get_DH(VALUE obj) +{ + ossl_dh *dhp = NULL; + DH *dh = NULL; + + OSSL_Check_Instance(obj, cDH); + GetDH(obj, dhp); + + dh = DHparams_dup(dhp->dh); + + if (!dh) + OSSL_Raise(eDHError, ""); + + return dh; +} + +EVP_PKEY * +ossl_dh_get_EVP_PKEY(VALUE obj) +{ + DH *dh = NULL; + EVP_PKEY *pkey = NULL; + + dh = ossl_dh_get_DH(obj); + + if (!(pkey = EVP_PKEY_new())) { + DH_free(dh); + OSSL_Raise(eDHError, ""); + } + + if (!EVP_PKEY_assign_DH(pkey, dh)) { /* NO DUP - don't free! */ + DH_free(dh); + EVP_PKEY_free(pkey); + OSSL_Raise(eDHError, ""); + } + + return pkey; +} + +/* + * Private + */ +static VALUE +ossl_dh_s_new_from_pem(VALUE klass, VALUE buffer) +{ + ossl_dh *dhp = NULL; + DH *dh = NULL; + BIO *in = NULL; + VALUE obj; + + buffer = rb_String(buffer); + + if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len))) + OSSL_Raise(eDHError, ""); + + if (!(dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL))) { + BIO_free(in); + OSSL_Raise(eDHError, ""); + } + BIO_free(in); + + MakeDH(obj, dhp); + dhp->dh = dh; + + return obj; +} + +/* + * CB for yielding when generating DH params + */ +static void +ossl_dh_generate_cb(int p, int n, void *arg) +{ + VALUE ary; + + ary = rb_ary_new2(2); + rb_ary_store(ary, 0, INT2NUM(p)); + rb_ary_store(ary, 1, INT2NUM(n)); + + rb_yield(ary); +} + +static VALUE +ossl_dh_s_generate(VALUE klass, VALUE size, VALUE gen) +{ + ossl_dh *dhp = NULL; + DH *dh = NULL; + void (*cb)(int, int, void *) = NULL; + VALUE obj; + + Check_Type(size, T_FIXNUM); + + if (rb_block_given_p()) + cb = ossl_dh_generate_cb; + + if (!(dh = DH_generate_parameters(FIX2INT(size), FIX2INT(gen), cb, NULL))) { /* arg to cb = NULL */ + OSSL_Raise(eDHError, ""); + } + if (!DH_generate_key(dh)) { + DH_free(dh); + OSSL_Raise(eDHError, ""); + } + + MakeDH(obj, dhp); + dhp->dh = dh; + + return obj; +} + +static VALUE +ossl_dh_is_public(VALUE self) +{ + ossl_dh *dhp = NULL; + + GetDH(self, dhp); + + /* + * Do we need to check dhp->dh->public_pkey? + * return Qtrue; + */ + return (dhp->dh->pub_key) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dh_is_private(VALUE self) +{ + ossl_dh *dhp = NULL; + + GetDH(self, dhp); + + return (DH_PRIVATE(dhp->dh)) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dh_export(VALUE self) +{ + ossl_dh *dhp = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetDH(self, dhp); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eDHError, ""); + } + + if (!PEM_write_bio_DHparams(out, dhp->dh)) { + BIO_free(out); + OSSL_Raise(eDHError, ""); + } + + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Prints all parameters of key to buffer + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_dh_to_text(VALUE self) +{ + ossl_dh *dhp = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetDH(self, dhp); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eDHError, ""); + } + if (!DHparams_print(out, dhp->dh)) { + BIO_free(out); + OSSL_Raise(eDHError, ""); + } + + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes new instance DH PUBLIC_KEY from PRIVATE_KEY + */ +static VALUE +ossl_dh_to_public_key(VALUE self) +{ + ossl_dh *dhp1 = NULL, *dhp2 = NULL; + VALUE obj; + + GetDH(self, dhp1); + + MakeDH(obj, dhp2); + + if (!(dhp2->dh = DHparams_dup(dhp1->dh))) { + OSSL_Raise(eDHError, ""); + } + + return obj; +} + +/* + * INIT + */ +void +Init_ossl_dh(VALUE mPKey, VALUE cPKey, VALUE ePKeyError) +{ + eDHError = rb_define_class_under(mPKey, "DHError", ePKeyError); + + cDH = rb_define_class_under(mPKey, "DH", cPKey); + + rb_define_singleton_method(cDH, "new_from_pem", ossl_dh_s_new_from_pem, 1); + rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, 2); + rb_define_alias(CLASS_OF(cDH), "new_from_fixnum", "generate"); + + rb_define_method(cDH, "public?", ossl_dh_is_public, 0); + rb_define_method(cDH, "private?", ossl_dh_is_private, 0); + rb_define_method(cDH, "to_text", ossl_dh_to_text, 0); + rb_define_method(cDH, "export", ossl_dh_export, 0); + rb_define_alias(cDH, "to_pem", "export"); + rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0); +} + +#else /* defined NO_DH */ +# warning >>> OpenSSL is compiled without DH support <<< + +void +Init_ossl_dh(VALUE mPKey, VALUE cPKey, VALUE ePKeyError) +{ + rb_warning("OpenSSL is compiled without DH support"); +} + +#endif /* NO_DH */ + diff --git a/ossl_pkey_dsa.c b/ossl_pkey_dsa.c new file mode 100644 index 0000000..d0779ce --- /dev/null +++ b/ossl_pkey_dsa.c @@ -0,0 +1,443 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_DSA) + +#include "ossl.h" +#include "ossl_pkey.h" + +#define MakeDSA(obj, dsap) do {\ + obj = Data_Make_Struct(cDSA, ossl_dsa, 0, ossl_dsa_free, dsap);\ + dsap->pkey.get_EVP_PKEY = ossl_dsa_get_EVP_PKEY;\ +} while (0) + +#define GetDSA(obj, dsap) do {\ + Data_Get_Struct(obj, ossl_dsa, dsap);\ + if (!dsap->dsa) rb_raise(eDSAError, "not initialized!");\ +} while (0) + +#define DSA_PRIVATE(dsa) ((dsa)->priv_key) + +/* + * Classes + */ +VALUE cDSA; +VALUE eDSAError; + +/* + * Struct + */ +typedef struct ossl_dsa_st { + ossl_pkey pkey; + DSA *dsa; +} ossl_dsa; + +static void +ossl_dsa_free(ossl_dsa *dsap) +{ + if (dsap) { + if (dsap->dsa) DSA_free(dsap->dsa); + dsap->dsa = NULL; + free(dsap); + } +} + +/* + * Public + */ +VALUE +ossl_dsa_new(DSA *dsa) +{ + ossl_dsa *dsap = NULL; + DSA *new = NULL; + VALUE obj; + + if (!dsa) + new = DSA_new(); + else new = (DSA_PRIVATE(dsa)) ? DSAPrivateKey_dup(dsa) : DSAPublicKey_dup(dsa); + + if (!new) + OSSL_Raise(eDSAError, ""); + + MakeDSA(obj, dsap); + dsap->dsa = new; + + return obj; +} + +DSA * +ossl_dsa_get_DSA(VALUE obj) +{ + ossl_dsa *dsap = NULL; + DSA *dsa = NULL; + + OSSL_Check_Type(obj, cDSA); + GetDSA(obj, dsap); + + dsa = (DSA_PRIVATE(dsap->dsa)) ? DSAPrivateKey_dup(dsap->dsa) : DSAPublicKey_dup(dsap->dsa); + if (!dsa) + OSSL_Raise(eDSAError, ""); + + return dsa; +} + +EVP_PKEY * +ossl_dsa_get_EVP_PKEY(VALUE obj) +{ + DSA *dsa = NULL; + EVP_PKEY *pkey = NULL; + + dsa = ossl_dsa_get_DSA(obj); + + if (!(pkey = EVP_PKEY_new())) { + DSA_free(dsa); + OSSL_Raise(eDSAError, ""); + } + + if (!EVP_PKEY_assign_DSA(pkey, dsa)) { /* NO DUP - don't free! */ + DSA_free(dsa); + EVP_PKEY_free(pkey); + OSSL_Raise(eDSAError, ""); + } + + return pkey; +} + +/* + * Private + */ +static VALUE +ossl_dsa_s_new_from_pem(int argc, VALUE *argv, VALUE klass) +{ + ossl_dsa *dsap = NULL; + DSA *dsa = NULL; + BIO *in = NULL; + char *passwd = NULL; + VALUE buffer, pass, obj; + + rb_scan_args(argc, argv, "11", &buffer, &pass); + + buffer = rb_String(buffer); + + if (!NIL_P(pass)) { + pass = rb_String(pass); + passwd = RSTRING(pass)->ptr; + } + /* else passwd = NULL; */ + + if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len))) + OSSL_Raise(eDSAError, ""); + + if (!(dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL))) { + BIO_reset(in); + + if (!(dsa = PEM_read_bio_DSAPrivateKey(in, NULL, NULL, passwd))) { + BIO_free(in); + OSSL_Raise(eDSAError, "Neither PUB key nor PRIV key:"); + } + } + BIO_free(in); + + MakeDSA(obj, dsap); + dsap->dsa = dsa; + + return obj; +} + +/* + * CB for yielding when generating DSA params + */ +static void +ossl_dsa_generate_cb(int p, int n, void *arg) +{ + VALUE ary; + + ary = rb_ary_new2(2); + rb_ary_store(ary, 0, INT2NUM(p)); + rb_ary_store(ary, 1, INT2NUM(n)); + + rb_yield(ary); +} + +static VALUE +ossl_dsa_s_generate(VALUE klass, VALUE size) +{ + ossl_dsa *dsap = NULL; + DSA *dsa = NULL; + unsigned char seed[20]; + int seed_len = 20, counter = 0; + unsigned long h = 0; + void (*cb)(int, int, void *) = NULL; + VALUE obj; + + Check_Type(size, T_FIXNUM); + + if (!RAND_bytes(seed, seed_len)) { + OSSL_Raise(eDSAError, ""); + } + if (rb_block_given_p()) + cb = ossl_dsa_generate_cb; + + if (!(dsa = DSA_generate_parameters(FIX2INT(size), seed, seed_len, &counter, &h, cb, NULL))) { /* arg to cb = NULL */ + OSSL_Raise(eDSAError, ""); + } + if (!DSA_generate_key(dsa)) { + DSA_free(dsa); + OSSL_Raise(eDSAError, ""); + } + + MakeDSA(obj, dsap); + dsap->dsa = dsa; + + return obj; +} + +static VALUE +ossl_dsa_is_public(VALUE self) +{ + ossl_dsa *dsap = NULL; + + GetDSA(self, dsap); + + /* + * Do we need to check dsap->dsa->public_pkey? + * return Qtrue; + */ + return (dsap->dsa->pub_key) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dsa_is_private(VALUE self) +{ + ossl_dsa *dsap = NULL; + + GetDSA(self, dsap); + + return (DSA_PRIVATE(dsap->dsa)) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dsa_export(int argc, VALUE *argv, VALUE self) +{ + ossl_dsa *dsap = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + const EVP_CIPHER *ciph = NULL; + char *pass = NULL; + VALUE cipher, password, str; + + GetDSA(self, dsap); + + rb_scan_args(argc, argv, "02", &cipher, &password); + + if (!NIL_P(cipher)) { + ciph = ossl_cipher_get_EVP_CIPHER(cipher); + + if (!NIL_P(password)) { + password = rb_String(password); + pass = RSTRING(password)->ptr; + } + } + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eDSAError, ""); + } + + if (DSA_PRIVATE(dsap->dsa)) { + if (!PEM_write_bio_DSAPrivateKey(out, dsap->dsa, ciph, NULL, 0, NULL, pass)) { + BIO_free(out); + OSSL_Raise(eDSAError, ""); + } + } else { + if (!PEM_write_bio_DSAPublicKey(out, dsap->dsa)) { + BIO_free(out); + OSSL_Raise(eDSAError, ""); + } + } + + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_dsa_to_der(VALUE self) +{ + DSA *dsa = NULL; + EVP_PKEY *pkey = NULL; + X509_PUBKEY *key = NULL; + VALUE str; + + dsa = ossl_dsa_get_DSA(self); + + if (!(pkey = EVP_PKEY_new())) { + DSA_free(dsa); + OSSL_Raise(eDSAError, ""); + } + if (!EVP_PKEY_assign_DSA(pkey, dsa)) { /* NO DUP - don't free! */ + DSA_free(dsa); + EVP_PKEY_free(pkey); + OSSL_Raise(eDSAError, ""); + } + if (!(key = X509_PUBKEY_new())) { + EVP_PKEY_free(pkey); + OSSL_Raise(eDSAError, ""); + } + if (!X509_PUBKEY_set(&key, pkey)) { /* safe to FREE pkey or NOT? */ + EVP_PKEY_free(pkey); + X509_PUBKEY_free(key); + OSSL_Raise(eDSAError, ""); + } + + str = rb_str_new(key->public_key->data, key->public_key->length); + /* EVP_PKEY_free(pkey) = this does X509_PUBKEY_free?? */ + X509_PUBKEY_free(key); + + return str; +} + +/* + * Prints all parameters of key to buffer + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_dsa_to_text(VALUE self) +{ + ossl_dsa *dsap = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetDSA(self, dsap); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eDSAError, ""); + } + if (!DSA_print(out, dsap->dsa, 0)) { //offset = 0 + BIO_free(out); + OSSL_Raise(eDSAError, ""); + } + + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes new instance DSA PUBLIC_KEY from PRIVATE_KEY + */ +static VALUE +ossl_dsa_to_public_key(VALUE self) +{ + ossl_dsa *dsap1 = NULL, *dsap2 = NULL; + VALUE obj; + + GetDSA(self, dsap1); + + MakeDSA(obj, dsap2); + + if (!(dsap2->dsa = DSAPublicKey_dup(dsap1->dsa))) { + OSSL_Raise(eDSAError, ""); + } + + return obj; +} + +static VALUE +ossl_dsa_sign(VALUE self, VALUE data) +{ + ossl_dsa *dsap = NULL; + char *sig = NULL; + int sig_len = 0; + VALUE str; + + GetDSA(self, dsap); + data = rb_String(data); + + if (!DSA_PRIVATE(dsap->dsa)) { + rb_raise(eDSAError, "Private DSA key needed!"); + } + + if (!(sig = OPENSSL_malloc(DSA_size(dsap->dsa)+16))) { + OSSL_Raise(eDSAError, ""); + } + + if (!DSA_sign(0, RSTRING(data)->ptr, RSTRING(data)->len, sig, &sig_len, dsap->dsa)) { /*type = 0*/ + OPENSSL_free(sig); + OSSL_Raise(eDSAError, ""); + } + str = rb_str_new(sig, sig_len); + OPENSSL_free(sig); + + return str; +} + +static VALUE +ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig) +{ + ossl_dsa *dsap = NULL; + int ret = -1; + + GetDSA(self, dsap); + + digest = rb_String(digest); + sig = rb_String(sig); + + ret = DSA_verify(0, RSTRING(digest)->ptr, RSTRING(digest)->len,\ + RSTRING(sig)->ptr, RSTRING(sig)->len, dsap->dsa); /*type = 0*/ + + if (ret < 0) { + OSSL_Raise(eDSAError, ""); + } else if (ret == 1) + return Qtrue; + + return Qfalse; +} + +/* + * INIT + */ +void +Init_ossl_dsa(VALUE mPKey, VALUE cPKey, VALUE ePKeyError) +{ + eDSAError = rb_define_class_under(mPKey, "DSAError", ePKeyError); + + cDSA = rb_define_class_under(mPKey, "DSA", cPKey); + + rb_define_singleton_method(cDSA, "new_from_pem", ossl_dsa_s_new_from_pem, -1); + rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1); + rb_define_alias(CLASS_OF(cDSA), "new_from_fixnum", "generate"); + + rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0); + rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0); + rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0); + rb_define_method(cDSA, "export", ossl_dsa_export, -1); + rb_define_alias(cDSA, "to_pem", "export"); + rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0); + rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0); + rb_define_method(cDSA, "sign_digest", ossl_dsa_sign, 1); + rb_define_method(cDSA, "verify_digest", ossl_dsa_verify, 2); +} + +#else /* defined NO_DSA */ +# warning >>> OpenSSL is compiled without DSA support <<< + +void +Init_ossl_dsa(VALUE mPKey, VALUE cPKey, VALUE ePKeyError) +{ + rb_warning("OpenSSL is compiled without DSA support"); +} + +#endif /* NO_DSA */ + diff --git a/ossl_pkey_rsa.c b/ossl_pkey_rsa.c new file mode 100644 index 0000000..e74892c --- /dev/null +++ b/ossl_pkey_rsa.c @@ -0,0 +1,579 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_RSA) + +#include "ossl.h" +#include "ossl_pkey.h" + +#define MakeRSA(obj, rsap) do {\ + obj = Data_Make_Struct(cRSA, ossl_rsa, 0, ossl_rsa_free, rsap);\ + rsap->pkey.get_EVP_PKEY = ossl_rsa_get_EVP_PKEY;\ +} while (0) + +#define GetRSA(obj, rsap) do {\ + Data_Get_Struct(obj, ossl_rsa, rsap);\ + if (!rsap->rsa) rb_raise(eRSAError, "not initialized!");\ +} while (0) + +#define RSA_PRIVATE(rsa) ((rsa)->p && (rsa)->q) + +/* + * Classes + */ +VALUE cRSA; +VALUE eRSAError; + +/* + * Struct + */ +typedef struct ossl_rsa_st { + ossl_pkey pkey; + RSA *rsa; +} ossl_rsa; + +static void +ossl_rsa_free(ossl_rsa *rsap) +{ + if (rsap) { + if (rsap->rsa) RSA_free(rsap->rsa); + rsap->rsa = NULL; + free(rsap); + } +} + +/* + * Public + */ +VALUE +ossl_rsa_new(RSA *rsa) +{ + ossl_rsa *rsap = NULL; + RSA *new = NULL; + VALUE obj; + + if (!rsa) + new = RSA_new(); + else new = (RSA_PRIVATE(rsa)) ? RSAPrivateKey_dup(rsa) : RSAPublicKey_dup(rsa); + + if (!new) + OSSL_Raise(eRSAError, ""); + + MakeRSA(obj, rsap); + rsap->rsa = new; + + return obj; +} + +RSA * +ossl_rsa_get_RSA(VALUE obj) +{ + ossl_rsa *rsap = NULL; + RSA *rsa = NULL; + + OSSL_Check_Type(obj, cRSA); + GetRSA(obj, rsap); + + rsa = (RSA_PRIVATE(rsap->rsa)) ? RSAPrivateKey_dup(rsap->rsa) : RSAPublicKey_dup(rsap->rsa); + + if (!rsa) + OSSL_Raise(eRSAError, ""); + + return rsa; +} + +EVP_PKEY * +ossl_rsa_get_EVP_PKEY(VALUE obj) +{ + RSA *rsa = NULL; + EVP_PKEY *pkey = NULL; + + rsa = ossl_rsa_get_RSA(obj); + + if (!(pkey = EVP_PKEY_new())) { + RSA_free(rsa); + OSSL_Raise(eRSAError, ""); + } + + if (!EVP_PKEY_assign_RSA(pkey, rsa)) { + RSA_free(rsa); + EVP_PKEY_free(pkey); + OSSL_Raise(eRSAError, ""); + } + + return pkey; +} + +/* + * Private + */ +static VALUE +ossl_rsa_s_new_from_pem(int argc, VALUE *argv, VALUE klass) +{ + ossl_rsa *rsap = NULL; + RSA *rsa = NULL; + BIO *in = NULL; + char *passwd = NULL; + VALUE buffer, pass, obj; + + rb_scan_args(argc, argv, "11", &buffer, &pass); + + buffer = rb_String(buffer); + + if (!NIL_P(pass)) { + pass = rb_String(pass); + passwd = RSTRING(pass)->ptr; + } + /* else passwd = NULL; */ + + if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len))) { + OSSL_Raise(eRSAError, ""); + } + if (!(rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL))) { + BIO_reset(in); + + if (!(rsa = PEM_read_bio_RSAPrivateKey(in, NULL, NULL, passwd))) { + BIO_free(in); + OSSL_Raise(eRSAError, "Neither PUB key nor PRIV key:"); + } + } + BIO_free(in); + + MakeRSA(obj, rsap); + rsap->rsa = rsa; + + return obj; +} + +/* + * CB for yielding when generating RSA data + */ +static void +ossl_rsa_generate_cb(int p, int n, void *arg) +{ + VALUE ary; + + ary = rb_ary_new2(2); + rb_ary_store(ary, 0, INT2NUM(p)); + rb_ary_store(ary, 1, INT2NUM(n)); + + rb_yield(ary); +} + +static VALUE +ossl_rsa_s_generate(VALUE klass, VALUE size) +{ + ossl_rsa *rsap = NULL; + RSA *rsa = NULL; + void (*cb)(int, int, void *) = NULL; + VALUE obj; + + Check_Type(size, T_FIXNUM); + + if (rb_block_given_p()) + cb = ossl_rsa_generate_cb; + + if (!(rsa = RSA_generate_key(FIX2INT(size), RSA_F4, cb, NULL))) { /* arg to cb = NULL */ + OSSL_Raise(eRSAError, ""); + } + + MakeRSA(obj, rsap); + rsap->rsa = rsa; + + return obj; +} + +static VALUE +ossl_rsa_is_public(VALUE self) +{ + ossl_rsa *rsap = NULL; + + GetRSA(self, rsap); + + /* + * SURPRISE! :-)) + * Every key is public at the same time! + */ + return Qtrue; +} + +static VALUE +ossl_rsa_is_private(VALUE self) +{ + ossl_rsa *rsap = NULL; + + GetRSA(self, rsap); + + return (RSA_PRIVATE(rsap->rsa)) ? Qtrue : Qfalse; +} + +static VALUE +ossl_rsa_export(int argc, VALUE *argv, VALUE self) +{ + ossl_rsa *rsap = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + const EVP_CIPHER *ciph = NULL; + char *passwd = NULL; + VALUE cipher, pass, str; + + GetRSA(self, rsap); + + rb_scan_args(argc, argv, "02", &cipher, &pass); + + if (!NIL_P(cipher)) { + ciph = ossl_cipher_get_EVP_CIPHER(cipher); + + if (!NIL_P(pass)) { + pass = rb_String(pass); + passwd = RSTRING(pass)->ptr; + } + } + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eRSAError, ""); + } + + if (RSA_PRIVATE(rsap->rsa)) { + if (!PEM_write_bio_RSAPrivateKey(out, rsap->rsa, ciph, NULL, 0, NULL, passwd)) { + BIO_free(out); + OSSL_Raise(eRSAError, ""); + } + } else { + if (!PEM_write_bio_RSAPublicKey(out, rsap->rsa)) { + BIO_free(out); + OSSL_Raise(eRSAError, ""); + } + } + + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_rsa_public_encrypt(VALUE self, VALUE buffer) +{ + ossl_rsa *rsap = NULL; + char *enc_text = NULL; + int len = 0, size = 0; + VALUE enc; + + GetRSA(self, rsap); + + buffer = rb_String(buffer); + + size = RSA_size(rsap->rsa); + + if (!(enc_text = OPENSSL_malloc(size + 16))) { + OSSL_Raise(eRSAError, ""); + } + if ((len = RSA_public_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, enc_text, rsap->rsa, RSA_PKCS1_PADDING)) < 0) { + OPENSSL_free(enc_text); + OSSL_Raise(eRSAError, ""); + } + enc = rb_str_new(enc_text, len); + OPENSSL_free(enc_text); + + return enc; +} + +static VALUE +ossl_rsa_public_decrypt(VALUE self, VALUE buffer) +{ + ossl_rsa *rsap = NULL; + char *txt = NULL; + int len = 0, size = 0; + VALUE text; + + GetRSA(self, rsap); + + buffer = rb_String(buffer); + + size = RSA_size(rsap->rsa); + + if (!(txt = OPENSSL_malloc(size + 16))) { + OSSL_Raise(eRSAError, ""); + } + if ((len = RSA_public_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, txt, rsap->rsa, RSA_PKCS1_PADDING)) < 0) { + OPENSSL_free(txt); + OSSL_Raise(eRSAError, ""); + } + text = rb_str_new(txt, len); + OPENSSL_free(txt); + + return text; +} + +static VALUE +ossl_rsa_private_encrypt(VALUE self, VALUE buffer) +{ + ossl_rsa *rsap = NULL; + char *enc_text = NULL; + int len = 0, size = 0; + VALUE enc; + + GetRSA(self, rsap); + + if (!RSA_PRIVATE(rsap->rsa)) { + rb_raise(eRSAError, "PRIVATE key needed for this operation!"); + } + + buffer = rb_String(buffer); + + size = RSA_size(rsap->rsa); + + if (!(enc_text = OPENSSL_malloc(size + 16))) { + OSSL_Raise(eRSAError, "Memory alloc error"); + } + if ((len = RSA_private_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, enc_text, rsap->rsa, RSA_PKCS1_PADDING)) < 0) { + OPENSSL_free(enc_text); + OSSL_Raise(eRSAError, ""); + } + enc = rb_str_new(enc_text, len); + OPENSSL_free(enc_text); + + return enc; +} + +static VALUE +ossl_rsa_private_decrypt(VALUE self, VALUE buffer) +{ + ossl_rsa *rsap = NULL; + char *txt = NULL; + int len = 0, size = 0; + VALUE text; + + GetRSA(self, rsap); + + if (!RSA_PRIVATE(rsap->rsa)) { + rb_raise(eRSAError, "Private RSA key needed!"); + } + + buffer = rb_String(buffer); + + size = RSA_size(rsap->rsa); + + if (!(txt = OPENSSL_malloc(size + 16))) { + OSSL_Raise(eRSAError, "Memory alloc error"); + } + if ((len = RSA_private_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, txt, rsap->rsa, RSA_PKCS1_PADDING)) < 0) { + OPENSSL_free(txt); + OSSL_Raise(eRSAError, ""); + } + text = rb_str_new(txt, len); + OPENSSL_free(txt); + + return text; +} + +/* + * Just sample + * (it's not (maybe) wise to show private RSA values) + * - if, then implement this via OpenSSL::BN + * +static VALUE +ossl_rsa_get_n(VALUE self) +{ + ossl_rsa *rsap = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE num; + + GetRSA(self, rsap); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eRSAError, ""); + } + if (!BN_print(out, rsap->rsa->n)) { + BIO_free(out); + OSSL_Raise(eRSAError, ""); + } + + BIO_get_mem_ptr(out, &buf); + num = rb_cstr2inum(buf->data, 16); + BIO_free(out); + + return num; +} + */ + +static VALUE +ossl_rsa_to_der(VALUE self) +{ + RSA *rsa = NULL; + EVP_PKEY *pkey = NULL; + X509_PUBKEY *key = NULL; + VALUE str; + + rsa = ossl_rsa_get_RSA(self); + + if (!(pkey = EVP_PKEY_new())) { + RSA_free(rsa); + OSSL_Raise(eRSAError, ""); + } + if (!EVP_PKEY_assign_RSA(pkey, rsa)) { /* NO DUP - don't free! */ + RSA_free(rsa); + EVP_PKEY_free(pkey); + OSSL_Raise(eRSAError, ""); + } + if (!(key = X509_PUBKEY_new())) { + EVP_PKEY_free(pkey); + OSSL_Raise(eRSAError, ""); + } + if (!X509_PUBKEY_set(&key, pkey)) { /* safe to FREE pkey??? */ + EVP_PKEY_free(pkey); + X509_PUBKEY_free(key); + OSSL_Raise(eRSAError, ""); + } + + str = rb_str_new(key->public_key->data, key->public_key->length); + /* EVP_PKEY_free(pkey) = this does X509_PUBKEY_free?? */ + X509_PUBKEY_free(key); + + return str; +} + +/* + * Prints all parameters of key to buffer + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_rsa_to_text(VALUE self) +{ + ossl_rsa *rsap = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetRSA(self, rsap); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eRSAError, ""); + } + if (!RSA_print(out, rsap->rsa, 0)) { //offset = 0 + BIO_free(out); + OSSL_Raise(eRSAError, ""); + } + + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes new instance RSA PUBLIC_KEY from PRIVATE_KEY + */ +static VALUE +ossl_rsa_to_public_key(VALUE self) +{ + ossl_rsa *rsap1 = NULL, *rsap2 = NULL; + VALUE obj; + + GetRSA(self, rsap1); + + MakeRSA(obj, rsap2); + + if (!(rsap2->rsa = RSAPublicKey_dup(rsap1->rsa))) { + OSSL_Raise(eRSAError, ""); + } + + return obj; +} + +/* + * Better to implement is in Ruby space? + * +static VALUE +ossl_rsa_sign(VALUE self, VALUE digest, VALUE text) +{ + ossl_rsa *rsap = NULL; + EVP_MD_CTX ctx; + const EVP_MD *md = NULL; + char *sign = NULL; + int sign_len = 0; + VALUE str; + + GetRSA(self, rsap); + OSSL_Check_type(digest, cDigest); + text = rb_String(text); + + if (!(sign = OPENSSL_malloc(RSA_size(rsap->rsa)+16))) { + OSSL_Raise(eRSAError, ""); + } + + md = ossl_digest_get_EVP_MD(digest); + EVP_SignInit(&ctx, md); + EVP_SignUpdate(&ctx, RSTRING(text)->ptr, RSTRING(text)->len); + if (!EVP_SignFinal(&ctx, sign, &sign_len, pkeyp->key)) { + OPENSSL_free(sign); + OSSL_Raise(eRSAError, ""); + } + + str = rb_str_new(sign, sign_len); + OPENSSL_free(sign); + + return str; +} + +static VALUE +ossl_rsa_verify(VALUE self, VALUE digest, VALUE text) +{ +} + */ + +/* + * INIT + */ +void +Init_ossl_rsa(VALUE mPKey, VALUE cPKey, VALUE ePKeyError) +{ + eRSAError = rb_define_class_under(mPKey, "RSAError", ePKeyError); + + cRSA = rb_define_class_under(mPKey, "RSA", cPKey); + + rb_define_singleton_method(cRSA, "new_from_pem", ossl_rsa_s_new_from_pem, -1); + rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, 1); + rb_define_alias(CLASS_OF(cRSA), "new_from_fixnum", "generate"); + rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0); + rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0); + rb_define_method(cRSA, "to_text", ossl_rsa_to_text, 0); + rb_define_method(cRSA, "export", ossl_rsa_export, -1); + rb_define_alias(cRSA, "to_pem", "export"); + rb_define_method(cRSA, "public_key", ossl_rsa_to_public_key, 0); + rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, 1); + rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, 1); + rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, 1); + rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, 1); + /*rb_define_method(cRSA, "n", ossl_rsa_get_n, 0);*/ + rb_define_method(cRSA, "to_der", ossl_rsa_to_der, 0); +/* + * Implemented in Ruby space... + * + rb_define_method(cRSA, "sign", ossl_rsa_sign, 2); + rb_define_method(cRSA, "verify", ossl_rsa_verify, 3); + */ +} + +#else /* defined NO_RSA */ +# warning >>> OpenSSL is compiled without RSA support <<< + +void +Init_ossl_rsa(VALUE mPKey, VALUE cPKey, VALUE ePKeyError) +{ + rb_warning("OpenSSL is compiled without RSA support"); +} + +#endif /* NO_RSA */ + diff --git a/ossl_rand.c b/ossl_rand.c new file mode 100644 index 0000000..f5fbf9f --- /dev/null +++ b/ossl_rand.c @@ -0,0 +1,126 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +/* + * Classes + */ +VALUE eRandomError; + +/* + * Struct + */ + +/* + * Public + */ + +/* + * Private + */ +static VALUE +ossl_rand_seed(VALUE self, VALUE str) +{ + str = rb_obj_as_string(str); + RAND_seed(RSTRING(str)->ptr, RSTRING(str)->len); + + return str; +} + +static VALUE +ossl_rand_load_file(VALUE self, VALUE filename) +{ + filename = rb_str_to_str(filename); + Check_SafeStr(filename); + + if(!RAND_load_file(RSTRING(filename)->ptr, -1)) { + OSSL_Raise(eRandomError, ""); + } + + return Qtrue; +} + +static VALUE +ossl_rand_write_file(VALUE self, VALUE filename) +{ + filename = rb_str_to_str(filename); + Check_SafeStr(filename); + + if (RAND_write_file(RSTRING(filename)->ptr) == -1) { + OSSL_Raise(eRandomError, ""); + } + + return Qtrue; +} + +static VALUE +ossl_rand_bytes(VALUE self, VALUE len) +{ + unsigned char *buffer = NULL; + VALUE str; + + Check_Type(len, T_FIXNUM); + + if (!(buffer = OPENSSL_malloc(FIX2INT(len)+1))) { + OSSL_Raise(eRandomError, ""); + } + + if (!RAND_bytes(buffer, FIX2INT(len))) { + OPENSSL_free(buffer); + OSSL_Raise(eRandomError, ""); + } + + str = rb_str_new(buffer, FIX2INT(len)); + OPENSSL_free(buffer); + + return str; +} + +static VALUE +ossl_rand_egd(VALUE self, VALUE filename) +{ + Check_SafeStr(filename); + if(!RAND_egd(RSTRING(filename)->ptr)) { + OSSL_Raise(eRandomError, ""); + } + + return Qtrue; +} + +static VALUE +ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) +{ + Check_SafeStr(filename); + Check_Type(len, T_FIXNUM); + + if (!RAND_egd_bytes(RSTRING(filename)->ptr, FIX2INT(len))) { + OSSL_Raise(eRandomError, ""); + } + + return Qtrue; +} + +/* + * INIT + */ +void +Init_ossl_rand(VALUE module) +{ + rb_define_method(module, "seed", ossl_rand_seed, 1); + rb_define_method(module, "load_random_file", ossl_rand_load_file, 1); + rb_define_method(module, "write_random_file", ossl_rand_write_file, 1); + rb_define_method(module, "random_bytes", ossl_rand_bytes, 1); + rb_define_method(module, "egd", ossl_rand_egd, 1); + rb_define_method(module, "egd_bytes", ossl_rand_egd_bytes, 2); + + eRandomError = rb_define_class_under(module, "RandomError", eOSSLError); +} + diff --git a/ossl_ssl.c b/ossl_ssl.c new file mode 100644 index 0000000..8a4fd3d --- /dev/null +++ b/ossl_ssl.c @@ -0,0 +1,650 @@ +/*- + * Copyright (c) 2000-2002 GOTOU YUUZOU <gotoyuzo@notwork.org> + * 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. + * + * $IPR: ssl.c,v 1.22 2001/09/21 17:35:51 gotoyuzo Exp $ + */ +/* + * $Id$ + * for 'OpenSSL for Ruby' project modified by Michal Rokos <m.rokos@sh.cvut.cz> + */ +#include "ossl.h" +#include <rubysig.h> +#include <rubyio.h> +#include <unistd.h> /* for read(), and write() */ + +#define numberof(ary) (sizeof(ary)/sizeof((ary)[0])) + +#define ssl_get_io(o) rb_ivar_get((o),rb_intern("@io")) +#define ssl_get_cert(o) rb_ivar_get((o),rb_intern("@cert")) +#define ssl_get_cert_file(o) rb_ivar_get((o),rb_intern("@cert_file")) +#define ssl_get_key(o) rb_ivar_get((o),rb_intern("@key")) +#define ssl_get_key_file(o) rb_ivar_get((o),rb_intern("@key_file")) +#define ssl_get_ca(o) rb_ivar_get((o),rb_intern("@ca_cert")) +#define ssl_get_ca_file(o) rb_ivar_get((o),rb_intern("@ca_file")) +#define ssl_get_ca_path(o) rb_ivar_get((o),rb_intern("@ca_path")) +#define ssl_get_timeout(o) rb_ivar_get((o),rb_intern("@timeout")) +#define ssl_get_verify_mode(o) rb_ivar_get((o),rb_intern("@verify_mode")) +#define ssl_get_verify_dep(o) rb_ivar_get((o),rb_intern("@verify_depth")) +#define ssl_get_verify_cb(o) rb_ivar_get((o),rb_intern("@verify_callback")) + +#define ssl_set_io(o,v) rb_ivar_set((o),rb_intern("@io"),(v)) +#define ssl_set_cert(o,v) rb_ivar_set((o),rb_intern("@cert"),(v)) +#define ssl_set_cert_file(o,v) rb_ivar_set((o),rb_intern("@cert_file"),(v)) +#define ssl_set_key(o,v) rb_ivar_set((o),rb_intern("@key"),(v)) +#define ssl_set_key_file(o,v) rb_ivar_set((o),rb_intern("@key_file"),(v)) +#define ssl_set_ca(o,v) rb_ivar_set((o),rb_intern("@ca_cert"),(v)) +#define ssl_set_ca_file(o,v) rb_ivar_set((o),rb_intern("@ca_file"),(v)) +#define ssl_set_ca_path(o,v) rb_ivar_set((o),rb_intern("@ca_path"),(v)) +#define ssl_set_timeout(o,v) rb_ivar_set((o),rb_intern("@timeout"),(v)) +#define ssl_set_verify_mode(o,v) rb_ivar_set((o),rb_intern("@verify_mode"),(v)) +#define ssl_set_verify_dep(o,v) rb_ivar_set((o),rb_intern("@verify_depth"),(v)) +#define ssl_set_verify_cb(o,v) rb_ivar_set((o),rb_intern("@verify_callback"),(v)) + +static VALUE ssl_set_cert2(VALUE, VALUE); +static VALUE ssl_set_cert_file2(VALUE, VALUE); +static VALUE ssl_set_key2(VALUE, VALUE); +static VALUE ssl_set_key_file2(VALUE, VALUE); + +/* + * Classes + */ +VALUE cSSLSocket; +VALUE eSSLError; + +/* + * List of instance vars + */ +char *ssl_attrs[] = { + "ca_cert", "ca_file", "ca_path", + "timeout", "verify_mode", "verify_depth", "verify_callback" +}; + +char *ssl_attr_readers[] = { + "io", "cert", "cert_file", "key", "key_file" +}; + +/* + * Struct + */ +typedef struct ssl_st_t{ + SSL *ssl; + SSL_CTX *ctx; +} ssl_st; + +static void +ssl_shutdown(ssl_st *p) +{ + if(p->ssl){ + SSL_shutdown(p->ssl); + SSL_clear(p->ssl); + } +} + +static void +ssl_free(ssl_st *p) +{ + ssl_shutdown(p); + SSL_free(p->ssl); + p->ssl = NULL; + SSL_CTX_free(p->ctx); + p->ctx = NULL; + free(p); +} + +static VALUE ssl_verify_callback_proc; + +static VALUE +ssl_call_callback_proc(VALUE args) +{ + VALUE proc, ok, x509stc; + + proc = rb_ary_entry(args, 0); + ok = rb_ary_entry(args, 1); + x509stc = rb_ary_entry(args, 2); + + return rb_funcall(proc, rb_intern("call"), 2, ok, x509stc); +} + +/* + * for rb_rescue in ssl_verify_callback + * see below + */ +static VALUE +ssl_false(VALUE dummy) +{ + return Qfalse; +} + +static int +ssl_verify_callback(int ok, X509_STORE_CTX *ctx) +{ + VALUE x509stc, args, ret = Qnil; + + if (!NIL_P(ssl_verify_callback_proc)) { + x509stc = ossl_x509store_new(ctx); + rb_funcall(x509stc, rb_intern("protect"), 0, NULL); + args = rb_ary_new2(3); + rb_ary_store(args, 0, ssl_verify_callback_proc); + rb_ary_store(args, 1, ok ? Qtrue : Qfalse); + rb_ary_store(args, 2, x509stc); + ret = rb_rescue(ssl_call_callback_proc, args, ssl_false, Qnil); + + if (ret == Qtrue) { + ok = 1; + X509_STORE_CTX_set_error(ctx, X509_V_OK); + } else { + ok = 0; + if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); + } + } + + return ok; +} + +static void +ssl_ctx_setup(VALUE self) +{ + ssl_st *p = NULL; + X509 *cert = NULL, *ca = NULL; + EVP_PKEY *key = NULL; + char *ca_path = NULL, *ca_file = NULL; + int verify_mode; + VALUE val; + + Data_Get_Struct(self, ssl_st, p); + + /* private key may be bundled in certificate file. */ + val = ssl_get_cert(self); + cert = NIL_P(val) ? NULL : ossl_x509_get_X509(val); + val = ssl_get_key(self); + key = NIL_P(val) ? NULL : ossl_pkey_get_EVP_PKEY(val); + + if (cert && key) { + if (!SSL_CTX_use_certificate(p->ctx, cert)) { /* Adds a ref => Safe to FREE */ + X509_free(cert); + EVP_PKEY_free(key); + OSSL_Raise(eSSLError, "SSL_CTX_use_certificate:"); + } + if (!SSL_CTX_use_PrivateKey(p->ctx, key)) { /* Adds a ref => Safe to FREE */ + X509_free(cert); + EVP_PKEY_free(key); + OSSL_Raise(eSSLError, "SSL_CTX_use_PrivateKey:"); + } + if (!SSL_CTX_check_private_key(p->ctx)) { + X509_free(cert); + EVP_PKEY_free(key); + OSSL_Raise(eSSLError, "SSL_CTX_check_private_key:"); + } + } + + /* + * Free cert, key (Used => Safe to FREE || Not used => Not needed) + */ + if (cert) X509_free(cert); + if (key) EVP_PKEY_free(key); + + val = ssl_get_ca(self); + ca = NIL_P(val) ? NULL : ossl_x509_get_X509(val); + val = ssl_get_ca_file(self); + ca_file = NIL_P(val) ? NULL : RSTRING(val)->ptr; + val = ssl_get_ca_path(self); + ca_path = NIL_P(val) ? NULL : RSTRING(val)->ptr; + + if (ca) { + if (!SSL_CTX_add_client_CA(p->ctx, ca)) { /* Copies X509_NAME => FREE it. */ + X509_free(ca); + OSSL_Raise(eSSLError, ""); + } + X509_free(ca); + } + if ((!SSL_CTX_load_verify_locations(p->ctx, ca_file, ca_path) || + !SSL_CTX_set_default_verify_paths(p->ctx))) { + OSSL_Warning("can't set verify locations"); + } + + val = ssl_get_verify_mode(self); + verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val); + SSL_CTX_set_verify(p->ctx, verify_mode, ssl_verify_callback); + + val = ssl_get_timeout(self); + if(!NIL_P(val)) SSL_CTX_set_timeout(p->ctx, NUM2LONG(val)); + + val = ssl_get_verify_dep(self); + if(!NIL_P(val)) SSL_CTX_set_verify_depth(p->ctx, NUM2LONG(val)); +} + +static void +ssl_setup(VALUE self) +{ + ssl_st *p; + VALUE io; + OpenFile *fptr; + + Data_Get_Struct(self, ssl_st, p); + if(!p->ssl){ + io = ssl_get_io(self); + GetOpenFile(io, fptr); + rb_io_check_readable(fptr); + rb_io_check_writable(fptr); + if((p->ssl = SSL_new(p->ctx)) == NULL) + OSSL_Raise(eSSLError, "SSL_new:"); + + SSL_set_fd(p->ssl, fileno(fptr->f)); + } +} + +static VALUE +ssl_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE obj; + ssl_st *p; + + obj = Data_Make_Struct(klass, ssl_st, 0, ssl_free, p); + memset(p, 0, sizeof(ssl_st)); + if((p->ctx = SSL_CTX_new(SSLv23_method())) == NULL) + OSSL_Raise(eSSLError, "SSL_CTX_new:"); + + SSL_CTX_set_options(p->ctx, SSL_OP_ALL); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ssl_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE io, key, cert; + + switch (rb_scan_args(argc, argv, "12", &io, &cert, &key)) { + case 3: + if (!NIL_P(key)){ + if(TYPE(key) == T_STRING) ssl_set_key_file2(self, key); + else{ + OSSL_Check_Type(key, cPKey); + ssl_set_key2(self, key); + } + } + /* FALLTHROUGH */ + case 2: + if (!NIL_P(cert)){ + if(TYPE(cert) == T_STRING) ssl_set_cert_file2(self, cert); + else{ + OSSL_Check_Type(cert, cX509Certificate); + ssl_set_cert2(self, cert); + } + } + /* FALLTHROUGH */ + case 1: + Check_Type(io, T_FILE); + ssl_set_io(self, io); + } + + return self; +} + +static VALUE +ssl_connect(VALUE self) +{ + ssl_st *p; + + Data_Get_Struct(self, ssl_st, p); + ssl_ctx_setup(self); + ssl_setup(self); + + ssl_verify_callback_proc = ssl_get_verify_cb(self); + if(SSL_connect(p->ssl) <= 0){ + OSSL_Raise(eSSLError, "SSL_connect:"); + } + + return self; +} + +static VALUE +ssl_accept(VALUE self) +{ + ssl_st *p; + + Data_Get_Struct(self, ssl_st, p); + ssl_ctx_setup(self); + ssl_setup(self); + + ssl_verify_callback_proc = ssl_get_verify_cb(self); + if(SSL_accept(p->ssl) <= 0){ + OSSL_Raise(eSSLError, "SSL_accept:"); + } + + return self; +} + +static VALUE +ssl_read(VALUE self, VALUE len) +{ + ssl_st *p; + size_t ilen, nread = 0; + VALUE str; + OpenFile *fptr; + + Data_Get_Struct(self, ssl_st, p); + ilen = NUM2INT(len); + str = rb_str_new(0, ilen); + + if (p->ssl) { + nread = SSL_read(p->ssl, RSTRING(str)->ptr, RSTRING(str)->len); + if(nread < 0) + OSSL_Raise(eSSLError, "SSL_read:"); + } else { + rb_warning("SSL session is not started yet."); + + GetOpenFile(ssl_get_io(self), fptr); + rb_io_check_readable(fptr); + + TRAP_BEG; + nread = read(fileno(fptr->f), RSTRING(str)->ptr, RSTRING(str)->len); + TRAP_END; + + if(nread < 0) + rb_raise(eSSLError, "read:%s", strerror(errno)); + } + + if(nread == 0) + rb_raise(rb_eEOFError, "End of file reached"); + + RSTRING(str)->len = nread; + RSTRING(str)->ptr[nread] = 0; + OBJ_TAINT(str); + + return str; +} + +static VALUE +ssl_write(VALUE self, VALUE str) +{ + ssl_st *p; + size_t nwrite = 0; + OpenFile *fptr; + FILE *fp; + + Data_Get_Struct(self, ssl_st, p); + str = rb_String(str); + + if (p->ssl) { + nwrite = SSL_write(p->ssl, RSTRING(str)->ptr, RSTRING(str)->len); + if (nwrite < 0) + OSSL_Raise(eSSLError, "SSL_write:"); + } else { + rb_warning("SSL session is not started yet."); + + GetOpenFile(ssl_get_io(self), fptr); + rb_io_check_writable(fptr); + fp = GetWriteFile(fptr); + nwrite = write(fileno(fp), RSTRING(str)->ptr, RSTRING(str)->len); + if(nwrite < 0) + rb_raise(eSSLError, "write:%s", strerror(errno)); + } + + return INT2NUM(nwrite); +} + +static VALUE +ssl_close(VALUE self) +{ + ssl_st *p; + + Data_Get_Struct(self, ssl_st, p); + ssl_shutdown(p); + return Qnil; +} + +static VALUE +ssl_get_certificate(VALUE self) +{ + ssl_st *p; + X509 *cert = NULL; + + Data_Get_Struct(self, ssl_st, p); + if(!p->ssl){ + rb_warning("SSL session is not started yet."); + return Qnil; + } + + /* + * Is this OpenSSL bug? Should add a ref? + * TODO: Ask for. + */ + if ((cert = SSL_get_certificate(p->ssl)) == NULL) return Qnil; /* NO DUPs => DON'T FREE. */ + + return ossl_x509_new(cert); +} + +static VALUE +ssl_get_peer_certificate(VALUE self) +{ + ssl_st *p; + X509 *cert = NULL; + VALUE obj; + + Data_Get_Struct(self, ssl_st, p); + + if (!p->ssl){ + rb_warning("SSL session is not started yet."); + return Qnil; + } + + if ((cert = SSL_get_peer_certificate(p->ssl)) == NULL) /* Adds a ref => Safe to FREE. */ + return Qnil; + + obj = ossl_x509_new(cert); + X509_free(cert); + + return obj; +} + +static VALUE +ssl_cipher_to_ary(SSL_CIPHER *cipher) +{ + VALUE ary; + int bits, alg_bits; + + ary = rb_ary_new2(4); + rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_name(cipher))); + rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_version(cipher))); + bits = SSL_CIPHER_get_bits(cipher, &alg_bits); + rb_ary_push(ary, INT2FIX(bits)); + rb_ary_push(ary, INT2FIX(alg_bits)); + + return ary; +} + +static VALUE +ssl_get_cipher(VALUE self) +{ + ssl_st *p; + SSL_CIPHER *cipher; + + Data_Get_Struct(self, ssl_st, p); + if(!p->ssl){ + rb_warning("SSL session is not started yet."); + return Qnil; + } + cipher = SSL_get_current_cipher(p->ssl); + + return ssl_cipher_to_ary(cipher); +} + +static VALUE +ssl_get_ciphers(VALUE self) +{ + ssl_st *p; + STACK_OF(SSL_CIPHER) *ciphers; + SSL_CIPHER *cipher; + VALUE ary; + int i; + + Data_Get_Struct(self, ssl_st, p); + if(!p->ctx){ + rb_warning("SSL_CTX is not initialized."); + return Qnil; + } + ciphers = p->ctx->cipher_list; + ary = rb_ary_new(); + if(ciphers){ + for(i = 0; i < sk_num((STACK*)ciphers); i++){ + cipher = (SSL_CIPHER*)sk_value((STACK*)ciphers, i); + rb_ary_push(ary, ssl_cipher_to_ary(cipher)); + } + } + return ary; +} + +static VALUE +ssl_set_ciphers(VALUE self, VALUE v) +{ + ssl_st *p; + VALUE str, elem; + int i; + + Data_Get_Struct(self, ssl_st, p); + if(!p->ctx){ + rb_raise(eSSLError, "SSL_CTX is not initialized."); + return Qnil; + } + + if (TYPE(v) == T_ARRAY) { + str = rb_str_new2(""); + for (i = 0; i < RARRAY(v)->len; i++) { + elem = rb_ary_entry(v, i); + if (TYPE(elem) == T_ARRAY) elem = rb_ary_entry(elem, 0); + elem = rb_String(elem); + rb_str_append(str, elem); + if (i < RARRAY(v)->len-1) rb_str_cat2(str, ":"); + } + } else str = rb_String(v); + + if (!SSL_CTX_set_cipher_list(p->ctx, RSTRING(str)->ptr)) { + OSSL_Raise(eSSLError, "SSL_CTX_set_ciphers:"); + } + return Qnil; +} + +static VALUE +ssl_get_state(VALUE self) +{ + ssl_st *p; + VALUE ret; + + Data_Get_Struct(self, ssl_st, p); + if(!p->ssl){ + rb_warning("SSL session is not started yet."); + return Qnil; + } + ret = rb_str_new2(SSL_state_string(p->ssl)); + if(ruby_verbose){ + rb_str_cat2(ret, ": "); + rb_str_cat2(ret, SSL_state_string_long(p->ssl)); + } + return ret; +} + +static VALUE +ssl_set_cert2(VALUE self, VALUE v) +{ + if(!NIL_P(v)) OSSL_Check_Type(v, cX509Certificate); + ssl_set_cert(self, v); + ssl_set_cert_file(self, Qnil); + return v; +} + +static VALUE +ssl_set_cert_file2(VALUE self, VALUE v) +{ + VALUE cert; + cert = NIL_P(v) ? Qnil :ossl_x509_new_from_file(v); + ssl_set_cert(self, cert); + ssl_set_cert_file(self, v); + return v; +} + +static VALUE +ssl_set_key2(VALUE self, VALUE v) +{ + if(!NIL_P(v)) OSSL_Check_Type(v, cPKey); + ssl_set_key(self, v); + ssl_set_key_file(self, Qnil); + return v; +} + +static VALUE +ssl_set_key_file2(VALUE self, VALUE v) +{ + VALUE key; + key = NIL_P(v) ? Qnil : ossl_pkey_new_from_file(v); + ssl_set_key(self, key); + ssl_set_key_file(self, v); + return v; +} + +void +Init_ossl_ssl(VALUE module) +{ + int i; + + /* class SSLError */ + eSSLError = rb_define_class_under(module, "SSLError", eOSSLError); + + /* class SSLSocket */ + cSSLSocket = rb_define_class_under(module, "SSLSocket", rb_cObject); + rb_define_singleton_method(cSSLSocket, "new", ssl_s_new, -1); + rb_define_method(cSSLSocket, "initialize", ssl_initialize, -1); + rb_define_method(cSSLSocket, "__connect", ssl_connect, 0); + rb_define_method(cSSLSocket, "__accept", ssl_accept, 0); + rb_define_method(cSSLSocket, "sysread", ssl_read, 1); + rb_define_method(cSSLSocket, "syswrite", ssl_write, 1); + rb_define_method(cSSLSocket, "sysclose", ssl_close, 0); + rb_define_method(cSSLSocket, "cert", ssl_get_certificate, 0); + rb_define_method(cSSLSocket, "peer_cert", ssl_get_peer_certificate, 0); + rb_define_method(cSSLSocket, "cipher", ssl_get_cipher, 0); + rb_define_method(cSSLSocket, "ciphers", ssl_get_ciphers, 0); + rb_define_method(cSSLSocket, "ciphers=", ssl_set_ciphers, 1); + rb_define_method(cSSLSocket, "state", ssl_get_state, 0); + rb_define_method(cSSLSocket, "cert=", ssl_set_cert2, 1); + rb_define_method(cSSLSocket, "cert_file=", ssl_set_cert_file2, 1); + rb_define_method(cSSLSocket, "key=", ssl_set_key2, 1); + rb_define_method(cSSLSocket, "key_file=", ssl_set_key_file2, 1); + for(i = 0; i < numberof(ssl_attrs); i++) + rb_attr(cSSLSocket, rb_intern(ssl_attrs[i]), 1, 1, Qfalse); + for(i = 0; i < numberof(ssl_attr_readers); i++) + rb_attr(cSSLSocket, rb_intern(ssl_attr_readers[i]), 1, 0, Qfalse); + rb_define_alias(cSSLSocket, "to_io", "io"); + +#define ssl_def_const(x) rb_define_const(module, #x, INT2FIX(SSL_##x)) + + ssl_def_const(VERIFY_NONE); + ssl_def_const(VERIFY_PEER); + ssl_def_const(VERIFY_FAIL_IF_NO_PEER_CERT); + ssl_def_const(VERIFY_CLIENT_ONCE); +} + diff --git a/ossl_version.h b/ossl_version.h new file mode 100644 index 0000000..a10476c --- /dev/null +++ b/ossl_version.h @@ -0,0 +1,18 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_VERSION_H_) +#define _OSSL_VERSION_H_ + +/*#define OSSL_VERSION "0.2.0"*/ +#define OSSL_VERSION "OSSL2 - CVS SNAPSHOT ($Date$)" + +#endif + diff --git a/ossl_x509.c b/ossl_x509.c new file mode 100644 index 0000000..63a121c --- /dev/null +++ b/ossl_x509.c @@ -0,0 +1,29 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +VALUE mX509; + +void +Init_ossl_x509() +{ + mX509 = rb_define_module_under(mOSSL, "X509"); + + Init_ossl_x509attr(mX509); + Init_ossl_x509cert(mX509); + Init_ossl_x509crl(mX509); + Init_ossl_x509ext(mX509); + Init_ossl_x509name(mX509); + Init_ossl_x509req(mX509); + Init_ossl_x509revoked(mX509); + Init_ossl_x509store(mX509); +} + diff --git a/ossl_x509.h b/ossl_x509.h new file mode 100644 index 0000000..6f6f272 --- /dev/null +++ b/ossl_x509.h @@ -0,0 +1,92 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_X509_H_) +#define _OSSL_X509_H_ + +extern VALUE mX509; +extern VALUE cX509Certificate; +extern VALUE eX509CertificateError; +extern VALUE cX509Attribute; +extern VALUE eX509AttributeError; +extern VALUE cX509CRL; +extern VALUE eX509CRLError; +extern VALUE cX509Extension; +extern VALUE cX509ExtensionFactory; +extern VALUE eX509ExtensionError; +extern VALUE cX509Name; +extern VALUE eX509NameError; +extern VALUE cX509Request; +extern VALUE eX509RequestError; +extern VALUE cX509Revoked; +extern VALUE eX509RevokedError; +extern VALUE cX509Store; +extern VALUE eX509StoreError; + +void Init_ossl_x509(void); + +/* + * X509 + */ +VALUE ossl_x509_new(X509 *); +VALUE ossl_x509_new_from_file(VALUE); +X509 *ossl_x509_get_X509(VALUE); +void Init_ossl_x509cert(VALUE); + +/* + * X509CRL + */ +X509_CRL *ossl_x509crl_get_X509_CRL(VALUE); +void Init_ossl_x509crl(VALUE); + +/* + * X509Name + */ +VALUE ossl_x509name_new(X509_NAME *); +X509_NAME *ossl_x509name_get_X509_NAME(VALUE); +void Init_ossl_x509name(VALUE); + +/* + * X509Request + */ +VALUE ossl_x509req_new(X509_REQ *); +X509_REQ *ossl_x509req_get_X509_REQ(VALUE); +void Init_ossl_x509req(VALUE); + +/* + * X509Revoked + */ +VALUE ossl_x509revoked_new(X509_REVOKED *); +X509_REVOKED *ossl_x509revoked_get_X509_REVOKED(VALUE); +void Init_ossl_x509revoked(VALUE); + +/* + * X509Store + */ +VALUE ossl_x509store_new(X509_STORE_CTX *); +X509_STORE *ossl_x509store_get_X509_STORE(VALUE); +void Init_ossl_x509store(VALUE); + +/* + * X509Extension + */ +VALUE ossl_x509ext_new(X509_EXTENSION *); +X509_EXTENSION *ossl_x509ext_get_X509_EXTENSION(VALUE); +void Init_ossl_x509ext(VALUE); + +/* + * X509Attribute + */ +VALUE ossl_x509attr_new(X509_ATTRIBUTE *); +X509_ATTRIBUTE *ossl_x509attr_get_X509_ATTRIBUTE(VALUE); +void Init_ossl_x509attr(VALUE); + +#endif /* _OSSL_X509_H_ */ + diff --git a/ossl_x509attr.c b/ossl_x509attr.c new file mode 100644 index 0000000..404dd20 --- /dev/null +++ b/ossl_x509attr.c @@ -0,0 +1,147 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Attr(obj, attr) obj = Data_Wrap_Struct(cX509Attribute, 0, X509_ATTRIBUTE_free, attr) +#define GetX509Attr(obj, attr) Data_Get_Struct(obj, X509_ATTRIBUTE, attr) + +/* + * Classes + */ +VALUE cX509Attribute; +VALUE eX509AttributeError; + +/* + * Public + */ +VALUE +ossl_x509attr_new(X509_ATTRIBUTE *attr) +{ + X509_ATTRIBUTE *new = NULL; + VALUE obj; + + if (!attr) + new = X509_ATTRIBUTE_new(); + else new = X509_ATTRIBUTE_dup(attr); + + if (!new) + OSSL_Raise(eX509AttributeError, ""); + + WrapX509Attr(obj, new); + + return obj; +} + +X509_ATTRIBUTE * +ossl_x509attr_get_X509_ATTRIBUTE(VALUE obj) +{ + X509_ATTRIBUTE *attr = NULL, *new; + + OSSL_Check_Type(obj, cX509Attribute); + + GetX509Attr(obj, attr); + + if (!(new = X509_ATTRIBUTE_dup(attr))) { + OSSL_Raise(eX509AttributeError, ""); + } + + return new; +} + +/* + * Private + */ +static VALUE +ossl_x509attr_s_new_from_array(VALUE klass, VALUE ary) +{ + X509_ATTRIBUTE *attr = NULL; + int nid = NID_undef; + VALUE item, obj; + + Check_Type(ary, T_ARRAY); + + if (RARRAY(ary)->len != 2) { + rb_raise(eX509AttributeError, "unsupported ary structure"); + } + + /* key [0] */ + item = RARRAY(ary)->ptr[0]; + item = rb_String(item); + if (!(nid = OBJ_ln2nid(RSTRING(item)->ptr))) + if (!(nid = OBJ_sn2nid(RSTRING(item)->ptr))) + OSSL_Raise(eX509AttributeError, ""); + + /* data [1] */ + item = RARRAY(ary)->ptr[1]; + item = rb_String(item); + + if (!(attr = X509_ATTRIBUTE_create(nid, MBSTRING_ASC, RSTRING(item)->ptr))) + OSSL_Raise(eX509AttributeError, ""); + + WrapX509Attr(obj, attr); + + return obj; +} + +/* + * is there any print for attribute? + * (NO, but check t_req.c in crypto/asn1) + * +static VALUE +ossl_x509attr_to_a(VALUE self) +{ + ossl_x509attr *attrp = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + int nid = NID_undef; + VALUE ary, value; + + GetX509Attr(obj, attrp); + + ary = rb_ary_new2(2); + + nid = OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attrp->attribute)); + rb_ary_push(ary, rb_str_new2(OBJ_nid2sn(nid))); + + if (!(out = BIO_new(BIO_s_mem()))) + OSSL_Raise(eX509ExtensionError, ""); + + if (!X509V3_???_print(out, extp->extension, 0, 0)) { + BIO_free(out); + OSSL_Raise(eX509ExtensionError, ""); + } + BIO_get_mem_ptr(out, &buf); + value = rb_str_new(buf->data, buf->length); + BIO_free(out); + + rb_funcall(value, rb_intern("tr!"), 2, rb_str_new2("\n"), rb_str_new2(",")); + rb_ary_push(ary, value); + + return ary; +} + */ + +/* + * X509_ATTRIBUTE init + */ +void +Init_ossl_x509attr(VALUE module) +{ + eX509AttributeError = rb_define_class_under(module, "AttributeError", eOSSLError); + + cX509Attribute = rb_define_class_under(module, "Attribute", rb_cObject); + rb_define_singleton_method(cX509Attribute, "new_from_array", ossl_x509attr_s_new_from_array, 1); +/* + * TODO: + rb_define_method(cX509Attribute, "to_a", ossl_x509attr_to_a, 0); + */ +} + diff --git a/ossl_x509cert.c b/ossl_x509cert.c new file mode 100644 index 0000000..378217e --- /dev/null +++ b/ossl_x509cert.c @@ -0,0 +1,647 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509(obj, x509) obj = Data_Wrap_Struct(cX509Certificate, 0, X509_free, x509) +#define GetX509(obj, x509) Data_Get_Struct(obj, X509, x509) + +/* + * Classes + */ +VALUE cX509Certificate; +VALUE eX509CertificateError; + +/* + * Public + */ +VALUE +ossl_x509_new(X509 *x509) +{ + X509 *new = NULL; + VALUE obj; + + if (!x509) + new = X509_new(); + else new = X509_dup(x509); + + if (!new) + OSSL_Raise(eX509CertificateError, ""); + + WrapX509(obj, new); + + return obj; +} + +VALUE +ossl_x509_new_from_file(VALUE filename) +{ + X509 *x509 = NULL; + char *path; + FILE *fp; + VALUE obj; + + filename = rb_str_to_str(filename); + Check_SafeStr(filename); + + path = RSTRING(filename)->ptr; + + if (!(fp = fopen(path, "r"))) + rb_raise(eX509CertificateError, "%s", strerror(errno)); + + x509 = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + + if (!x509) + OSSL_Raise(eX509CertificateError, ""); + + WrapX509(obj, x509); + + return obj; +} + +X509 * +ossl_x509_get_X509(VALUE obj) +{ + X509 *x509 = NULL, *new; + + OSSL_Check_Type(obj, cX509Certificate); + + GetX509(obj, x509); + + if (!(new = X509_dup(x509))) { + OSSL_Raise(eX509CertificateError, ""); + } + return new; +} + +/* + * Private + */ +static VALUE +ossl_x509_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE obj; + + obj = ossl_x509_new(NULL); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_x509_initialize(int argc, VALUE *argv, VALUE self) +{ + BIO *in = NULL; + VALUE buffer; + + if (argc == 0) + return self; + + buffer = rb_String(argv[0]); + + if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len))) { + OSSL_Raise(eX509CertificateError, ""); + } + if (!PEM_read_bio_X509(in, (X509 **)&DATA_PTR(self), NULL, NULL)) { + BIO_free(in); + OSSL_Raise(eX509CertificateError, ""); + } + BIO_free(in); + + return self; +} + +static VALUE +ossl_x509_to_der(VALUE self) +{ + X509 *x509 = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetX509(self, x509); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509CertificateError, ""); + } + if (!i2d_X509_bio(out, x509)) { + BIO_free(out); + OSSL_Raise(eX509CertificateError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_x509_to_pem(VALUE self) +{ + X509 *x509 = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetX509(self, x509); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509CertificateError, ""); + } + if (!PEM_write_bio_X509(out, x509)) { + BIO_free(out); + OSSL_Raise(eX509CertificateError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_x509_to_text(VALUE self) +{ + X509 *x509 = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetX509(self, x509); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509CertificateError, ""); + } + if (!X509_print(out, x509)) { + BIO_free(out); + OSSL_Raise(eX509CertificateError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes from X509 X509_REQuest + * +static VALUE +ossl_x509_to_req(VALUE self) +{ + X509 *x509 = NULL; + X509_REQ *req = NULL; + + GetX509(self, x509); + + if (!(req = X509_to_X509_REQ(x509, NULL, EVP_md5()))) { + OSSL_Raise(eX509CertificateError, ""); + } + + return ossl_x509req_new(req); +} + */ + +static VALUE +ossl_x509_get_version(VALUE self) +{ + X509 *x509 = NULL; + long ver = 0; + + GetX509(self, x509); + + ver = X509_get_version(x509); + + return INT2NUM(ver); +} + +static VALUE +ossl_x509_set_version(VALUE self, VALUE version) +{ + X509 *x509 = NULL; + long ver = 0; + + GetX509(self, x509); + + if ((ver = FIX2LONG(version)) < 0) { + rb_raise(eX509CertificateError, "version must be >= 0!"); + } + if (!X509_set_version(x509, ver)) { + OSSL_Raise(eX509CertificateError, ""); + } + + return version; +} + +static VALUE +ossl_x509_get_serial(VALUE self) +{ + X509 *x509 = NULL; + ASN1_INTEGER *asn1int = NULL; + long serial = 0; + + GetX509(self, x509); + + if (!(asn1int = X509_get_serialNumber(x509))) { /* NO DUP - don't free */ + OSSL_Raise(eX509CertificateError, ""); + } + serial = ASN1_INTEGER_get(asn1int); + + return INT2NUM(serial); +} + +static VALUE +ossl_x509_set_serial(VALUE self, VALUE serial) +{ + X509 *x509 = NULL; + ASN1_INTEGER *asn1int = NULL; + + GetX509(self, x509); + + if (!(asn1int = ASN1_INTEGER_new())) { + OSSL_Raise(eX509CertificateError, ""); + } + if (!ASN1_INTEGER_set(asn1int, FIX2LONG(serial))) { + ASN1_INTEGER_free(asn1int); + OSSL_Raise(eX509CertificateError, ""); + } + if (!X509_set_serialNumber(x509, asn1int)) { /* DUPs asn1int - FREE it */ + ASN1_INTEGER_free(asn1int); + OSSL_Raise(eX509CertificateError, ""); + } + ASN1_INTEGER_free(asn1int); + + return serial; +} + +static VALUE +ossl_x509_get_subject(VALUE self) +{ + X509 *x509 = NULL; + X509_NAME *name = NULL; + + GetX509(self, x509); + + if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */ + OSSL_Raise(eX509CertificateError, ""); + } + + return ossl_x509name_new(name); +} + +static VALUE +ossl_x509_set_subject(VALUE self, VALUE subject) +{ + X509 *x509 = NULL; + X509_NAME *name = NULL; + + GetX509(self, x509); + + name = ossl_x509name_get_X509_NAME(subject); + + if (!X509_set_subject_name(x509, name)) { /* DUPs name - FREE it */ + X509_NAME_free(name); + OSSL_Raise(eX509CertificateError, ""); + } + X509_NAME_free(name); + + return subject; +} + +static VALUE +ossl_x509_get_issuer(VALUE self) +{ + X509 *x509 = NULL; + X509_NAME *name = NULL; + + GetX509(self, x509); + + if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */ + OSSL_Raise(eX509CertificateError, ""); + } + + return ossl_x509name_new(name); +} + +static VALUE +ossl_x509_set_issuer(VALUE self, VALUE issuer) +{ + X509 *x509 = NULL; + X509_NAME *name = NULL; + + GetX509(self, x509); + + name = ossl_x509name_get_X509_NAME(issuer); + + if (!X509_set_issuer_name(x509, name)) { /* DUPs name - FREE it */ + X509_NAME_free(name); + OSSL_Raise(eX509CertificateError, ""); + } + X509_NAME_free(name); + + return issuer; +} + +static VALUE +ossl_x509_get_not_before(VALUE self) +{ + X509 *x509 = NULL; + ASN1_UTCTIME *asn1time = NULL; + + GetX509(self, x509); + + if (!(asn1time = X509_get_notBefore(x509))) { /* NO DUP - don't free! */ + OSSL_Raise(eX509CertificateError, ""); + } + + return asn1time_to_time(asn1time); +} + +static VALUE +ossl_x509_set_not_before(VALUE self, VALUE time) +{ + X509 *x509 = NULL; + time_t sec; + + GetX509(self, x509); + + sec = time_to_time_t(time); + + if (!ASN1_UTCTIME_set(X509_get_notBefore(x509), sec)) { + OSSL_Raise(eX509CertificateError, ""); + } + return time; +} + +static VALUE +ossl_x509_get_not_after(VALUE self) +{ + X509 *x509 = NULL; + ASN1_UTCTIME *asn1time = NULL; + + GetX509(self, x509); + + if (!(asn1time = X509_get_notAfter(x509))) { /* NO DUP - don't free! */ + OSSL_Raise(eX509CertificateError, ""); + } + + return asn1time_to_time(asn1time); +} + +static VALUE +ossl_x509_set_not_after(VALUE self, VALUE time) +{ + X509 *x509 = NULL; + time_t sec; + + GetX509(self, x509); + + sec = time_to_time_t(time); + + if (!ASN1_UTCTIME_set(X509_get_notAfter(x509), sec)) { + OSSL_Raise(eX509CertificateError, ""); + } + return time; +} + +static VALUE +ossl_x509_get_public_key(VALUE self) +{ + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + VALUE pub_key; + + GetX509(self, x509); + + if (!(pkey = X509_get_pubkey(x509))) { /* adds an reference - safe to FREE */ + OSSL_Raise(eX509CertificateError, ""); + } + pub_key = ossl_pkey_new(pkey); + EVP_PKEY_free(pkey); + + return pub_key; +} + +static VALUE +ossl_x509_set_public_key(VALUE self, VALUE pubk) +{ + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + + GetX509(self, x509); + + pkey = ossl_pkey_get_EVP_PKEY(pubk); + + if (!X509_set_pubkey(x509, pkey)) { /* DUPs pkey - FREE it */ + EVP_PKEY_free(pkey); + OSSL_Raise(eX509CertificateError, ""); + } + EVP_PKEY_free(pkey); + + return self; +} + +static VALUE +ossl_x509_sign(VALUE self, VALUE key, VALUE digest) +{ + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + const EVP_MD *md = NULL; + + GetX509(self, x509); + + OSSL_Check_Type(key, cPKey); + OSSL_Check_Type(digest, cDigest); + + if (rb_funcall(key, rb_intern("private?"), 0, NULL) == Qfalse) { + rb_raise(eX509CertificateError, "PRIVATE key needed to sign X509 Certificate!"); + } + + pkey = ossl_pkey_get_EVP_PKEY(key); + md = ossl_digest_get_EVP_MD(digest); + + if (!X509_sign(x509, pkey, md)) { + EVP_PKEY_free(pkey); + OSSL_Raise(eX509CertificateError, ""); + } + EVP_PKEY_free(pkey); + + return self; +} + +/* + * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' + */ +static VALUE +ossl_x509_verify(VALUE self, VALUE key) +{ + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + int i = 0; + + GetX509(self, x509); + + pkey = ossl_pkey_get_EVP_PKEY(key); + + i = X509_verify(x509, pkey); + EVP_PKEY_free(pkey); + + if (i < 0) { + OSSL_Raise(eX509CertificateError, ""); + } else if (i > 0) + return Qtrue; + + return Qfalse; +} + +/* + * Checks is 'key' is PRIV key for this cert + */ +static VALUE +ossl_x509_check_private_key(VALUE self, VALUE key) +{ + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + VALUE result; + + GetX509(self, x509); + + pkey = ossl_pkey_get_EVP_PKEY(key); + + if (!X509_check_private_key(x509, pkey)) { + OSSL_Warning("Check private key:"); + result = Qfalse; + } else + result = Qtrue; + + EVP_PKEY_free(pkey); + + return result; +} + +/* + * Gets X509v3 extensions as array of X509Ext objects + */ +static VALUE +ossl_x509_get_extensions(VALUE self) +{ + X509 *x509 = NULL; + int count = 0, i; + X509_EXTENSION *ext = NULL; + VALUE ary; + + GetX509(self, x509); + + count = X509_get_ext_count(x509); + + if (count > 0) + ary = rb_ary_new2(count); + else + return rb_ary_new(); + + for (i=0; i<count; i++) { + ext = X509_get_ext(x509, i); /* NO DUP - don't free! */ + rb_ary_push(ary, ossl_x509ext_new(ext)); + } + + return ary; +} + +/* + * Sets X509_EXTENSIONs + */ +static VALUE +ossl_x509_set_extensions(VALUE self, VALUE ary) +{ + X509 *x509 = NULL; + X509_EXTENSION *ext = NULL; + int i = 0; + + GetX509(self, x509); + + Check_Type(ary, T_ARRAY); + for (i=0; i<RARRAY(ary)->len; i++) { /* All ary's members should be X509Extension */ + OSSL_Check_Type(RARRAY(ary)->ptr[i], cX509Extension); + } + + sk_X509_EXTENSION_pop_free(x509->cert_info->extensions, X509_EXTENSION_free); + x509->cert_info->extensions = NULL; + + for (i=0; i<RARRAY(ary)->len; i++) { + ext = ossl_x509ext_get_X509_EXTENSION(RARRAY(ary)->ptr[i]); + + if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + OSSL_Raise(eX509CertificateError, ""); + } + X509_EXTENSION_free(ext); + } + + return ary; +} + +static VALUE +ossl_x509_add_extension(VALUE self, VALUE extension) +{ + X509 *x509 = NULL; + X509_EXTENSION *ext = NULL; + + GetX509(self, x509); + + ext = ossl_x509ext_get_X509_EXTENSION(extension); + + if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + OSSL_Raise(eX509CertificateError, ""); + } + X509_EXTENSION_free(ext); + + return extension; +} + +/* + * INIT + */ +void +Init_ossl_x509cert(VALUE module) +{ + eX509CertificateError = rb_define_class_under(module, "CertificateError", eOSSLError); + + cX509Certificate = rb_define_class_under(module, "Certificate", rb_cObject); + rb_define_singleton_method(cX509Certificate, "new", ossl_x509_s_new, -1); + rb_define_method(cX509Certificate, "initialize", ossl_x509_initialize, -1); + rb_define_method(cX509Certificate, "to_der", ossl_x509_to_der, 0); + rb_define_method(cX509Certificate, "to_pem", ossl_x509_to_pem, 0); + rb_define_alias(cX509Certificate, "to_s", "to_pem"); + rb_define_method(cX509Certificate, "to_text", ossl_x509_to_text, 0); + rb_define_method(cX509Certificate, "version", ossl_x509_get_version, 0); + rb_define_method(cX509Certificate, "version=", ossl_x509_set_version, 1); + rb_define_method(cX509Certificate, "serial", ossl_x509_get_serial, 0); + rb_define_method(cX509Certificate, "serial=", ossl_x509_set_serial, 1); + rb_define_method(cX509Certificate, "subject", ossl_x509_get_subject, 0); + rb_define_method(cX509Certificate, "subject=", ossl_x509_set_subject, 1); + rb_define_method(cX509Certificate, "issuer", ossl_x509_get_issuer, 0); + rb_define_method(cX509Certificate, "issuer=", ossl_x509_set_issuer, 1); + rb_define_method(cX509Certificate, "not_before", ossl_x509_get_not_before, 0); + rb_define_method(cX509Certificate, "not_before=", ossl_x509_set_not_before, 1); + rb_define_method(cX509Certificate, "not_after", ossl_x509_get_not_after, 0); + rb_define_method(cX509Certificate, "not_after=", ossl_x509_set_not_after, 1); + rb_define_method(cX509Certificate, "public_key", ossl_x509_get_public_key, 0); + rb_define_method(cX509Certificate, "public_key=", ossl_x509_set_public_key, 1); + rb_define_method(cX509Certificate, "sign", ossl_x509_sign, 2); + rb_define_method(cX509Certificate, "verify", ossl_x509_verify, 1); + rb_define_method(cX509Certificate, "check_private_key", ossl_x509_check_private_key, 1); + rb_define_method(cX509Certificate, "extensions", ossl_x509_get_extensions, 0); + rb_define_method(cX509Certificate, "extensions=", ossl_x509_set_extensions, 1); + rb_define_method(cX509Certificate, "add_extension", ossl_x509_add_extension, 1); +} + diff --git a/ossl_x509crl.c b/ossl_x509crl.c new file mode 100644 index 0000000..accf598 --- /dev/null +++ b/ossl_x509crl.c @@ -0,0 +1,488 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509CRL(obj, crl) obj = Data_Wrap_Struct(cX509CRL, 0, X509_CRL_free, crl) +#define GetX509CRL(obj, crl) Data_Get_Struct(obj, X509_CRL, crl) + +/* + * Classes + */ +VALUE cX509CRL; +VALUE eX509CRLError; + +/* + * PUBLIC + */ +X509_CRL * +ossl_x509crl_get_X509_CRL(VALUE obj) +{ + X509_CRL *crl = NULL, *new; + + OSSL_Check_Type(obj, cX509CRL); + + GetX509CRL(obj, crl); + + if (!(new = X509_CRL_dup(crl))) { + OSSL_Raise(eX509CRLError, ""); + } + + return new; +} + +/* + * PRIVATE + */ +static VALUE +ossl_x509crl_s_new(int argc, VALUE *argv, VALUE klass) +{ + X509_CRL *crl = NULL; + VALUE obj; + + if (!(crl = X509_CRL_new())) { + OSSL_Raise(eX509CRLError, ""); + } + + WrapX509CRL(obj, crl); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self) +{ + BIO *in = NULL; + VALUE buffer; + + if (argc == 0) + return self; + + buffer = rb_String(argv[0]); + + if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, -1))) { + OSSL_Raise(eX509CRLError, ""); + } + if (!PEM_read_bio_X509_CRL(in, (X509_CRL **)&DATA_PTR(self), NULL, NULL)) { + BIO_free(in); + OSSL_Raise(eX509CRLError, ""); + } + BIO_free(in); + + return self; +} + +static VALUE +ossl_x509crl_get_version(VALUE self) +{ + X509_CRL *crl = NULL; + long ver = 0; + + GetX509CRL(self, crl); + + ver = ASN1_INTEGER_get(crl->crl->version); + + return INT2NUM(ver); +} + +static VALUE +ossl_x509crl_set_version(VALUE self, VALUE version) +{ + X509_CRL *crl = NULL; + ASN1_INTEGER *asn1int = NULL; + long ver = 0; + + GetX509CRL(self, crl); + + if ((ver = NUM2LONG(version)) < 0) { + rb_raise(eX509CRLError, "version must be >= 0!"); + } + if (!(asn1int = ASN1_INTEGER_new())) { + OSSL_Raise(eX509CRLError, ""); + } + if (!ASN1_INTEGER_set(asn1int, ver)) { + OSSL_Raise(eX509CRLError, ""); + } + + ASN1_INTEGER_free(crl->crl->version); + crl->crl->version = asn1int; + + return version; +} + +static VALUE +ossl_x509crl_get_issuer(VALUE self) +{ + X509_CRL *crl = NULL; + + GetX509CRL(self, crl); + + return ossl_x509name_new(crl->crl->issuer); +} + +static VALUE +ossl_x509crl_set_issuer(VALUE self, VALUE issuer) +{ + X509_CRL *crl = NULL; + X509_NAME *name = NULL; + + GetX509CRL(self, crl); + + OSSL_Check_Type(issuer, cX509Name); + name = ossl_x509name_get_X509_NAME(issuer); + + if (!X509_NAME_set(&(crl->crl->issuer), name)) { /* DUPs name - FREE it */ + X509_NAME_free(name); + OSSL_Raise(eX509CRLError, ""); + } + X509_NAME_free(name); + + return issuer; +} + +static VALUE +ossl_x509crl_get_last_update(VALUE self) +{ + X509_CRL *crl = NULL; + + GetX509CRL(self, crl); + + return asn1time_to_time(crl->crl->lastUpdate); +} + +static VALUE +ossl_x509crl_set_last_update(VALUE self, VALUE time) +{ + X509_CRL *crl = NULL; + time_t sec; + + GetX509CRL(self, crl); + + sec = time_to_time_t(time); + + if (!ASN1_UTCTIME_set(crl->crl->lastUpdate, sec)) { + OSSL_Raise(eX509CRLError, ""); + } + + return time; +} + +static VALUE +ossl_x509crl_get_next_update(VALUE self) +{ + X509_CRL *crl = NULL; + + GetX509CRL(self, crl); + + return asn1time_to_time(crl->crl->nextUpdate); +} + +static VALUE +ossl_x509crl_set_next_update(VALUE self, VALUE time) +{ + X509_CRL *crl = NULL; + time_t sec; + + GetX509CRL(self, crl); + + sec = time_to_time_t(time); + + if (!ASN1_UTCTIME_set(crl->crl->nextUpdate, sec)) { + OSSL_Raise(eX509CRLError, ""); + } + + return time; +} + +static VALUE +ossl_x509crl_get_revoked(VALUE self) +{ + X509_CRL *crl = NULL; + int i, num = 0; + X509_REVOKED *rev = NULL; + VALUE ary, revoked; + + GetX509CRL(self, crl); + + num = sk_X509_CRL_num(crl->crl->revoked); + + if (num < 0) + return rb_ary_new(); + + ary = rb_ary_new2(num); + + for(i=0; i<num; i++) { + rev = (X509_REVOKED *)sk_X509_CRL_value(crl->crl->revoked, i); /* NO DUP - don't free! */ + revoked = ossl_x509revoked_new(rev); + rb_ary_push(ary, revoked); + } + + return ary; +} + +static VALUE +ossl_x509crl_set_revoked(VALUE self, VALUE ary) +{ + X509_CRL *crl = NULL; + X509_REVOKED *rev = NULL; + int i; + + GetX509CRL(self, crl); + + Check_Type(ary, T_ARRAY); + for (i=0; i<RARRAY(ary)->len; i++) { /* All ary members should be X509 Revoked */ + OSSL_Check_Type(RARRAY(ary)->ptr[i], cX509Revoked); + } + + sk_X509_REVOKED_pop_free(crl->crl->revoked, X509_REVOKED_free); + crl->crl->revoked = NULL; + M_ASN1_New(crl->crl->revoked, sk_X509_REVOKED_new_null); + + for (i=0; i<RARRAY(ary)->len; i++) { + rev = ossl_x509revoked_get_X509_REVOKED(RARRAY(ary)->ptr[i]); + + if (!sk_X509_CRL_push(crl->crl->revoked, rev)) { /* NO DUP - don't free! */ + OSSL_Raise(eX509CRLError, ""); + } + } + sk_X509_REVOKED_sort(crl->crl->revoked); + + return ary; +} + +static VALUE +ossl_x509crl_add_revoked(VALUE self, VALUE revoked) +{ + X509_CRL *crl = NULL; + X509_REVOKED *rev = NULL; + + GetX509CRL(self, crl); + + OSSL_Check_Type(revoked, cX509Revoked); + rev = ossl_x509revoked_get_X509_REVOKED(revoked); + + if (!sk_X509_CRL_push(crl->crl->revoked, rev)) { /* NO DUP - don't free! */ + OSSL_Raise(eX509CRLError, ""); + } + sk_X509_REVOKED_sort(crl->crl->revoked); + + return revoked; +} + +static VALUE +ossl_x509crl_sign(VALUE self, VALUE key, VALUE digest) +{ + X509_CRL *crl = NULL; + EVP_PKEY *pkey = NULL; + const EVP_MD *md = NULL; + + GetX509CRL(self, crl); + + OSSL_Check_Type(key, cPKey); + OSSL_Check_Type(digest, cDigest); + + if (rb_funcall(key, id_private_q, 0, NULL) == Qfalse) { + rb_raise(eX509CRLError, "PRIVATE key needed to sign CRL!"); + } + + pkey = ossl_pkey_get_EVP_PKEY(key); + md = ossl_digest_get_EVP_MD(digest); + + if (!X509_CRL_sign(crl, pkey, md)) { + EVP_PKEY_free(pkey); + OSSL_Raise(eX509CRLError, ""); + } + EVP_PKEY_free(pkey); + + return self; +} + +static VALUE +ossl_x509crl_verify(VALUE self, VALUE key) +{ + X509_CRL *crl = NULL; + EVP_PKEY *pkey = NULL; + int result = 0; + + GetX509CRL(self, crl); + + OSSL_Check_Type(key, cPKey); + pkey = ossl_pkey_get_EVP_PKEY(key); + + result = X509_CRL_verify(crl, pkey); + EVP_PKEY_free(pkey); + + if (result == 1) return Qtrue; + return Qfalse; +} + +static VALUE +ossl_x509crl_to_pem(VALUE self) +{ + X509_CRL *crl = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetX509CRL(self, crl); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509CRLError, ""); + } + if (!PEM_write_bio_X509_CRL(out, crl)) { + BIO_free(out); + OSSL_Raise(eX509CRLError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_x509crl_to_text(VALUE self) +{ + X509_CRL *crl = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetX509CRL(self, crl); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509CRLError, ""); + } + if (!X509_CRL_print(out, crl)) { + BIO_free(out); + OSSL_Raise(eX509CRLError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Gets X509v3 extensions as array of X509Ext objects + */ +static VALUE +ossl_x509crl_get_extensions(VALUE self) +{ + X509_CRL *crl = NULL; + int count = 0, i; + X509_EXTENSION *ext = NULL; + VALUE ary; + + GetX509CRL(self, crl); + + count = X509_CRL_get_ext_count(crl); + + if (count > 0) + ary = rb_ary_new2(count); + else + return rb_ary_new(); + + for (i=0; i<count; i++) { + ext = X509_CRL_get_ext(crl, i); /* NO DUP - don't free! */ + rb_ary_push(ary, ossl_x509ext_new(ext)); + } + + return ary; +} + +/* + * Sets X509_EXTENSIONs + */ +static VALUE +ossl_x509crl_set_extensions(VALUE self, VALUE ary) +{ + X509_CRL *crl = NULL; + X509_EXTENSION *ext = NULL; + int i = 0; + + GetX509CRL(self, crl); + + Check_Type(ary, T_ARRAY); + for (i=0; i<RARRAY(ary)->len; i++) { /* All ary members should be X509 Extensions */ + OSSL_Check_Type(RARRAY(ary)->ptr[i], cX509Extension); + } + + sk_X509_EXTENSION_pop_free(crl->crl->extensions, X509_EXTENSION_free); + crl->crl->extensions = NULL; + + for (i=0; i<RARRAY(ary)->len; i++) { + ext = ossl_x509ext_get_X509_EXTENSION(RARRAY(ary)->ptr[i]); + + if(!X509_CRL_add_ext(crl, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + OSSL_Raise(eX509CRLError, ""); + } + X509_EXTENSION_free(ext); + } + + return ary; +} + +static VALUE +ossl_x509crl_add_extension(VALUE self, VALUE extension) +{ + X509_CRL *crl = NULL; + X509_EXTENSION *ext = NULL; + + GetX509CRL(self, crl); + + OSSL_Check_Type(extension, cX509Extension); + ext = ossl_x509ext_get_X509_EXTENSION(extension); + + if (!X509_CRL_add_ext(crl, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + OSSL_Raise(eX509CRLError, ""); + } + X509_EXTENSION_free(ext); + + return extension; +} + +/* + * INIT + */ +void +Init_ossl_x509crl(VALUE module) +{ + eX509CRLError = rb_define_class_under(module, "CRLError", eOSSLError); + + cX509CRL = rb_define_class_under(module, "CRL", rb_cObject); + rb_define_singleton_method(cX509CRL, "new", ossl_x509crl_s_new, -1); + rb_define_method(cX509CRL, "initialize", ossl_x509crl_initialize, -1); + rb_define_method(cX509CRL, "version", ossl_x509crl_get_version, 0); + rb_define_method(cX509CRL, "version=", ossl_x509crl_set_version, 1); + rb_define_method(cX509CRL, "issuer", ossl_x509crl_get_issuer, 0); + rb_define_method(cX509CRL, "issuer=", ossl_x509crl_set_issuer, 1); + rb_define_method(cX509CRL, "last_update", ossl_x509crl_get_last_update, 0); + rb_define_method(cX509CRL, "last_update=", ossl_x509crl_set_last_update, 1); + rb_define_method(cX509CRL, "next_update", ossl_x509crl_get_next_update, 0); + rb_define_method(cX509CRL, "next_update=", ossl_x509crl_set_next_update, 1); + rb_define_method(cX509CRL, "revoked", ossl_x509crl_get_revoked, 0); + rb_define_method(cX509CRL, "revoked=", ossl_x509crl_set_revoked, 1); + rb_define_method(cX509CRL, "add_revoked", ossl_x509crl_add_revoked, 1); + rb_define_method(cX509CRL, "sign", ossl_x509crl_sign, 1); + rb_define_method(cX509CRL, "verify", ossl_x509crl_verify, 1); + rb_define_method(cX509CRL, "to_pem", ossl_x509crl_to_pem, 0); + rb_define_alias(cX509CRL, "to_s", "to_pem"); + rb_define_method(cX509CRL, "to_text", ossl_x509crl_to_text, 0); + rb_define_method(cX509CRL, "extensions", ossl_x509crl_get_extensions, 0); + rb_define_method(cX509CRL, "extensions=", ossl_x509crl_set_extensions, 1); + rb_define_method(cX509CRL, "add_extension", ossl_x509crl_add_extension, 1); +} + diff --git a/ossl_x509ext.c b/ossl_x509ext.c new file mode 100644 index 0000000..555712a --- /dev/null +++ b/ossl_x509ext.c @@ -0,0 +1,283 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Ext(obj, ext) \ + obj = Data_Wrap_Struct(cX509Extension, 0, X509_EXTENSION_free, ext) +#define GetX509Ext(obj, ext) \ + Data_Get_Struct(obj, X509_EXTENSION, ext) + +#define MakeX509ExtFactory(obj, ctx) \ + obj = Data_Make_Struct(cX509ExtensionFactory, X509V3_CTX, 0, CRYPTO_free, ctx) +#define GetX509ExtFactory(obj, ctx) \ + Data_Get_Struct(obj, X509V3_CTX, ctx) + +/* + * Classes + */ +VALUE cX509Extension; +VALUE cX509ExtensionFactory; +VALUE eX509ExtensionError; + +/* + * Public + */ +VALUE +ossl_x509ext_new(X509_EXTENSION *ext) +{ + X509_EXTENSION *new = NULL; + VALUE obj; + + if (!ext) + new = X509_EXTENSION_new(); + else new = X509_EXTENSION_dup(ext); + + if (!new) + OSSL_Raise(eX509ExtensionError, ""); + + WrapX509Ext(obj, new); + + return obj; +} + +X509_EXTENSION * +ossl_x509ext_get_X509_EXTENSION(VALUE obj) +{ + X509_EXTENSION *ext = NULL, *new; + + OSSL_Check_Type(obj, cX509Extension); + + GetX509Ext(obj, ext); + + if (!(new = X509_EXTENSION_dup(ext))) { + OSSL_Raise(eX509ExtensionError, ""); + } + + return new; +} + +/* + * Private + */ +/* + * Extension factory + */ +static VALUE +ossl_x509extfactory_s_new(int argc, VALUE *argv, VALUE klass) +{ + X509V3_CTX *ctx = NULL; + VALUE obj; + + MakeX509ExtFactory(obj, ctx); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_x509extfactory_set_issuer_cert(VALUE self, VALUE cert) +{ + X509V3_CTX *ctx = NULL; + + GetX509ExtFactory(self, ctx); + + ctx->issuer_cert = ossl_x509_get_X509(cert); + + return cert; +} + +static VALUE +ossl_x509extfactory_set_subject_cert(VALUE self, VALUE cert) +{ + X509V3_CTX *ctx = NULL; + + GetX509ExtFactory(self, ctx); + + ctx->subject_cert = ossl_x509_get_X509(cert); + + return cert; +} + +static VALUE +ossl_x509extfactory_set_subject_req(VALUE self, VALUE req) +{ + X509V3_CTX *ctx = NULL; + + GetX509ExtFactory(self, ctx); + + ctx->subject_req = ossl_x509req_get_X509_REQ(req); + + return req; +} + +static VALUE +ossl_x509extfactory_set_crl(VALUE self, VALUE crl) +{ + X509V3_CTX *ctx = NULL; + + GetX509ExtFactory(self, ctx); + + ctx->crl = ossl_x509crl_get_X509_CRL(crl); + + return crl; +} + +static VALUE +ossl_x509extfactory_initialize(int argc, VALUE *argv, VALUE self) +{ + /*X509V3_CTX *ctx = NULL;*/ + VALUE issuer_cert, subject_cert, subject_req, crl; + + /*GetX509ExtFactory(self, ctx);*/ + + rb_scan_args(argc, argv, "04", &issuer_cert, &subject_cert, &subject_req, &crl); + + if (!NIL_P(issuer_cert)) { + ossl_x509extfactory_set_issuer_cert(self, issuer_cert); + } + if (!NIL_P(subject_cert)) { + ossl_x509extfactory_set_subject_cert(self, subject_cert); + } + if (!NIL_P(subject_req)) { + ossl_x509extfactory_set_subject_req(self, subject_req); + } + if (!NIL_P(crl)) { + ossl_x509extfactory_set_crl(self, crl); + } + + return self; +} + +/* + * Array to X509_EXTENSION + * Structure: + * ["ln", "value", bool_critical] or + * ["sn", "value", bool_critical] or + * ["ln", "critical,value"] or the same for sn + * ["ln", "value"] => not critical + */ +static VALUE +ossl_x509extfactory_create_ext_from_array(VALUE self, VALUE ary) +{ + X509V3_CTX *ctx = NULL; + X509_EXTENSION *ext = NULL; + int nid = NID_undef; + char *value = NULL; + VALUE item, obj; + + GetX509ExtFactory(self, ctx); + + Check_Type(ary, T_ARRAY); + + if ((RARRAY(ary)->len) < 2 || (RARRAY(ary)->len > 3)) { /*2 or 3 allowed*/ + rb_raise(eX509ExtensionError, "unsupported structure"); + } + + /* key [0] */ + item = RARRAY(ary)->ptr[0]; + item = rb_String(item); + if (!(nid = OBJ_ln2nid(RSTRING(item)->ptr))) + if (!(nid = OBJ_sn2nid(RSTRING(item)->ptr))) { + OSSL_Raise(eX509ExtensionError, ""); + } + + /* data [1] */ + item = RARRAY(ary)->ptr[1]; + item = rb_String(item); + + /* (optional) critical [2] */ + if (RARRAY(ary)->len == 3 && RARRAY(ary)->ptr[2] == Qtrue) { + if (!(value = malloc(strlen("critical,")+(RSTRING(item)->len)+1))) { + rb_raise(eX509ExtensionError, "malloc error"); + } + strcpy(value, "critical,"); + strncat(value, RSTRING(item)->ptr, RSTRING(item)->len); + } else + value = strdup(RSTRING(item)->ptr); + + if (!(ext = X509V3_EXT_conf_nid(NULL, ctx, nid, value))) { + free(value); + OSSL_Raise(eX509ExtensionError, ""); + } + free(value); + + WrapX509Ext(obj, ext); + + return obj; +} + +/* + * Extension + */ +static VALUE +ossl_x509ext_to_a(VALUE obj) +{ + X509_EXTENSION *ext = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + int nid = NID_undef, critical; + VALUE ary, value; + + GetX509Ext(obj, ext); + + ary = rb_ary_new2(3); + + nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); + rb_ary_push(ary, rb_str_new2(OBJ_nid2sn(nid))); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509ExtensionError, ""); + } + if (!X509V3_EXT_print(out, ext, 0, 0)) { + BIO_free(out); + OSSL_Raise(eX509ExtensionError, ""); + } + BIO_get_mem_ptr(out, &buf); + value = rb_str_new(buf->data, buf->length); + BIO_free(out); + + rb_funcall(value, rb_intern("tr!"), 2, rb_str_new2("\n"), rb_str_new2(",")); + rb_ary_push(ary, value); + + critical = X509_EXTENSION_get_critical(ext); + rb_ary_push(ary, (critical) ? Qtrue : Qfalse); + + return ary; +} + +/* + * INIT + */ +void +Init_ossl_x509ext(VALUE module) +{ + + eX509ExtensionError = rb_define_class_under(module, "ExtensionError", eOSSLError); + + cX509ExtensionFactory = rb_define_class_under(module, "ExtensionFactory", rb_cObject); + rb_define_singleton_method(cX509ExtensionFactory, "new", ossl_x509extfactory_s_new, -1); + rb_define_method(cX509ExtensionFactory, "initialize", ossl_x509extfactory_initialize, -1); + rb_define_method(cX509ExtensionFactory, "issuer_certificate=", ossl_x509extfactory_set_issuer_cert, 1); + rb_define_method(cX509ExtensionFactory, "subject_certificate=", ossl_x509extfactory_set_subject_cert, 1); + rb_define_method(cX509ExtensionFactory, "subject_request=", ossl_x509extfactory_set_subject_req, 1); + rb_define_method(cX509ExtensionFactory, "crl=", ossl_x509extfactory_set_crl, 1); + rb_define_method(cX509ExtensionFactory, "create_ext_from_array", ossl_x509extfactory_create_ext_from_array, 1); + + cX509Extension = rb_define_class_under(module, "Extension", rb_cObject); + rb_undef_method(CLASS_OF(cX509Extension), "new"); +/* + rb_define_singleton_method(cX509Extension, "new", ossl_x509ext_s_new, -1); + rb_define_method(cX509Extension, "initialize", ossl_x509ext_initialize, -1); + */ + rb_define_method(cX509Extension, "to_a", ossl_x509ext_to_a, 0); +} + diff --git a/ossl_x509name.c b/ossl_x509name.c new file mode 100644 index 0000000..89e1ae3 --- /dev/null +++ b/ossl_x509name.c @@ -0,0 +1,156 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" +#include "st.h" /* For st_foreach -- ST_CONTINUE */ + +#define WrapX509Name(obj, name) obj = Data_Wrap_Struct(cX509Name, 0, X509_NAME_free, name) +#define GetX509Name(obj, name) Data_Get_Struct(obj, X509_NAME, name) + +/* + * Classes + */ +VALUE cX509Name; +VALUE eX509NameError; + +/* + * Public + */ +VALUE +ossl_x509name_new(X509_NAME *name) +{ + X509_NAME *new = NULL; + VALUE obj; + + if (!name) + new = X509_NAME_new(); + else new = X509_NAME_dup(name); + + if (!new) + OSSL_Raise(eX509NameError, ""); + + WrapX509Name(obj, new); + + return obj; +} + +X509_NAME * +ossl_x509name_get_X509_NAME(VALUE obj) +{ + X509_NAME *name = NULL, *new; + + OSSL_Check_Type(obj, cX509Name); + + GetX509Name(obj, name); + + if (!(new = X509_NAME_dup(name))) { + OSSL_Raise(eX509NameError, ""); + } + + return new; +} + +/* + * Private + */ +/* + * Iterator for ossl_x509name_new_from_hash + */ +static int +ossl_x509name_hash_i(VALUE key, VALUE value, X509_NAME *name) +{ + int id, type; + + key = rb_String(key); + value = rb_String(value); + + if (!(id = OBJ_ln2nid(RSTRING(key)->ptr))) + if (!(id = OBJ_sn2nid(RSTRING(key)->ptr))) { + X509_NAME_free(name); + OSSL_Raise(eX509NameError, "OBJ_name2nid:"); + } + + type = ASN1_PRINTABLE_type(RSTRING(value)->ptr, -1); + + if (!X509_NAME_add_entry_by_NID(name, id, type, RSTRING(value)->ptr, RSTRING(value)->len, -1, 0)) { + X509_NAME_free(name); + OSSL_Raise(eX509NameError, ""); + } + + return ST_CONTINUE; +} + +static VALUE +ossl_x509name_s_new_from_hash(VALUE klass, VALUE hash) +{ + X509_NAME *name = NULL; + VALUE obj; + + Check_Type(hash, T_HASH); + + if (!(name = X509_NAME_new())) + OSSL_Raise(eX509NameError, ""); + + st_foreach(RHASH(hash)->tbl, ossl_x509name_hash_i, name); + + WrapX509Name(obj, name); + + return obj; +} + +static VALUE +ossl_x509name_to_h(VALUE self) +{ + X509_NAME *name = NULL; + X509_NAME_ENTRY *entry = NULL; + int i,entries = 0; + char long_name[512]; + const char *short_name = NULL; + VALUE hash; + + GetX509Name(self, name); + + entries = X509_NAME_entry_count(name); + + hash = rb_hash_new(); + + if (entries < 0) { + rb_warning("name entries < 0!"); + return hash; + } + + for (i=0; i<entries; i++) { + if (!(entry = X509_NAME_get_entry(name, i))) { + OSSL_Raise(eX509NameError, ""); + } + if (!i2t_ASN1_OBJECT(long_name, sizeof(long_name), entry->object)) { + OSSL_Raise(eX509NameError, ""); + } + short_name = OBJ_nid2sn(OBJ_ln2nid(long_name)); + + rb_hash_aset(hash, rb_str_new2(short_name), rb_str_new(entry->value->data, entry->value->length)); + } + + return hash; +} + +/* + * INIT + */ +void +Init_ossl_x509name(VALUE module) +{ + eX509NameError = rb_define_class_under(module, "NameError", eOSSLError); + + cX509Name = rb_define_class_under(module, "Name", rb_cObject); + rb_define_singleton_method(cX509Name, "new_from_hash", ossl_x509name_s_new_from_hash, 1); + rb_define_method(cX509Name, "to_h", ossl_x509name_to_h, 0); +} + diff --git a/ossl_x509req.c b/ossl_x509req.c new file mode 100644 index 0000000..7208cd0 --- /dev/null +++ b/ossl_x509req.c @@ -0,0 +1,416 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Req(obj, req) obj = Data_Wrap_Struct(cX509Request, 0, X509_REQ_free, req) +#define GetX509Req(obj, req) Data_Get_Struct(obj, X509_REQ, req) + +/* + * Classes + */ +VALUE cX509Request; +VALUE eX509RequestError; + +/* + * Public functions + */ +VALUE +ossl_x509req_new(X509_REQ *req) +{ + X509_REQ *new = NULL; + VALUE self; + + if (!req) + new = X509_REQ_new(); + else new = X509_REQ_dup(req); + + if (!new) + OSSL_Raise(eX509RequestError, ""); + + WrapX509Req(self, new); + + return self; +} + +X509_REQ * +ossl_x509req_get_X509_REQ(VALUE obj) +{ + X509_REQ *req = NULL, *new; + + OSSL_Check_Type(obj, cX509Request); + + GetX509Req(obj, req); + + if (!(new = X509_REQ_dup(req))) { + OSSL_Raise(eX509RequestError, ""); + } + + return new; +} + +/* + * Private functions + */ +static VALUE +ossl_x509req_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE obj; + + obj = ossl_x509req_new(NULL); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_x509req_initialize(int argc, VALUE *argv, VALUE self) +{ + BIO *in = NULL; + VALUE buffer; + + if (argc == 0) + return self; + + buffer = rb_String(argv[0]); + if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, -1))) { + OSSL_Raise(eX509RequestError, ""); + } + if (!PEM_read_bio_X509_REQ(in, (X509_REQ **)&DATA_PTR(self), NULL, NULL)) { + BIO_free(in); + OSSL_Raise(eX509RequestError, ""); + } + BIO_free(in); + + return self; +} + +static VALUE +ossl_x509req_to_pem(VALUE self) +{ + X509_REQ *req = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetX509Req(self, req); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509RequestError, ""); + } + if (!PEM_write_bio_X509_REQ(out, req)) { + BIO_free(out); + OSSL_Raise(eX509RequestError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_x509req_to_text(VALUE self) +{ + X509_REQ *req = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + VALUE str; + + GetX509Req(self, req); + + if (!(out = BIO_new(BIO_s_mem()))) { + OSSL_Raise(eX509RequestError, ""); + } + if (!X509_REQ_print(out, req)) { + BIO_free(out); + OSSL_Raise(eX509RequestError, ""); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes X509 from X509_REQuest + * +static VALUE +ossl_x509req_to_x509(VALUE self, VALUE days, VALUE key) +{ + X509_REQ *req = NULL; + X509 *x509 = NULL; + + GetX509Req(self, req); + ... + if (!(x509 = X509_REQ_to_X509(req, d, pkey))) { + OSSL_Raise(eX509RequestError, ""); + } + + return ossl_x509_new(x509); +} + */ + +static VALUE +ossl_x509req_get_version(VALUE self) +{ + X509_REQ *req = NULL; + long version = 0; + + GetX509Req(self, req); + + version = X509_REQ_get_version(req); + + return INT2NUM(version); +} + +static VALUE +ossl_x509req_set_version(VALUE self, VALUE version) +{ + X509_REQ *req = NULL; + long ver = 0; + + GetX509Req(self, req); + + if ((ver = NUM2INT(version)) < 0) { + rb_raise(eX509RequestError, "version must be >= 0!"); + } + if (!X509_REQ_set_version(req, ver)) { + OSSL_Raise(eX509RequestError, ""); + } + + return version; +} + +static VALUE +ossl_x509req_get_subject(VALUE self) +{ + X509_REQ *req = NULL; + X509_NAME *name = NULL; + VALUE subject; + + GetX509Req(self, req); + + if (!(name = X509_REQ_get_subject_name(req))) { + OSSL_Raise(eX509RequestError, ""); + } + subject = ossl_x509name_new(name); + /*X509_NAME_free(name);*/ + + return subject; +} + +static VALUE +ossl_x509req_set_subject(VALUE self, VALUE subject) +{ + X509_REQ *req = NULL; + X509_NAME *name = NULL; + + GetX509Req(self, req); + + name = ossl_x509name_get_X509_NAME(subject); + + if (!X509_REQ_set_subject_name(req, name)) { + OSSL_Raise(eX509RequestError, ""); + } + /*X509_NAME_free(name);*/ + + return subject; +} + +static VALUE +ossl_x509req_get_public_key(VALUE self) +{ + X509_REQ *req = NULL; + EVP_PKEY *pkey = NULL; + VALUE pub_key; + + GetX509Req(self, req); + + if (!(pkey = X509_REQ_get_pubkey(req))) { + OSSL_Raise(eX509RequestError, ""); + } + pub_key = ossl_pkey_new(pkey); + EVP_PKEY_free(pkey); + + return pub_key; +} + +static VALUE +ossl_x509req_set_public_key(VALUE self, VALUE pubk) +{ + X509_REQ *req = NULL; + EVP_PKEY *pkey = NULL; + + GetX509Req(self, req); + + pkey = ossl_pkey_get_EVP_PKEY(pubk); + + if (!X509_REQ_set_pubkey(req, pkey)) { + EVP_PKEY_free(pkey); + OSSL_Raise(eX509RequestError, ""); + } + EVP_PKEY_free(pkey); + + return pubk; +} + +static VALUE +ossl_x509req_sign(VALUE self, VALUE key, VALUE digest) +{ + X509_REQ *req = NULL; + EVP_PKEY *pkey = NULL; + const EVP_MD *md = NULL; + + GetX509Req(self, req); + OSSL_Check_Type(key, cPKey); + OSSL_Check_Type(digest, cDigest); + + if (rb_funcall(key, id_private_q, 0, NULL) == Qfalse) { + rb_raise(eX509RequestError, "PRIVATE key needed to sign REQ!"); + } + + pkey = ossl_pkey_get_EVP_PKEY(key); + md = ossl_digest_get_EVP_MD(digest); + + if (!X509_REQ_sign(req, pkey, md)) { + EVP_PKEY_free(pkey); + OSSL_Raise(eX509RequestError, ""); + } + EVP_PKEY_free(pkey); + + return self; +} + +/* + * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' + */ +static VALUE +ossl_x509req_verify(VALUE self, VALUE key) +{ + X509_REQ *req = NULL; + EVP_PKEY *pkey = NULL; + int i = 0; + + GetX509Req(self, req); + + pkey = ossl_pkey_get_EVP_PKEY(key); + + i = X509_REQ_verify(req, pkey); + EVP_PKEY_free(pkey); + + if (i < 0) + OSSL_Raise(eX509RequestError, ""); + else if (i > 0) + return Qtrue; + + return Qfalse; +} + +static VALUE +ossl_x509req_get_attributes(VALUE self) +{ + X509_REQ *req = NULL; + int count = 0, i; + X509_ATTRIBUTE *attr = NULL; + VALUE ary; + + GetX509Req(self, req); + + count = X509_REQ_get_attr_count(req); + + if (count > 0) + ary = rb_ary_new2(count); + else + return rb_ary_new(); + + for (i=0; i<count; i++) { + attr = X509_REQ_get_attr(req, i); + rb_ary_push(ary, ossl_x509attr_new(attr)); + } + + return ary; +} + +static VALUE +ossl_x509req_set_attributes(VALUE self, VALUE ary) +{ + X509_REQ *req = NULL; + X509_ATTRIBUTE *attr = NULL; + int i = 0; + VALUE item; + + GetX509Req(self, req); + + Check_Type(ary, T_ARRAY); + + sk_X509_ATTRIBUTE_pop_free(req->req_info->attributes, X509_ATTRIBUTE_free); + req->req_info->attributes = NULL; + + for (i=0;i<RARRAY(ary)->len; i++) { + item = RARRAY(ary)->ptr[i]; + + OSSL_Check_Type(item, cX509Attribute); + + attr = ossl_x509attr_get_X509_ATTRIBUTE(item); + + if (!X509_REQ_add1_attr(req, attr)) { + OSSL_Raise(eX509RequestError, ""); + } + } + + return ary; +} + +static VALUE +ossl_x509req_add_attribute(VALUE self, VALUE attr) +{ + X509_REQ *req = NULL; + + GetX509Req(self, req); + + OSSL_Check_Type(attr, cX509Attribute); + + if (!X509_REQ_add1_attr(req, ossl_x509attr_get_X509_ATTRIBUTE(attr))) { + OSSL_Raise(eX509RequestError, ""); + } + + return attr; +} + +/* + * X509_REQUEST init + */ +void +Init_ossl_x509req(VALUE module) +{ + eX509RequestError = rb_define_class_under(module, "RequestError", eOSSLError); + + cX509Request = rb_define_class_under(module, "Request", rb_cObject); + rb_define_singleton_method(cX509Request, "new", ossl_x509req_s_new, -1); + rb_define_method(cX509Request, "initialize", ossl_x509req_initialize, -1); + rb_define_method(cX509Request, "to_pem", ossl_x509req_to_pem, 0); + rb_define_alias(cX509Request, "to_s", "to_pem"); + rb_define_method(cX509Request, "to_text", ossl_x509req_to_text, 0); + rb_define_method(cX509Request, "version", ossl_x509req_get_version, 0); + rb_define_method(cX509Request, "version=", ossl_x509req_set_version, 1); + rb_define_method(cX509Request, "subject", ossl_x509req_get_subject, 0); + rb_define_method(cX509Request, "subject=", ossl_x509req_set_subject, 1); + rb_define_method(cX509Request, "public_key", ossl_x509req_get_public_key, 0); + rb_define_method(cX509Request, "public_key=", ossl_x509req_set_public_key, 1); + rb_define_method(cX509Request, "sign", ossl_x509req_sign, 2); + rb_define_method(cX509Request, "verify", ossl_x509req_verify, 1); + rb_define_method(cX509Request, "attributes", ossl_x509req_get_attributes, 0); + rb_define_method(cX509Request, "attributes=", ossl_x509req_set_attributes, 1); + rb_define_method(cX509Request, "add_attribute", ossl_x509req_add_attribute, 1); +} + diff --git a/ossl_x509revoked.c b/ossl_x509revoked.c new file mode 100644 index 0000000..eab88c6 --- /dev/null +++ b/ossl_x509revoked.c @@ -0,0 +1,230 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Revoked(obj, rev) obj = Data_Wrap_Struct(cX509Revoked, 0, X509_REVOKED_free, rev) +#define GetX509Revoked(obj, rev) Data_Get_Struct(obj, X509_REVOKED, rev) + +/* + * Classes + */ +VALUE cX509Revoked; +VALUE eX509RevokedError; + +/* + * PUBLIC + */ +VALUE +ossl_x509revoked_new(X509_REVOKED *rev) +{ + X509_REVOKED *new = NULL; + VALUE obj; + + if (!rev) + new = X509_REVOKED_new(); + else new = X509_REVOKED_dup(rev); + + if (!new) + OSSL_Raise(eX509RevokedError, ""); + + WrapX509Revoked(obj, new); + + return obj; +} + +X509_REVOKED * +ossl_x509revoked_get_X509_REVOKED(VALUE obj) +{ + X509_REVOKED *rev = NULL, *new; + + OSSL_Check_Type(obj, cX509Revoked); + + GetX509Revoked(obj, rev); + + if (!(new = X509_REVOKED_dup(rev))) { + OSSL_Raise(eX509RevokedError, ""); + } + return new; +} + +/* + * PRIVATE + */ +static VALUE +ossl_x509revoked_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE obj; + + obj = ossl_x509revoked_new(NULL); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_x509revoked_initialize(int argc, VALUE *argv, VALUE self) +{ + /* EMPTY */ + return self; +} + +static VALUE +ossl_x509revoked_get_serial(VALUE self) +{ + X509_REVOKED *rev = NULL; + + GetX509Revoked(self, rev); + + return INT2NUM(ASN1_INTEGER_get(rev->serialNumber)); +} + +static VALUE +ossl_x509revoked_set_serial(VALUE self, VALUE serial) +{ + X509_REVOKED *rev = NULL; + + GetX509Revoked(self, rev); + + if (!ASN1_INTEGER_set(rev->serialNumber, NUM2INT(serial))) { + OSSL_Raise(eX509RevokedError, ""); + } + + return serial; +} + +static VALUE +ossl_x509revoked_get_time(VALUE self) +{ + X509_REVOKED *rev = NULL; + + GetX509Revoked(self, rev); + + return asn1time_to_time(rev->revocationDate); +} + +static VALUE +ossl_x509revoked_set_time(VALUE self, VALUE time) +{ + X509_REVOKED *rev = NULL; + time_t sec; + + GetX509Revoked(self, rev); + + sec = time_to_time_t(time); + + if (!ASN1_UTCTIME_set(rev->revocationDate, sec)) { + OSSL_Raise(eX509RevokedError, ""); + } + + return time; +} +/* + * Gets X509v3 extensions as array of X509Ext objects + */ +static VALUE +ossl_x509revoked_get_extensions(VALUE self) +{ + X509_REVOKED *rev = NULL; + int count = 0, i; + X509_EXTENSION *ext = NULL; + VALUE ary; + + GetX509Revoked(self, rev); + + count = X509_REVOKED_get_ext_count(rev); + + if (count > 0) + ary = rb_ary_new2(count); + else + return rb_ary_new(); + + for (i=0; i<count; i++) { + ext = X509_REVOKED_get_ext(rev, i); + rb_ary_push(ary, ossl_x509ext_new(ext)); + } + + return ary; +} + +/* + * Sets X509_EXTENSIONs + */ +static VALUE +ossl_x509revoked_set_extensions(VALUE self, VALUE ary) +{ + X509_REVOKED *rev = NULL; + X509_EXTENSION *ext = NULL; + int i = 0; + VALUE item; + + GetX509Revoked(self, rev); + + Check_Type(ary, T_ARRAY); + /* + for (i=0; i<RARRAY(ary)->len; i++) { + OSSL_Check_Type(RARRAY(ary)->ptr[i], cX509Extension); + } + */ + sk_X509_EXTENSION_pop_free(rev->extensions, X509_EXTENSION_free); + rev->extensions = NULL; + + for (i=0; i<RARRAY(ary)->len; i++) { + item = RARRAY(ary)->ptr[i]; + + OSSL_Check_Type(item, cX509Extension); + + ext = ossl_x509ext_get_X509_EXTENSION(item); + + if(!X509_REVOKED_add_ext(rev, ext, -1)) { + OSSL_Raise(eX509RevokedError, ""); + } + } + + return ary; +} + +static VALUE +ossl_x509revoked_add_extension(VALUE self, VALUE ext) +{ + X509_REVOKED *rev = NULL; + + GetX509Revoked(self, rev); + + OSSL_Check_Type(ext, cX509Extension); + + if(!X509_REVOKED_add_ext(rev, ossl_x509ext_get_X509_EXTENSION(ext), -1)) { + OSSL_Raise(eX509RevokedError, ""); + } + + return ext; +} + +/* + * INIT + */ +void +Init_ossl_x509revoked(VALUE module) +{ + eX509RevokedError = rb_define_class_under(module, "RevokedError", eOSSLError); + + cX509Revoked = rb_define_class_under(module, "Revoked", rb_cObject); + rb_define_singleton_method(cX509Revoked, "new", ossl_x509revoked_s_new, -1); + rb_define_method(cX509Revoked, "initialize", ossl_x509revoked_initialize, -1); + rb_define_method(cX509Revoked, "serial", ossl_x509revoked_get_serial, 0); + rb_define_method(cX509Revoked, "serial=", ossl_x509revoked_set_serial, 1); + rb_define_method(cX509Revoked, "time", ossl_x509revoked_get_time, 0); + rb_define_method(cX509Revoked, "time=", ossl_x509revoked_set_time, 1); + rb_define_method(cX509Revoked, "extensions", ossl_x509revoked_get_extensions, 0); + rb_define_method(cX509Revoked, "extensions=", ossl_x509revoked_set_extensions, 1); + rb_define_method(cX509Revoked, "add_extension", ossl_x509revoked_add_extension, 1); +} + diff --git a/ossl_x509store.c b/ossl_x509store.c new file mode 100644 index 0000000..00ee0c8 --- /dev/null +++ b/ossl_x509store.c @@ -0,0 +1,529 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" +#include <rubysig.h> + +#define MakeX509Store(obj, storep) obj = Data_Make_Struct(cX509Store, ossl_x509store, 0, ossl_x509store_free, storep) +#define GetX509Store_unsafe(obj, storep) Data_Get_Struct(obj, ossl_x509store, storep) +#define GetX509Store(obj, storep) do {\ + GetX509Store_unsafe(obj, storep);\ + if (!storep->store) rb_raise(eX509StoreError, "not initialized!");\ +} while (0) + +/* + * Classes + */ +VALUE cX509Store; +VALUE eX509StoreError; + +/* + * General callback for OpenSSL verify + */ +int ossl_x509store_verify_cb(int, X509_STORE_CTX *); + +/* + * Struct + */ +typedef struct ossl_x509store_st { + char protect; + X509_STORE_CTX *store; +} ossl_x509store; + +static void +ossl_x509store_free(ossl_x509store *storep) +{ + if (storep) { + if (storep->store && storep->protect == 0) + X509_STORE_CTX_free(storep->store); + + storep->store = NULL; + free(storep); + } +} + +/* + * Public functions + */ +VALUE +ossl_x509store_new(X509_STORE_CTX *ctx) +{ + ossl_x509store *storep = NULL; + VALUE obj; + + MakeX509Store(obj, storep); + + /* + * Is there any way to _dup X509_STORE_CTX? + */ + /* + if (!(ctx2 = X509_STORE_CTX_new())) { + OSSL_Raise(eX509StoreError, ""); + } + X509_STORE_CTX_init(ctx2, X509_STORE_dup(ctx->ctx), X509_dup(ctx->cert), NULL); + */ + storep->store = ctx; + storep->protect = 1; /* we're using pointer without DUP - don't free this one */ + + return obj; +} + +X509_STORE * +ossl_x509store_get_X509_STORE(VALUE obj) +{ + ossl_x509store *storep = NULL; + + OSSL_Check_Type(obj, cX509Store); + GetX509Store(obj, storep); + + storep->protect = 1; /* we gave out internal pointer without DUP - don't free this one */ + return storep->store->ctx; +} + +/* + * verify_cb DATABASE for Stores + * TODO: + * clean entries when garbage collecting + */ +typedef struct ossl_session_db_st { + void *key; + VALUE data; + struct ossl_session_db_st *next; +} ossl_session_db; + +ossl_session_db *db_root; + +static VALUE +ossl_session_db_get(void *key) +{ + ossl_session_db *item = db_root; + + rb_thread_critical = 1; + while (item) { + if (item->key == key) { + rb_thread_critical = 0; + return item->data; + } + item = item->next; + } + rb_thread_critical = 0; + return Qnil; +} + +static VALUE +ossl_session_db_set(void *key, VALUE data) +{ + ossl_session_db *item = db_root, *last = NULL; + + rb_thread_critical = 1; + while (item) { + if (item->key == key) { + item->data = data; + rb_thread_critical = 0; + return data; + } + last = item; + item = last->next; + } + if (!(item = (ossl_session_db *)OPENSSL_malloc(sizeof(ossl_session_db)))) { + rb_thread_critical = 0; + OSSL_Raise(eX509StoreError, ""); + } + item->key = key; + item->data = data; + item->next = NULL; + if (last) + last->next = item; + else + db_root = item; + rb_thread_critical = 0; + + return data; +} + +/* + * Private functions + */ +static VALUE +ossl_x509store_s_new(int argc, VALUE *argv, VALUE klass) +{ + ossl_x509store *storep = NULL; + VALUE obj; + + MakeX509Store(obj, storep); + + rb_obj_call_init(obj, argc, argv); + + return obj; +} + +static VALUE +ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) +{ + ossl_x509store *storep = NULL; + X509_STORE *store = NULL; + + GetX509Store_unsafe(self, storep); + + if (!(store = X509_STORE_new())) { + OSSL_Raise(eX509StoreError, ""); + } + if (!(storep->store = X509_STORE_CTX_new())) { + OSSL_Raise(eX509StoreError, ""); + } + X509_STORE_set_verify_cb_func(store, ossl_x509store_verify_cb); + /* OpenSSL 0.9.6c + * X509_STORE_CTX_set_verify_cb(ctx, func); + */ + X509_STORE_CTX_init(storep->store, store, NULL, NULL); + + /* + * instance variable + */ + rb_ivar_set(self, rb_intern("@verify_callback"), Qnil); + + return self; +} + +static VALUE +ossl_x509store_add_trusted(VALUE self, VALUE cert) +{ + ossl_x509store *storep = NULL; + X509 *x509 = NULL; + + GetX509Store(self, storep); + + OSSL_Check_Type(cert, cX509Certificate); + x509 = ossl_x509_get_X509(cert); + + if (!X509_STORE_add_cert(storep->store->ctx, x509)) { + X509_free(x509); + OSSL_Raise(eX509StoreError, ""); + } + X509_free(x509); + + return cert; +} + +static VALUE +ossl_x509store_get_chain(VALUE self) +{ + ossl_x509store *storep = NULL; + X509 *x509 = NULL; + int i, num; + VALUE ary; + + GetX509Store(self, storep); + + num = sk_X509_num(storep->store->chain); + + if (num < 0) { + rb_warning("certs in chain < 0???"); + return rb_ary_new(); + } + + ary = rb_ary_new2(num); + + for(i=0; i<num; i++) { + x509 = sk_X509_value(storep->store->chain, i); + rb_ary_push(ary, ossl_x509_new(x509)); + X509_free(x509); + } + + return ary; +} + +static VALUE +ossl_x509store_add_crl(VALUE self, VALUE crlst) +{ + ossl_x509store *storep = NULL; + X509_CRL *crl = NULL; + + GetX509Store(self, storep); + + OSSL_Check_Type(crlst, cX509CRL); + + crl = ossl_x509crl_get_X509_CRL(crlst); + + if (!X509_STORE_add_crl(storep->store->ctx, crl)) { + X509_CRL_free(crl); + OSSL_Raise(eX509StoreError, ""); + } + X509_CRL_free(crl); + + return crlst; +} + +static VALUE +ossl_x509store_call_verify_cb_proc(VALUE args) +{ + VALUE proc, ok, store_ctx; + + proc = rb_ary_entry(args, 0); + ok = rb_ary_entry(args, 1); + store_ctx = rb_ary_entry(args, 2); + + return rb_funcall(proc, rb_intern("call"), 2, ok, store_ctx); +} + +/* + * rescue! + */ +static VALUE +ossl_x509store_verify_false(VALUE dummy) +{ + return Qfalse; +} + +int +ossl_x509store_verify_cb(int ok, X509_STORE_CTX *ctx) +{ + VALUE proc, store_ctx, args, ret = Qnil; + + /* + * Get Proc from verify_cb Database + */ + proc = ossl_session_db_get((void *)ctx->ctx); + + if (!NIL_P(proc)) { + store_ctx = ossl_x509store_new(ctx); + /* rb_funcall(store_ctx, rb_intern("protect"), 0, NULL); -- called default by ossl_..new */ + args = rb_ary_new2(3); + rb_ary_store(args, 0, proc); + rb_ary_store(args, 1, ok ? Qtrue : Qfalse); + rb_ary_store(args, 2, store_ctx); + ret = rb_rescue(ossl_x509store_call_verify_cb_proc, args, ossl_x509store_verify_false, Qnil); + + if (ret == Qtrue) { + ok = 1; + X509_STORE_CTX_set_error(ctx, X509_V_OK); + } else { + ok = 0; + if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); + } + } + + return ok; +} + +static VALUE +ossl_x509store_verify(VALUE self, VALUE cert) +{ + ossl_x509store *storep = NULL; + X509 *x509 = NULL; + int result = 0; + + GetX509Store(self, storep); + + OSSL_Check_Type(cert, cX509Certificate); + x509 = ossl_x509_get_X509(cert); + X509_STORE_CTX_set_cert(storep->store, x509); + + result = X509_verify_cert(storep->store); + /*X509_STORE_CTX_cleanup(storep->store); *clears chain*/ + + if (result == 1) return Qtrue; + return Qfalse; +} + +static VALUE +ossl_x509store_get_verify_status(VALUE self) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + return INT2FIX(X509_STORE_CTX_get_error(storep->store)); +} + +static VALUE +ossl_x509store_set_verify_status(VALUE self, VALUE err) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + X509_STORE_CTX_set_error(storep->store, FIX2INT(err)); + + return err; +} + +static VALUE +ossl_x509store_get_verify_message(VALUE self) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + return rb_str_new2(X509_verify_cert_error_string(storep->store->error)); +} + +static VALUE +ossl_x509store_get_verify_depth(VALUE self) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + return INT2FIX(X509_STORE_CTX_get_error_depth(storep->store)); +} + +static VALUE +ossl_x509store_get_cert(VALUE self) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + return ossl_x509_new(X509_STORE_CTX_get_current_cert(storep->store)); +} + +static VALUE +ossl_x509store_protect(VALUE self) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + storep->protect = 1; + + return self; +} + +static VALUE +ossl_x509store_set_default_paths(VALUE self) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + if (!X509_STORE_set_default_paths(storep->store->ctx)) { + OSSL_Raise(eX509StoreError, ""); + } + + return self; +} + +static VALUE +ossl_x509store_load_locations(VALUE self, VALUE path) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + Check_SafeStr(path); + + if (!X509_STORE_load_locations(storep->store->ctx, NULL, RSTRING(path)->ptr)) { + OSSL_Raise(eX509StoreError, ""); + } + + return self; +} + +static VALUE +ossl_x509store_set_verify_cb(VALUE self, VALUE proc) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + /* + * Associate verify_cb with Store in DB + */ + ossl_session_db_set((void *)storep->store->ctx, proc); + rb_ivar_set(self, rb_intern("@verify_callback"), proc); + + return proc; +} + +static VALUE +ossl_x509store_cleanup(VALUE self) +{ + ossl_x509store *storep = NULL; + + GetX509Store(self, storep); + + X509_STORE_CTX_cleanup(storep->store); + + return self; +} + +/* + * INIT + */ +void +Init_ossl_x509store(VALUE module) +{ + /* + * INIT verify_cb DB + */ + db_root = NULL; + + eX509StoreError = rb_define_class_under(module, "StoreError", eOSSLError); + + cX509Store = rb_define_class_under(module, "Store", rb_cObject); + rb_define_singleton_method(cX509Store, "new", ossl_x509store_s_new, -1); + rb_define_method(cX509Store, "initialize", ossl_x509store_initialize, -1); + + rb_attr(cX509Store, rb_intern("verify_callback"), 1, 0, Qfalse); + rb_define_method(cX509Store, "verify_callback=", ossl_x509store_set_verify_cb, 1); + + rb_define_method(cX509Store, "add_trusted", ossl_x509store_add_trusted, 1); + rb_define_method(cX509Store, "add_crl", ossl_x509store_add_crl, 1); + + rb_define_method(cX509Store, "verify", ossl_x509store_verify, 1); + rb_define_method(cX509Store, "verify_status", ossl_x509store_get_verify_status, 0); + rb_define_method(cX509Store, "verify_status=", ossl_x509store_set_verify_status, 1); + rb_define_method(cX509Store, "verify_message", ossl_x509store_get_verify_message, 0); + rb_define_method(cX509Store, "verify_depth", ossl_x509store_get_verify_depth, 0); + rb_define_method(cX509Store, "chain", ossl_x509store_get_chain, 0); + rb_define_method(cX509Store, "cert", ossl_x509store_get_cert, 0); + rb_define_method(cX509Store, "protect", ossl_x509store_protect, 0); + rb_define_method(cX509Store, "set_default_paths", ossl_x509store_set_default_paths, 0); + rb_define_method(cX509Store, "load_locations", ossl_x509store_load_locations, 1); + + rb_define_method(cX509Store, "cleanup!", ossl_x509store_cleanup, 0); + +#define DefX509StoreConst(x) rb_define_const(cX509Store, #x, INT2FIX(X509_V_ERR_##x)) + + DefX509StoreConst(UNABLE_TO_GET_ISSUER_CERT); + DefX509StoreConst(UNABLE_TO_GET_CRL); + DefX509StoreConst(UNABLE_TO_DECRYPT_CERT_SIGNATURE); + DefX509StoreConst(UNABLE_TO_DECRYPT_CRL_SIGNATURE); + DefX509StoreConst(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); + DefX509StoreConst(CERT_SIGNATURE_FAILURE); + DefX509StoreConst(CRL_SIGNATURE_FAILURE); + DefX509StoreConst(CERT_NOT_YET_VALID); + DefX509StoreConst(CERT_HAS_EXPIRED); + DefX509StoreConst(CRL_NOT_YET_VALID); + DefX509StoreConst(CRL_HAS_EXPIRED); + DefX509StoreConst(ERROR_IN_CERT_NOT_BEFORE_FIELD); + DefX509StoreConst(ERROR_IN_CERT_NOT_AFTER_FIELD); + DefX509StoreConst(ERROR_IN_CRL_LAST_UPDATE_FIELD); + DefX509StoreConst(ERROR_IN_CRL_NEXT_UPDATE_FIELD); + DefX509StoreConst(OUT_OF_MEM); + DefX509StoreConst(DEPTH_ZERO_SELF_SIGNED_CERT); + DefX509StoreConst(SELF_SIGNED_CERT_IN_CHAIN); + DefX509StoreConst(UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + DefX509StoreConst(UNABLE_TO_VERIFY_LEAF_SIGNATURE); + DefX509StoreConst(CERT_CHAIN_TOO_LONG); + DefX509StoreConst(CERT_REVOKED); + DefX509StoreConst(INVALID_CA); + DefX509StoreConst(PATH_LENGTH_EXCEEDED); + DefX509StoreConst(INVALID_PURPOSE); + DefX509StoreConst(CERT_UNTRUSTED); + DefX509StoreConst(CERT_REJECTED); + DefX509StoreConst(SUBJECT_ISSUER_MISMATCH); + DefX509StoreConst(AKID_SKID_MISMATCH); + DefX509StoreConst(AKID_ISSUER_SERIAL_MISMATCH); + DefX509StoreConst(KEYUSAGE_NO_CERTSIGN); + DefX509StoreConst(APPLICATION_VERIFICATION); +} + |