├── .editorconfig ├── .github └── workflows │ └── test.yml ├── .gitignore ├── AUTHORS ├── COPYING ├── COPYING.LESSER ├── Gemfile ├── Manifest.txt ├── NEWS ├── README.rdoc ├── Rakefile ├── THANKS ├── examples ├── edit.rb ├── genkey.rb ├── keylist.rb ├── roundtrip.rb ├── sign.rb └── verify.rb ├── ext └── gpgme │ ├── extconf.rb │ └── gpgme_n.c ├── gpgme.gemspec ├── lib ├── gpgme.rb └── gpgme │ ├── compat.rb │ ├── constants.rb │ ├── crypto.rb │ ├── ctx.rb │ ├── data.rb │ ├── engine.rb │ ├── error.rb │ ├── io_callbacks.rb │ ├── key.rb │ ├── key_common.rb │ ├── key_sig.rb │ ├── misc.rb │ ├── signature.rb │ ├── sub_key.rb │ ├── user_id.rb │ └── version.rb ├── ports └── archives │ ├── gpgme-1.21.0.tar.bz2 │ ├── libassuan-2.5.6.tar.bz2 │ └── libgpg-error-1.47.tar.bz2 └── test ├── crypto_test.rb ├── ctx_test.rb ├── data_test.rb ├── files ├── testkey_pub.gpg ├── testkey_pub_invalid.gpg └── testkey_sec.gpg ├── gpgme_test.rb ├── key_test.rb ├── pinentry ├── signature_test.rb ├── sub_key_test.rb ├── support └── resources.rb └── test_helper.rb /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # 2 space indentation 4 | [*.{rb,c}] 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Matrix Testing 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | ruby: ['3.3', '3.2', '3.1', '3.0', '2.7'] 17 | use_system_libraries: [false, true] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up Ruby ${{ matrix.ruby-version }} 22 | uses: ruby/setup-ruby@v1 23 | with: 24 | ruby-version: ${{ matrix.ruby }} 25 | - name: Install libgpgme-dev 26 | if: ${{ matrix.use_system_libraries }} 27 | run: | 28 | sudo apt install -y libgpgme-dev 29 | echo "RUBY_GPGME_USE_SYSTEM_LIBRARIES=1" >> $GITHUB_ENV 30 | - name: Install dependencies 31 | run: bundle install 32 | - name: Run tests 33 | run: | 34 | gpg --version 35 | gpg2 --version 36 | gpgconf --list-options gpg-agent 37 | bundle exec rake TESTOPTS="-v" 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc 2 | gpgme_n.* 3 | mkmf.log 4 | Makefile 5 | coverage 6 | Gemfile.lock 7 | /tmp/* 8 | /ports/* 9 | .byebug_history 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Daiki Ueno (wrote version 1.0) 2 | Albert Llop (major rework of version 2.0) 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 19yy 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) 19yy name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /COPYING.LESSER: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | [This is the first released version of the Lesser GPL. It also counts 11 | as the successor of the GNU Library Public License, version 2, hence 12 | the version number 2.1.] 13 | 14 | Preamble 15 | 16 | The licenses for most software are designed to take away your 17 | freedom to share and change it. By contrast, the GNU General Public 18 | Licenses are intended to guarantee your freedom to share and change 19 | free software--to make sure the software is free for all its users. 20 | 21 | This license, the Lesser General Public License, applies to some 22 | specially designated software packages--typically libraries--of the 23 | Free Software Foundation and other authors who decide to use it. You 24 | can use it too, but we suggest you first think carefully about whether 25 | this license or the ordinary General Public License is the better 26 | strategy to use in any particular case, based on the explanations 27 | below. 28 | 29 | When we speak of free software, we are referring to freedom of use, 30 | not price. Our General Public Licenses are designed to make sure that 31 | you have the freedom to distribute copies of free software (and charge 32 | for this service if you wish); that you receive source code or can get 33 | it if you want it; that you can change the software and use pieces of 34 | it in new free programs; and that you are informed that you can do 35 | these things. 36 | 37 | To protect your rights, we need to make restrictions that forbid 38 | distributors to deny you these rights or to ask you to surrender these 39 | rights. These restrictions translate to certain responsibilities for 40 | you if you distribute copies of the library or if you modify it. 41 | 42 | For example, if you distribute copies of the library, whether gratis 43 | or for a fee, you must give the recipients all the rights that we gave 44 | you. You must make sure that they, too, receive or can get the source 45 | code. If you link other code with the library, you must provide 46 | complete object files to the recipients, so that they can relink them 47 | with the library after making changes to the library and recompiling 48 | it. And you must show them these terms so they know their rights. 49 | 50 | We protect your rights with a two-step method: (1) we copyright the 51 | library, and (2) we offer you this license, which gives you legal 52 | permission to copy, distribute and/or modify the library. 53 | 54 | To protect each distributor, we want to make it very clear that 55 | there is no warranty for the free library. Also, if the library is 56 | modified by someone else and passed on, the recipients should know 57 | that what they have is not the original version, so that the original 58 | author's reputation will not be affected by problems that might be 59 | introduced by others. 60 | ^L 61 | Finally, software patents pose a constant threat to the existence of 62 | any free program. We wish to make sure that a company cannot 63 | effectively restrict the users of a free program by obtaining a 64 | restrictive license from a patent holder. Therefore, we insist that 65 | any patent license obtained for a version of the library must be 66 | consistent with the full freedom of use specified in this license. 67 | 68 | Most GNU software, including some libraries, is covered by the 69 | ordinary GNU General Public License. This license, the GNU Lesser 70 | General Public License, applies to certain designated libraries, and 71 | is quite different from the ordinary General Public License. We use 72 | this license for certain libraries in order to permit linking those 73 | libraries into non-free programs. 74 | 75 | When a program is linked with a library, whether statically or using 76 | a shared library, the combination of the two is legally speaking a 77 | combined work, a derivative of the original library. The ordinary 78 | General Public License therefore permits such linking only if the 79 | entire combination fits its criteria of freedom. The Lesser General 80 | Public License permits more lax criteria for linking other code with 81 | the library. 82 | 83 | We call this license the "Lesser" General Public License because it 84 | does Less to protect the user's freedom than the ordinary General 85 | Public License. It also provides other free software developers Less 86 | of an advantage over competing non-free programs. These disadvantages 87 | are the reason we use the ordinary General Public License for many 88 | libraries. However, the Lesser license provides advantages in certain 89 | special circumstances. 90 | 91 | For example, on rare occasions, there may be a special need to 92 | encourage the widest possible use of a certain library, so that it 93 | becomes a de-facto standard. To achieve this, non-free programs must 94 | be allowed to use the library. A more frequent case is that a free 95 | library does the same job as widely used non-free libraries. In this 96 | case, there is little to gain by limiting the free library to free 97 | software only, so we use the Lesser General Public License. 98 | 99 | In other cases, permission to use a particular library in non-free 100 | programs enables a greater number of people to use a large body of 101 | free software. For example, permission to use the GNU C Library in 102 | non-free programs enables many more people to use the whole GNU 103 | operating system, as well as its variant, the GNU/Linux operating 104 | system. 105 | 106 | Although the Lesser General Public License is Less protective of the 107 | users' freedom, it does ensure that the user of a program that is 108 | linked with the Library has the freedom and the wherewithal to run 109 | that program using a modified version of the Library. 110 | 111 | The precise terms and conditions for copying, distribution and 112 | modification follow. Pay close attention to the difference between a 113 | "work based on the library" and a "work that uses the library". The 114 | former contains code derived from the library, whereas the latter must 115 | be combined with the library in order to run. 116 | ^L 117 | GNU LESSER GENERAL PUBLIC LICENSE 118 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 119 | 120 | 0. This License Agreement applies to any software library or other 121 | program which contains a notice placed by the copyright holder or 122 | other authorized party saying it may be distributed under the terms of 123 | this Lesser General Public License (also called "this License"). 124 | Each licensee is addressed as "you". 125 | 126 | A "library" means a collection of software functions and/or data 127 | prepared so as to be conveniently linked with application programs 128 | (which use some of those functions and data) to form executables. 129 | 130 | The "Library", below, refers to any such software library or work 131 | which has been distributed under these terms. A "work based on the 132 | Library" means either the Library or any derivative work under 133 | copyright law: that is to say, a work containing the Library or a 134 | portion of it, either verbatim or with modifications and/or translated 135 | straightforwardly into another language. (Hereinafter, translation is 136 | included without limitation in the term "modification".) 137 | 138 | "Source code" for a work means the preferred form of the work for 139 | making modifications to it. For a library, complete source code means 140 | all the source code for all modules it contains, plus any associated 141 | interface definition files, plus the scripts used to control 142 | compilation and installation of the library. 143 | 144 | Activities other than copying, distribution and modification are not 145 | covered by this License; they are outside its scope. The act of 146 | running a program using the Library is not restricted, and output from 147 | such a program is covered only if its contents constitute a work based 148 | on the Library (independent of the use of the Library in a tool for 149 | writing it). Whether that is true depends on what the Library does 150 | and what the program that uses the Library does. 151 | 152 | 1. You may copy and distribute verbatim copies of the Library's 153 | complete source code as you receive it, in any medium, provided that 154 | you conspicuously and appropriately publish on each copy an 155 | appropriate copyright notice and disclaimer of warranty; keep intact 156 | all the notices that refer to this License and to the absence of any 157 | warranty; and distribute a copy of this License along with the 158 | Library. 159 | 160 | You may charge a fee for the physical act of transferring a copy, 161 | and you may at your option offer warranty protection in exchange for a 162 | fee. 163 | 164 | 2. You may modify your copy or copies of the Library or any portion 165 | of it, thus forming a work based on the Library, and copy and 166 | distribute such modifications or work under the terms of Section 1 167 | above, provided that you also meet all of these conditions: 168 | 169 | a) The modified work must itself be a software library. 170 | 171 | b) You must cause the files modified to carry prominent notices 172 | stating that you changed the files and the date of any change. 173 | 174 | c) You must cause the whole of the work to be licensed at no 175 | charge to all third parties under the terms of this License. 176 | 177 | d) If a facility in the modified Library refers to a function or a 178 | table of data to be supplied by an application program that uses 179 | the facility, other than as an argument passed when the facility 180 | is invoked, then you must make a good faith effort to ensure that, 181 | in the event an application does not supply such function or 182 | table, the facility still operates, and performs whatever part of 183 | its purpose remains meaningful. 184 | 185 | (For example, a function in a library to compute square roots has 186 | a purpose that is entirely well-defined independent of the 187 | application. Therefore, Subsection 2d requires that any 188 | application-supplied function or table used by this function must 189 | be optional: if the application does not supply it, the square 190 | root function must still compute square roots.) 191 | 192 | These requirements apply to the modified work as a whole. If 193 | identifiable sections of that work are not derived from the Library, 194 | and can be reasonably considered independent and separate works in 195 | themselves, then this License, and its terms, do not apply to those 196 | sections when you distribute them as separate works. But when you 197 | distribute the same sections as part of a whole which is a work based 198 | on the Library, the distribution of the whole must be on the terms of 199 | this License, whose permissions for other licensees extend to the 200 | entire whole, and thus to each and every part regardless of who wrote 201 | it. 202 | 203 | Thus, it is not the intent of this section to claim rights or contest 204 | your rights to work written entirely by you; rather, the intent is to 205 | exercise the right to control the distribution of derivative or 206 | collective works based on the Library. 207 | 208 | In addition, mere aggregation of another work not based on the Library 209 | with the Library (or with a work based on the Library) on a volume of 210 | a storage or distribution medium does not bring the other work under 211 | the scope of this License. 212 | 213 | 3. You may opt to apply the terms of the ordinary GNU General Public 214 | License instead of this License to a given copy of the Library. To do 215 | this, you must alter all the notices that refer to this License, so 216 | that they refer to the ordinary GNU General Public License, version 2, 217 | instead of to this License. (If a newer version than version 2 of the 218 | ordinary GNU General Public License has appeared, then you can specify 219 | that version instead if you wish.) Do not make any other change in 220 | these notices. 221 | ^L 222 | Once this change is made in a given copy, it is irreversible for 223 | that copy, so the ordinary GNU General Public License applies to all 224 | subsequent copies and derivative works made from that copy. 225 | 226 | This option is useful when you wish to copy part of the code of 227 | the Library into a program that is not a library. 228 | 229 | 4. You may copy and distribute the Library (or a portion or 230 | derivative of it, under Section 2) in object code or executable form 231 | under the terms of Sections 1 and 2 above provided that you accompany 232 | it with the complete corresponding machine-readable source code, which 233 | must be distributed under the terms of Sections 1 and 2 above on a 234 | medium customarily used for software interchange. 235 | 236 | If distribution of object code is made by offering access to copy 237 | from a designated place, then offering equivalent access to copy the 238 | source code from the same place satisfies the requirement to 239 | distribute the source code, even though third parties are not 240 | compelled to copy the source along with the object code. 241 | 242 | 5. A program that contains no derivative of any portion of the 243 | Library, but is designed to work with the Library by being compiled or 244 | linked with it, is called a "work that uses the Library". Such a 245 | work, in isolation, is not a derivative work of the Library, and 246 | therefore falls outside the scope of this License. 247 | 248 | However, linking a "work that uses the Library" with the Library 249 | creates an executable that is a derivative of the Library (because it 250 | contains portions of the Library), rather than a "work that uses the 251 | library". The executable is therefore covered by this License. 252 | Section 6 states terms for distribution of such executables. 253 | 254 | When a "work that uses the Library" uses material from a header file 255 | that is part of the Library, the object code for the work may be a 256 | derivative work of the Library even though the source code is not. 257 | Whether this is true is especially significant if the work can be 258 | linked without the Library, or if the work is itself a library. The 259 | threshold for this to be true is not precisely defined by law. 260 | 261 | If such an object file uses only numerical parameters, data 262 | structure layouts and accessors, and small macros and small inline 263 | functions (ten lines or less in length), then the use of the object 264 | file is unrestricted, regardless of whether it is legally a derivative 265 | work. (Executables containing this object code plus portions of the 266 | Library will still fall under Section 6.) 267 | 268 | Otherwise, if the work is a derivative of the Library, you may 269 | distribute the object code for the work under the terms of Section 6. 270 | Any executables containing that work also fall under Section 6, 271 | whether or not they are linked directly with the Library itself. 272 | ^L 273 | 6. As an exception to the Sections above, you may also combine or 274 | link a "work that uses the Library" with the Library to produce a 275 | work containing portions of the Library, and distribute that work 276 | under terms of your choice, provided that the terms permit 277 | modification of the work for the customer's own use and reverse 278 | engineering for debugging such modifications. 279 | 280 | You must give prominent notice with each copy of the work that the 281 | Library is used in it and that the Library and its use are covered by 282 | this License. You must supply a copy of this License. If the work 283 | during execution displays copyright notices, you must include the 284 | copyright notice for the Library among them, as well as a reference 285 | directing the user to the copy of this License. Also, you must do one 286 | of these things: 287 | 288 | a) Accompany the work with the complete corresponding 289 | machine-readable source code for the Library including whatever 290 | changes were used in the work (which must be distributed under 291 | Sections 1 and 2 above); and, if the work is an executable linked 292 | with the Library, with the complete machine-readable "work that 293 | uses the Library", as object code and/or source code, so that the 294 | user can modify the Library and then relink to produce a modified 295 | executable containing the modified Library. (It is understood 296 | that the user who changes the contents of definitions files in the 297 | Library will not necessarily be able to recompile the application 298 | to use the modified definitions.) 299 | 300 | b) Use a suitable shared library mechanism for linking with the 301 | Library. A suitable mechanism is one that (1) uses at run time a 302 | copy of the library already present on the user's computer system, 303 | rather than copying library functions into the executable, and (2) 304 | will operate properly with a modified version of the library, if 305 | the user installs one, as long as the modified version is 306 | interface-compatible with the version that the work was made with. 307 | 308 | c) Accompany the work with a written offer, valid for at least 309 | three years, to give the same user the materials specified in 310 | Subsection 6a, above, for a charge no more than the cost of 311 | performing this distribution. 312 | 313 | d) If distribution of the work is made by offering access to copy 314 | from a designated place, offer equivalent access to copy the above 315 | specified materials from the same place. 316 | 317 | e) Verify that the user has already received a copy of these 318 | materials or that you have already sent this user a copy. 319 | 320 | For an executable, the required form of the "work that uses the 321 | Library" must include any data and utility programs needed for 322 | reproducing the executable from it. However, as a special exception, 323 | the materials to be distributed need not include anything that is 324 | normally distributed (in either source or binary form) with the major 325 | components (compiler, kernel, and so on) of the operating system on 326 | which the executable runs, unless that component itself accompanies 327 | the executable. 328 | 329 | It may happen that this requirement contradicts the license 330 | restrictions of other proprietary libraries that do not normally 331 | accompany the operating system. Such a contradiction means you cannot 332 | use both them and the Library together in an executable that you 333 | distribute. 334 | ^L 335 | 7. You may place library facilities that are a work based on the 336 | Library side-by-side in a single library together with other library 337 | facilities not covered by this License, and distribute such a combined 338 | library, provided that the separate distribution of the work based on 339 | the Library and of the other library facilities is otherwise 340 | permitted, and provided that you do these two things: 341 | 342 | a) Accompany the combined library with a copy of the same work 343 | based on the Library, uncombined with any other library 344 | facilities. This must be distributed under the terms of the 345 | Sections above. 346 | 347 | b) Give prominent notice with the combined library of the fact 348 | that part of it is a work based on the Library, and explaining 349 | where to find the accompanying uncombined form of the same work. 350 | 351 | 8. You may not copy, modify, sublicense, link with, or distribute 352 | the Library except as expressly provided under this License. Any 353 | attempt otherwise to copy, modify, sublicense, link with, or 354 | distribute the Library is void, and will automatically terminate your 355 | rights under this License. However, parties who have received copies, 356 | or rights, from you under this License will not have their licenses 357 | terminated so long as such parties remain in full compliance. 358 | 359 | 9. You are not required to accept this License, since you have not 360 | signed it. However, nothing else grants you permission to modify or 361 | distribute the Library or its derivative works. These actions are 362 | prohibited by law if you do not accept this License. Therefore, by 363 | modifying or distributing the Library (or any work based on the 364 | Library), you indicate your acceptance of this License to do so, and 365 | all its terms and conditions for copying, distributing or modifying 366 | the Library or works based on it. 367 | 368 | 10. Each time you redistribute the Library (or any work based on the 369 | Library), the recipient automatically receives a license from the 370 | original licensor to copy, distribute, link with or modify the Library 371 | subject to these terms and conditions. You may not impose any further 372 | restrictions on the recipients' exercise of the rights granted herein. 373 | You are not responsible for enforcing compliance by third parties with 374 | this License. 375 | ^L 376 | 11. If, as a consequence of a court judgment or allegation of patent 377 | infringement or for any other reason (not limited to patent issues), 378 | conditions are imposed on you (whether by court order, agreement or 379 | otherwise) that contradict the conditions of this License, they do not 380 | excuse you from the conditions of this License. If you cannot 381 | distribute so as to satisfy simultaneously your obligations under this 382 | License and any other pertinent obligations, then as a consequence you 383 | may not distribute the Library at all. For example, if a patent 384 | license would not permit royalty-free redistribution of the Library by 385 | all those who receive copies directly or indirectly through you, then 386 | the only way you could satisfy both it and this License would be to 387 | refrain entirely from distribution of the Library. 388 | 389 | If any portion of this section is held invalid or unenforceable under 390 | any particular circumstance, the balance of the section is intended to 391 | apply, and the section as a whole is intended to apply in other 392 | circumstances. 393 | 394 | It is not the purpose of this section to induce you to infringe any 395 | patents or other property right claims or to contest validity of any 396 | such claims; this section has the sole purpose of protecting the 397 | integrity of the free software distribution system which is 398 | implemented by public license practices. Many people have made 399 | generous contributions to the wide range of software distributed 400 | through that system in reliance on consistent application of that 401 | system; it is up to the author/donor to decide if he or she is willing 402 | to distribute software through any other system and a licensee cannot 403 | impose that choice. 404 | 405 | This section is intended to make thoroughly clear what is believed to 406 | be a consequence of the rest of this License. 407 | 408 | 12. If the distribution and/or use of the Library is restricted in 409 | certain countries either by patents or by copyrighted interfaces, the 410 | original copyright holder who places the Library under this License 411 | may add an explicit geographical distribution limitation excluding those 412 | countries, so that distribution is permitted only in or among 413 | countries not thus excluded. In such case, this License incorporates 414 | the limitation as if written in the body of this License. 415 | 416 | 13. The Free Software Foundation may publish revised and/or new 417 | versions of the Lesser General Public License from time to time. 418 | Such new versions will be similar in spirit to the present version, 419 | but may differ in detail to address new problems or concerns. 420 | 421 | Each version is given a distinguishing version number. If the Library 422 | specifies a version number of this License which applies to it and 423 | "any later version", you have the option of following the terms and 424 | conditions either of that version or of any later version published by 425 | the Free Software Foundation. If the Library does not specify a 426 | license version number, you may choose any version ever published by 427 | the Free Software Foundation. 428 | ^L 429 | 14. If you wish to incorporate parts of the Library into other free 430 | programs whose distribution conditions are incompatible with these, 431 | write to the author to ask for permission. For software which is 432 | copyrighted by the Free Software Foundation, write to the Free 433 | Software Foundation; we sometimes make exceptions for this. Our 434 | decision will be guided by the two goals of preserving the free status 435 | of all derivatives of our free software and of promoting the sharing 436 | and reuse of software generally. 437 | 438 | NO WARRANTY 439 | 440 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 441 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 442 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 443 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 444 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 445 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 446 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 447 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 448 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 449 | 450 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 451 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 452 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 453 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 454 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 455 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 456 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 457 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 458 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 459 | DAMAGES. 460 | 461 | END OF TERMS AND CONDITIONS 462 | ^L 463 | How to Apply These Terms to Your New Libraries 464 | 465 | If you develop a new library, and you want it to be of the greatest 466 | possible use to the public, we recommend making it free software that 467 | everyone can redistribute and change. You can do so by permitting 468 | redistribution under these terms (or, alternatively, under the terms 469 | of the ordinary General Public License). 470 | 471 | To apply these terms, attach the following notices to the library. 472 | It is safest to attach them to the start of each source file to most 473 | effectively convey the exclusion of warranty; and each file should 474 | have at least the "copyright" line and a pointer to where the full 475 | notice is found. 476 | 477 | 478 | 479 | Copyright (C) 480 | 481 | This library is free software; you can redistribute it and/or 482 | modify it under the terms of the GNU Lesser General Public 483 | License as published by the Free Software Foundation; either 484 | version 2.1 of the License, or (at your option) any later version. 485 | 486 | This library is distributed in the hope that it will be useful, 487 | but WITHOUT ANY WARRANTY; without even the implied warranty of 488 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 489 | Lesser General Public License for more details. 490 | 491 | You should have received a copy of the GNU Lesser General Public 492 | License along with this library; if not, write to the Free Software 493 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 494 | 495 | Also add information on how to contact you by electronic and paper mail. 496 | 497 | You should also get your employer (if you work as a programmer) or 498 | your school, if any, to sign a "copyright disclaimer" for the library, 499 | if necessary. Here is a sample; alter the names: 500 | 501 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 502 | library `Frob' (a library for tweaking knobs) written by James 503 | Random Hacker. 504 | 505 | , 1 April 1990 506 | Ty Coon, President of Vice 507 | 508 | That's all there is to it! 509 | 510 | 511 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in gpgme.gemspec 4 | gemspec 5 | 6 | gem 'rake' 7 | -------------------------------------------------------------------------------- /Manifest.txt: -------------------------------------------------------------------------------- 1 | COPYING 2 | COPYING.LESSER 3 | Makefile 4 | Manifest.txt 5 | README 6 | Rakefile 7 | THANKS 8 | examples/edit.rb 9 | examples/genkey.rb 10 | examples/keylist.rb 11 | examples/roundtrip.rb 12 | examples/sign.rb 13 | examples/verify.rb 14 | extconf.rb 15 | gpgme_n.c 16 | lib/gpgme.rb 17 | lib/gpgme/compat.rb 18 | lib/gpgme/constants.rb 19 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 2.0.24 January 31, 2024 2 | 3 | - Fix --clean option (#178, #179) 4 | - Add the ignore_mdc_error flag setter and getter (#180) 5 | - Add workaround for building gpgme with musl >= 1.2.4 (#181) 6 | - Don't add gem root to LOAD_PATH (#120) 7 | 8 | 2.0.23 August 16, 2023 9 | 10 | - Fix incompatible function pointer types with “gpgme_op_keylist_ext_start” (Clang 16) (#176) 11 | - Update dependencies (#174, ...) 12 | * libgpg-error 1.47 13 | * libassuan 2.5.6 14 | - Fix rubydoc URL (#169) 15 | - Improve checks for pkg-config and fall back to gpgme-config (#168) 16 | 17 | 2.0.22 November 25, 2022 18 | 19 | - Make mini_portile2 less restrictive (#163) 20 | - Undefine the alloc Ctx, Data and Key functions for Ruby 3.2 (#158) 21 | - Prefer pkg-config over gpgme-config (#166) 22 | 23 | 2.0.21 October 8, 2022 24 | 25 | - Update dependencies (#154) 26 | * libgpg-error 1.46 27 | * libassuan 2.5.5 28 | * gpgme to 1.18.0 29 | 30 | 2.0.20 February 18, 2020 31 | 32 | - Update dependencies (Issue#134) 33 | * libgpg-error 1.37 34 | * libassuan 2.5.3 35 | * gpgme to 1.13.1 36 | - Set `IO#autoclose = false` on the pass_function `IO` object to avoid GC race conditions 37 | * Vicent Martí 38 | 39 | 2.0.19 October 3, 2019 40 | 41 | - Significant test/CI improvements 42 | * Sebastian Skałacki 43 | * Daiki Ueno 44 | - New features 45 | * Enable minimal key exports. (#132) 46 | * Added GPGME::Key.valid? method (#133) 47 | 48 | 49 | 2.0.18 November 22, 2018 50 | 51 | - Broken build fix 52 | 53 | 2.0.17 November 22, 2018 54 | 55 | - Dependency updates to help with Docker/gpgme 1.9 poor performance 56 | * gpgme 1.12.0 57 | * assuan 2.5.1 58 | * error 1.32 59 | 60 | 2.0.16 January 3, 2018 61 | 62 | - API change 63 | * Expose gpgme_data_set_file_name (Issue#101) 64 | 65 | 2.0.15 December 19, 2017 66 | 67 | - Bug fixes 68 | * Nokogiri 1.8.1 compatibility update (Issue#106) 69 | 70 | 2.0.14 October 30, 2017 71 | 72 | - Bug fixes 73 | * Fix build issues on Ubuntu by only compiling needed APIs (Issue#93) 74 | 75 | 2.0.13 July 2, 2017 76 | 77 | - API change 78 | * Expose gpgme_ctx_get_engine_info and gpgme_ctx_set_engine_info (Issue#83) 79 | 80 | - Bug fixes 81 | * Update included libraries (GPGME 1.9.0, libgpg-error 1.27) 82 | * Use HTTPS to retrieve tarballs and check the checksums (Issue#91) 83 | * Relax dependency on mini_portile2 (Issue#88) 84 | * Fix constructing capabilities value for GPGME::Key (Issue#76) 85 | 86 | 2.0.12 January 19, 2016 87 | 88 | - Bug fixes 89 | * Switch to mini_portile2 (Issue#74) 90 | 91 | 2.0.11 December 15, 2015 92 | 93 | - Bug fixes 94 | * Fix building with mini_portile > 0.7.0rc* (Issue#67) 95 | * Update included libraries (libassuan 2.4.2, libgpg-error 1.21) 96 | 97 | 2.0.10 August 27, 2015 98 | 99 | - Bug fixes 100 | * Update included libraries (GPGME 1.6.5, libassuan 2.2.1, libgpg-error 1.20) 101 | 102 | 2.0.9 July 24, 2015 103 | 104 | - Bug fixes 105 | * Update included libraries (GPGME 1.5.5, libassuan 2.2.1, libgpg-error 1.19) 106 | 107 | 2.0.8 December 31, 2014 108 | 109 | - API change 110 | * New convenience methods: Key#expires? and SubKey#expires? 111 | - Bug fixes 112 | * Update included libraries (GPGME 1.5.3, libassuan 2.2.0, libgpg-error 1.17) 113 | * Signal error if gpgme_op_*_result is called with an invalid state (Issue#57) 114 | * Switch to byebug instead of debugger, when building with ruby 2.x 115 | * Add NULL check of gpgme_op_decrypt_result_t->file_name (Issue#56) 116 | * Relax mini_portile dependency (Issue#52) 117 | 118 | 2.0.7 September 9, 2014 119 | 120 | - API change 121 | * Update to GPGME 1.5.1 interface 122 | - Bug fixes 123 | * Update included libraries (GPGME 1.5.1, libassuan 2.1.2) 124 | * Fix build when a static library is available in system's libdir (Issue#50) 125 | * Remove rcov support, which is incompatible with ruby 2.x 126 | 127 | 2.0.6 June 26, 2014 128 | 129 | - API change 130 | * Update to GPGME 1.5.0 interface 131 | - Bug fixes 132 | * Update included libraries (GPGME 1.5.0, libgpg-error 1.13) 133 | * Depend on newer mini_portile, if available (Issue#44) 134 | * Don't refer to missing PINENTRY_MODE_* constants (Issue#39) 135 | 136 | 2.0.5 March 14, 2014 137 | 138 | - Bug fixes 139 | * Fix build on Capistrano (Issue#37) 140 | 141 | 2.0.4 March 13, 2014 142 | 143 | - Bug fixes 144 | * Fix build with ruby 1.8 145 | 146 | 2.0.3 March 13, 2014 147 | 148 | - API change 149 | * Expose gpgme_recipient_t to ruby (Issue#36) 150 | - Build enhancement 151 | * Support system GPGME libraries (Issue#21) 152 | - Bug fixes 153 | * Better support for UTF-8 input (Issue#24, Issue#27) 154 | * Update included libraries (GPGME 1.4.3, libassuan 2.1.1) 155 | * Rework the build stuff using mini_portile 156 | * Specify license in gpgme.gemspec 157 | * Bump required debugger gem version 158 | 159 | 2.0.2 March 5, 2013 160 | 161 | - Bug fixes 162 | * Update included libraries 163 | * Fix build when --libdir is specified in config.site 164 | * Fix equality check of GPGME::Key and other objects 165 | 166 | 2.0.1 May 14, 2012 167 | 168 | - Bug fixes 169 | * Fix linking order of GPG related libraries 170 | * Skip some tests when gpg command is not available 171 | * Fix signature notation handling 172 | * Add GPGME::Data#to_s to retrieve data content without rewinding 173 | 174 | 2.0.0 October 3, 2011 175 | 176 | - Major rework mostly done by Albert Llop 177 | * Add test coverage of some type. 178 | * Make documentation a little bit more newbie friendly. 179 | * Improve the API to be more idiomatic. 180 | 181 | 1.0.9 May 21, 2010 182 | 183 | - Stop using Hoe. 184 | 185 | 1.0.8 September, 2009 186 | 187 | - Follow the ABI changes of GPGME 1.2. 188 | * Add bindings for gpgme_op_{export,import}_keys_start. 189 | * GPGME.{decrypt,verify,sign,encrypt} now calls 190 | gpgme_check_version() before creating a context. 191 | Fixes Bug#26829 reported by Sam Hall. 192 | - Use Hoe for packaging contributed by Kouhei Sutou. 193 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = GPGME 2 | 3 | This README is better viewed through the YARD formatted documentation: 4 | https://www.rubydoc.info/github/ueno/ruby-gpgme for latest github version, 5 | or https://www.rubydoc.info/gems/gpgme for latest gem release. 6 | 7 | {Build Status}[https://github.com/ueno/ruby-gpgme/actions/workflows/test.yml] 8 | {Coverage Status}[https://coveralls.io/r/ueno/ruby-gpgme] 9 | 10 | == Requirements 11 | 12 | * Ruby 1.8 or later 13 | * GPGME 1.1.2 or later 14 | * gpg-agent (optional, but recommended) 15 | 16 | == Installation 17 | 18 | $ gem install gpgme 19 | 20 | == API 21 | 22 | GPGME provides three levels of API. The highest level API is as simple as it 23 | gets, the mid level API provides more functionality but might be less 24 | user-friendly, and the lowest level API is close to the C interface of GPGME. 25 | 26 | === The highest level API 27 | 28 | For example, to create a cleartext signature of the plaintext from 29 | stdin and write the result to stdout can be written as follows. 30 | 31 | crypto = GPGME::Crypto.new 32 | crypto.clearsign $stdin, :output => $stdout 33 | 34 | === The mid level API 35 | 36 | The same example can be rewritten in the mid level API as follows. 37 | 38 | plain = GPGME::Data.new($stdin) 39 | sig = GPGME::Data.new($stdout) 40 | GPGME::Ctx.new do |ctx| 41 | ctx.sign(plain, sig, GPGME::SIG_MODE_CLEAR) 42 | end 43 | 44 | === The lowest level API 45 | 46 | The same example can be rewritten in the lowest level API as follows. 47 | 48 | ret = [] 49 | GPGME::gpgme_new(ret) 50 | ctx = ret.shift 51 | GPGME::gpgme_data_new_from_fd(ret, 0) 52 | plain = ret.shift 53 | GPGME::gpgme_data_new_from_fd(ret, 1) 54 | sig = ret.shift 55 | GPGME::gpgme_op_sign(ctx, plain, sig, GPGME::SIG_MODE_CLEAR) 56 | 57 | As you see, it's much harder to write a program in this API than the 58 | highest level API. However, if you are already familiar with the C 59 | interface of GPGME and want to control detailed behavior of GPGME, it 60 | might be useful. 61 | 62 | == Usage 63 | 64 | All the high level methods attack the mid level GPGME::Ctx API. It is 65 | recommended to read through the GPGME::Ctx.new methods for common options. 66 | 67 | Also, most of the input/output is done via GPGME::Data objects that create a 68 | common interface for reading/writing to normal strings, or other common 69 | objects like files. Read the GPGME::Data documentation to understand 70 | how it works. Every time the lib needs a GPGME::Data object, it will be 71 | automatically converted to it. 72 | 73 | === Crypto 74 | 75 | The GPGME::Crypto class has the high level convenience methods to encrypt, 76 | decrypt, sign and verify signatures. Here are some examples, but it is 77 | recommended to read through the GPGME::Crypto class to see all the options. 78 | 79 | * Document encryption via GPGME::Crypto#encrypt: 80 | crypto = GPGME::Crypto.new 81 | crypto.encrypt "Hello world!", :recipients => "someone@example.com" 82 | 83 | * Symmetric encryption: 84 | crypto = GPGME::Crypto.new :password => "gpgme" 85 | crypto.encrypt "Hello world!", :symmetric => true 86 | 87 | 88 | * Document decryption via GPGME::Crypto#decrypt (including signature verification): 89 | crypto.decrypt File.open("text.gpg") 90 | 91 | * Document signing via GPGME::Crypto#sign. Also the clearsigning and detached signing. 92 | crypto.sign "I hereby proclaim Github the beneficiary of all my money when I die" 93 | 94 | * Sign verification via GPGME::Crypto#verify 95 | sign = crypto.sign "Some text" 96 | data = crypto.verify(sign) { |signature| signature.valid? } 97 | 98 | === Key 99 | 100 | The GPGME::Key object represents a key, and has the high level related 101 | methods to work with them and find them, export, import, deletetion and 102 | creation. 103 | 104 | * Key listing 105 | GPGME::Key.find(:secret, "someone@example.com") 106 | # => Returns an array with all the secret keys available in the keychain. 107 | # that match "someone@example.com" 108 | 109 | * Key exporting 110 | GPGME::Key.export("someone@example.com") 111 | # => Returns a GPGME::Data object with the exported key. 112 | 113 | key = GPGME::Key.find(:secret, "someone@example.com").first 114 | key.export 115 | # => Returns a GPGME::Data object with the exported key. 116 | 117 | * Key importing 118 | GPGME::Key.import(File.open("my.key")) 119 | 120 | * Key validation 121 | GPGME::Key.valid?(public_key) 122 | # => Returns wheter this key is valid or not 123 | 124 | 125 | * TODO: Key generation 126 | 127 | === Engine 128 | 129 | Provides three convenience methods to obtain information about the gpg engine 130 | one is currently using. For example: 131 | 132 | * Getting current information 133 | GPGME::Engine.info.first 134 | # => # 139 | 140 | * Changing home directory to work with different settings: 141 | GPGME::Engine.home_dir = '/tmp' 142 | 143 | === Round trip example using keychain keys 144 | 145 | Rather than importing the keys it's possible to specify the recipient 146 | when performing crypto functions. Here's a roundtrip example, 147 | and note that as this is for a console, the conf.echo = false 148 | line is to stop IRB complaining when echoing binary data 149 | 150 | # Stop IRB echoing everything, which errors with binary data. 151 | # Not required for production code 152 | conf.echo = false 153 | 154 | class PassphraseCallback 155 | def initialize(passphrase) 156 | @passphrase = passphrase 157 | end 158 | 159 | def call(*args) 160 | fd = args.last 161 | io = IO.for_fd(fd, 'w') 162 | io.puts(@passphrase) 163 | io.flush 164 | end 165 | end 166 | 167 | # recipients can be found using $ gpg --list-keys --homedir ./keychain_location 168 | # pub 2048R/A1B2C3D4 2014-01-17 169 | # Use that line to substitute your own. 2048R is the key length and type (RSA in this case) 170 | 171 | # If you want to substitute a non-default keychain into the engine do this: 172 | # home_dir = Rails.root.join('keychain_location').to_s 173 | # GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, '/usr/local/bin/gpg', home_dir) 174 | # Note GPG executable location will change across platforms 175 | 176 | 177 | crypto = GPGME::Crypto.new 178 | options = {:recipients => 'A1B2C3D4'} 179 | 180 | plaintext = GPGME::Data.new(File.open(Rails.root.join('Gemfile'))) 181 | 182 | data = crypto.encrypt plaintext, options 183 | 184 | f = File.open(Rails.root.join('Gemfile.gpg'), 'wb') 185 | bytes_written = f.write(data) 186 | f.close 187 | 188 | puts bytes_written 189 | 190 | 191 | crypto = GPGME::Crypto.new 192 | options = {:recipients => 'A1B2C3D4', :passphrase_callback => PassphraseCallback.new('my_passphrase')} 193 | 194 | cipthertext = GPGME::Data.new(File.open(Rails.root.join('Gemfile.gpg'))) 195 | 196 | data = crypto.decrypt cipthertext, options 197 | puts data 198 | 199 | 200 | == Contributing 201 | 202 | To run the local test suite you need bundler and gpg: 203 | 204 | bundle 205 | rake compile # simple rake task to compile the extension 206 | rake # runs the test suite 207 | 208 | == License 209 | 210 | The library itself is licensed under LGPLv2.1+. See the file 211 | COPYING.LESSER and each file for copyright and warranty information. 212 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rake/testtask' 5 | require 'yard' 6 | 7 | 8 | desc "Re-compile the extensions" 9 | task :compile do 10 | FileUtils.rm_rf('tmp') if File.directory?('tmp') 11 | mkdir 'tmp' 12 | 13 | Dir.chdir('tmp') do 14 | system "ruby #{File.dirname(__FILE__)}/ext/gpgme/extconf.rb" 15 | system "make" 16 | end 17 | end 18 | 19 | task :default => [:test] 20 | 21 | Rake::TestTask.new(:test => :compile) do |t| 22 | t.libs << 'test' 23 | t.pattern = "test/**/*_test.rb" 24 | t.verbose = true 25 | end 26 | Rake::Task['test'].comment = "Run all tests" 27 | 28 | YARD::Rake::YardocTask.new 29 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | Ruby-GPGME was originally written by Daiki Ueno. Other people 2 | contributed by reporting problems, suggesting various improvements or 3 | submitting actual code. Here is a list of those people. Help us keep 4 | it complete and free of errors. 5 | 6 | 7 | 8 | Ben Burkert 9 | Carl Corliss 10 | Jérémie Pierson 11 | Kouhei Sutou 12 | Kris Nuttycombe 13 | Marc Dequènes 14 | Peter Meier 15 | Rob Pitt 16 | Rory McKinley 17 | Sam Hall 18 | -------------------------------------------------------------------------------- /examples/edit.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gpgme' 3 | 4 | # If you do not have gpg-agent installed, comment out the following 5 | # and set it as :passphrase_callback. 6 | # 7 | # def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd) 8 | # $stderr.write("Passphrase for #{uid_hint}: ") 9 | # $stderr.flush 10 | # begin 11 | # system('stty -echo') 12 | # io = IO.for_fd(fd, 'w') 13 | # io.puts(gets) 14 | # io.flush 15 | # ensure 16 | # (0 ... $_.length).each do |i| $_[i] = ?0 end if $_ 17 | # system('stty echo') 18 | # end 19 | # $stderr.puts 20 | # end 21 | 22 | unless ENV['GPG_AGENT_INFO'] 23 | $stderr.puts("gpg-agent is not running. See the comment in #{$0}.") 24 | exit(1) 25 | end 26 | 27 | unless ENV['GNUPGHOME'] 28 | $stderr.write('As GNUPGHOME is not set, the generated key pair will be stored into *your* keyring. Really proceed? (y/N) ') 29 | $stderr.flush 30 | exit(1) unless gets.chomp == 'y' 31 | end 32 | 33 | unless ARGV.length == 1 34 | $stderr.puts("Usage: #{$0} KEYGRIP") 35 | exit(1) 36 | end 37 | 38 | def progfunc(hook, what, type, current, total) 39 | $stderr.write("#{what}: #{current}/#{total}\r") 40 | $stderr.flush 41 | end 42 | 43 | def editfunc(hook, status, args, fd) 44 | case status 45 | when GPGME::GPGME_STATUS_GET_BOOL 46 | begin 47 | $stderr.write("#{args} (y/n) ") 48 | $stderr.flush 49 | line = gets 50 | end until line =~ /\A\s*[ny]\s*\z/ 51 | io = IO.for_fd(fd) 52 | io.puts(line.strip) 53 | io.flush 54 | when GPGME::GPGME_STATUS_GET_LINE, GPGME::GPGME_STATUS_GET_HIDDEN 55 | $stderr.write("#{args}: ") 56 | $stderr.flush 57 | line = gets 58 | io = IO.for_fd(fd) 59 | io.puts(line) 60 | io.flush 61 | else 62 | $stderr.puts([status, args].inspect) 63 | end 64 | end 65 | 66 | ctx = GPGME::Ctx.new({:progress_callback => method(:progfunc), 67 | # :passphrase_callback => method(:passfunc) 68 | }) 69 | keystr = ARGV.shift 70 | keys = ctx.keys(keystr) 71 | if keys.empty? 72 | $stderr.puts("Can't find key for \"#{keystr}\"") 73 | exit(1) 74 | end 75 | 76 | $stderr.puts(keys.first.inspect) 77 | ctx.edit_key(keys.first, method(:editfunc)) 78 | -------------------------------------------------------------------------------- /examples/genkey.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gpgme' 3 | 4 | # If you do not have gpg-agent installed, comment out the following 5 | # and set it as :passphrase_callback. 6 | # 7 | # def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd) 8 | # $stderr.write("Passphrase for #{uid_hint}: ") 9 | # $stderr.flush 10 | # begin 11 | # system('stty -echo') 12 | # io = IO.for_fd(fd, 'w') 13 | # io.puts(gets) 14 | # io.flush 15 | # ensure 16 | # (0 ... $_.length).each do |i| $_[i] = ?0 end if $_ 17 | # system('stty echo') 18 | # end 19 | # $stderr.puts 20 | # end 21 | 22 | unless ENV['GPG_AGENT_INFO'] 23 | $stderr.puts("gpg-agent is not running. See the comment in #{$0}.") 24 | exit(1) 25 | end 26 | 27 | unless ENV['GNUPGHOME'] 28 | $stderr.write('As GNUPGHOME is not set, the generated key pair will be stored into *your* keyring. Really proceed? (y/N) ') 29 | $stderr.flush 30 | exit(1) unless gets.chomp == 'y' 31 | end 32 | 33 | def progfunc(hook, what, type, current, total) 34 | $stderr.write("#{what}: #{current}/#{total}\r") 35 | $stderr.flush 36 | end 37 | 38 | ctx = GPGME::Ctx.new({:progress_callback => method(:progfunc), 39 | # :passphrase_callback => method(:passfunc) 40 | }) 41 | 42 | ctx.genkey(<<'EOF', nil, nil) 43 | 44 | Key-Type: DSA 45 | Key-Length: 1024 46 | Subkey-Type: ELG-E 47 | Subkey-Length: 1024 48 | Name-Real: Joe Tester 49 | Name-Comment: with stupid passphrase 50 | Name-Email: joe@foo.bar 51 | Expire-Date: 0 52 | Passphrase: abc 53 | 54 | EOF 55 | $stderr.puts 56 | -------------------------------------------------------------------------------- /examples/keylist.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gpgme' 3 | 4 | ctx = GPGME::Ctx.new 5 | ctx.each_key(ARGV.shift) do |key| 6 | puts(key) 7 | end 8 | -------------------------------------------------------------------------------- /examples/roundtrip.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gpgme' 3 | 4 | # If you do not have gpg-agent installed, comment out the following 5 | # and set it as :passphrase_callback. 6 | # 7 | # def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd) 8 | # $stderr.write("Passphrase for #{uid_hint}: ") 9 | # $stderr.flush 10 | # begin 11 | # system('stty -echo') 12 | # io = IO.for_fd(fd, 'w') 13 | # io.puts(gets) 14 | # io.flush 15 | # ensure 16 | # (0 ... $_.length).each do |i| $_[i] = ?0 end if $_ 17 | # system('stty echo') 18 | # end 19 | # $stderr.puts 20 | # end 21 | 22 | unless ENV['GPG_AGENT_INFO'] 23 | $stderr.puts("gpg-agent is not running. See the comment in #{$0}.") 24 | exit(1) 25 | end 26 | 27 | plain = 'test test test' 28 | puts("Plaintext:\n#{plain}") 29 | 30 | # Perform symmetric encryption on PLAIN. 31 | crypto = GPGME::Crypto.new(:armor => true) 32 | cipher = crypto.encrypt(plain, {:symmetric => true, 33 | # :passphrase_callback => method(:passfunc) 34 | }) 35 | str = cipher.read 36 | puts("Ciphertext:\n#{str}") 37 | 38 | cipher = GPGME::Data.new(str) 39 | plain = crypto.decrypt(cipher, { 40 | # :passphrase_callback => method(:passfunc) 41 | }) 42 | puts("Plaintext:\n#{plain.read}") 43 | -------------------------------------------------------------------------------- /examples/sign.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gpgme' 3 | 4 | # If you do not have gpg-agent installed, comment out the following 5 | # and set it as :passphrase_callback. 6 | # 7 | # def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd) 8 | # $stderr.write("Passphrase for #{uid_hint}: ") 9 | # $stderr.flush 10 | # begin 11 | # system('stty -echo') 12 | # io = IO.for_fd(fd, 'w') 13 | # io.puts(gets) 14 | # io.flush 15 | # ensure 16 | # (0 ... $_.length).each do |i| $_[i] = ?0 end if $_ 17 | # system('stty echo') 18 | # end 19 | # $stderr.puts 20 | # end 21 | 22 | unless ENV['GPG_AGENT_INFO'] 23 | $stderr.puts("gpg-agent is not running. See the comment in #{$0}.") 24 | exit(1) 25 | end 26 | 27 | crypto = GPGME::Crypto.new 28 | signature = crypto.clearsign('test test test', { 29 | # :passphrase_callback => method(:passfunc) 30 | }) 31 | puts signature.read 32 | -------------------------------------------------------------------------------- /examples/verify.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gpgme' 3 | 4 | crypto = GPGME::Crypto.new 5 | signature = GPGME::Data.new(ARGF.read) 6 | crypto.verify(signature) do |sig| 7 | puts(sig.to_s) 8 | end 9 | -------------------------------------------------------------------------------- /ext/gpgme/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | # Available options: 4 | # 5 | # --enable-clean (default) 6 | # --disable-clean 7 | # 8 | # This file is largely based on Nokogiri's extconf.rb. 9 | 10 | ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) 11 | 12 | if arg_config('--clean') 13 | require 'pathname' 14 | require 'fileutils' 15 | 16 | root = Pathname(ROOT) 17 | pwd = Pathname(Dir.pwd) 18 | 19 | # Skip if this is a development work tree 20 | unless (root + '.git').exist? 21 | message "Cleaning files only used during build.\n" 22 | 23 | # (root + 'tmp') cannot be removed at this stage because 24 | # gpgme_n.so is yet to be copied to lib. 25 | 26 | # clean the ports build directory 27 | Pathname.glob(pwd.join('tmp', '*', 'ports')) { |dir| 28 | FileUtils.rm_rf(dir, verbose: true) 29 | FileUtils.rmdir(dir.parent, parents: true, verbose: true) 30 | } 31 | 32 | # ports installation can be safely removed if statically linked. 33 | FileUtils.rm_rf(root + 'ports', verbose: true) 34 | end 35 | 36 | exit 37 | end 38 | 39 | if arg_config('--use-system-libraries', ENV['RUBY_GPGME_USE_SYSTEM_LIBRARIES']) 40 | if find_executable('pkg-config') && system('pkg-config gpgme --exists') 41 | $CFLAGS += ' ' << `pkg-config --cflags gpgme`.chomp 42 | $libs += ' ' << `pkg-config --libs gpgme`.chomp 43 | elsif find_executable('gpgme-config') 44 | $CFLAGS += ' ' << `gpgme-config --cflags`.chomp 45 | $libs += ' ' << `gpgme-config --libs`.chomp 46 | else 47 | $stderr.puts("pkg-config with gpgme.pc and gpgme-config not found") 48 | exit(1) 49 | end 50 | else 51 | message <<-'EOS' 52 | ************************************************************************ 53 | IMPORTANT! gpgme gem uses locally built versions of required C libraries, 54 | namely libgpg-error, libassuan, and gpgme. 55 | 56 | If this is a concern for you and you want to use the system library 57 | instead, abort this installation process and reinstall gpgme gem as 58 | follows: 59 | 60 | gem install gpgme -- --use-system-libraries 61 | 62 | ************************************************************************ 63 | EOS 64 | 65 | require 'rubygems' 66 | require 'mini_portile2' 67 | 68 | libgpg_error_recipe = MiniPortile.new('libgpg-error', '1.47').tap do |recipe| 69 | recipe.target = File.join(ROOT, "ports") 70 | recipe.files = [{ 71 | :url => "https://www.gnupg.org/ftp/gcrypt/#{recipe.name}/#{recipe.name}-#{recipe.version}.tar.bz2", 72 | :sha256 => '9e3c670966b96ecc746c28c2c419541e3bcb787d1a73930f5e5f5e1bcbbb9bdb' 73 | }] 74 | recipe.configure_options = [ 75 | '--enable-install-gpg-error-config', 76 | '--disable-shared', 77 | '--enable-static', 78 | '--disable-nls', 79 | "CFLAGS=-fPIC #{ENV["CFLAGS"]}", 80 | ] 81 | checkpoint = "#{recipe.target}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed" 82 | unless File.exist?(checkpoint) 83 | recipe.cook 84 | FileUtils.touch checkpoint 85 | end 86 | recipe.activate 87 | end 88 | 89 | libassuan_recipe = MiniPortile.new('libassuan', '2.5.6').tap do |recipe| 90 | recipe.target = File.join(ROOT, "ports") 91 | recipe.files = [{ 92 | :url => "https://www.gnupg.org/ftp/gcrypt/#{recipe.name}/#{recipe.name}-#{recipe.version}.tar.bz2", 93 | :sha256 => 'e9fd27218d5394904e4e39788f9b1742711c3e6b41689a31aa3380bd5aa4f426' 94 | }] 95 | recipe.configure_options = [ 96 | '--disable-shared', 97 | '--enable-static', 98 | "--with-gpg-error-prefix=#{libgpg_error_recipe.path}", 99 | "CFLAGS=-fPIC #{ENV["CFLAGS"]}", 100 | ] 101 | checkpoint = "#{recipe.target}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed" 102 | unless File.exist?(checkpoint) 103 | recipe.cook 104 | FileUtils.touch checkpoint 105 | end 106 | recipe.activate 107 | end 108 | 109 | pkg_config_paths = [ 110 | File.join(libgpg_error_recipe.lib_path, 'pkgconfig'), 111 | File.join(libassuan_recipe.lib_path, 'pkgconfig'), 112 | ] 113 | 114 | # Ensure that the locally-built libraries take precedence. gpgme runs 115 | # `gpgrt-config libassuan` and could pull in the system libassuan 116 | # if PKG_CONFIG_PATH is not set properly. 117 | ENV["PKG_CONFIG_PATH"] = [*pkg_config_paths, ENV["PKG_CONFIG_PATH"]].compact.join(File::PATH_SEPARATOR) 118 | 119 | gpgme_recipe = MiniPortile.new('gpgme', '1.21.0').tap do |recipe| 120 | recipe.target = File.join(ROOT, "ports") 121 | recipe.files = [{ 122 | :url => "https://www.gnupg.org/ftp/gcrypt/#{recipe.name}/#{recipe.name}-#{recipe.version}.tar.bz2", 123 | :sha256 => '416e174e165734d84806253f8c96bda2993fd07f258c3aad5f053a6efd463e88' 124 | }] 125 | recipe.configure_options = [ 126 | '--disable-shared', 127 | '--enable-static', 128 | "--with-gpg-error-prefix=#{libgpg_error_recipe.path}", 129 | "--with-libassuan-prefix=#{libassuan_recipe.path}", 130 | # GPGME 1.5.0 assumes gpgsm is present if gpgconf is found. 131 | # However, on some systems (e.g. Debian), they are splitted into 132 | # separate packages. 133 | '--disable-gpgconf-test', 134 | '--disable-gpg-test', 135 | '--disable-gpgsm-test', 136 | '--disable-g13-test', 137 | # We only need the C API. 138 | '--disable-languages', 139 | "CFLAGS=-D_LARGEFILE64_SOURCE -fPIC #{ENV["CFLAGS"]}", 140 | ] 141 | checkpoint = "#{recipe.target}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed" 142 | unless File.exist?(checkpoint) 143 | recipe.cook 144 | FileUtils.touch checkpoint 145 | end 146 | recipe.activate 147 | end 148 | 149 | # special treatment to link with static libraries 150 | $libs = $libs.shellsplit.tap {|libs| 151 | File.join(gpgme_recipe.path, "bin", "gpgme-config").tap {|config| 152 | # call config scripts explicit with 'sh' for compat with Windows 153 | $CPPFLAGS = `sh #{config} --cflags`.strip << ' ' << $CPPFLAGS 154 | `sh #{config} --libs`.strip.shellsplit.each {|arg| 155 | case arg 156 | when /\A-L(.+)\z/ 157 | lpath=$1 158 | # Prioritize ports' directories 159 | if lpath.start_with?(ROOT + '/') 160 | $LIBPATH = [lpath] | $LIBPATH 161 | else 162 | $LIBPATH = $LIBPATH | [lpath] 163 | end 164 | when /\A-l(.+)\z/ 165 | # Resolve absolute paths of local static libraries to avoid 166 | # linking with system libraries. 167 | libname_to_recipe = { 168 | 'gpgme' => gpgme_recipe, 169 | 'assuan' => libassuan_recipe, 170 | 'gpg-error' => libgpg_error_recipe 171 | } 172 | recipe = libname_to_recipe[$1] 173 | if recipe 174 | libs.push(File.join(recipe.path, 'lib', "lib#{$1}.#{$LIBEXT}")) 175 | else 176 | libs.push(arg) 177 | end 178 | else 179 | $LDFLAGS << ' ' << arg.shellescape 180 | end 181 | } 182 | } 183 | }.shelljoin 184 | 185 | message 'checking for linker flags for static linking... ' 186 | case 187 | when try_link('int main(void) { return 0; }', 188 | ['-Wl,-Bstatic', '-lgpgme', '-Wl,-Bdynamic'].shelljoin) 189 | message "-Wl,-Bstatic\n" 190 | 191 | $libs = $libs.shellsplit.map {|arg| 192 | case arg 193 | when '-lgpgme', '-lassuan', '-lgpg-error' 194 | ['-Wl,-Bstatic', arg, '-Wl,-Bdynamic'] 195 | else 196 | arg 197 | end 198 | }.flatten.shelljoin 199 | else 200 | message "NONE\n" 201 | end 202 | 203 | unless have_header 'gpgme.h' 204 | abort <<-EOS 205 | ************************************************************************ 206 | ERROR! Cannot locate 'gpgme.h'. 207 | ************************************************************************ 208 | EOS 209 | end 210 | end 211 | 212 | checking_for('gpgme >= 1.1.3') do 213 | if try_run(<<'End') 214 | #include 215 | #include 216 | int main (void) { 217 | return gpgme_check_version ("1.1.3") == NULL; 218 | } 219 | End 220 | true 221 | else 222 | $CFLAGS += ' -DRUBY_GPGME_NEED_WORKAROUND_KEYLIST_NEXT' 223 | false 224 | end 225 | end 226 | 227 | have_func('gpgme_op_export_keys') 228 | 229 | create_makefile ('gpgme_n') 230 | 231 | if enable_config('clean', true) 232 | # Do not clean if run in a development work tree. 233 | File.open('Makefile', 'a') { |mk| 234 | mk.print < 2.7" 21 | 22 | s.add_development_dependency "mocha", "~> 0.9.12" 23 | s.add_development_dependency "minitest", "~> 2.1.0" 24 | s.add_development_dependency "yard", "~> 0.9.11" 25 | s.add_development_dependency "coveralls_reborn" 26 | s.add_development_dependency "byebug" 27 | end 28 | -------------------------------------------------------------------------------- /lib/gpgme.rb: -------------------------------------------------------------------------------- 1 | require 'gpgme_n' 2 | 3 | # TODO without this call one can't GPGME::Ctx.new, find out why 4 | GPGME::gpgme_check_version(nil) 5 | 6 | require 'gpgme/constants' 7 | require 'gpgme/ctx' 8 | require 'gpgme/data' 9 | require 'gpgme/error' 10 | require 'gpgme/io_callbacks' 11 | require 'gpgme/key_common' 12 | require 'gpgme/key' 13 | require 'gpgme/sub_key' 14 | require 'gpgme/key_sig' 15 | require 'gpgme/misc' 16 | require 'gpgme/signature' 17 | require 'gpgme/user_id' 18 | require 'gpgme/engine' 19 | require 'gpgme/crypto' 20 | 21 | module GPGME 22 | class << self 23 | 24 | # From the c extension 25 | alias pubkey_algo_name gpgme_pubkey_algo_name 26 | alias hash_algo_name gpgme_hash_algo_name 27 | 28 | ## 29 | # Auxiliary method used by all the library to generate exceptions 30 | # from error codes returned by the C extension. 31 | def error_to_exception(err) 32 | case GPGME::gpgme_err_code(err) 33 | when GPG_ERR_EOF 34 | EOFError.new 35 | when GPG_ERR_NO_ERROR 36 | nil 37 | when GPG_ERR_GENERAL 38 | Error::General.new(err) 39 | when GPG_ERR_ENOMEM 40 | Errno::ENOMEM.new 41 | when GPG_ERR_INV_VALUE 42 | Error::InvalidValue.new(err) 43 | when GPG_ERR_UNUSABLE_PUBKEY 44 | Error::UnusablePublicKey.new(err) 45 | when GPG_ERR_UNUSABLE_SECKEY 46 | Error::UnusableSecretKey.new(err) 47 | when GPG_ERR_NO_DATA 48 | Error::NoData.new(err) 49 | when GPG_ERR_CONFLICT 50 | Error::Conflict.new(err) 51 | when GPG_ERR_NOT_IMPLEMENTED 52 | Error::NotImplemented.new(err) 53 | when GPG_ERR_DECRYPT_FAILED 54 | Error::DecryptFailed.new(err) 55 | when GPG_ERR_BAD_PASSPHRASE 56 | Error::BadPassphrase.new(err) 57 | when GPG_ERR_CANCELED 58 | Error::Canceled.new(err) 59 | when GPG_ERR_INV_ENGINE 60 | Error::InvalidEngine.new(err) 61 | when GPG_ERR_AMBIGUOUS_NAME 62 | Error::AmbiguousName.new(err) 63 | when GPG_ERR_WRONG_KEY_USAGE 64 | Error::WrongKeyUsage.new(err) 65 | when GPG_ERR_CERT_REVOKED 66 | Error::CertificateRevoked.new(err) 67 | when GPG_ERR_CERT_EXPIRED 68 | Error::CertificateExpired.new(err) 69 | when GPG_ERR_NO_CRL_KNOWN 70 | Error::NoCRLKnown.new(err) 71 | when GPG_ERR_NO_POLICY_MATCH 72 | Error::NoPolicyMatch.new(err) 73 | when GPG_ERR_NO_SECKEY 74 | Error::NoSecretKey.new(err) 75 | when GPG_ERR_MISSING_CERT 76 | Error::MissingCertificate.new(err) 77 | when GPG_ERR_BAD_CERT_CHAIN 78 | Error::BadCertificateChain.new(err) 79 | when GPG_ERR_UNSUPPORTED_ALGORITHM 80 | Error::UnsupportedAlgorithm.new(err) 81 | when GPG_ERR_BAD_SIGNATURE 82 | Error::BadSignature.new(err) 83 | when GPG_ERR_NO_PUBKEY 84 | Error::NoPublicKey.new(err) 85 | else 86 | Error.new(err) 87 | end 88 | end 89 | 90 | ## 91 | # TODO find out what it does, can't seem to find a proper parameter that 92 | # returns something other than nil. 93 | def check_version(options = nil) 94 | version = nil 95 | if options.kind_of?(String) 96 | version = options 97 | elsif options.include?(:version) 98 | version = options[:version] 99 | end 100 | unless GPGME::gpgme_check_version(version) 101 | raise Error::InvalidVersion.new 102 | end 103 | end 104 | 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/gpgme/compat.rb: -------------------------------------------------------------------------------- 1 | require 'gpgme' 2 | 3 | # TODO: Find why is this needed. I guess the name compat means it's just 4 | # backwards compatibility. Consider removing? 5 | module GPGME 6 | GpgmeError = Error 7 | GpgmeData = Data 8 | GpgmeEngineInfo = EngineInfo 9 | GpgmeCtx = Ctx 10 | GpgmeKey = Key 11 | GpgmeSubKey = SubKey 12 | GpgmeUserID = UserID 13 | GpgmeKeySig = KeySig 14 | GpgmeVerifyResult = VerifyResult 15 | GpgmeSignature = Signature 16 | GpgmeDecryptResult = DecryptResult 17 | GpgmeSignResult = SignResult 18 | GpgmeEncryptResult = EncryptResult 19 | GpgmeInvalidKey = InvalidKey 20 | GpgmeNewSignature = NewSignature 21 | GpgmeImportStatus = ImportStatus 22 | GpgmeImportResult = ImportResult 23 | 24 | class Ctx 25 | # Set the data pointer to the beginning. 26 | def rewind 27 | seek(0) 28 | end 29 | end 30 | 31 | def gpgme_data_rewind(dh) 32 | begin 33 | GPGME::gpgme_data_seek(dh, 0, IO::SEEK_SET) 34 | rescue SystemCallError => e 35 | return e.errno 36 | end 37 | end 38 | module_function :gpgme_data_rewind 39 | 40 | def gpgme_op_import_ext(ctx, keydata, nr) 41 | err = GPGME::gpgme_op_import(ctx, keydata) 42 | if GPGME::gpgme_err_code(err) == GPGME::GPG_ERR_NO_ERROR 43 | result = GPGME::gpgme_op_import_result(ctx) 44 | nr.push(result.considered) 45 | end 46 | end 47 | module_function :gpgme_op_import_ext 48 | end 49 | -------------------------------------------------------------------------------- /lib/gpgme/constants.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | 3 | ATTR_ALGO = GPGME_ATTR_ALGO 4 | ATTR_CAN_CERTIFY = GPGME_ATTR_CAN_CERTIFY 5 | ATTR_CAN_ENCRYPT = GPGME_ATTR_CAN_ENCRYPT 6 | ATTR_CAN_SIGN = GPGME_ATTR_CAN_SIGN 7 | ATTR_CHAINID = GPGME_ATTR_CHAINID 8 | ATTR_COMMENT = GPGME_ATTR_COMMENT 9 | ATTR_CREATED = GPGME_ATTR_CREATED 10 | ATTR_EMAIL = GPGME_ATTR_EMAIL 11 | ATTR_ERRTOK = GPGME_ATTR_ERRTOK 12 | ATTR_EXPIRE = GPGME_ATTR_EXPIRE 13 | ATTR_FPR = GPGME_ATTR_FPR 14 | ATTR_ISSUER = GPGME_ATTR_ISSUER 15 | ATTR_IS_SECRET = GPGME_ATTR_IS_SECRET 16 | ATTR_KEYID = GPGME_ATTR_KEYID 17 | ATTR_KEY_CAPS = GPGME_ATTR_KEY_CAPS 18 | ATTR_KEY_DISABLED = GPGME_ATTR_KEY_DISABLED 19 | ATTR_KEY_EXPIRED = GPGME_ATTR_KEY_EXPIRED 20 | ATTR_KEY_INVALID = GPGME_ATTR_KEY_INVALID 21 | ATTR_KEY_REVOKED = GPGME_ATTR_KEY_REVOKED 22 | ATTR_LEN = GPGME_ATTR_LEN 23 | ATTR_LEVEL = GPGME_ATTR_LEVEL 24 | ATTR_NAME = GPGME_ATTR_NAME 25 | ATTR_OTRUST = GPGME_ATTR_OTRUST 26 | ATTR_SERIAL = GPGME_ATTR_SERIAL 27 | ATTR_SIG_STATUS = GPGME_ATTR_SIG_STATUS 28 | ATTR_SIG_SUMMARY = GPGME_ATTR_SIG_SUMMARY 29 | ATTR_TYPE = GPGME_ATTR_TYPE 30 | ATTR_UID_INVALID = GPGME_ATTR_UID_INVALID 31 | ATTR_UID_REVOKED = GPGME_ATTR_UID_REVOKED 32 | ATTR_USERID = GPGME_ATTR_USERID 33 | ATTR_VALIDITY = GPGME_ATTR_VALIDITY 34 | DATA_ENCODING_ARMOR = GPGME_DATA_ENCODING_ARMOR 35 | DATA_ENCODING_BASE64 = GPGME_DATA_ENCODING_BASE64 36 | DATA_ENCODING_BINARY = GPGME_DATA_ENCODING_BINARY 37 | DATA_ENCODING_NONE = GPGME_DATA_ENCODING_NONE 38 | ENCRYPT_ALWAYS_TRUST = GPGME_ENCRYPT_ALWAYS_TRUST 39 | if defined?(GPGME_ENCRYPT_NO_ENCRYPT_TO) 40 | ENCRYPT_NO_ENCRYPT_TO = GPGME_ENCRYPT_NO_ENCRYPT_TO 41 | end 42 | IMPORT_NEW = GPGME_IMPORT_NEW 43 | IMPORT_SECRET = GPGME_IMPORT_SECRET 44 | IMPORT_SIG = GPGME_IMPORT_SIG 45 | IMPORT_SUBKEY = GPGME_IMPORT_SUBKEY 46 | IMPORT_UID = GPGME_IMPORT_UID 47 | KEYLIST_MODE_EXTERN = GPGME_KEYLIST_MODE_EXTERN 48 | KEYLIST_MODE_LOCAL = GPGME_KEYLIST_MODE_LOCAL 49 | KEYLIST_MODE_SIGS = GPGME_KEYLIST_MODE_SIGS 50 | KEYLIST_MODE_VALIDATE = GPGME_KEYLIST_MODE_VALIDATE 51 | if defined?(GPGME_KEYLIST_MODE_SIG_NOTATIONS) 52 | KEYLIST_MODE_SIG_NOTATIONS = GPGME_KEYLIST_MODE_SIG_NOTATIONS 53 | end 54 | if defined?(GPGME_KEYLIST_MODE_EPHEMERAL) 55 | KEYLIST_MODE_EPHEMERAL = GPGME_KEYLIST_MODE_EPHEMERAL 56 | end 57 | if defined?(GPGME_KEYLIST_MODE_WITH_SECRET) 58 | KEYLIST_MODE_WITH_SECRET = GPGME_KEYLIST_MODE_WITH_SECRET 59 | end 60 | MD_CRC24_RFC2440 = GPGME_MD_CRC24_RFC2440 61 | MD_CRC32 = GPGME_MD_CRC32 62 | MD_CRC32_RFC1510 = GPGME_MD_CRC32_RFC1510 63 | MD_HAVAL = GPGME_MD_HAVAL 64 | MD_MD2 = GPGME_MD_MD2 65 | MD_MD4 = GPGME_MD_MD4 66 | MD_MD5 = GPGME_MD_MD5 67 | MD_RMD160 = GPGME_MD_RMD160 68 | MD_SHA1 = GPGME_MD_SHA1 69 | MD_SHA256 = GPGME_MD_SHA256 70 | if defined?(MD_SHA224) 71 | MD_SHA224 = GPGME_MD_SHA224 72 | end 73 | MD_SHA384 = GPGME_MD_SHA384 74 | MD_SHA512 = GPGME_MD_SHA512 75 | MD_TIGER = GPGME_MD_TIGER 76 | if defined?(gpgme_set_pinentry_mode) 77 | PINENTRY_MODE_DEFAULT = GPGME_PINENTRY_MODE_DEFAULT 78 | PINENTRY_MODE_ASK = GPGME_PINENTRY_MODE_ASK 79 | PINENTRY_MODE_CANCEL = GPGME_PINENTRY_MODE_CANCEL 80 | PINENTRY_MODE_ERROR = GPGME_PINENTRY_MODE_ERROR 81 | PINENTRY_MODE_LOOPBACK = GPGME_PINENTRY_MODE_LOOPBACK 82 | PINENTRY_MODE_NAMES = { 83 | PINENTRY_MODE_DEFAULT => :default, 84 | PINENTRY_MODE_ASK => :ask, 85 | PINENTRY_MODE_CANCEL => :cancel, 86 | PINENTRY_MODE_ERROR => :error, 87 | PINENTRY_MODE_LOOPBACK => :loopback 88 | } 89 | end 90 | PK_DSA = GPGME_PK_DSA 91 | PK_ELG = GPGME_PK_ELG 92 | PK_ELG_E = GPGME_PK_ELG_E 93 | PK_RSA = GPGME_PK_RSA 94 | if defined?(GPGME_PK_ECC) 95 | PK_ECC = GPGME_PK_ECC 96 | end 97 | PROTOCOL_CMS = GPGME_PROTOCOL_CMS 98 | PROTOCOL_OpenPGP = GPGME_PROTOCOL_OpenPGP 99 | SIGSUM_BAD_POLICY = GPGME_SIGSUM_BAD_POLICY 100 | SIGSUM_CRL_MISSING = GPGME_SIGSUM_CRL_MISSING 101 | SIGSUM_CRL_TOO_OLD = GPGME_SIGSUM_CRL_TOO_OLD 102 | SIGSUM_GREEN = GPGME_SIGSUM_GREEN 103 | SIGSUM_KEY_EXPIRED = GPGME_SIGSUM_KEY_EXPIRED 104 | SIGSUM_KEY_MISSING = GPGME_SIGSUM_KEY_MISSING 105 | SIGSUM_KEY_REVOKED = GPGME_SIGSUM_KEY_REVOKED 106 | SIGSUM_RED = GPGME_SIGSUM_RED 107 | SIGSUM_SIG_EXPIRED = GPGME_SIGSUM_SIG_EXPIRED 108 | SIGSUM_SYS_ERROR = GPGME_SIGSUM_SYS_ERROR 109 | SIGSUM_VALID = GPGME_SIGSUM_VALID 110 | SIG_MODE_CLEAR = GPGME_SIG_MODE_CLEAR 111 | SIG_MODE_DETACH = GPGME_SIG_MODE_DETACH 112 | SIG_MODE_NORMAL = GPGME_SIG_MODE_NORMAL 113 | SIG_STAT_BAD = GPGME_SIG_STAT_BAD 114 | SIG_STAT_DIFF = GPGME_SIG_STAT_DIFF 115 | SIG_STAT_ERROR = GPGME_SIG_STAT_ERROR 116 | SIG_STAT_GOOD = GPGME_SIG_STAT_GOOD 117 | SIG_STAT_GOOD_EXP = GPGME_SIG_STAT_GOOD_EXP 118 | SIG_STAT_GOOD_EXPKEY = GPGME_SIG_STAT_GOOD_EXPKEY 119 | SIG_STAT_NOKEY = GPGME_SIG_STAT_NOKEY 120 | SIG_STAT_NONE = GPGME_SIG_STAT_NONE 121 | SIG_STAT_NOSIG = GPGME_SIG_STAT_NOSIG 122 | STATUS_ABORT = GPGME_STATUS_ABORT 123 | STATUS_ALREADY_SIGNED = GPGME_STATUS_ALREADY_SIGNED 124 | STATUS_BADARMOR = GPGME_STATUS_BADARMOR 125 | STATUS_BADMDC = GPGME_STATUS_BADMDC 126 | STATUS_BADSIG = GPGME_STATUS_BADSIG 127 | STATUS_BAD_PASSPHRASE = GPGME_STATUS_BAD_PASSPHRASE 128 | STATUS_BEGIN_DECRYPTION = GPGME_STATUS_BEGIN_DECRYPTION 129 | STATUS_BEGIN_ENCRYPTION = GPGME_STATUS_BEGIN_ENCRYPTION 130 | STATUS_BEGIN_STREAM = GPGME_STATUS_BEGIN_STREAM 131 | STATUS_DECRYPTION_FAILED = GPGME_STATUS_DECRYPTION_FAILED 132 | STATUS_DECRYPTION_OKAY = GPGME_STATUS_DECRYPTION_OKAY 133 | STATUS_DELETE_PROBLEM = GPGME_STATUS_DELETE_PROBLEM 134 | STATUS_ENC_TO = GPGME_STATUS_ENC_TO 135 | STATUS_END_DECRYPTION = GPGME_STATUS_END_DECRYPTION 136 | STATUS_END_ENCRYPTION = GPGME_STATUS_END_ENCRYPTION 137 | STATUS_END_STREAM = GPGME_STATUS_END_STREAM 138 | STATUS_ENTER = GPGME_STATUS_ENTER 139 | STATUS_EOF = GPGME_STATUS_EOF 140 | STATUS_ERRMDC = GPGME_STATUS_ERRMDC 141 | STATUS_ERROR = GPGME_STATUS_ERROR 142 | STATUS_ERRSIG = GPGME_STATUS_ERRSIG 143 | STATUS_EXPKEYSIG = GPGME_STATUS_EXPKEYSIG 144 | STATUS_EXPSIG = GPGME_STATUS_EXPSIG 145 | STATUS_FILE_DONE = GPGME_STATUS_FILE_DONE 146 | STATUS_FILE_ERROR = GPGME_STATUS_FILE_ERROR 147 | STATUS_FILE_START = GPGME_STATUS_FILE_START 148 | STATUS_GET_BOOL = GPGME_STATUS_GET_BOOL 149 | STATUS_GET_HIDDEN = GPGME_STATUS_GET_HIDDEN 150 | STATUS_GET_LINE = GPGME_STATUS_GET_LINE 151 | STATUS_GOODMDC = GPGME_STATUS_GOODMDC 152 | STATUS_GOODSIG = GPGME_STATUS_GOODSIG 153 | STATUS_GOOD_PASSPHRASE = GPGME_STATUS_GOOD_PASSPHRASE 154 | STATUS_GOT_IT = GPGME_STATUS_GOT_IT 155 | STATUS_IMPORTED = GPGME_STATUS_IMPORTED 156 | STATUS_IMPORT_RES = GPGME_STATUS_IMPORT_RES 157 | STATUS_INV_RECP = GPGME_STATUS_INV_RECP 158 | STATUS_KEYEXPIRED = GPGME_STATUS_KEYEXPIRED 159 | STATUS_KEYREVOKED = GPGME_STATUS_KEYREVOKED 160 | STATUS_KEY_CREATED = GPGME_STATUS_KEY_CREATED 161 | STATUS_LEAVE = GPGME_STATUS_LEAVE 162 | STATUS_MISSING_PASSPHRASE = GPGME_STATUS_MISSING_PASSPHRASE 163 | STATUS_NEED_PASSPHRASE = GPGME_STATUS_NEED_PASSPHRASE 164 | STATUS_NEED_PASSPHRASE_SYM = GPGME_STATUS_NEED_PASSPHRASE_SYM 165 | STATUS_NODATA = GPGME_STATUS_NODATA 166 | STATUS_NOTATION_DATA = GPGME_STATUS_NOTATION_DATA 167 | STATUS_NOTATION_NAME = GPGME_STATUS_NOTATION_NAME 168 | STATUS_NO_PUBKEY = GPGME_STATUS_NO_PUBKEY 169 | STATUS_NO_RECP = GPGME_STATUS_NO_RECP 170 | STATUS_NO_SECKEY = GPGME_STATUS_NO_SECKEY 171 | if defined?(GPGME_STATUS_PKA_TRUST_BAD) 172 | STATUS_PKA_TRUST_BAD = GPGME_STATUS_PKA_TRUST_BAD 173 | end 174 | if defined?(GPGME_STATUS_PKA_TRUST_GOOD) 175 | STATUS_PKA_TRUST_GOOD = GPGME_STATUS_PKA_TRUST_GOOD 176 | end 177 | if defined?(GPGME_STATUS_PLAINTEXT_LENGTH) 178 | STATUS_PLAINTEXT_LENGTH = GPGME_STATUS_PLAINTEXT_LENGTH 179 | end 180 | if defined?(GPGME_STATUS_MOUNTPOINT) 181 | STATUS_MOUNTPOINT = GPGME_STATUS_MOUNTPOINT 182 | end 183 | if defined?(GPGME_STATUS_PINENTRY_LAUNCHED) 184 | STATUS_PINENTRY_LAUNCHED = GPGME_STATUS_PINENTRY_LAUNCHED 185 | end 186 | if defined?(GPGME_STATUS_ATTRIBUTE) 187 | STATUS_ATTRIBUTE = GPGME_STATUS_ATTRIBUTE 188 | end 189 | if defined?(GPGME_STATUS_BEGIN_SIGNING) 190 | STATUS_BEGIN_SIGNING = GPGME_STATUS_BEGIN_SIGNING 191 | end 192 | if defined?(GPGME_STATUS_KEY_NOT_CREATED) 193 | STATUS_KEY_NOT_CREATED = GPGME_STATUS_KEY_NOT_CREATED 194 | end 195 | STATUS_POLICY_URL = GPGME_STATUS_POLICY_URL 196 | STATUS_PROGRESS = GPGME_STATUS_PROGRESS 197 | STATUS_RSA_OR_IDEA = GPGME_STATUS_RSA_OR_IDEA 198 | STATUS_SESSION_KEY = GPGME_STATUS_SESSION_KEY 199 | STATUS_SHM_GET = GPGME_STATUS_SHM_GET 200 | STATUS_SHM_GET_BOOL = GPGME_STATUS_SHM_GET_BOOL 201 | STATUS_SHM_GET_HIDDEN = GPGME_STATUS_SHM_GET_HIDDEN 202 | STATUS_SHM_INFO = GPGME_STATUS_SHM_INFO 203 | STATUS_SIGEXPIRED = GPGME_STATUS_SIGEXPIRED 204 | STATUS_SIG_CREATED = GPGME_STATUS_SIG_CREATED 205 | STATUS_SIG_ID = GPGME_STATUS_SIG_ID 206 | STATUS_TRUNCATED = GPGME_STATUS_TRUNCATED 207 | STATUS_TRUST_FULLY = GPGME_STATUS_TRUST_FULLY 208 | STATUS_TRUST_MARGINAL = GPGME_STATUS_TRUST_MARGINAL 209 | STATUS_TRUST_NEVER = GPGME_STATUS_TRUST_NEVER 210 | STATUS_TRUST_ULTIMATE = GPGME_STATUS_TRUST_ULTIMATE 211 | STATUS_TRUST_UNDEFINED = GPGME_STATUS_TRUST_UNDEFINED 212 | STATUS_UNEXPECTED = GPGME_STATUS_UNEXPECTED 213 | STATUS_USERID_HINT = GPGME_STATUS_USERID_HINT 214 | STATUS_VALIDSIG = GPGME_STATUS_VALIDSIG 215 | VALIDITY_FULL = GPGME_VALIDITY_FULL 216 | VALIDITY_MARGINAL = GPGME_VALIDITY_MARGINAL 217 | VALIDITY_NEVER = GPGME_VALIDITY_NEVER 218 | VALIDITY_ULTIMATE = GPGME_VALIDITY_ULTIMATE 219 | VALIDITY_UNDEFINED = GPGME_VALIDITY_UNDEFINED 220 | VALIDITY_UNKNOWN = GPGME_VALIDITY_UNKNOWN 221 | 222 | PROTOCOL_NAMES = { 223 | PROTOCOL_OpenPGP => :OpenPGP, 224 | PROTOCOL_CMS => :CMS 225 | } 226 | 227 | if defined?(GPGME_PROTOCOL_ASSUAN) 228 | PROTOCOL_ASSUAN = GPGME_PROTOCOL_ASSUAN 229 | PROTOCOL_NAMES[PROTOCOL_ASSUAN] = :ASSUAN 230 | end 231 | 232 | if defined?(GPGME_PROTOCOL_SPAWN) 233 | PROTOCOL_SPAWN = GPGME_PROTOCOL_SPAWN 234 | PROTOCOL_NAMES[PROTOCOL_SPAWN] = :SPAWN 235 | SPAWN_DETACHED = GPGME_SPAWN_DETACHED 236 | SPAWN_ALLOW_SET_FG = GPGME_SPAWN_ALLOW_SET_FG 237 | end 238 | 239 | if defined?(GPGME_EXPORT_MODE_EXTERN) 240 | EXPORT_MODE_EXTERN = GPGME_EXPORT_MODE_EXTERN 241 | end 242 | 243 | if defined?(GPGME_EXPORT_MODE_MINIMAL) 244 | EXPORT_MODE_MINIMAL = GPGME_EXPORT_MODE_MINIMAL 245 | end 246 | 247 | if defined?(GPGME_EXPORT_MODE_SECRET) 248 | EXPORT_MODE_SECRET = GPGME_EXPORT_MODE_SECRET 249 | EXPORT_MODE_RAW = GPGME_EXPORT_MODE_RAW 250 | EXPORT_MODE_PKCS12 = GPGME_EXPORT_MODE_PKCS12 251 | end 252 | 253 | KEYLIST_MODE_NAMES = { 254 | KEYLIST_MODE_LOCAL => :local, 255 | KEYLIST_MODE_EXTERN => :extern, 256 | KEYLIST_MODE_SIGS => :sigs, 257 | KEYLIST_MODE_VALIDATE => :validate 258 | } 259 | 260 | VALIDITY_NAMES = { 261 | VALIDITY_UNKNOWN => :unknown, 262 | VALIDITY_UNDEFINED => :undefined, 263 | VALIDITY_NEVER => :never, 264 | VALIDITY_MARGINAL => :marginal, 265 | VALIDITY_FULL => :full, 266 | VALIDITY_ULTIMATE => :ultimate 267 | } 268 | 269 | if defined?(GPGME_DELETE_ALLOW_SECRET) 270 | DELETE_ALLOW_SECRET = GPGME_DELETE_ALLOW_SECRET 271 | end 272 | 273 | if defined?(GPGME_DELETE_FORCE) 274 | DELETE_FORCE = GPGME_DELETE_FORCE 275 | end 276 | end 277 | -------------------------------------------------------------------------------- /lib/gpgme/crypto.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | 3 | ## 4 | # Different, independent methods providing the simplest possible API to 5 | # execute crypto operations via GPG. All methods accept as options the same 6 | # common options as {GPGME::Ctx.new}. Read the documentation for that class to 7 | # know how to customize things further (like output stuff in ASCII armored 8 | # format, for example). 9 | # 10 | # @example 11 | # crypto = GPGME::Crypto.new :armor => true 12 | # encrypted = crypto.encrypt 'Plain text' 13 | # 14 | class Crypto 15 | 16 | attr_reader :default_options 17 | 18 | def initialize(options = {}) 19 | @default_options = options 20 | end 21 | 22 | ## 23 | # Encrypts an element 24 | # 25 | # crypto.encrypt something, options 26 | # 27 | # Will return a {GPGME::Data} element which can then be read. 28 | # 29 | # Must have some key imported, look for {GPGME::Key.import} to know how 30 | # to import one, or the gpg documentation to know how to create one 31 | # 32 | # @param plain 33 | # Must be something that can be converted into a {GPGME::Data} object, or 34 | # a {GPGME::Data} object itself. 35 | # 36 | # @param [Hash] options 37 | # The optional parameters are as follows: 38 | # * +:recipients+ for which recipient do you want to encrypt this file. It 39 | # will pick the first one available if none specified. Can be an array of 40 | # identifiers or just one (a string). 41 | # * +:symmetric+ if set to true, will ignore +:recipients+, and will perform 42 | # a symmetric encryption. Must provide a password via the +:password+ 43 | # option. 44 | # * +:always_trust+ if set to true specifies all the recipients to be 45 | # trusted, thus not requiring confirmation. 46 | # * +:sign+ if set to true, performs a combined sign and encrypt operation. 47 | # * +:signers+ if +:sign+ specified to true, a list of additional possible 48 | # signers. Must be an array of sign identifiers. 49 | # * +:output+ if specified, it will write the output into it. It will be 50 | # converted to a {GPGME::Data} object, so it could be a file for example. 51 | # * Any other option accepted by {GPGME::Ctx.new} 52 | # 53 | # @return [GPGME::Data] a {GPGME::Data} object that can be read. 54 | # 55 | # @example returns a {GPGME::Data} that can be later encrypted 56 | # encrypted = crypto.encrypt "Hello world!" 57 | # encrypted.read # => Encrypted stuff 58 | # 59 | # @example to be decrypted by someone@example.com. 60 | # crypto.encrypt "Hello", :recipients => "someone@example.com" 61 | # 62 | # @example If I didn't trust any of my keys by default 63 | # crypto.encrypt "Hello" # => GPGME::Error::General 64 | # crypto.encrypt "Hello", :always_trust => true # => Will work fine 65 | # 66 | # @example encrypted string that can be decrypted and/or *verified* 67 | # crypto.encrypt "Hello", :sign => true 68 | # 69 | # @example multiple signers 70 | # crypto.encrypt "Hello", :sign => true, :signers => "extra@example.com" 71 | # 72 | # @example writing to a file instead 73 | # file = File.open("signed.sec","w+") 74 | # crypto.encrypt "Hello", :output => file # output written to signed.sec 75 | # 76 | # @raise [GPGME::Error::General] when trying to encrypt with a key that is 77 | # not trusted, and +:always_trust+ wasn't specified 78 | # 79 | def encrypt(plain, options = {}) 80 | options = @default_options.merge options 81 | 82 | plain_data = Data.new(plain) 83 | cipher_data = Data.new(options[:output]) 84 | keys = Key.find(:public, options[:recipients]) 85 | keys = nil if options[:symmetric] 86 | 87 | flags = 0 88 | flags |= GPGME::ENCRYPT_ALWAYS_TRUST if options[:always_trust] 89 | 90 | GPGME::Ctx.new(options) do |ctx| 91 | begin 92 | if options[:sign] 93 | if options[:signers] 94 | signers = Key.find(:public, options[:signers], :sign) 95 | ctx.add_signer(*signers) 96 | end 97 | ctx.encrypt_sign(keys, plain_data, cipher_data, flags) 98 | else 99 | ctx.encrypt(keys, plain_data, cipher_data, flags) 100 | end 101 | rescue GPGME::Error::UnusablePublicKey => exc 102 | exc.keys = ctx.encrypt_result.invalid_recipients 103 | raise exc 104 | rescue GPGME::Error::UnusableSecretKey => exc 105 | exc.keys = ctx.sign_result.invalid_signers 106 | raise exc 107 | end 108 | end 109 | 110 | cipher_data.seek(0) 111 | cipher_data 112 | end 113 | 114 | ## 115 | # Decrypts a previously encrypted element 116 | # 117 | # crypto.decrypt cipher, options, &block 118 | # 119 | # Must have the appropiate key to be able to decrypt, of course. Returns 120 | # a {GPGME::Data} object which can then be read. 121 | # 122 | # @param cipher 123 | # Must be something that can be converted into a {GPGME::Data} object, 124 | # or a {GPGME::Data} object itself. It is the element that will be 125 | # decrypted. 126 | # 127 | # @param [Hash] options 128 | # The optional parameters: 129 | # * +:output+ if specified, it will write the output into it. It will 130 | # me converted to a {GPGME::Data} object, so it can also be a file, 131 | # for example. 132 | # * If the file was encrypted with symmetric encryption, must provide 133 | # a :password option. 134 | # * Any other option accepted by {GPGME::Ctx.new} 135 | # 136 | # @param &block 137 | # In the block all the signatures are yielded, so one could verify them. 138 | # See examples. 139 | # 140 | # @return [GPGME::Data] a {GPGME::Data} that can be read. 141 | # 142 | # @example Simple decrypt 143 | # crypto.decrypt encrypted_data 144 | # 145 | # @example symmetric encryption, or passwored key 146 | # crypto.decrypt encrypted_data, :password => "gpgme" 147 | # 148 | # @example Output to file 149 | # file = File.open("decrypted.txt", "w+") 150 | # crypto.decrypt encrypted_data, :output => file 151 | # 152 | # @example Verifying signatures 153 | # crypto.decrypt encrypted_data do |signature| 154 | # raise "Signature could not be verified" unless signature.valid? 155 | # end 156 | # 157 | # @raise [GPGME::Error::UnsupportedAlgorithm] when the cipher was encrypted 158 | # using an algorithm that's not supported currently. 159 | # 160 | # @raise [GPGME::Error::WrongKeyUsage] TODO Don't know when 161 | # 162 | # @raise [GPGME::Error::DecryptFailed] when the cipher was encrypted 163 | # for a key that's not available currently. 164 | def decrypt(cipher, options = {}) 165 | options = @default_options.merge options 166 | 167 | plain_data = Data.new(options[:output]) 168 | cipher_data = Data.new(cipher) 169 | 170 | GPGME::Ctx.new(options) do |ctx| 171 | begin 172 | ctx.decrypt_verify(cipher_data, plain_data) 173 | rescue GPGME::Error::UnsupportedAlgorithm => exc 174 | exc.algorithm = ctx.decrypt_result.unsupported_algorithm 175 | raise exc 176 | rescue GPGME::Error::WrongKeyUsage => exc 177 | exc.key_usage = ctx.decrypt_result.wrong_key_usage 178 | raise exc 179 | end 180 | 181 | verify_result = ctx.verify_result 182 | if verify_result && block_given? 183 | verify_result.signatures.each do |signature| 184 | yield signature 185 | end 186 | end 187 | 188 | end 189 | 190 | plain_data.seek(0) 191 | plain_data 192 | end 193 | 194 | ## 195 | # Creates a signature of a text 196 | # 197 | # crypto.sign text, options 198 | # 199 | # Must have the appropiate key to be able to decrypt, of course. Returns 200 | # a {GPGME::Data} object which can then be read. 201 | # 202 | # @param text 203 | # The object that will be signed. Must be something that can be converted 204 | # to {GPGME::Data}. 205 | # 206 | # @param [Hash] options 207 | # Optional parameters. 208 | # * +:signer+ sign identifier to sign the text with. Will use the first 209 | # key it finds if none specified. 210 | # * +:output+ if specified, it will write the output into it. It will be 211 | # converted to a {GPGME::Data} object, so it could be a file for example. 212 | # * +:mode+ Desired type of signature. Options are: 213 | # - +GPGME::SIG_MODE_NORMAL+ for a normal signature. The default one if 214 | # not specified. 215 | # - +GPGME::SIG_MODE_DETACH+ for a detached signature 216 | # - +GPGME::SIG_MODE_CLEAR+ for a cleartext signature 217 | # * Any other option accepted by {GPGME::Ctx.new} 218 | # 219 | # @return [GPGME::Data] a {GPGME::Data} that can be read. 220 | # 221 | # @example normal sign 222 | # crypto.sign "Hi there" 223 | # 224 | # @example outputing to a file 225 | # file = File.open("text.sign", "w+") 226 | # crypto.sign "Hi there", :options => file 227 | # 228 | # @example doing a detached signature 229 | # crypto.sign "Hi there", :mode => GPGME::SIG_MODE_DETACH 230 | # 231 | # @example specifying the signer 232 | # crypto.sign "Hi there", :signer => "mrsimo@example.com" 233 | # 234 | # @raise [GPGME::Error::UnusableSecretKey] TODO don't know when 235 | def sign(text, options = {}) 236 | options = @default_options.merge options 237 | 238 | plain = Data.new(text) 239 | output = Data.new(options[:output]) 240 | mode = options[:mode] || GPGME::SIG_MODE_NORMAL 241 | 242 | GPGME::Ctx.new(options) do |ctx| 243 | if options[:signer] 244 | signers = Key.find(:secret, options[:signer], :sign) 245 | ctx.add_signer(*signers) 246 | end 247 | 248 | begin 249 | ctx.sign(plain, output, mode) 250 | rescue GPGME::Error::UnusableSecretKey => exc 251 | exc.keys = ctx.sign_result.invalid_signers 252 | raise exc 253 | end 254 | end 255 | 256 | output.seek(0) 257 | output 258 | end 259 | 260 | # Verifies a previously signed element 261 | # 262 | # crypto.verify sig, options, &block 263 | # 264 | # Must have the proper keys available. 265 | # 266 | # @param sig 267 | # The signature itself. Must be possible to convert into a {GPGME::Data} 268 | # object, so can be a file. 269 | # 270 | # @param [Hash] options 271 | # * +:signed_text+ if the sign is detached, then must be the plain text 272 | # for which the signature was created. 273 | # * +:output+ where to store the result of the signature. Will be 274 | # converted to a {GPGME::Data} object. 275 | # * Any other option accepted by {GPGME::Ctx.new} 276 | # 277 | # @param &block 278 | # In the block all the signatures are yielded, so one could verify them. 279 | # See examples. 280 | # 281 | # @return [GPGME::Data] unless the sign is detached, the {GPGME::Data} 282 | # object with the plain text. If the sign is detached, will return nil. 283 | # 284 | # @example simple verification 285 | # sign = crypto.sign("Hi there") 286 | # data = crypto.verify(sign) { |signature| signature.valid? } 287 | # data.read # => "Hi there" 288 | # 289 | # @example saving output to file 290 | # sign = crypto.sign("Hi there") 291 | # out = File.open("test.asc", "w+") 292 | # crypto.verify(sign, :output => out) {|signature| signature.valid?} 293 | # out.read # => "Hi there" 294 | # 295 | # @example verifying a detached signature 296 | # sign = crypto.detach_sign("Hi there") 297 | # # Will fail 298 | # crypto.verify(sign) { |signature| signature.valid? } 299 | # # Will succeed 300 | # crypto.verify(sign, :signed_text => "hi there") do |signature| 301 | # signature.valid? 302 | # end 303 | # 304 | def verify(sig, options = {}) 305 | options = @default_options.merge options 306 | 307 | sig = Data.new(sig) 308 | signed_text = Data.new(options[:signed_text]) 309 | output = Data.new(options[:output]) unless options[:signed_text] 310 | 311 | GPGME::Ctx.new(options) do |ctx| 312 | ctx.verify(sig, signed_text, output) 313 | ctx.verify_result.signatures.each do |signature| 314 | yield signature 315 | end 316 | end 317 | 318 | if output 319 | output.seek(0) 320 | output 321 | end 322 | end 323 | 324 | # Clearsigns an element 325 | # 326 | # crypto.clearsign text, options 327 | # 328 | # Same functionality of {.sign} only doing clearsigns by default. 329 | # 330 | def clearsign(text, options = {}) 331 | sign text, options.merge(:mode => GPGME::SIG_MODE_CLEAR) 332 | end 333 | 334 | # Creates a detached signature of an element 335 | # 336 | # crypto.detach_sign text, options 337 | # 338 | # Same functionality of {.sign} only doing detached signs by default. 339 | # 340 | def detach_sign(text, options = {}) 341 | sign text, options.merge(:mode => GPGME::SIG_MODE_DETACH) 342 | end 343 | 344 | ## 345 | # Allows calling of methods directly in the module without the need to 346 | # create a new instance. 347 | def self.method_missing(method, *args, &block) 348 | if GPGME::Crypto.instance_methods(false).include?(method) 349 | crypto = GPGME::Crypto.new 350 | crypto.send method, *args, &block 351 | else 352 | super 353 | end 354 | end 355 | 356 | end # module Crypto 357 | end # module GPGME 358 | -------------------------------------------------------------------------------- /lib/gpgme/ctx.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | 3 | ## 4 | # A context within which all cryptographic operations are performed. 5 | # 6 | # More operations can be done which are not available in the higher level 7 | # API. Note how to create a new instance of this class in {GPGME::Ctx.new}. 8 | # 9 | class Ctx 10 | 11 | ## 12 | # Create a new instance from the given +options+. Must be released either 13 | # executing the operations inside a block, or executing {GPGME::Ctx#release} 14 | # afterwards. 15 | # 16 | # @param [Hash] options 17 | # The optional parameters are as follows: 18 | # * +:protocol+ Either +PROTOCOL_OpenPGP+ or +PROTOCOL_CMS+. 19 | # * +:armor+ will return ASCII armored outputs if specified true. 20 | # * +:textmode+ if +true+, inform the recipient that the input is text. 21 | # * +:keylist_mode+ One of: +KEYLIST_MODE_LOCAL+, +KEYLIST_MODE_EXTERN+, 22 | # +KEYLIST_MODE_SIGS+ or +KEYLIST_MODE_VALIDATE+. 23 | # * +:pinentry_mode+ One of: +PINENTRY_MODE_DEFAULT+, 24 | # +PINENTRY_MODE_ASK+, +PINENTRY_MODE_CANCEL+, 25 | # +PINENTRY_MODE_ERROR+, or +PINENTRY_MODE_LOOPBACK+. 26 | # * +:offline+ if set to true, dirmngr will not contact external services 27 | # * +:password+ password of the passphrased password being used. 28 | # * +:passphrase_callback+ A callback function. See {#set_passphrase_callback}. 29 | # * +:passphrase_callback_value+ An object passed to passphrase_callback. 30 | # * +:progress_callback+ A callback function. See {#set_progress_callback}. 31 | # * +:progress_callback_value+ An object passed to progress_callback. 32 | # * +:status_callback+ A callback function. See {#set_status_callback}. 33 | # * +:status_callback_value+ An object passed to status_callback. 34 | # 35 | # @example 36 | # ctx = GPGME::Ctx.new 37 | # # operate on ctx 38 | # ctx.release 39 | # 40 | # @example 41 | # GPGME::Ctx.new do |ctx| 42 | # # operate on ctx 43 | # end 44 | # 45 | def self.new(options = {}) 46 | rctx = [] 47 | err = GPGME::gpgme_new(rctx) 48 | exc = GPGME::error_to_exception(err) 49 | raise exc if exc 50 | ctx = rctx[0] 51 | 52 | ctx.protocol = options[:protocol] if options[:protocol] 53 | ctx.armor = options[:armor] if options[:armor] 54 | ctx.textmode = options[:textmode] if options[:textmode] 55 | ctx.keylist_mode = options[:keylist_mode] if options[:keylist_mode] 56 | ctx.pinentry_mode = options[:pinentry_mode] if options[:pinentry_mode] 57 | ctx.offline = options[:offline] if options[:offline] 58 | ctx.ignore_mdc_error = options[:ignore_mdc_error] if options[:ignore_mdc_error] 59 | 60 | if options[:password] 61 | ctx.set_passphrase_callback GPGME::Ctx.method(:pass_function), 62 | options[:password] 63 | else 64 | if options[:passphrase_callback] 65 | ctx.set_passphrase_callback options[:passphrase_callback], 66 | options[:passphrase_callback_value] 67 | end 68 | end 69 | if options[:progress_callback] 70 | ctx.set_progress_callback options[:progress_callback], 71 | options[:progress_callback_value] 72 | end 73 | if options[:status_callback] 74 | ctx.set_status_callback options[:status_callback], 75 | options[:status_callback_value] 76 | end 77 | 78 | if block_given? 79 | begin 80 | yield ctx 81 | ensure 82 | GPGME::gpgme_release(ctx) 83 | end 84 | else 85 | ctx 86 | end 87 | end 88 | 89 | 90 | ## 91 | # Releases the Ctx instance. Must be called if it was initialized without 92 | # a block. 93 | # 94 | # @example 95 | # ctx = GPGME::Ctx.new 96 | # # operate on ctx 97 | # ctx.release 98 | # 99 | def release 100 | GPGME::gpgme_release(self) 101 | end 102 | 103 | ## 104 | # Getters and setters 105 | ## 106 | 107 | # Get the value of the Ctx flag with the given name. 108 | # 109 | # Allowed flag names may include: 110 | # - 'redraw' 111 | # - 'full-status' 112 | # - 'raw-description' 113 | # - 'export-session-key' 114 | # - 'override-session-key' 115 | # - 'include-key-block' 116 | # - 'auto-key-import' 117 | # - 'auto-key-retrieve' 118 | # - 'request-origin' 119 | # - 'no-symkey-cache' 120 | # - 'ignore-mdc-error' 121 | # - 'auto-key-locate' 122 | # - 'trust-model' 123 | # - 'extended-edit' 124 | # - 'cert-expire' 125 | # - 'key-origin' 126 | # - 'import-filter' 127 | # - 'no-auto-check-trustdb' 128 | # 129 | # Please consult the GPGPME documentation for more details 130 | # 131 | def get_ctx_flag(flag_name) 132 | GPGME::gpgme_get_ctx_flag(self, flag_name.to_s) 133 | end 134 | 135 | # Set the Ctx flag with the given name 136 | # to the given value. 137 | def set_ctx_flag(flag_name, val) 138 | err = GPGME::gpgme_set_ctx_flag(self, flag_name.to_s, val.to_s) 139 | exc = GPGME::error_to_exception(err) 140 | raise exc if exc 141 | val 142 | end 143 | 144 | # Set the +protocol+ used within this context. See {GPGME::Ctx.new} for 145 | # possible values. 146 | def protocol=(proto) 147 | err = GPGME::gpgme_set_protocol(self, proto) 148 | exc = GPGME::error_to_exception(err) 149 | raise exc if exc 150 | proto 151 | end 152 | 153 | # Return the +protocol+ used within this context. 154 | def protocol 155 | GPGME::gpgme_get_protocol(self) 156 | end 157 | 158 | # Tell whether the output should be ASCII armored. 159 | def armor=(yes) 160 | GPGME::gpgme_set_armor(self, yes ? 1 : 0) 161 | yes 162 | end 163 | 164 | # Return true if the output is ASCII armored. 165 | def armor 166 | GPGME::gpgme_get_armor(self) == 1 ? true : false 167 | end 168 | 169 | # This option ignores a MDC integrity protection failure. 170 | # It is required to decrypt old messages which did not use an MDC. 171 | # It may also be useful if a message is partially garbled, 172 | # but it is necessary to get as much data as possible out of that garbled message. 173 | # Be aware that a missing or failed MDC can be an indication of an attack. 174 | # Use with great caution. 175 | def ignore_mdc_error=(yes) 176 | GPGME::gpgme_set_ignore_mdc_error(self, yes ? 1 : 0) 177 | yes 178 | end 179 | 180 | # Return true if the MDC integrity protection is disabled. 181 | def ignore_mdc_error 182 | GPGME::gpgme_get_ignore_mdc_error(self) == 1 ? true : false 183 | end 184 | 185 | # Tell whether canonical text mode should be used. 186 | def textmode=(yes) 187 | GPGME::gpgme_set_textmode(self, yes ? 1 : 0) 188 | yes 189 | end 190 | 191 | # Return true if canonical text mode is enabled. 192 | def textmode 193 | GPGME::gpgme_get_textmode(self) == 1 ? true : false 194 | end 195 | 196 | # Change the default behaviour of the key listing functions. 197 | def keylist_mode=(mode) 198 | GPGME::gpgme_set_keylist_mode(self, mode) 199 | mode 200 | end 201 | 202 | # Return the current key listing mode. 203 | def keylist_mode 204 | GPGME::gpgme_get_keylist_mode(self) 205 | end 206 | 207 | # Change the default behaviour of the pinentry invocation. 208 | def pinentry_mode=(mode) 209 | GPGME::gpgme_set_pinentry_mode(self, mode) 210 | mode 211 | end 212 | 213 | # Return the current pinentry mode. 214 | def pinentry_mode 215 | GPGME::gpgme_get_pinentry_mode(self) 216 | end 217 | 218 | # Change the default behaviour of the dirmngr that might require 219 | # connections to external services. 220 | def offline=(mode) 221 | GPGME::gpgme_set_offline(self, mode) 222 | mode 223 | end 224 | 225 | # Return the current offline mode. 226 | def offline 227 | GPGME::gpgme_get_offline(self) 228 | end 229 | 230 | ## 231 | # Passphrase and progress callbacks 232 | ## 233 | 234 | # Set the passphrase callback with given hook value. 235 | # +passfunc+ should respond to +call+ with 5 arguments. 236 | # 237 | # * +obj+ the parameter +:passphrase_callback_value+ passed when creating 238 | # the {GPGME::Ctx} object. 239 | # * +uid_hint+ hint as to what key are we asking the password for. Ex: 240 | # 241 | # +CFB3294A50C2CFD7 Albert Llop + 242 | # 243 | # * +passphrase_info+ 244 | # * +prev_was_bad+ 0 if it's the first time the password is being asked, 245 | # 1 otherwise. 246 | # * +fd+ file descriptor where the password must be written too. 247 | # 248 | # Expects a Method object which can be obtained by the +method+ method 249 | # (really..). 250 | # 251 | # ctx.set_passphrase_callback(MyModule.method(:passfunc)) 252 | # 253 | # @example this method will simply return +maria+ as password. 254 | # def pass_function(obj, uid_hint, passphrase_info, prev_was_bad, fd) 255 | # io = IO.for_fd(fd, 'w') 256 | # io.puts "maria" 257 | # io.flush 258 | # end 259 | # 260 | # @example this will interactively ask for the password 261 | # def passfunc(obj, uid_hint, passphrase_info, prev_was_bad, fd) 262 | # $stderr.write("Passphrase for #{uid_hint}: ") 263 | # $stderr.flush 264 | # begin 265 | # system('stty -echo') 266 | # io = IO.for_fd(fd, 'w') 267 | # io.puts(gets) 268 | # io.flush 269 | # ensure 270 | # (0 ... $_.length).each do |i| $_[i] = ?0 end if $_ 271 | # system('stty echo') 272 | # end 273 | # $stderr.puts 274 | # end 275 | # 276 | # Note that this function doesn't work with GnuPG 2.0. You can 277 | # use either GnuPG 1.x, which can be installed in parallel with 278 | # GnuPG 2.0, or GnuPG 2.1, which has loopback pinentry feature (see 279 | # {#pinentry_mode}). 280 | def set_passphrase_callback(passfunc, hook_value = nil) 281 | GPGME::gpgme_set_passphrase_cb(self, passfunc, hook_value) 282 | end 283 | alias set_passphrase_cb set_passphrase_callback 284 | 285 | # Set the progress callback with given hook value. 286 | # progfunc should respond to call with 5 arguments. 287 | # 288 | # def progfunc(hook, what, type, current, total) 289 | # $stderr.write("#{what}: #{current}/#{total}\r") 290 | # $stderr.flush 291 | # end 292 | # 293 | # ctx.set_progress_callback(method(:progfunc)) 294 | # 295 | def set_progress_callback(progfunc, hook_value = nil) 296 | GPGME::gpgme_set_progress_cb(self, progfunc, hook_value) 297 | end 298 | alias set_progress_cb set_progress_callback 299 | 300 | # Set the status callback with given hook value. 301 | # +statusfunc+ should respond to +call+ with 3 arguments. 302 | # 303 | # * +obj+ the parameter +:status_callback_value+ passed when creating 304 | # the {GPGME::Ctx} object. 305 | # * +keyword+ the name of the status message 306 | # * +args+ any arguments for the status message 307 | # 308 | # def status_function(obj, keyword, args) 309 | # $stderr.puts("#{keyword} #{args}") 310 | # return 0 311 | # end 312 | def set_status_callback(statusfunc, hook_value = nil) 313 | GPGME::gpgme_set_status_cb(self, statusfunc, hook_value) 314 | end 315 | alias set_status_cb set_status_callback 316 | 317 | ## 318 | # Searching and iterating through keys. Used by {GPGME::Key.find} 319 | ## 320 | 321 | # Initiate a key listing operation for given pattern. If +pattern+ is 322 | # +nil+, all available keys are returned. If +secret_only<+ is +true+, 323 | # only secret keys are returned. 324 | # 325 | # Used by {GPGME::Ctx#each_key} 326 | def keylist_start(pattern = nil, secret_only = false) 327 | err = GPGME::gpgme_op_keylist_start(self, pattern, secret_only ? 1 : 0) 328 | exc = GPGME::error_to_exception(err) 329 | raise exc if exc 330 | end 331 | 332 | # Advance to the next key in the key listing operation. 333 | # 334 | # Used by {GPGME::Ctx#each_key} 335 | def keylist_next 336 | rkey = [] 337 | err = GPGME::gpgme_op_keylist_next(self, rkey) 338 | exc = GPGME::error_to_exception(err) 339 | raise exc if exc 340 | rkey[0] 341 | end 342 | 343 | # End a pending key list operation. 344 | # 345 | # Used by {GPGME::Ctx#each_key} 346 | def keylist_end 347 | err = GPGME::gpgme_op_keylist_end(self) 348 | exc = GPGME::error_to_exception(err) 349 | raise exc if exc 350 | end 351 | 352 | # Convenient method to iterate over keys. 353 | # 354 | # If +pattern+ is +nil+, all available keys are returned. If +secret_only+ 355 | # is +true+, only secret keys are returned. 356 | # 357 | # See {GPGME::Key.find} for an example of how to use, or for an easier way 358 | # to use. 359 | def each_key(pattern = nil, secret_only = false, &block) 360 | keylist_start(pattern, secret_only) 361 | begin 362 | loop { yield keylist_next } 363 | rescue EOFError 364 | # The last key in the list has already been returned. 365 | ensure 366 | keylist_end 367 | end 368 | end 369 | alias each_keys each_key 370 | 371 | # Returns the keys that match the +pattern+, or all if +pattern+ is nil. 372 | # Returns only secret keys if +secret_only+ is true. 373 | def keys(pattern = nil, secret_only = nil) 374 | keys = [] 375 | each_key(pattern, secret_only) do |key| 376 | keys << key 377 | end 378 | keys 379 | end 380 | 381 | # Get the key with the +fingerprint+. 382 | # If +secret+ is +true+, secret key is returned. 383 | def get_key(fingerprint, secret = false) 384 | rkey = [] 385 | err = GPGME::gpgme_get_key(self, fingerprint, rkey, secret ? 1 : 0) 386 | exc = GPGME::error_to_exception(err) 387 | raise exc if exc 388 | rkey[0] 389 | end 390 | 391 | ## 392 | # Import/export and generation/deletion of keys 393 | ## 394 | 395 | # Generate a new key pair. 396 | # +parms+ is a string which looks like 397 | # 398 | # 399 | # Key-Type: DSA 400 | # Key-Length: 1024 401 | # Subkey-Type: ELG-E 402 | # Subkey-Length: 1024 403 | # Name-Real: Joe Tester 404 | # Name-Comment: with stupid passphrase 405 | # Name-Email: joe@foo.bar 406 | # Expire-Date: 0 407 | # Passphrase: abc 408 | # 409 | # 410 | # If +pubkey+ and +seckey+ are both set to +nil+, it stores the generated 411 | # key pair into your key ring. 412 | def generate_key(parms, pubkey = nil, seckey = nil) 413 | err = GPGME::gpgme_op_genkey(self, parms, pubkey, seckey) 414 | exc = GPGME::error_to_exception(err) 415 | raise exc if exc 416 | end 417 | alias genkey generate_key 418 | 419 | # Extract the public keys that match the +recipients+. Returns a 420 | # {GPGME::Data} object which is not rewinded (should do +seek(0)+ 421 | # before reading). 422 | # 423 | # Private keys cannot be exported due to GPGME restrictions. 424 | # 425 | # If passed, the key will be exported to +keydata+, which must be 426 | # a {GPGME::Data} object. 427 | def export_keys(recipients, keydata = Data.new, mode=0) 428 | err = GPGME::gpgme_op_export(self, recipients, mode, keydata) 429 | exc = GPGME::error_to_exception(err) 430 | raise exc if exc 431 | keydata 432 | end 433 | alias export export_keys 434 | 435 | # Add the keys in the data buffer to the key ring. 436 | def import_keys(keydata) 437 | err = GPGME::gpgme_op_import(self, keydata) 438 | exc = GPGME::error_to_exception(err) 439 | raise exc if exc 440 | end 441 | alias import import_keys 442 | 443 | def import_result 444 | GPGME::gpgme_op_import_result(self) 445 | end 446 | 447 | # Delete the key from the key ring. 448 | # If allow_secret is false, only public keys are deleted, 449 | # otherwise secret keys are deleted as well. 450 | # If force is true, the confirmation dialog will not be displayed. 451 | def delete_key(key, allow_secret = false, force = false) 452 | err = nil 453 | if defined?(GPGME::gpgme_op_delete_ext) 454 | flag = 0 455 | flag ^= GPGME::DELETE_ALLOW_SECRET if allow_secret 456 | flag ^= GPGME::DELETE_FORCE if force 457 | err = GPGME::gpgme_op_delete_ext(self, key, flag) 458 | else 459 | err = GPGME::gpgme_op_delete(self, key, allow_secret ? 1 : 0) 460 | end 461 | exc = GPGME::error_to_exception(err) 462 | raise exc if exc 463 | end 464 | alias delete delete_key 465 | 466 | # Edit attributes of the key in the local key ring. 467 | def edit_key(key, editfunc, hook_value = nil, out = Data.new) 468 | err = GPGME::gpgme_op_edit(self, key, editfunc, hook_value, out) 469 | exc = GPGME::error_to_exception(err) 470 | raise exc if exc 471 | end 472 | alias edit edit_key 473 | 474 | # Edit attributes of the key on the card. 475 | def edit_card_key(key, editfunc, hook_value = nil, out = Data.new) 476 | err = GPGME::gpgme_op_card_edit(self, key, editfunc, hook_value, out) 477 | exc = GPGME::error_to_exception(err) 478 | raise exc if exc 479 | end 480 | alias edit_card edit_card_key 481 | alias card_edit edit_card_key 482 | 483 | ## 484 | # Crypto operations 485 | ## 486 | 487 | # Decrypt the ciphertext and return the plaintext. 488 | def decrypt(cipher, plain = Data.new) 489 | err = GPGME::gpgme_op_decrypt(self, cipher, plain) 490 | exc = GPGME::error_to_exception(err) 491 | raise exc if exc 492 | plain 493 | end 494 | 495 | def decrypt_verify(cipher, plain = Data.new) 496 | err = GPGME::gpgme_op_decrypt_verify(self, cipher, plain) 497 | exc = GPGME::error_to_exception(err) 498 | raise exc if exc 499 | plain 500 | end 501 | 502 | def decrypt_result 503 | GPGME::gpgme_op_decrypt_result(self) 504 | end 505 | 506 | # Verify that the signature in the data object is a valid signature. 507 | def verify(sig, signed_text = nil, plain = Data.new) 508 | err = GPGME::gpgme_op_verify(self, sig, signed_text, plain) 509 | exc = GPGME::error_to_exception(err) 510 | raise exc if exc 511 | plain 512 | end 513 | 514 | def verify_result 515 | GPGME::gpgme_op_verify_result(self) 516 | end 517 | 518 | # Remove the list of signers from this object. 519 | def clear_signers 520 | GPGME::gpgme_signers_clear(self) 521 | end 522 | 523 | # Add _keys_ to the list of signers. 524 | def add_signer(*keys) 525 | keys.each do |key| 526 | err = GPGME::gpgme_signers_add(self, key) 527 | exc = GPGME::error_to_exception(err) 528 | raise exc if exc 529 | end 530 | end 531 | 532 | # Create a signature for the text. 533 | # +plain+ is a data object which contains the text. 534 | # +sig+ is a data object where the generated signature is stored. 535 | def sign(plain, sig = Data.new, mode = GPGME::SIG_MODE_NORMAL) 536 | err = GPGME::gpgme_op_sign(self, plain, sig, mode) 537 | exc = GPGME::error_to_exception(err) 538 | raise exc if exc 539 | sig 540 | end 541 | 542 | def sign_result 543 | GPGME::gpgme_op_sign_result(self) 544 | end 545 | 546 | # Encrypt the plaintext in the data object for the recipients and 547 | # return the ciphertext. 548 | def encrypt(recp, plain, cipher = Data.new, flags = 0) 549 | err = GPGME::gpgme_op_encrypt(self, recp, flags, plain, cipher) 550 | exc = GPGME::error_to_exception(err) 551 | raise exc if exc 552 | cipher 553 | end 554 | 555 | def encrypt_result 556 | GPGME::gpgme_op_encrypt_result(self) 557 | end 558 | 559 | def encrypt_sign(recp, plain, cipher = Data.new, flags = 0) 560 | err = GPGME::gpgme_op_encrypt_sign(self, recp, flags, plain, cipher) 561 | exc = GPGME::error_to_exception(err) 562 | raise exc if exc 563 | cipher 564 | end 565 | 566 | def spawn(file, argv, datain, dataout, dataerr, flags = 0) 567 | err = GPGME::gpgme_op_spawn(self, file, argv, datain, dataout, dataerr, 568 | flags) 569 | exc = GPGME::error_to_exception(err) 570 | raise exc if exc 571 | end 572 | 573 | def inspect 574 | "#<#{self.class} protocol=#{PROTOCOL_NAMES[protocol] || protocol}, \ 575 | armor=#{armor}, textmode=#{textmode}, \ 576 | keylist_mode=#{KEYLIST_MODE_NAMES[keylist_mode]}>" 577 | end 578 | 579 | private 580 | 581 | def self.pass_function(pass, uid_hint, passphrase_info, prev_was_bad, fd) 582 | io = IO.for_fd(fd, 'w') 583 | io.autoclose = false 584 | io.puts pass 585 | io.flush 586 | end 587 | 588 | end 589 | end 590 | -------------------------------------------------------------------------------- /lib/gpgme/data.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | 3 | ## 4 | # A class whose purpose is to unify the way we work with the data (both input 5 | # and output). Most of the calls expect instances of this class, or will try 6 | # to create one from your parameters. 7 | # 8 | # Read the {#read}, {#write} and {#seek} methods for the most commonly used 9 | # methods. 10 | class Data 11 | 12 | BLOCK_SIZE = 4096 13 | 14 | class << self 15 | 16 | ## 17 | # We implement +self.new+ instead of initialize because objects are actually 18 | # instantiated through the C API with stuff like +gpgme_data_new+. 19 | # 20 | # We try to create a {GPGME::Data} smartly depending on the object passed, and if 21 | # another {GPGME::Data} object is passed, it just returns it, so when in 22 | # doubt, you can always pass a {GPGME::Data} object. 23 | # 24 | # @example empty 25 | # data = GPGME::Data.new 26 | # data.write("stuff") 27 | # 28 | # @example from a string 29 | # data = GPGME::Data.new("From a string") 30 | # 31 | # @example from a file 32 | # data = GPGME::Data.new(File.open("secure.pass")) 33 | # 34 | # @example from a file descriptor 35 | # data = GPGME::Data.new(0) # Standard input 36 | # data = GPGME::Data.new(1) # Standard output 37 | # 38 | # file = File.open("secure.pass") 39 | # data = GPGME::Data.new(file.fileno) # file descriptor 40 | # 41 | def new(object = nil) 42 | if object.nil? 43 | empty! 44 | elsif object.is_a?(Data) 45 | object 46 | elsif object.is_a?(Integer) 47 | from_fd(object) 48 | elsif object.respond_to? :to_str 49 | from_str(object.to_str) 50 | elsif object.respond_to? :to_io 51 | from_io(object.to_io) 52 | elsif object.respond_to? :open 53 | from_io(object.open) 54 | elsif defined?(StringIO) and object.is_a?(StringIO) 55 | from_io(object) 56 | end 57 | end 58 | 59 | # Create a new instance with an empty buffer. 60 | def empty! 61 | rdh = [] 62 | err = GPGME::gpgme_data_new(rdh) 63 | exc = GPGME::error_to_exception(err) 64 | raise exc if exc 65 | rdh.first 66 | end 67 | 68 | # Create a new instance with internal buffer. 69 | def from_str(string) 70 | rdh = [] 71 | err = GPGME::gpgme_data_new_from_mem(rdh, string, string.bytesize) 72 | exc = GPGME::error_to_exception(err) 73 | raise exc if exc 74 | rdh.first 75 | end 76 | 77 | # Create a new instance associated with a given IO. 78 | def from_io(io) 79 | from_callbacks(IOCallbacks.new(io)) 80 | end 81 | 82 | # Create a new instance from the specified file descriptor. 83 | def from_fd(fd) 84 | rdh = [] 85 | err = GPGME::gpgme_data_new_from_fd(rdh, fd) 86 | exc = GPGME::error_to_exception(err) 87 | raise exc if exc 88 | rdh.first 89 | end 90 | 91 | # Create a new instance from the specified callbacks. 92 | def from_callbacks(callbacks, hook_value = nil) 93 | rdh = [] 94 | err = GPGME::gpgme_data_new_from_cbs(rdh, callbacks, hook_value) 95 | exc = GPGME::error_to_exception(err) 96 | raise exc if exc 97 | rdh.first 98 | end 99 | end # class << self 100 | 101 | # Read at most +length+ bytes from the data object, or to the end 102 | # of file if +length+ is omitted or is +nil+. 103 | # 104 | # @example 105 | # data = GPGME::Data.new("From a string") 106 | # data.read # => "From a string" 107 | # 108 | # @example 109 | # data = GPGME::Data.new("From a string") 110 | # data.read(4) # => "From" 111 | # 112 | def read(length = nil) 113 | if length 114 | GPGME::gpgme_data_read(self, length) 115 | else 116 | buf = String.new 117 | loop do 118 | s = GPGME::gpgme_data_read(self, BLOCK_SIZE) 119 | break unless s 120 | buf << s 121 | end 122 | buf 123 | end 124 | end 125 | 126 | ## 127 | # Seek to a given +offset+ in the data object according to the 128 | # value of +whence+. 129 | # 130 | # @example going to the beginning of the buffer after writing something 131 | # data = GPGME::Data.new("Some data") 132 | # data.read # => "Some data" 133 | # data.read # => "" 134 | # data.seek 0 135 | # data.read # => "Some data" 136 | # 137 | def seek(offset, whence = IO::SEEK_SET) 138 | GPGME::gpgme_data_seek(self, offset, IO::SEEK_SET) 139 | end 140 | 141 | ## 142 | # Writes +length+ bytes from +buffer+ into the data object. 143 | # Writes the full buffer if no length passed. 144 | # 145 | # @example 146 | # data = GPGME::Data.new 147 | # data.write "hola" 148 | # data.seek 0 149 | # data.read # => "hola" 150 | # 151 | # @example 152 | # data = GPGME::Data.new 153 | # data.write "hola", 2 154 | # data.seek 0 155 | # data.read # => "ho" 156 | # 157 | def write(buffer, length = buffer.length) 158 | GPGME::gpgme_data_write(self, buffer, length) 159 | end 160 | 161 | ## 162 | # Return the encoding of the underlying data. 163 | def encoding 164 | GPGME::gpgme_data_get_encoding(self) 165 | end 166 | 167 | ## 168 | # Sets the encoding for this buffer. Accepts only integer values 0 to 7: 169 | # 170 | # 0 = GPGME_DATA_ENCODING_NONE (Not specified) 171 | # 1 = GPGME_DATA_ENCODING_BINARY 172 | # 2 = GPGME_DATA_ENCODING_BASE64 173 | # 3 = GPGME_DATA_ENCODING_ARMOR (Either PEM or OpenPGP Armor) 174 | # 4 = GPGME_DATA_ENCODING_URL (LF delimited URL list) 175 | # 5 = GPGME_DATA_ENCODING_URLESC (Ditto, but percent escaped) 176 | # 6 = GPGME_DATA_ENCODING_URL0 (Nul delimited URL list) 177 | # 7 = GPGME_DATA_ENCODING_MIME (Data is a MIME part) 178 | # 179 | # @raise [GPGME::Error::InvalidValue] if the value isn't accepted. 180 | def encoding=(encoding) 181 | err = GPGME::gpgme_data_set_encoding(self, encoding) 182 | exc = GPGME::error_to_exception(err) 183 | raise exc if exc 184 | encoding 185 | end 186 | 187 | ## 188 | # Return the file name of the underlying data. 189 | def file_name 190 | GPGME::gpgme_data_get_file_name(self) 191 | end 192 | 193 | ## 194 | # Sets the file name for this buffer. 195 | # 196 | # @raise [GPGME::Error::InvalidValue] if the value isn't accepted. 197 | def file_name=(file_name) 198 | err = GPGME::gpgme_data_set_file_name(self, file_name) 199 | exc = GPGME::error_to_exception(err) 200 | raise exc if exc 201 | file_name 202 | end 203 | 204 | ## 205 | # Return the entire content of the data object as string. 206 | def to_s 207 | pos = seek(0, IO::SEEK_CUR) 208 | begin 209 | seek(0) 210 | read 211 | ensure 212 | seek(pos) 213 | end 214 | end 215 | end 216 | end 217 | -------------------------------------------------------------------------------- /lib/gpgme/engine.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | 3 | ## 4 | # Convenience methods to check different aspects of the gpg system 5 | # installation. 6 | module Engine 7 | class << self 8 | 9 | ## 10 | # Verify that the engine implementing the protocol +proto+ is installed in 11 | # the system. Can be one of +PROTOCOL_OpenPGP+ or +PROTOCOL_CMS+. 12 | # 13 | # @return [Boolean] true if the engine is installed. 14 | # 15 | # @example 16 | # GPGME::Engine.check_version(GPGME::PROTOCOL_OpenPGP) # => true 17 | # 18 | def check_version(proto) 19 | err = GPGME::gpgme_engine_check_version(proto) 20 | exc = GPGME::error_to_exception(err) 21 | !exc 22 | end 23 | 24 | ## 25 | # Return an array of {GPGME::EngineInfo} structures of enabled engines. 26 | # 27 | # @example 28 | # GPGME::Engine.info.first 29 | # # => # 34 | # 35 | def info 36 | rinfo = [] 37 | GPGME::gpgme_get_engine_info(rinfo) 38 | rinfo 39 | end 40 | 41 | ## 42 | # Change the default configuration of the crypto engine implementing 43 | # protocol +proto+. 44 | # 45 | # @param proto 46 | # Can be one of +PROTOCOL_OpenPGP+ or +PROTOCOL_CMS+. 47 | # 48 | # @param file_name 49 | # The file name of the executable program implementing the protocol. 50 | # 51 | # @param home_dir 52 | # The directory name of the configuration directory. 53 | # 54 | # @example 55 | # GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, '/usr/local/bin/gpg', home_dir) 56 | # 57 | def set_info(proto, file_name, home_dir) 58 | err = GPGME::gpgme_set_engine_info(proto, file_name, home_dir) 59 | exc = GPGME::error_to_exception(err) 60 | raise exc if exc 61 | end 62 | 63 | ## 64 | # Sets the home dir for the configuration options. This way one could, 65 | # for example, load the keys from a customized keychain. 66 | # 67 | # @example 68 | # GPGME::Engine.home_dir = '/tmp' 69 | # 70 | def home_dir=(home_dir) 71 | current = info.first 72 | set_info current.protocol, current.file_name, home_dir 73 | end 74 | 75 | ## 76 | # Return the default configuration. 77 | # 78 | # @example 79 | # GPGME::Engine.dirinfo('homedir') 80 | # # => '/home/user/.gnupg" 81 | # 82 | def dirinfo(what) 83 | GPGME::gpgme_get_dirinfo(what) 84 | end 85 | end # class << self 86 | end # class Engine 87 | end # module GPGME 88 | -------------------------------------------------------------------------------- /lib/gpgme/error.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | class Error < StandardError 3 | def initialize(error) 4 | @error = error 5 | end 6 | attr_reader :error 7 | 8 | # Return the error code. 9 | # 10 | # The error code indicates the type of an error, or the reason why 11 | # an operation failed. 12 | def code 13 | GPGME::gpgme_err_code(@error) 14 | end 15 | 16 | # Return the error source. 17 | # 18 | # The error source has not a precisely defined meaning. Sometimes 19 | # it is the place where the error happened, sometimes it is the 20 | # place where an error was encoded into an error value. Usually 21 | # the error source will give an indication to where to look for 22 | # the problem. This is not always true, but it is attempted to 23 | # achieve this goal. 24 | def source 25 | GPGME::gpgme_err_source(@error) 26 | end 27 | 28 | # Return a description of the error code. 29 | def message 30 | GPGME::gpgme_strerror(@error) 31 | end 32 | 33 | class General < self; end 34 | class InvalidValue < self; end 35 | class UnusablePublicKey < self 36 | attr_accessor :keys 37 | end 38 | class UnusableSecretKey < self 39 | attr_accessor :keys 40 | end 41 | class NoData < self; end 42 | class Conflict < self; end 43 | class NotImplemented < self; end 44 | class DecryptFailed < self; end 45 | class BadPassphrase < self; end 46 | class Canceled < self; end 47 | class InvalidEngine < self; end 48 | class AmbiguousName < self; end 49 | class WrongKeyUsage < self 50 | attr_accessor :key_usage 51 | end 52 | class CertificateRevoked < self; end 53 | class CertificateExpired < self; end 54 | class NoCRLKnown < self; end 55 | class NoPolicyMatch < self; end 56 | class NoSecretKey < self; end 57 | class MissingCertificate < self; end 58 | class BadCertificateChain < self; end 59 | class UnsupportedAlgorithm < self 60 | attr_accessor :algorithm 61 | end 62 | class BadSignature < self; end 63 | class NoPublicKey < self; end 64 | class InvalidVersion < self; end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/gpgme/io_callbacks.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | class IOCallbacks 3 | def initialize(io) 4 | @io = io 5 | end 6 | 7 | def read(hook, length) 8 | @io.read(length) 9 | end 10 | 11 | def write(hook, buffer, length) 12 | @io.write(buffer[0 .. length]) 13 | end 14 | 15 | def seek(hook, offset, whence) 16 | return @io.pos if offset == 0 && whence == IO::SEEK_CUR 17 | @io.seek(offset, whence) 18 | @io.pos 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/gpgme/key.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | 3 | ## 4 | # A ruby representation of a public or a secret key. 5 | # 6 | # Every key has two instances of {GPGME::SubKey}, accessible through 7 | # {.subkeys}, and with a {.primary_subkey} where most attributes are 8 | # derived from, like the +fingerprint+. 9 | # 10 | # Also, every key has at least a {GPGME::UserID}, accessible through 11 | # {.uids}, with a {.primary_uid}, where other attributes are derived from, 12 | # like +email+ or +name+ 13 | class Key 14 | private_class_method :new 15 | 16 | attr_reader :keylist_mode, :protocol, :owner_trust 17 | attr_reader :issuer_serial, :issuer_name, :chain_id 18 | attr_reader :subkeys, :uids 19 | 20 | include KeyCommon 21 | 22 | class << self 23 | 24 | ## 25 | # Returns an array of {GPGME::Key} objects that match the parameters. 26 | # * +secret+ set to +:secret+ to get only secret keys, or to +:public+ to 27 | # get only public keys. 28 | # * +keys_or_names+ an array or an item that can be either {GPGME::Key} 29 | # elements, or string identifiers like the email or the sha. Leave 30 | # blank to get all. 31 | # * +purposes+ get only keys that are usable for any of these purposes. 32 | # See {GPGME::Key} for a list of possible key capabilities. 33 | # 34 | # @example 35 | # GPGME::Key.find :secret # => first secret key found 36 | # 37 | # @example 38 | # GPGME::Key.find(:public, "mrsimo@example.com") 39 | # # => return only public keys that match mrsimo@example.com 40 | # 41 | # @example 42 | # GPGME::Key.find(:public, "mrsimo@example.com", :sign) 43 | # # => return the public keys that match mrsimo@example.com and are 44 | # # capable of signing 45 | def find(secret, keys_or_names = nil, purposes = []) 46 | secret = (secret == :secret) 47 | keys_or_names = [""] if keys_or_names.nil? || (keys_or_names.is_a?(Array) && keys_or_names.empty?) 48 | keys_or_names = [keys_or_names].flatten 49 | purposes = [purposes].flatten.compact.uniq 50 | 51 | keys = [] 52 | keys_or_names.each do |key_or_name| 53 | case key_or_name 54 | when Key then keys << key_or_name 55 | when String 56 | GPGME::Ctx.new do |ctx| 57 | keys += ctx.keys(key_or_name, secret).select do |k| 58 | k.usable_for?(purposes) 59 | end 60 | end 61 | end 62 | end 63 | keys 64 | end 65 | 66 | def get(fingerprint) 67 | Ctx.new do |ctx| 68 | ctx.get_key(fingerprint) 69 | end 70 | end 71 | 72 | # Exports public keys 73 | # 74 | # GPGME::Key.export pattern, options 75 | # 76 | # Private keys cannot be exported due to GPGME restrictions. 77 | # 78 | # @param pattern 79 | # Identifier of the key to export. 80 | # 81 | # @param [Hash] options 82 | # * +:output+ specify where to write the key to. It will be converted to 83 | # a {GPGME::Data}, so it could be a file, for example. 84 | # * +:minimal+ set to true to let the export mode be 'minimal'. 85 | # * Any other option accepted by {GPGME::Ctx.new} 86 | # 87 | # @return [GPGME::Data] the exported key. 88 | # 89 | # @example 90 | # key = GPGME::Key.export "mrsimo@example.com" 91 | # 92 | # @example writing to a file 93 | # out = File.open("my.key", "w+") 94 | # GPGME::Key.export "mrsimo@example.com", :output => out 95 | # 96 | def export(pattern, options = {}) 97 | output = Data.new(options[:output]) 98 | if options.delete(:minimal) == true 99 | export_mode = 4 100 | else 101 | export_mode = 0 102 | end 103 | 104 | GPGME::Ctx.new(options) do |ctx| 105 | ctx.export_keys(pattern, output, export_mode) 106 | end 107 | 108 | output.seek(0) 109 | output 110 | end 111 | 112 | # Imports a key 113 | # 114 | # GPGME::Key.import keydata, options 115 | # 116 | # @param keydata 117 | # The key to import. It will be converted to a {GPGME::Data} object, 118 | # so could be a file, for example. 119 | # @param options 120 | # Any other option accepted by {GPGME::Ctx.new} 121 | # 122 | # @example 123 | # GPGME::Key.import(File.open("my.key")) 124 | # 125 | def import(keydata, options = {}) 126 | GPGME::Ctx.new(options) do |ctx| 127 | ctx.import_keys(Data.new(keydata)) 128 | ctx.import_result 129 | end 130 | end 131 | 132 | # Checks if a key is valid 133 | def valid?(key) 134 | GPGME::Key.import(key).considered == 1 135 | end 136 | 137 | end 138 | 139 | ## 140 | # Exports this key. Accepts the same options as {GPGME::Ctx.new}, and 141 | # +options[:output]+, where you can specify something that can become a 142 | # {GPGME::Data}, where the output will go. 143 | # 144 | # @example 145 | # key.export(:armor => true) 146 | # # => GPGME::Data you can read with ASCII armored format 147 | # 148 | # @example 149 | # file = File.open("key.asc", "w+") 150 | # key.export(:output => file) 151 | # # => the key will be written to the file. 152 | # 153 | def export(options = {}) 154 | Key.export self.sha, options 155 | end 156 | 157 | ## 158 | # Delete this key. If it's public, and has a secret one it will fail unless 159 | # +allow_secret+ is specified as true. Suppress the confirmation dialog, if 160 | # +force+ is specified as true. 161 | def delete!(allow_secret = false, force = false) 162 | GPGME::Ctx.new do |ctx| 163 | ctx.delete_key self, allow_secret, force 164 | end 165 | end 166 | 167 | ## 168 | # Returns true if the key has an expiry date else false 169 | def expires? 170 | primary_subkey.expires? 171 | end 172 | 173 | ## 174 | # Returns the expiry date for this key 175 | def expires 176 | primary_subkey.expires 177 | end 178 | 179 | ## 180 | # Returns true if the key is expired 181 | def expired 182 | subkeys.any?(&:expired) 183 | end 184 | 185 | def primary_subkey 186 | @primary_subkey ||= subkeys.first 187 | end 188 | 189 | ## 190 | # Short descriptive value. Can be used to identify the key. 191 | def sha 192 | primary_subkey.sha 193 | end 194 | 195 | ## 196 | # Longer descriptive value. Can be used to identify the key. 197 | def fingerprint 198 | primary_subkey.fingerprint 199 | end 200 | 201 | ## 202 | # Returns the main {GPGME::UserID} for this key. 203 | def primary_uid 204 | uids.first 205 | end 206 | 207 | ## 208 | # Returns the email for this key. 209 | def email 210 | primary_uid.email 211 | end 212 | 213 | ## 214 | # Returns the issuer name for this key. 215 | def name 216 | primary_uid.name 217 | end 218 | 219 | ## 220 | # Returns the issuer comment for this key. 221 | def comment 222 | primary_uid.comment 223 | end 224 | 225 | def ==(another_key) 226 | self.class === another_key and fingerprint == another_key.fingerprint 227 | end 228 | 229 | def inspect 230 | sprintf("#<#{self.class} %s %4d%s/%s %s trust=%s, owner_trust=%s, \ 231 | capability=%s, subkeys=%s, uids=%s>", 232 | primary_subkey.secret? ? 'sec' : 'pub', 233 | primary_subkey.length, 234 | primary_subkey.pubkey_algo_letter, 235 | primary_subkey.fingerprint[-8 .. -1], 236 | primary_subkey.timestamp.strftime('%Y-%m-%d'), 237 | trust.inspect, 238 | VALIDITY_NAMES[@owner_trust].inspect, 239 | capability.inspect, 240 | subkeys.inspect, 241 | uids.inspect) 242 | end 243 | 244 | def to_s 245 | primary_subkey = subkeys[0] 246 | s = sprintf("%s %4d%s/%s %s\n", 247 | primary_subkey.secret? ? 'sec' : 'pub', 248 | primary_subkey.length, 249 | primary_subkey.pubkey_algo_letter, 250 | primary_subkey.fingerprint[-8 .. -1], 251 | primary_subkey.timestamp.strftime('%Y-%m-%d')) 252 | uids.each do |user_id| 253 | s << "uid\t\t#{user_id.name} <#{user_id.email}>\n" 254 | end 255 | subkeys.each do |subkey| 256 | s << subkey.to_s 257 | end 258 | s 259 | end 260 | end 261 | end 262 | -------------------------------------------------------------------------------- /lib/gpgme/key_common.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | module KeyCommon 3 | 4 | ## 5 | # Returns nil if the trust is valid. 6 | # Returns one of +:revoked+, +:expired+, +:disabled+, +:invalid+ 7 | def trust 8 | return :revoked if @revoked == 1 9 | return :expired if @expired == 1 10 | return :disabled if @disabled == 1 11 | return :invalid if @invalid == 1 12 | end 13 | 14 | ## 15 | # Array of capabilities for this key. It can contain any combination of 16 | # +:encrypt+, +:sign+, +:certify+ or +:authenticate+ 17 | def capability 18 | caps = [] 19 | caps << :encrypt if @can_encrypt == 1 20 | caps << :sign if @can_sign == 1 21 | caps << :certify if @can_certify == 1 22 | caps << :authenticate if @can_authenticate == 1 23 | caps 24 | end 25 | 26 | ## 27 | # Checks if the key is capable of all of these actions. If empty array 28 | # is passed then will return true. 29 | # 30 | # Returns false if the keys trust has been invalidated. 31 | def usable_for?(purposes) 32 | unless purposes.kind_of? Array 33 | purposes = [purposes] 34 | end 35 | return false if [:revoked, :expired, :disabled, :invalid].include? trust 36 | return (purposes - capability).empty? 37 | end 38 | 39 | def secret? 40 | @secret == 1 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/gpgme/key_sig.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | class KeySig 3 | private_class_method :new 4 | 5 | attr_reader :pubkey_algo, :keyid 6 | 7 | def revoked? 8 | @revoked == 1 9 | end 10 | 11 | def expired? 12 | @expired == 1 13 | end 14 | 15 | def invalid? 16 | @invalid == 1 17 | end 18 | 19 | def exportable? 20 | @exportable == 1 21 | end 22 | 23 | def timestamp 24 | Time.at(@timestamp) 25 | end 26 | 27 | def expires 28 | Time.at(@expires) 29 | end 30 | 31 | def inspect 32 | "#<#{self.class} #{keyid} timestamp=#{timestamp}, expires=#{expires}>" 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/gpgme/misc.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | class EngineInfo 3 | private_class_method :new 4 | 5 | attr_reader :protocol, :file_name, :version, :req_version, :home_dir 6 | alias required_version req_version 7 | end 8 | 9 | class VerifyResult 10 | private_class_method :new 11 | 12 | attr_reader :signatures 13 | end 14 | 15 | class Recipient 16 | private_class_method :new 17 | 18 | attr_reader :pubkey_algo, :keyid, :status 19 | end 20 | 21 | class DecryptResult 22 | private_class_method :new 23 | 24 | attr_reader :unsupported_algorithm, :wrong_key_usage 25 | attr_reader :recipients, :file_name 26 | end 27 | 28 | class SignResult 29 | private_class_method :new 30 | 31 | attr_reader :invalid_signers, :signatures 32 | end 33 | 34 | class EncryptResult 35 | private_class_method :new 36 | 37 | attr_reader :invalid_recipients 38 | end 39 | 40 | class InvalidKey 41 | private_class_method :new 42 | 43 | attr_reader :fpr, :reason 44 | alias fingerprint fpr 45 | end 46 | 47 | class NewSignature 48 | private_class_method :new 49 | 50 | attr_reader :type, :pubkey_algo, :hash_algo, :sig_class, :fpr 51 | alias fingerprint fpr 52 | 53 | def timestamp 54 | Time.at(@timestamp) 55 | end 56 | end 57 | 58 | class ImportStatus 59 | private_class_method :new 60 | 61 | attr_reader :fpr, :result, :status 62 | alias fingerprint fpr 63 | end 64 | 65 | class ImportResult 66 | private_class_method :new 67 | 68 | attr_reader :considered, :no_user_id, :imported, :imported_rsa, :unchanged 69 | attr_reader :new_user_ids, :new_sub_keys, :new_signatures, :new_revocations 70 | attr_reader :secret_read, :secret_imported, :secret_unchanged 71 | attr_reader :not_imported, :imports 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/gpgme/signature.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | class Signature 3 | private_class_method :new 4 | 5 | attr_reader :summary, :fpr, :status, :notations, :wrong_key_usage 6 | attr_reader :validity, :validity_reason 7 | attr_reader :pka_trust, :pka_address 8 | alias fingerprint fpr 9 | 10 | ## 11 | # Returns true if the signature is correct 12 | def valid? 13 | status_code == GPGME::GPG_ERR_NO_ERROR 14 | end 15 | 16 | def expired_signature? 17 | status_code == GPGME::GPG_ERR_SIG_EXPIRED 18 | end 19 | 20 | def expired_key? 21 | status_code == GPGME::GPG_ERR_KEY_EXPIRED 22 | end 23 | 24 | def revoked_key? 25 | status_code == GPGME::GPG_ERR_CERT_REVOKED 26 | end 27 | 28 | def bad? 29 | status_code == GPGME::GPG_ERR_BAD_SIGNATURE 30 | end 31 | 32 | def no_key? 33 | status_code == GPGME::GPG_ERR_NO_PUBKEY 34 | end 35 | 36 | def status_code 37 | GPGME::gpgme_err_code(status) 38 | end 39 | 40 | def from 41 | @from ||= begin 42 | Ctx.new do |ctx| 43 | if from_key = ctx.get_key(fingerprint) 44 | "#{from_key.subkeys[0].keyid} #{from_key.uids[0].uid}" 45 | else 46 | fingerprint 47 | end 48 | end 49 | end 50 | end 51 | 52 | def key 53 | @key ||= begin 54 | Ctx.new do |ctx| 55 | @key = ctx.get_key(fingerprint) 56 | end 57 | end 58 | end 59 | 60 | def timestamp 61 | Time.at(@timestamp) 62 | end 63 | 64 | def exp_timestamp 65 | Time.at(@exp_timestamp) 66 | end 67 | 68 | def to_s 69 | case status_code 70 | when GPGME::GPG_ERR_NO_ERROR 71 | "Good signature from #{from}" 72 | when GPGME::GPG_ERR_SIG_EXPIRED 73 | "Expired signature from #{from}" 74 | when GPGME::GPG_ERR_KEY_EXPIRED 75 | "Signature made from expired key #{from}" 76 | when GPGME::GPG_ERR_CERT_REVOKED 77 | "Signature made from revoked key #{from}" 78 | when GPGME::GPG_ERR_BAD_SIGNATURE 79 | "Bad signature from #{from}" 80 | when GPGME::GPG_ERR_NO_PUBKEY 81 | "No public key for #{from}" 82 | end 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/gpgme/sub_key.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | class SubKey 3 | private_class_method :new 4 | 5 | attr_reader :pubkey_algo, :length, :keyid, :fpr 6 | alias fingerprint fpr 7 | 8 | include KeyCommon 9 | 10 | def timestamp 11 | case @timestamp 12 | when -1, 0 13 | # FIXME: add a special value for invalid timestamp, or throw an error 14 | return nil 15 | else 16 | return Time.at(@timestamp) 17 | end 18 | end 19 | 20 | def expires? 21 | @expires != 0 22 | end 23 | 24 | def expires 25 | expires? ? Time.at(@expires) : nil 26 | end 27 | 28 | def expired 29 | expires? && @expires < Time.now.to_i 30 | end 31 | 32 | def sha 33 | (@fpr || @keyid)[-8 .. -1] 34 | end 35 | 36 | PUBKEY_ALGO_LETTERS = { 37 | PK_RSA => "R", 38 | PK_ELG_E => "g", 39 | PK_ELG => "G", 40 | PK_DSA => "D" 41 | } 42 | 43 | def pubkey_algo_letter 44 | PUBKEY_ALGO_LETTERS[@pubkey_algo] || "?" 45 | end 46 | 47 | def inspect 48 | sprintf("#<#{self.class} %s %4d%s/%s %s trust=%s, capability=%s>", 49 | secret? ? 'ssc' : 'sub', 50 | length, 51 | pubkey_algo_letter, 52 | (@fpr || @keyid)[-8 .. -1], 53 | timestamp.strftime('%Y-%m-%d'), 54 | trust.inspect, 55 | capability.inspect) 56 | end 57 | 58 | def to_s 59 | sprintf("%s %4d%s/%s %s\n", 60 | secret? ? 'ssc' : 'sub', 61 | length, 62 | pubkey_algo_letter, 63 | (@fpr || @keyid)[-8 .. -1], 64 | timestamp.strftime('%Y-%m-%d')) 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/gpgme/user_id.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | class UserID 3 | private_class_method :new 4 | 5 | attr_reader :validity, :uid, :name, :comment, :email, :signatures 6 | 7 | def revoked? 8 | @revoked == 1 9 | end 10 | 11 | def invalid? 12 | @invalid == 1 13 | end 14 | 15 | def inspect 16 | "#<#{self.class} #{name} <#{email}> \ 17 | validity=#{VALIDITY_NAMES[validity]}, signatures=#{signatures.inspect}>" 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/gpgme/version.rb: -------------------------------------------------------------------------------- 1 | module GPGME 2 | # The version of GPGME ruby binding you are using 3 | VERSION = "2.0.24" 4 | end 5 | -------------------------------------------------------------------------------- /ports/archives/gpgme-1.21.0.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ueno/ruby-gpgme/85c16a2c031b4b45996e0188145c51e0568a7868/ports/archives/gpgme-1.21.0.tar.bz2 -------------------------------------------------------------------------------- /ports/archives/libassuan-2.5.6.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ueno/ruby-gpgme/85c16a2c031b4b45996e0188145c51e0568a7868/ports/archives/libassuan-2.5.6.tar.bz2 -------------------------------------------------------------------------------- /ports/archives/libgpg-error-1.47.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ueno/ruby-gpgme/85c16a2c031b4b45996e0188145c51e0568a7868/ports/archives/libgpg-error-1.47.tar.bz2 -------------------------------------------------------------------------------- /test/crypto_test.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require 'test_helper' 3 | require 'tempfile' 4 | 5 | describe GPGME::Crypto do 6 | before do 7 | skip unless ensure_keys GPGME::PROTOCOL_OpenPGP 8 | end 9 | 10 | describe "default options functionality" do 11 | it "allows operation from instances normally" do 12 | crypto = GPGME::Crypto.new 13 | encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :recipients => KEYS.first[:sha] 14 | assert_equal TEXT[:plain], crypto.decrypt(encrypted).read 15 | end 16 | 17 | it "can set default options when using the instance way" do 18 | crypto = GPGME::Crypto.new :always_trust => true 19 | encrypted = crypto.encrypt TEXT[:plain], :recipients => KEYS.first[:sha] 20 | assert_equal TEXT[:plain], crypto.decrypt(encrypted).read 21 | end 22 | 23 | it "but they can still be overwritten" do 24 | crypto = GPGME::Crypto.new :always_trust => false 25 | encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :recipients => KEYS.first[:sha] 26 | assert_equal TEXT[:plain], crypto.decrypt(encrypted).read 27 | end 28 | end 29 | 30 | describe "roundtrip encryption/decryption" do 31 | it "does the roundtrip encrypting" do 32 | crypto = GPGME::Crypto.new 33 | encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :recipients => KEYS.first[:sha] 34 | assert_equal TEXT[:plain], crypto.decrypt(encrypted).read 35 | end 36 | 37 | it "does so even with armored encrypted stuff" do 38 | crypto = GPGME::Crypto.new 39 | encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :armor => true 40 | assert_equal TEXT[:plain], crypto.decrypt(encrypted).read 41 | end 42 | end 43 | 44 | describe :encrypt do 45 | it "should raise an error if the recipients aren't trusted" do 46 | assert_raises GPGME::Error::UnusablePublicKey do 47 | GPGME::Crypto.new.encrypt TEXT[:plain] 48 | end 49 | end 50 | 51 | it "doesn't raise an error and returns something when encrypting nothing" do 52 | data = GPGME::Crypto.new.encrypt nil, :always_trust => true 53 | refute_empty data.read 54 | data = GPGME::Crypto.new.encrypt "", :always_trust => true 55 | refute_empty data.read 56 | end 57 | 58 | it "can specify which key(s) to use for encrypting with a string" do 59 | crypto = GPGME::Crypto.new :always_trust => true 60 | key = KEYS.last 61 | encrypted = crypto.encrypt TEXT[:plain], :recipients => key[:sha] 62 | assert_equal TEXT[:plain], crypto.decrypt(encrypted).read 63 | 64 | remove_key key 65 | encrypted.seek 0 66 | assert_raises GPGME::Error::NoSecretKey do 67 | crypto.decrypt(encrypted) 68 | end 69 | import_key key 70 | end 71 | 72 | it "can specify which key to use for encrypting with a Key object" do 73 | crypto = GPGME::Crypto.new :always_trust => true 74 | key = KEYS.last 75 | real_key = GPGME::Key.find(:public, key[:sha]).first 76 | 77 | encrypted = crypto.encrypt TEXT[:plain], :recipients => real_key 78 | assert_equal TEXT[:plain], crypto.decrypt(encrypted).read 79 | 80 | remove_key key 81 | encrypted.seek 0 82 | assert_raises GPGME::Error::NoSecretKey do 83 | crypto.decrypt(encrypted) 84 | end 85 | import_key key 86 | end 87 | 88 | it "can also sign at the same time" do 89 | crypto = GPGME::Crypto.new :always_trust => true 90 | encrypted = crypto.encrypt TEXT[:plain], :sign => true 91 | signatures = 0 92 | 93 | crypto.verify(encrypted) do |signature| 94 | assert_instance_of GPGME::Signature, signature 95 | signatures += 1 96 | end 97 | 98 | assert_equal 1, signatures 99 | end 100 | 101 | it "can be signed by more than one person" do 102 | crypto = GPGME::Crypto.new :always_trust => true 103 | encrypted = crypto.encrypt TEXT[:plain], :sign => true, :signers => KEYS.map{|k| k[:sha]} 104 | signatures = 0 105 | 106 | crypto.verify(encrypted) do |signature| 107 | assert_instance_of GPGME::Signature, signature 108 | signatures += 1 109 | end 110 | 111 | assert_equal 4, signatures 112 | end 113 | 114 | it "outputs to a file if specified" do 115 | crypto = GPGME::Crypto.new :always_trust => true 116 | file = Tempfile.new "test" 117 | crypto.encrypt TEXT[:plain], :output => file 118 | file_contents = file.read 119 | file.seek 0 120 | 121 | refute_empty file_contents 122 | assert_equal TEXT[:plain], crypto.decrypt(file).read 123 | end 124 | 125 | # TODO find how to test 126 | # it "raises GPGME::Error::UnusablePublicKey" 127 | # it "raises GPGME::Error::UnusableSecretKey" 128 | end 129 | 130 | describe "symmetric encryption/decryption" do 131 | before do 132 | info = GPGME::Engine.info.first 133 | skip if /\A2\.[01]|\A1\./ === info.version 134 | end 135 | 136 | it "requires a password to encrypt" do 137 | GPGME::Crypto.new.encrypt TEXT[:plain], :symmetric => true 138 | end 139 | 140 | it "requires a password to decrypt" do 141 | crypto = GPGME::Crypto.new 142 | encrypted_data = crypto.encrypt TEXT[:plain], 143 | :symmetric => true, :password => "gpgme" 144 | 145 | crypto.decrypt encrypted_data 146 | end 147 | 148 | it "can encrypt and decrypt with the same password" do 149 | crypto = GPGME::Crypto.new :symmetric => true, :password => "gpgme" 150 | encrypted_data = crypto.encrypt TEXT[:plain] 151 | plain = crypto.decrypt encrypted_data 152 | 153 | assert_equal "Hi there", plain.read 154 | end 155 | end 156 | 157 | describe :decrypt do 158 | it "decrypts encrypted stuff" do 159 | assert_equal TEXT[:plain], GPGME::Crypto.new.decrypt(TEXT[:encrypted]).read 160 | end 161 | 162 | it "will not get into the signatures block if there's none" do 163 | GPGME::Crypto.new.decrypt(TEXT[:encrypted]) do |signature| 164 | flunk "If I'm here means there was some signature" 165 | end 166 | pass 167 | end 168 | 169 | it "will get signature elements if the encrypted thing was signed" do 170 | signatures = 0 171 | GPGME::Crypto.new.decrypt(TEXT[:signed]) do |signature| 172 | assert_instance_of GPGME::Signature, signature 173 | signatures += 1 174 | end 175 | assert_equal 1, signatures 176 | end 177 | 178 | it "writes to the output if passed" do 179 | buffer = GPGME::Data.new 180 | GPGME::Crypto.new.decrypt(TEXT[:encrypted], :output => buffer) 181 | assert_equal TEXT[:plain], buffer.read 182 | end 183 | 184 | # TODO find ways to test this 185 | # it "raises UnsupportedAlgorithm" 186 | # it "raises WrongKeyUsage" 187 | 188 | it "raises DecryptFailed when the decrypting key isn't available" do 189 | assert_raises GPGME::Error::NoSecretKey do 190 | GPGME::Crypto.new.decrypt(TEXT[:unavailable]) 191 | end 192 | end 193 | end 194 | 195 | describe :sign do 196 | it "signs normal strings" do 197 | crypto = GPGME::Crypto.new 198 | signatures = 0 199 | sign = crypto.sign "Hi there" 200 | 201 | crypto.verify(sign) do |signature| 202 | assert_instance_of GPGME::Signature, signature 203 | assert signature.valid? 204 | signatures += 1 205 | end 206 | 207 | assert_equal 1, signatures 208 | end 209 | 210 | # TODO Find how to import an expired public key 211 | # it "raises an error if trying to sign with an expired key" do 212 | # with_key EXPIRED_KEY do 213 | # crypto = GPGME::Crypto.new 214 | # assert_raises GPGME::Error::General do 215 | # sign = crypto.sign "Hi there", :signer => EXPIRED_KEY[:sha] 216 | # end 217 | # end 218 | # end 219 | 220 | it "selects who to sign for" do 221 | crypto = GPGME::Crypto.new 222 | sign = crypto.sign "Hi there", :signer => KEYS.last[:sha] 223 | key = GPGME::Key.get(KEYS.last[:sha]) 224 | 225 | signatures = 0 226 | 227 | crypto.verify(sign) do |signature| 228 | assert_instance_of GPGME::Signature, signature 229 | assert_equal key, signature.key 230 | signatures += 1 231 | end 232 | 233 | assert_equal 1, signatures 234 | end 235 | 236 | end 237 | end 238 | -------------------------------------------------------------------------------- /test/ctx_test.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require 'test_helper' 3 | 4 | describe GPGME::Ctx do 5 | before do 6 | skip unless ensure_keys GPGME::PROTOCOL_OpenPGP 7 | end 8 | 9 | it "can instantiate" do 10 | assert_instance_of GPGME::Ctx, GPGME::Ctx.new 11 | end 12 | 13 | it "doesn't close itself" do 14 | GPGME.expects(:gpgme_release).never 15 | GPGME::Ctx.new 16 | end 17 | 18 | it "closes itself if called with a block" do 19 | GPGME.expects(:gpgme_release).with(anything) 20 | GPGME::Ctx.new { |ctx| } 21 | end 22 | 23 | it "can be closed with the release method" do 24 | GPGME.expects(:gpgme_release).with(anything) 25 | ctx = GPGME::Ctx.new 26 | ctx.release 27 | end 28 | 29 | describe :new do 30 | before do 31 | info = GPGME::Engine.info.first 32 | skip if /\A2\.[01]|\A1\./ === info.version 33 | end 34 | 35 | # We consider :armor, :protocol, :textmode and :keylist_mode as tested 36 | # with the other tests of this file. Here we test the rest 37 | 38 | it ":password sets the password for the key" do 39 | with_key PASSWORD_KEY do 40 | input = GPGME::Data.new(TEXT[:passwored]) 41 | output = GPGME::Data.new 42 | 43 | GPGME::Ctx.new(:password => 'gpgme') do |ctx| 44 | ctx.decrypt_verify input, output 45 | 46 | output.seek 0 47 | assert_equal "Hi there", output.read.chomp 48 | 49 | recipients = ctx.decrypt_result.recipients 50 | assert_equal 1, recipients.size 51 | 52 | recipient_key = ctx.get_key(recipients.first.keyid) 53 | key = ctx.get_key(PASSWORD_KEY[:sha]) 54 | 55 | assert_equal recipient_key, key 56 | end 57 | end 58 | end 59 | end 60 | 61 | describe :decrypt_result do 62 | it "returns the list of encyption recipients" do 63 | cipher = GPGME::Data.new(KEY_1_ENCRYPTED) 64 | output = GPGME::Data.new 65 | 66 | GPGME::Ctx.new do |ctx| 67 | ctx.decrypt_verify(cipher, output) 68 | assert_equal 1, ctx.decrypt_result.recipients.size 69 | end 70 | end 71 | 72 | it "should not segfault" do 73 | cipher = GPGME::Data.new(KEY_1_ENCRYPTED) 74 | ouput = GPGME::Data.new 75 | 76 | GPGME::Ctx.new do |ctx| 77 | assert_raises ArgumentError do 78 | ctx.decrypt_result 79 | end 80 | end 81 | end 82 | end 83 | 84 | describe :get_ctx_flag do 85 | it "reads flags with getters and setters" do 86 | ctx = GPGME::Ctx.new 87 | 88 | refute ctx.ignore_mdc_error 89 | assert_equal "", ctx.get_ctx_flag("ignore-mdc-error") 90 | 91 | ctx.ignore_mdc_error = true 92 | 93 | assert ctx.ignore_mdc_error 94 | assert_equal "1", ctx.get_ctx_flag("ignore-mdc-error") 95 | end 96 | 97 | it "can get flags without getters and setters" do 98 | ctx = GPGME::Ctx.new 99 | 100 | assert_equal "", ctx.get_ctx_flag("auto-key-locate") 101 | ctx.set_ctx_flag("auto-key-locate", "cert") 102 | assert_equal "cert", ctx.get_ctx_flag("auto-key-locate") 103 | end 104 | 105 | it "raises an error when a flag doesn't exist" do 106 | ctx = GPGME::Ctx.new 107 | 108 | assert_raises ArgumentError do 109 | ctx.get_ctx_flag("foo") 110 | end 111 | end 112 | end 113 | 114 | describe :set_ctx_flag do 115 | it "sets the value for a flag with a getter" do 116 | ctx = GPGME::Ctx.new 117 | refute ctx.ignore_mdc_error 118 | 119 | ctx.set_ctx_flag("ignore-mdc-error", "1") 120 | assert ctx.ignore_mdc_error 121 | end 122 | 123 | it "unsets the value for a flag with a getter" do 124 | ctx = GPGME::Ctx.new(ignore_mdc_error: true) 125 | assert ctx.ignore_mdc_error 126 | 127 | ctx.set_ctx_flag("ignore-mdc-error", "0") 128 | refute ctx.ignore_mdc_error 129 | end 130 | 131 | it "can set flags without getters and setters" do 132 | ctx = GPGME::Ctx.new 133 | 134 | assert_equal "", ctx.get_ctx_flag("auto-key-locate") 135 | ctx.set_ctx_flag("auto-key-locate", "cert") 136 | assert_equal "cert", ctx.get_ctx_flag("auto-key-locate") 137 | end 138 | 139 | it "raises an error when a flag doesn't exist" do 140 | ctx = GPGME::Ctx.new 141 | 142 | assert_raises GPGME::Error do 143 | ctx.set_ctx_flag("foo", "bar") 144 | end 145 | end 146 | end 147 | 148 | describe :armor do 149 | it "sets false by default" do 150 | ctx = GPGME::Ctx.new 151 | refute ctx.armor 152 | end 153 | 154 | it "can set" do 155 | ctx = GPGME::Ctx.new 156 | ctx.armor = true 157 | assert ctx.armor 158 | end 159 | 160 | it "can set and get armor" do 161 | ctx = GPGME::Ctx.new(:armor => false) 162 | refute ctx.armor 163 | ctx = GPGME::Ctx.new(:armor => true) 164 | assert ctx.armor 165 | end 166 | end 167 | 168 | describe :ignore_mdc_error do 169 | it "sets false by default" do 170 | ctx = GPGME::Ctx.new 171 | refute ctx.ignore_mdc_error 172 | end 173 | 174 | it "can set" do 175 | ctx = GPGME::Ctx.new 176 | 177 | ctx.ignore_mdc_error = true 178 | assert ctx.ignore_mdc_error 179 | end 180 | 181 | it "can unset" do 182 | ctx = GPGME::Ctx.new(ignore_mdc_error: true) 183 | assert ctx.ignore_mdc_error 184 | 185 | ctx.ignore_mdc_error = false 186 | refute ctx.ignore_mdc_error 187 | end 188 | 189 | it "can set and get in constructor" do 190 | ctx = GPGME::Ctx.new(:ignore_mdc_error => false) 191 | refute ctx.ignore_mdc_error 192 | 193 | ctx = GPGME::Ctx.new(:ignore_mdc_error => true) 194 | assert ctx.ignore_mdc_error 195 | end 196 | end 197 | 198 | describe :protocol do 199 | it "sets 0 by default" do 200 | ctx = GPGME::Ctx.new 201 | assert_equal 0, ctx.protocol 202 | end 203 | 204 | it "can set" do 205 | ctx = GPGME::Ctx.new 206 | ctx.protocol = 1 207 | assert_equal 1, ctx.protocol 208 | end 209 | 210 | it "can set and get protocol" do 211 | ctx = GPGME::Ctx.new(:protocol => GPGME::PROTOCOL_OpenPGP) 212 | assert_equal GPGME::PROTOCOL_OpenPGP, ctx.protocol 213 | end 214 | 215 | it "doesn't allow just any value" do 216 | assert_raises GPGME::Error::InvalidValue do 217 | ctx = GPGME::Ctx.new(:protocol => -200) 218 | end 219 | end 220 | end 221 | 222 | describe :textmode do 223 | it "sets false by default" do 224 | ctx = GPGME::Ctx.new 225 | refute ctx.textmode 226 | end 227 | 228 | it "can set" do 229 | ctx = GPGME::Ctx.new 230 | ctx.textmode = true 231 | assert ctx.textmode 232 | end 233 | 234 | it "can set and get textmode" do 235 | ctx = GPGME::Ctx.new(:textmode => false) 236 | refute ctx.textmode 237 | ctx = GPGME::Ctx.new(:textmode => true) 238 | assert ctx.textmode 239 | end 240 | end 241 | 242 | describe :keylist_mode do 243 | it "sets local by default" do 244 | ctx = GPGME::Ctx.new 245 | assert_equal GPGME::KEYLIST_MODE_LOCAL, ctx.keylist_mode 246 | end 247 | 248 | it "can set and get" do 249 | ctx = GPGME::Ctx.new(:keylist_mode => GPGME::KEYLIST_MODE_SIGS) 250 | assert_equal GPGME::KEYLIST_MODE_SIGS, ctx.keylist_mode 251 | end 252 | 253 | it "can set" do 254 | ctx = GPGME::Ctx.new 255 | ctx.keylist_mode = GPGME::KEYLIST_MODE_SIGS 256 | assert_equal GPGME::KEYLIST_MODE_SIGS, ctx.keylist_mode 257 | end 258 | 259 | it "allows the four possible values" do 260 | [GPGME::KEYLIST_MODE_LOCAL, GPGME::KEYLIST_MODE_EXTERN, 261 | GPGME::KEYLIST_MODE_SIGS, GPGME::KEYLIST_MODE_VALIDATE].each do |mode| 262 | GPGME::Ctx.new(:keylist_mode => mode) 263 | end 264 | end 265 | 266 | # It's not crashing? 267 | # it "crashes with other values" do 268 | # GPGME::Ctx.new(:keylist_mode => -200) 269 | # end 270 | end 271 | 272 | # describe :set_passphrase_callback do 273 | # def test_pass_func(par1,par2,par3,par4,par5) 274 | # par1 275 | # end 276 | 277 | # test "it sets the passphrase" 278 | 279 | # end 280 | 281 | describe "keylist operations" do 282 | it "can return all of the keys" do 283 | ctx = GPGME::Ctx.new 284 | keys = ctx.keys 285 | ctx.release 286 | 287 | assert keys.size >= 4 288 | KEYS.each do |key| 289 | assert keys.map(&:email).include?(key[:sha]) 290 | end 291 | end 292 | 293 | it "can return keys filtering by a pattern" do 294 | ctx = GPGME::Ctx.new 295 | keys = ctx.keys(KEYS.first[:sha]) 296 | ctx.release 297 | 298 | assert_equal 1, keys.size 299 | assert_equal KEYS.first[:sha], keys.first.email 300 | end 301 | 302 | it "can return only secret keys" do 303 | ctx = GPGME::Ctx.new 304 | keys = ctx.keys(KEYS.first[:sha], true) 305 | ctx.release 306 | 307 | assert keys.all?(&:secret?) 308 | end 309 | 310 | it "can return only public keys" do 311 | ctx = GPGME::Ctx.new 312 | keys = ctx.keys(KEYS.first[:sha], false) 313 | ctx.release 314 | 315 | refute keys.any?(&:secret?) 316 | end 317 | 318 | it "returns only public keys by default" do 319 | ctx = GPGME::Ctx.new 320 | keys = ctx.keys(KEYS.first[:sha]) 321 | ctx.release 322 | 323 | refute keys.any?(&:secret?) 324 | end 325 | 326 | it "can iterate through them returning only public keys" do 327 | GPGME::Ctx.new do |ctx| 328 | ctx.each_key do |key| 329 | assert_instance_of GPGME::Key, key 330 | refute key.secret? 331 | end 332 | end 333 | end 334 | 335 | it "can iterate through them getting only secret ones" do 336 | GPGME::Ctx.new do |ctx| 337 | ctx.each_key("", true) do |key| 338 | assert_instance_of GPGME::Key, key 339 | assert key.secret? 340 | end 341 | end 342 | end 343 | 344 | it "can iterate through them filtering by pattern" do 345 | num = 0 346 | GPGME::Ctx.new do |ctx| 347 | ctx.each_key(KEYS.first[:sha]) do |key| 348 | assert_instance_of GPGME::Key, key 349 | assert_equal KEYS.first[:sha], key.email 350 | num += 1 351 | end 352 | end 353 | assert_equal 1, num 354 | end 355 | 356 | it "can get only a specific key" do 357 | GPGME::Ctx.new do |ctx| 358 | key = ctx.get_key(KEYS.first[:sha]) 359 | assert_instance_of GPGME::Key, key 360 | assert_equal KEYS.first[:sha], key.email 361 | end 362 | end 363 | end 364 | 365 | describe "key generation" do 366 | it "generates a key according to specifications" do 367 | key = <<-RUBY 368 | 369 | Key-Type: DSA 370 | Key-Length: 1024 371 | Subkey-Type: ELG-E 372 | Subkey-Length: 1024 373 | Name-Real: Key Testér 374 | Name-Comment: with some comments 375 | Name-Email: test_generation@example.com 376 | Expire-Date: 0 377 | Passphrase: wadus 378 | 379 | RUBY 380 | 381 | if RUBY_VERSION > "1.9" 382 | assert_equal key.encoding, Encoding::UTF_8 383 | end 384 | 385 | keys_amount = GPGME::Key.find(:public).size 386 | GPGME::Ctx.new do |ctx| 387 | ctx.generate_key(key.chomp) 388 | end 389 | 390 | assert_equal keys_amount + 1, GPGME::Key.find(:public).size 391 | 392 | GPGME::Key.find(:public, "test_generation@example.com").each do |k| 393 | 394 | if RUBY_VERSION > "1.9" 395 | # Make sure UTF-8 in and UTF-8 out. 396 | assert_equal "Key Testér", k.name 397 | assert_equal k.name.encoding, Encoding::UTF_8 398 | end 399 | k.delete!(true) 400 | end 401 | end 402 | end 403 | 404 | describe "key export/import" do 405 | it "exports and imports all keys when passing an empty string" do 406 | original_keys = GPGME::Key.find(:public) 407 | export = "" 408 | GPGME::Ctx.new do |ctx| 409 | export = ctx.export_keys("") 410 | end 411 | export.seek(0) 412 | 413 | GPGME::Key.find(:public).each{|k| k.delete!(true)} 414 | assert_equal 0, GPGME::Key.find(:public).size 415 | 416 | result = GPGME::Key.import(export) 417 | current_keys = GPGME::Key.find(:public) 418 | assert_equal original_keys.size, current_keys.size 419 | assert_equal result.imports.size, current_keys.size 420 | assert result.imports.all?{|import| import.status == 1} 421 | 422 | assert_equal original_keys.map(&:sha), original_keys.map(&:sha) 423 | 424 | import_keys # If the test fails for some reason, it won't break others. 425 | end 426 | 427 | it "exports a minimal key if given the mode" do 428 | remove_all_keys 429 | GPGME::Key.import(KEY_WITH_SIGNATURE[:public]) 430 | key = GPGME::Key.find(KEY_WITH_SIGNATURE[:sha]).first 431 | output_normal = GPGME::Data.new 432 | output_minimal = GPGME::Data.new 433 | ctx = GPGME::Ctx.new 434 | 435 | ctx.export_keys(key.sha, output_normal) 436 | ctx.export_keys(key.sha, output_minimal, 4) 437 | 438 | output_normal.seek(0) 439 | output_minimal.seek(0) 440 | 441 | assert_equal output_normal.read.size, 849 442 | assert_equal output_minimal.read.size, 668 443 | 444 | import_keys # If the test fails for some reason, it won't break others. 445 | end 446 | 447 | it "exports only one key" do 448 | original_keys = GPGME::Key.find(:public) 449 | key = original_keys.first 450 | export = "" 451 | GPGME::Ctx.new do |ctx| 452 | export = ctx.export_keys(key.sha) 453 | end 454 | export.seek(0) 455 | 456 | key.delete!(true) 457 | 458 | result = GPGME::Key.import(export) 459 | assert_equal 1, result.imports.size 460 | 461 | import = result.imports.first 462 | 463 | imported_key = GPGME::Key.find(:public, import.fpr).first 464 | assert_equal key.sha, imported_key.sha 465 | assert_equal key.email, imported_key.email 466 | import_keys # If the test fails for some reason, it won't break others. 467 | end 468 | 469 | it "imports keys and can get a result object" do 470 | without_key KEYS.last do 471 | public_amount = GPGME::Key.find(:public).size 472 | secret_amount = GPGME::Key.find(:secret).size 473 | 474 | result = nil 475 | GPGME::Ctx.new do |ctx| 476 | ctx.import_keys(GPGME::Data.new(KEYS.last[:public])) 477 | ctx.import_keys(GPGME::Data.new(KEYS.last[:secret])) 478 | 479 | result = ctx.import_result 480 | end 481 | 482 | assert_equal secret_amount + 1, GPGME::Key.find(:secret).size 483 | assert_equal public_amount + 1, GPGME::Key.find(:public).size 484 | assert_instance_of GPGME::ImportResult, result 485 | assert_instance_of GPGME::ImportStatus, result.imports.first 486 | end 487 | end 488 | end 489 | 490 | describe "deleting/editing of keys" do 491 | it "can delete keys" do 492 | original_keys = GPGME::Key.find(:public) 493 | key = original_keys.first 494 | 495 | GPGME::Ctx.new do |ctx| 496 | ctx.delete_key key, true 497 | end 498 | 499 | assert_empty GPGME::Key.find(:public, key.sha) 500 | import_keys 501 | end 502 | 503 | it "raises error if there's a secret key attached but secret key deletion isn't marked" do 504 | original_keys = GPGME::Key.find(:public) 505 | key = original_keys.first 506 | 507 | assert_raises GPGME::Error::Conflict do 508 | GPGME::Ctx.new do |ctx| 509 | ctx.delete_key key 510 | end 511 | end 512 | end 513 | end 514 | 515 | # Don't know how to test or use edit_key and edit_card 516 | end 517 | -------------------------------------------------------------------------------- /test/data_test.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require 'test_helper' 3 | 4 | describe GPGME::Data do 5 | describe :new do 6 | it "smartly creates an empty buffer if nothing passed" do 7 | data = GPGME::Data.new 8 | assert_instance_of GPGME::Data, data 9 | assert_respond_to data, :read 10 | assert_respond_to data, :write 11 | end 12 | 13 | it "doesn't create a new object if the object passed is a Data already" do 14 | data = GPGME::Data.new 15 | new_data = GPGME::Data.new(data) 16 | 17 | assert_equal data, new_data 18 | end 19 | 20 | it "creates a data from strings" do 21 | data = GPGME::Data.new("wadus") 22 | assert_equal "wadus", data.read 23 | end 24 | 25 | it "creates a data from a file" do 26 | # magic fromfile 27 | data = GPGME::Data.new(File.open(__FILE__)) 28 | assert_match(/magic fromfile/, data.read) 29 | end 30 | 31 | it "creates a data from file descriptor" do 32 | # magic filedescriptor 33 | File.open(__FILE__) do |f| 34 | data = GPGME::Data.new(f.fileno) 35 | assert_match(/magic filedescriptor/, data.read) 36 | end 37 | end 38 | end 39 | 40 | describe :read do 41 | it "allows to read only a length of the object" do 42 | data = GPGME::Data.new("wadus") 43 | assert_equal "wad", data.read(3) 44 | end 45 | 46 | it "returns nil if reading 0 length" do 47 | data = GPGME::Data.new("wadus") 48 | assert_nil data.read(0) 49 | end 50 | 51 | it "returns the full thing if reading without parameter" do 52 | data = GPGME::Data.new("wadus") 53 | assert_equal "wadus", data.read 54 | end 55 | end 56 | 57 | ## 58 | # We consider seek tested by these ones, since we have to seek(0) before 59 | # reading. 60 | describe :write do 61 | it "writes data to it" do 62 | data = GPGME::Data.new 63 | data.write("wadus") 64 | data.seek(0) 65 | assert_equal "wadus", data.read 66 | end 67 | 68 | it "writes data to it, specifying the length of the things to write" do 69 | data = GPGME::Data.new 70 | data.write("wadus", 5) 71 | data.seek(0) 72 | assert_equal "wadus", data.read 73 | end 74 | 75 | it "writes only a limited part if specified a small number" do 76 | data = GPGME::Data.new 77 | data.write("wadus", 3) 78 | data.seek(0) 79 | assert_equal "wad", data.read 80 | end 81 | 82 | # TODO: test doesn't pass, I believe there might be a security issue here, 83 | # random crap is written to the buffer if a longer size is passed. 84 | # 85 | # it "writes only the full data passed even if the length is bigger" do 86 | # data = GPGME::Data.new 87 | # data.write("wadus", 100) 88 | # data.seek(0) 89 | # assert_equal "wadus", data.read 90 | # end 91 | end 92 | 93 | describe :encoding do 94 | it "has encoding 0 by default (DATA_ENCODING_NONE)" do 95 | data = GPGME::Data.new("wadus") 96 | assert_equal GPGME::DATA_ENCODING_NONE, data.encoding 97 | end 98 | 99 | it "can set encodings" do 100 | data = GPGME::Data.new("wadus") 101 | [ GPGME::DATA_ENCODING_ARMOR, GPGME::DATA_ENCODING_BASE64, 102 | GPGME::DATA_ENCODING_BINARY,GPGME::DATA_ENCODING_NONE ].each do |encoding| 103 | data.encoding = encoding 104 | assert_equal encoding, data.encoding 105 | end 106 | end 107 | 108 | it "breaks if not set a proper encoding value" do 109 | data = GPGME::Data.new("wadus") 110 | assert_raises GPGME::Error::InvalidValue do 111 | data.encoding = 64 112 | end 113 | end 114 | end 115 | 116 | describe :file_name do 117 | it "has no name by default" do 118 | data = GPGME::Data.new("wadus") 119 | assert_nil data.file_name 120 | end 121 | 122 | it "can set file_name" do 123 | data = GPGME::Data.new("wadus") 124 | [ "foo.bar", nil ].each do |file_name| 125 | data.file_name = file_name 126 | assert_equal file_name, data.file_name 127 | end 128 | end 129 | end 130 | 131 | describe :to_s do 132 | it "returns the entire content of data" do 133 | data = GPGME::Data.new("wadus") 134 | data.read 135 | old_pos = data.seek(0, IO::SEEK_CUR) 136 | assert_equal "wadus", data.to_s 137 | new_pos = data.seek(0, IO::SEEK_CUR) 138 | assert_equal old_pos, new_pos 139 | end 140 | end 141 | end 142 | 143 | -------------------------------------------------------------------------------- /test/files/testkey_pub.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1.4.11 (Darwin) 3 | 4 | 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 51 | =T/Ce 52 | -----END PGP PUBLIC KEY BLOCK----- 53 | -------------------------------------------------------------------------------- /test/files/testkey_pub_invalid.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1.4.11 (Darwin) 3 | 4 | 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 51 | =T/Ce 52 | -----END PGP PUBLIC KEY BLOCK----- 53 | -------------------------------------------------------------------------------- /test/files/testkey_sec.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | Version: GnuPG v1.4.11 (Darwin) 3 | 4 | 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 52 | u+DMAdQjSwNi0/7uqGbnAyZuRpBaKWJu9s4YRdU= 53 | =kQA4 54 | -----END PGP PRIVATE KEY BLOCK----- 55 | -------------------------------------------------------------------------------- /test/gpgme_test.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require 'test_helper' 3 | 4 | describe GPGME do 5 | it "should pass" do 6 | assert true 7 | end 8 | 9 | it "should also pass" do 10 | assert true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/key_test.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require 'test_helper' 3 | 4 | describe GPGME::Key do 5 | before do 6 | skip unless ensure_keys GPGME::PROTOCOL_OpenPGP 7 | end 8 | 9 | it "has certain attributes" do 10 | key = GPGME::Key.find(:secret).first 11 | [:keylist_mode, :protocol, :owner_trust, :issuer_serial, 12 | :issuer_name, :chain_id, :subkeys, :uids].each do |attrib| 13 | assert key.respond_to?(attrib), "Key doesn't respond to #{attrib}" 14 | end 15 | end 16 | 17 | it "won't allow the creation of GPGME::Key's without the C API" do 18 | assert_raises NoMethodError do 19 | GPGME::Key.new 20 | end 21 | end 22 | 23 | it "can compare one with another even though if they're not the same object" do 24 | key1 = GPGME::Key.find(:secret).first 25 | key2 = GPGME::Key.find(:secret).first 26 | refute_equal key1.object_id, key2.object_id 27 | assert_equal key1, key2 28 | end 29 | 30 | it "can compare a key with any other object" do 31 | key1 = GPGME::Key.find(:secret).first 32 | refute_equal key1, nil 33 | end 34 | 35 | describe :find do 36 | it "should return all by default" do 37 | keys = GPGME::Key.find :secret 38 | assert_instance_of GPGME::Key, keys.first 39 | assert 0 < keys.size 40 | end 41 | 42 | it "returns an array even if you pass only one descriptor" do 43 | keys_one = GPGME::Key.find(:secret, KEYS.first[:sha]).map{|key| key.subkeys.map(&:keyid)} 44 | keys_array = GPGME::Key.find(:secret, [KEYS.first[:sha]]).map{|key| key.subkeys.map(&:keyid)} 45 | assert_equal keys_one, keys_array 46 | end 47 | 48 | it "returns only secret keys if told to do so" do 49 | keys = GPGME::Key.find :secret 50 | assert keys.all?(&:secret?) 51 | end 52 | 53 | it "returns only public keys if told to do so" do 54 | keys = GPGME::Key.find :public 55 | assert keys.none?(&:secret?) 56 | end 57 | 58 | it "filters by capabilities" do 59 | GPGME::Key.any_instance.stubs(:usable_for?).returns(false) 60 | keys = GPGME::Key.find :public, "", :wadusing 61 | assert keys.empty? 62 | end 63 | end 64 | 65 | describe :export do 66 | # Testing the lazy way with expectations. I think tests in 67 | # the Ctx class are enough. 68 | it "exports any key that matches the pattern" do 69 | GPGME::Ctx.any_instance.expects(:export_keys).with("", anything, 0) 70 | GPGME::Key.export("") 71 | end 72 | 73 | it "exports any key that matches the pattern, can specify output" do 74 | data = GPGME::Data.new 75 | GPGME::Ctx.any_instance.expects(:export_keys).with("wadus", data, 0) 76 | ret = GPGME::Key.export("wadus", :output => data) 77 | assert_equal data, ret 78 | end 79 | 80 | it "can specify options for Ctx" do 81 | GPGME::Ctx.expects(:new).with(:armor => true).yields(mock(:export_keys => true)) 82 | GPGME::Key.export("wadus", :armor => true) 83 | end 84 | 85 | it "can export a minimal key" do 86 | GPGME::Ctx.any_instance.expects(:export_keys).with("wadus", anything, 4) 87 | GPGME::Key.export("wadus", :minimal => true) 88 | end 89 | end 90 | 91 | describe "#export" do 92 | it "can export from the key instance" do 93 | key = GPGME::Key.find(:public).first 94 | GPGME::Key.expects(:export).with(key.sha, {}) 95 | 96 | key.export 97 | end 98 | 99 | it "can export from the key instance passing variables" do 100 | key = GPGME::Key.find(:public).first 101 | GPGME::Key.expects(:export).with(key.sha, {:armor => true}) 102 | 103 | key.export :armor => true 104 | end 105 | end 106 | 107 | describe :import do 108 | it "can import keys" do 109 | data = GPGME::Data.new 110 | GPGME::Ctx.any_instance.expects(:import_keys).with(data) 111 | GPGME::Ctx.any_instance.expects(:import_result).returns("wadus") 112 | 113 | assert_equal "wadus", GPGME::Key.import(data) 114 | end 115 | 116 | it "can specify options for Ctx" do 117 | GPGME::Ctx.expects(:new).with(:armor => true).yields(mock(:import_keys => true, :import_result => true)) 118 | GPGME::Key.import("wadus", :armor => true) 119 | end 120 | end 121 | 122 | # describe :trust do 123 | # it "returns :revoked if it is so" 124 | # it "returns :expired if it is expired" 125 | # it "returns :disabled if it is so" 126 | # it "returns :invalid if it is so" 127 | # it "returns nil otherwise" 128 | # end 129 | 130 | # describe :capability do 131 | # it "returns an array of possible capabilities" 132 | # end 133 | 134 | # describe :secret? do 135 | # "returns true/false depending on the instance variable" 136 | # end 137 | 138 | describe :usable_for? do 139 | it "checks for the capabilities of the key and returns true if it matches all" do 140 | key = GPGME::Key.find(:secret).first 141 | 142 | key.stubs(:capability).returns([:encrypt, :sign]) 143 | assert key.usable_for?([]) 144 | 145 | key.stubs(:capability).returns([:encrypt, :sign]) 146 | assert key.usable_for?([:encrypt]) 147 | 148 | key.stubs(:capability).returns([:encrypt, :sign]) 149 | refute key.usable_for?([:certify]) 150 | end 151 | 152 | it "returns false if the key is expired or revoked or disabled or disabled" do 153 | key = GPGME::Key.find(:secret).first 154 | key.stubs(:trust).returns(:revoked) 155 | key.stubs(:capability).returns([:encrypt, :sign]) 156 | refute key.usable_for?([:encrypt]) 157 | end 158 | end 159 | 160 | describe :delete! do 161 | it "deletes the key itself and its secret one if called with true" do 162 | begin 163 | key = KEYS.first 164 | GPGME::Key.find(:public, key[:sha]).first.delete!(true) 165 | 166 | assert_empty GPGME::Key.find(:public, key[:sha]) 167 | assert_empty GPGME::Key.find(:secret, key[:sha]) 168 | ensure 169 | import_key key 170 | end 171 | end 172 | 173 | it "raises GPGME::Error::Conflict if we're deleting a key that is secret" do 174 | key = KEYS.first 175 | assert_raises GPGME::Error::Conflict do 176 | GPGME::Key.find(:secret, key[:sha]).first.delete! 177 | end 178 | refute_empty GPGME::Key.find(:secret, key[:sha]) 179 | end 180 | end 181 | 182 | it "knows if the key is expired" do 183 | key = GPGME::Key.find(:secret).first 184 | refute key.expired 185 | 186 | with_key EXPIRED_KEY do 187 | key = GPGME::Key.find(:secret, EXPIRED_KEY[:sha]).first 188 | assert key.expired if key 189 | end 190 | end 191 | 192 | it "returns the expiry date of the first subkey" do 193 | key = GPGME::Key.find(:secret).first 194 | subkey = key.primary_subkey 195 | subkey.expects(:expired).returns(true) 196 | 197 | assert key.expired 198 | end 199 | 200 | describe :inspect do 201 | it "can be inspected" do 202 | key = GPGME::Key.find(:secret).first 203 | key.inspect 204 | end 205 | end 206 | 207 | describe :valid? do 208 | it "returns true on a valid key" do 209 | valid_key = File.read("test/files/testkey_pub.gpg") 210 | assert GPGME::Key.valid?(valid_key) 211 | end 212 | 213 | it "returns false on an invalid key" do 214 | invalid_key = File.read("test/files/testkey_pub_invalid.gpg") 215 | assert !GPGME::Key.valid?(invalid_key) 216 | end 217 | end 218 | 219 | describe :to_s do 220 | it "can be coerced into a String" do 221 | key = GPGME::Key.find(:secret).first 222 | key.to_s 223 | end 224 | end 225 | end 226 | -------------------------------------------------------------------------------- /test/pinentry: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Dummy pinentry 3 | # 4 | # Copyright 2008 g10 Code GmbH 5 | # 6 | # This file is free software; as a special exception the author gives 7 | # unlimited permission to copy and/or distribute it, with or without 8 | # modifications, as long as this notice is preserved. 9 | # 10 | # This file is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the 12 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 13 | # PURPOSE. 14 | 15 | echo OK Your orders please 16 | 17 | while read cmd; do 18 | case $cmd in 19 | GETPIN) echo D gpgme; echo OK;; 20 | *) echo OK;; 21 | esac 22 | done 23 | -------------------------------------------------------------------------------- /test/signature_test.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require 'test_helper' 3 | 4 | describe GPGME::Signature do 5 | before do 6 | skip unless ensure_keys GPGME::PROTOCOL_OpenPGP 7 | end 8 | 9 | it "#valid? is true when the signature is valid" do 10 | crypto = GPGME::Crypto.new 11 | signatures = 0 12 | sign = crypto.sign "Hi there" 13 | 14 | crypto.verify(sign) do |signature| 15 | assert_instance_of GPGME::Signature, signature 16 | assert signature.valid? 17 | refute signature.expired_signature? 18 | refute signature.expired_key? 19 | refute signature.revoked_key? 20 | refute signature.bad? 21 | refute signature.no_key? 22 | signatures += 1 23 | end 24 | 25 | assert_equal 1, signatures 26 | end 27 | 28 | it "#expired_key? is true when the key has expired" do 29 | with_key EXPIRED_KEY do 30 | crypto = GPGME::Crypto.new 31 | signatures = 0 32 | crypto.verify(TEXT[:expired_key_sign]) do |signature| 33 | assert_instance_of GPGME::Signature, signature 34 | refute signature.valid? 35 | refute signature.expired_signature? 36 | assert signature.expired_key? 37 | refute signature.revoked_key? 38 | refute signature.bad? 39 | refute signature.no_key? 40 | signatures += 1 41 | end 42 | 43 | assert_equal 1, signatures 44 | end 45 | end 46 | 47 | # TODO Find how to test these 48 | # it "#expired_signature? is true when the signature has expired" 49 | # it "#revoked_key? is true when the key has been revoked" 50 | # it "#bad? is true when the signature is bad" 51 | # it "#no_key? is true when we don't have the key to verify the signature" 52 | end 53 | -------------------------------------------------------------------------------- /test/sub_key_test.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require 'test_helper' 3 | 4 | describe GPGME::SubKey do 5 | before do 6 | skip unless ensure_keys GPGME::PROTOCOL_OpenPGP 7 | end 8 | 9 | # We trust Key for common methods that come from KeyCommon 10 | 11 | it "has certain attributes" do 12 | subkey = GPGME::Key.find(:secret).first.primary_subkey 13 | [:pubkey_algo, :length, :keyid, :fpr, :fingerprint].each do |attrib| 14 | assert subkey.respond_to?(attrib), "Key doesn't respond to #{attrib}" 15 | end 16 | end 17 | 18 | it "won't allow the creation of GPGME::SubKey's without the C API" do 19 | assert_raises NoMethodError do 20 | GPGME::SubKey.new 21 | end 22 | end 23 | 24 | it "knows if the key is expired" do 25 | subkey = GPGME::Key.find(:secret).first.primary_subkey 26 | refute subkey.expired 27 | 28 | with_key EXPIRED_KEY do 29 | key = GPGME::Key.find(:secret, EXPIRED_KEY[:sha]).first 30 | if key 31 | subkey = key.primary_subkey 32 | assert subkey.expired 33 | end 34 | end 35 | end 36 | 37 | describe :inspect do 38 | it "can be inspected" do 39 | subkey = GPGME::Key.find(:secret).first.primary_subkey 40 | subkey.inspect 41 | end 42 | end 43 | 44 | describe :to_s do 45 | it "can be coerced into a String" do 46 | subkey = GPGME::Key.find(:secret).first.primary_subkey 47 | subkey.to_s 48 | end 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | require 'coveralls' 4 | Coveralls.wear! 5 | 6 | # include compiled gpgme_n.bundle 7 | tmp_dir = File.join(File.dirname(__FILE__), '..', 'tmp') 8 | $:.unshift(tmp_dir) if File.directory?(tmp_dir) 9 | 10 | # this interfers otherwise with our tests 11 | ENV.delete('GPG_AGENT_INFO') 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | require 'minitest/autorun' 16 | require 'minitest/spec' 17 | require 'minitest/pride' 18 | require 'mocha' 19 | require 'gpgme' 20 | 21 | if RUBY_VERSION.split('.').first.to_i > 1 22 | require 'byebug' 23 | else 24 | require 'ruby-debug' 25 | end 26 | 27 | require File.dirname(__FILE__) + "/support/resources" 28 | 29 | 30 | def import_keys 31 | KEYS.each do |key| 32 | import_key(key) 33 | end 34 | end 35 | 36 | def import_key(key, only = :all) 37 | GPGME::Key.import(key[:public]) unless only == :secret 38 | GPGME::Key.import(key[:secret]) unless only == :public 39 | end 40 | 41 | def remove_keys 42 | KEYS.each do |key| 43 | remove_key(key) 44 | end 45 | end 46 | 47 | def remove_all_keys 48 | GPGME::Key.find(:public).each do |k| 49 | k.delete!(true) 50 | end 51 | GPGME::Key.find(:secret).each do |k| 52 | k.delete!(true) 53 | end 54 | end 55 | 56 | def remove_key(key) 57 | GPGME::Key.find(:public, key[:sha]).each do |k| 58 | k.delete!(true) 59 | end 60 | GPGME::Key.find(:secret, key[:sha]).each do |k| 61 | k.delete!(true) 62 | end 63 | end 64 | 65 | def with_key(key, only = :all, &block) 66 | import_key key, only 67 | 68 | begin 69 | yield 70 | ensure 71 | remove_key key 72 | end 73 | end 74 | 75 | def without_key(key, &block) 76 | remove_key key 77 | 78 | begin 79 | yield 80 | ensure 81 | import_key key 82 | end 83 | end 84 | 85 | DIRS = [] 86 | 87 | at_exit do 88 | DIRS.each do |dir| 89 | FileUtils.remove_entry dir 90 | end 91 | DIRS.clear 92 | end 93 | 94 | def ensure_keys(proto) 95 | return false unless GPGME::Engine.check_version proto 96 | 97 | case proto 98 | when GPGME::PROTOCOL_OpenPGP 99 | # We use a different home directory for the keys to not disturb current 100 | # installation 101 | require 'tmpdir' 102 | require 'pathname' 103 | 104 | if DIRS.empty? 105 | dir = Dir.mktmpdir 106 | GPGME::Engine.home_dir = dir 107 | DIRS.push(dir) 108 | pinentry = Pathname.new(__FILE__).dirname + 'pinentry' 109 | gpg_agent_conf = Pathname.new(dir) + 'gpg-agent.conf' 110 | gpg_agent_conf.open('w+') {|io| 111 | io.write("pinentry-program #{pinentry}\n") 112 | } 113 | remove_all_keys 114 | import_keys 115 | end 116 | true 117 | else 118 | return false 119 | end 120 | end 121 | --------------------------------------------------------------------------------