├── .classpath ├── .gitignore ├── .jcop ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── LICENSE ├── README.md ├── build.properties ├── build.xml ├── lib ├── api.jar ├── api_export_files │ ├── java │ │ ├── io │ │ │ └── javacard │ │ │ │ └── io.exp │ │ ├── lang │ │ │ └── javacard │ │ │ │ └── lang.exp │ │ └── rmi │ │ │ └── javacard │ │ │ └── rmi.exp │ ├── javacard │ │ ├── framework │ │ │ ├── javacard │ │ │ │ └── framework.exp │ │ │ └── service │ │ │ │ └── javacard │ │ │ │ └── service.exp │ │ └── security │ │ │ └── javacard │ │ │ └── security.exp │ └── javacardx │ │ └── crypto │ │ └── javacard │ │ └── crypto.exp ├── converter.jar ├── gpj.jar ├── jctasks.jar └── offcardverifier.jar └── src └── openpgpcard ├── OpenPGPApplet.java ├── OpenPGPSecureMessaging.java └── PGPKey.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | -------------------------------------------------------------------------------- /.jcop: -------------------------------------------------------------------------------- 1 | 1.0D276000124011.0D2760001240102000000000000010000 -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenPGP JavaCard 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | com.ibm.bluez.jcop.eclipse.jcopbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | com.ibm.bluez.jcop.eclipse.jcopnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.3 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore 11 | org.eclipse.jdt.core.compiler.source=1.3 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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) 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 along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Card OpenPGP Card 2 | 3 | This is a Java Card implementation of the OpenPGP smart card specifications. 4 | -------------------------------------------------------------------------------- /build.properties: -------------------------------------------------------------------------------- 1 | project.name=OpenPGP JavaCard 2 | 3 | base.dir=. 4 | bin.dir=bin 5 | lib.dir=lib 6 | src.dir=src 7 | 8 | jar.jctasks=jctasks.jar 9 | jar.gpj=gpj.jar 10 | 11 | cap.package=openpgpcard 12 | cap.package_aid=0xD2:0x76:0x00:0x01:0x24:0x01 13 | cap.applet=openpgpcard.OpenPGPApplet 14 | cap.applet_aid=0xD2:0x76:0x00:0x01:0x24:0x01:0x02:0x00:0x00:0x00:0x00:0x00:0x00:0x01:0x00:0x00 15 | 16 | gpj.sd_aid=0xA0:0x00:0x00:0x00:0x03:0x00:0x00:0x00 17 | 18 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /lib/api.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api.jar -------------------------------------------------------------------------------- /lib/api_export_files/java/io/javacard/io.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api_export_files/java/io/javacard/io.exp -------------------------------------------------------------------------------- /lib/api_export_files/java/lang/javacard/lang.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api_export_files/java/lang/javacard/lang.exp -------------------------------------------------------------------------------- /lib/api_export_files/java/rmi/javacard/rmi.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api_export_files/java/rmi/javacard/rmi.exp -------------------------------------------------------------------------------- /lib/api_export_files/javacard/framework/javacard/framework.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api_export_files/javacard/framework/javacard/framework.exp -------------------------------------------------------------------------------- /lib/api_export_files/javacard/framework/service/javacard/service.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api_export_files/javacard/framework/service/javacard/service.exp -------------------------------------------------------------------------------- /lib/api_export_files/javacard/security/javacard/security.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api_export_files/javacard/security/javacard/security.exp -------------------------------------------------------------------------------- /lib/api_export_files/javacardx/crypto/javacard/crypto.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/api_export_files/javacardx/crypto/javacard/crypto.exp -------------------------------------------------------------------------------- /lib/converter.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/converter.jar -------------------------------------------------------------------------------- /lib/gpj.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/gpj.jar -------------------------------------------------------------------------------- /lib/jctasks.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/jctasks.jar -------------------------------------------------------------------------------- /lib/offcardverifier.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/cada19e2a9dcd19398f9a856fd660850cbef5558/lib/offcardverifier.jar -------------------------------------------------------------------------------- /src/openpgpcard/OpenPGPApplet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Java Card implementation of the OpenPGP card 3 | * Copyright (C) 2011 Joeri de Ruiter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | package openpgpcard; 20 | 21 | import javacard.framework.*; 22 | import javacard.security.*; 23 | import javacardx.crypto.*; 24 | 25 | /** 26 | * AID of the applet should be according to the OpenPGP card standard v2.0.1 27 | * E.g. D2760001240102000000000000010000: 28 | * D276000124 - RID of FSFE 29 | * 01 - OpenPGP application 30 | * Version needs to be higher than 0.07, otherwise tags for fingerprints are of by one in gpg 31 | * 0200 - Version 32 | * 0000 - Manufacturer 33 | * 00000001 - Serial number 34 | * 0000 - RFU 35 | * 36 | * @author Joeri de Ruiter (joeri@cs.ru.nl) 37 | * @version $Revision: 13 $ by $Author: joeridr $ 38 | * $LastChangedDate: 2015-04-13 16:02:31 +0200 (Mon, 13 Apr 2015) $ 39 | */ 40 | public class OpenPGPApplet extends Applet implements ISO7816 { 41 | //TODO Check atomicity of all storage commands 42 | 43 | private static final short _0 = 0; 44 | 45 | private static final boolean FORCE_SM_GET_CHALLENGE = true; 46 | 47 | private static final byte[] HISTORICAL = { 0x00, 0x73, 0x00, 0x00, 48 | (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00 }; 50 | 51 | private static final byte[] EXTENDED_CAP = { 52 | (byte) 0xF0, // Support for GET CHALLENGE 53 | // Support for Key Import 54 | // PW1 Status byte changeable 55 | 0x00, // Secure messaging using 3DES 56 | 0x00, (byte) 0xFF, // Maximum length of challenges 57 | 0x00, (byte) 0xFF, // Maximum length Cardholder Certificate 58 | 0x00, (byte) 0xFF, // Maximum length command data 59 | 0x00, (byte) 0xFF // Maximum length response data 60 | }; 61 | 62 | private static short RESPONSE_MAX_LENGTH = 255; 63 | private static short RESPONSE_SM_MAX_LENGTH = 231; 64 | private static short CHALLENGES_MAX_LENGTH = 255; 65 | 66 | private static short BUFFER_MAX_LENGTH = 674; 67 | 68 | private static short LOGINDATA_MAX_LENGTH = 254; 69 | private static short URL_MAX_LENGTH = 254; 70 | private static short NAME_MAX_LENGTH = 39; 71 | private static short LANG_MAX_LENGTH = 8; 72 | private static short CERT_MAX_LENGTH = 500; 73 | 74 | private static byte PW1_MIN_LENGTH = 6; 75 | private static byte PW1_MAX_LENGTH = 127; 76 | // Default PW1 '123456' 77 | private static byte[] PW1_DEFAULT = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 }; 78 | private static byte PW1_MODE_NO81 = 0; 79 | private static byte PW1_MODE_NO82 = 0; 80 | 81 | private static final byte RC_MIN_LENGTH = 8; 82 | private static final byte RC_MAX_LENGTH = 127; 83 | 84 | private static final byte PW3_MIN_LENGTH = 8; 85 | private static final byte PW3_MAX_LENGTH = 127; 86 | // Default PW3 '12345678' 87 | private static final byte[] PW3_DEFAULT = { 0x31, 0x32, 0x33, 0x34, 0x35, 88 | 0x36, 0x37, 0x38 }; 89 | 90 | private byte[] loginData = new byte[LOGINDATA_MAX_LENGTH]; 91 | private short loginData_length = 0; 92 | 93 | private byte[] url = new byte[URL_MAX_LENGTH]; 94 | private short url_length = 0; 95 | 96 | private byte[] name = new byte[NAME_MAX_LENGTH]; 97 | private short name_length = 0; 98 | 99 | private byte[] lang = new byte[LANG_MAX_LENGTH]; 100 | private short lang_length = 0; 101 | 102 | private byte[] cert = new byte[CERT_MAX_LENGTH]; 103 | private short cert_length = 0; 104 | 105 | private byte sex = 0x39; 106 | 107 | private OwnerPIN pw1; 108 | private byte pw1_length = 0; 109 | private byte pw1_status = 0x00; 110 | private boolean[] pw1_modes; 111 | 112 | private OwnerPIN rc; 113 | private byte rc_length = 0; 114 | 115 | private OwnerPIN pw3; 116 | private byte pw3_length = 0; 117 | 118 | private byte[] ds_counter = { 0x00, 0x00, 0x00 }; 119 | 120 | private PGPKey sig_key; 121 | private PGPKey dec_key; 122 | private PGPKey auth_key; 123 | 124 | private byte[] ca1_fp = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 126 | 0x00 }; 127 | private byte[] ca2_fp = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 129 | 0x00 }; 130 | private byte[] ca3_fp = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 132 | 0x00 }; 133 | 134 | private Cipher cipher; 135 | private RandomData random; 136 | 137 | private byte[] tmp; 138 | 139 | private byte[] buffer; 140 | private short out_left = 0; 141 | private short out_sent = 0; 142 | private short in_received = 0; 143 | 144 | private boolean chain = false; 145 | private byte chain_ins = 0; 146 | private short chain_p1p2 = 0; 147 | 148 | private OpenPGPSecureMessaging sm; 149 | private boolean sm_success = false; 150 | 151 | public static void install(byte[] bArray, short bOffset, byte bLength) { 152 | new OpenPGPApplet().register(bArray, (short) (bOffset + 1), 153 | bArray[bOffset]); 154 | } 155 | 156 | public OpenPGPApplet() { 157 | // Create temporary arrays 158 | tmp = JCSystem.makeTransientByteArray(BUFFER_MAX_LENGTH, 159 | JCSystem.CLEAR_ON_DESELECT); 160 | buffer = JCSystem.makeTransientByteArray(BUFFER_MAX_LENGTH, 161 | JCSystem.CLEAR_ON_DESELECT); 162 | pw1_modes = JCSystem.makeTransientBooleanArray((short) 2, 163 | JCSystem.CLEAR_ON_DESELECT); 164 | 165 | // Initialize PW1 with default password 166 | pw1 = new OwnerPIN((byte) 3, PW1_MAX_LENGTH); 167 | pw1.update(PW1_DEFAULT, _0, (byte) PW1_DEFAULT.length); 168 | pw1_length = (byte) PW1_DEFAULT.length; 169 | 170 | // Initialize RC 171 | rc = new OwnerPIN((byte) 3, RC_MAX_LENGTH); 172 | 173 | // Initialize PW3 with default password 174 | pw3 = new OwnerPIN((byte) 3, PW3_MAX_LENGTH); 175 | pw3.update(PW3_DEFAULT, _0, (byte) PW3_DEFAULT.length); 176 | pw3_length = (byte) PW3_DEFAULT.length; 177 | 178 | // Create empty keys 179 | sig_key = new PGPKey(); 180 | dec_key = new PGPKey(); 181 | auth_key = new PGPKey(); 182 | 183 | // 184 | cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false); 185 | random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); 186 | 187 | // Initialize Secure Messaging 188 | sm = new OpenPGPSecureMessaging(); 189 | } 190 | 191 | public void process(APDU apdu) { 192 | if (selectingApplet()) { 193 | // Reset PW1 modes 194 | pw1_modes[PW1_MODE_NO81] = false; 195 | pw1_modes[PW1_MODE_NO82] = false; 196 | 197 | return; 198 | } 199 | 200 | byte[] buf = apdu.getBuffer(); 201 | byte cla= buf[OFFSET_CLA]; 202 | byte ins = buf[OFFSET_INS]; 203 | byte p1 = buf[OFFSET_P1]; 204 | byte p2 = buf[OFFSET_P2]; 205 | short p1p2 = Util.makeShort(p1, p2); 206 | short lc = (short) (buf[OFFSET_LC] & 0xFF); 207 | 208 | // Secure messaging 209 | //TODO Force SM if contactless is used 210 | sm_success = false; 211 | if ((byte) (cla & (byte) 0x0C) == (byte) 0x0C) { 212 | // Force initialization of SSC before using SM to prevent replays 213 | if(FORCE_SM_GET_CHALLENGE && !sm.isSetSSC() && (ins != (byte) 0x84)) ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 214 | 215 | lc = sm.unwrapCommandAPDU(); 216 | sm_success = true; 217 | } 218 | 219 | short status = SW_NO_ERROR; 220 | short le = 0; 221 | 222 | try { 223 | // Support for command chaining 224 | commandChaining(apdu); 225 | 226 | // Reset buffer for GET RESPONSE 227 | if (ins != (byte) 0xC0) { 228 | out_sent = 0; 229 | out_left = 0; 230 | } 231 | 232 | // Other instructions 233 | switch (ins) { 234 | // GET RESPONSE 235 | case (byte) 0xC0: 236 | // Will be handled in finally clause 237 | break; 238 | 239 | // VERIFY 240 | case (byte) 0x20: 241 | verify(apdu, p2); 242 | break; 243 | 244 | // CHANGE REFERENCE DATA 245 | case (byte) 0x24: 246 | changeReferenceData(apdu, p2); 247 | break; 248 | 249 | // RESET RETRY COUNTER 250 | case (byte) 0x2C: 251 | // Reset only available for PW1 252 | if (p2 != (byte) 0x81) 253 | ISOException.throwIt(SW_INCORRECT_P1P2); 254 | 255 | resetRetryCounter(apdu, p1); 256 | break; 257 | 258 | // PERFORM SECURITY OPERATION 259 | case (byte) 0x2A: 260 | // COMPUTE DIGITAL SIGNATURE 261 | if (p1p2 == (short) 0x9E9A) { 262 | le = computeDigitalSignature(apdu); 263 | } 264 | // DECIPHER 265 | else if (p1p2 == (short) 0x8086) { 266 | le = decipher(apdu); 267 | } else { 268 | ISOException.throwIt(SW_WRONG_P1P2); 269 | } 270 | 271 | break; 272 | 273 | // INTERNAL AUTHENTICATE 274 | case (byte) 0x88: 275 | le = internalAuthenticate(apdu); 276 | break; 277 | 278 | // GENERATE ASYMMETRIC KEY PAIR 279 | case (byte) 0x47: 280 | le = genAsymKey(apdu, p1); 281 | break; 282 | 283 | // GET CHALLENGE 284 | case (byte) 0x84: 285 | le = getChallenge(apdu, lc); 286 | break; 287 | 288 | // GET DATA 289 | case (byte) 0xCA: 290 | le = getData(p1p2); 291 | break; 292 | 293 | // PUT DATA 294 | case (byte) 0xDA: 295 | putData(p1p2); 296 | break; 297 | 298 | // DB - PUT DATA (Odd) 299 | case (byte) 0xDB: 300 | // Odd PUT DATA only supported for importing keys 301 | // 4D - Extended Header list 302 | if (p1p2 == (short) 0x3FFF) { 303 | importKey(apdu); 304 | } else { 305 | ISOException.throwIt(SW_RECORD_NOT_FOUND); 306 | } 307 | break; 308 | 309 | default: 310 | // good practice: If you don't know the INStruction, say so: 311 | ISOException.throwIt(SW_INS_NOT_SUPPORTED); 312 | } 313 | } 314 | catch(ISOException e) { 315 | status = e.getReason(); 316 | } 317 | finally { 318 | if(status != (short)0x9000) { 319 | // Send the exception that was thrown 320 | sendException(apdu, status); 321 | } 322 | else { 323 | // GET RESPONSE 324 | if (ins == (byte) 0xC0) { 325 | sendNext(apdu); 326 | } 327 | else { 328 | sendBuffer(apdu, le); 329 | } 330 | } 331 | } 332 | } 333 | 334 | /** 335 | * Provide support for command chaining by storing the received data in 336 | * buffer 337 | * 338 | * @param apdu 339 | */ 340 | private void commandChaining(APDU apdu) { 341 | byte[] buf = apdu.getBuffer(); 342 | short p1p2 = Util.makeShort(buf[OFFSET_P1], 343 | buf[OFFSET_P2]); 344 | short len = (short) (buf[OFFSET_LC] & 0xFF); 345 | 346 | // Reset chaining if it was not yet initiated 347 | if (!chain) 348 | resetChaining(); 349 | 350 | if ((byte) (buf[OFFSET_CLA] & (byte) 0x10) == (byte) 0x10) { 351 | // If chaining was already initiated, INS and P1P2 should match 352 | if (chain 353 | && (buf[OFFSET_INS] != chain_ins && p1p2 != chain_p1p2)) { 354 | resetChaining(); 355 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 356 | } 357 | 358 | // Check whether data to be received is larger than size of the 359 | // buffer 360 | if ((short) (in_received + len) > BUFFER_MAX_LENGTH) { 361 | resetChaining(); 362 | ISOException.throwIt(SW_WRONG_LENGTH); 363 | } 364 | 365 | // Store received data in buffer 366 | in_received = Util.arrayCopyNonAtomic(buf, OFFSET_CDATA, 367 | buffer, in_received, len); 368 | 369 | chain = true; 370 | chain_ins = buf[OFFSET_INS]; 371 | chain_p1p2 = p1p2; 372 | 373 | ISOException.throwIt(SW_NO_ERROR); 374 | } 375 | 376 | if (chain && buf[OFFSET_INS] == chain_ins && p1p2 == chain_p1p2) { 377 | chain = false; 378 | 379 | // Check whether data to be received is larger than size of the 380 | // buffer 381 | if ((short) (in_received + len) > BUFFER_MAX_LENGTH) { 382 | resetChaining(); 383 | ISOException.throwIt(SW_WRONG_LENGTH); 384 | } 385 | 386 | // Add received data to the buffer 387 | in_received = Util.arrayCopyNonAtomic(buf, OFFSET_CDATA, 388 | buffer, in_received, len); 389 | } else if (chain) { 390 | // Chained command expected 391 | resetChaining(); 392 | ISOException.throwIt(SW_UNKNOWN); 393 | } else { 394 | // No chaining was used, so copy data to buffer 395 | in_received = Util.arrayCopyNonAtomic(buf, OFFSET_CDATA, 396 | buffer, _0, len); 397 | } 398 | } 399 | 400 | private void resetChaining() { 401 | chain = false; 402 | in_received = 0; 403 | } 404 | 405 | /** 406 | * Provide the VERIFY command (INS 20) 407 | * 408 | * Verify one of the passwords depending on mode: - 81: PW1 for a PSO:CDS 409 | * command - 82: PW1 for other commands - 83: PW3 410 | * 411 | * @param apdu 412 | * @param mode 413 | * Password and mode to be verified 414 | */ 415 | private void verify(APDU apdu, byte mode) { 416 | if (mode == (byte) 0x81 || mode == (byte) 0x82) { 417 | // Check length of input 418 | if (in_received < PW1_MIN_LENGTH || in_received > PW1_MAX_LENGTH) 419 | ISOException.throwIt(SW_WRONG_LENGTH); 420 | 421 | // Check given PW1 and set requested mode if verified succesfully 422 | if (pw1.check(buffer, _0, (byte) in_received)) { 423 | if (mode == (byte) 0x81) 424 | pw1_modes[PW1_MODE_NO81] = true; 425 | else 426 | pw1_modes[PW1_MODE_NO82] = true; 427 | } else { 428 | ISOException 429 | .throwIt((short) (0x63C0 | pw1.getTriesRemaining())); 430 | } 431 | } else if (mode == (byte) 0x83) { 432 | // Check length of input 433 | if (in_received < PW3_MIN_LENGTH || in_received > PW3_MAX_LENGTH) 434 | ISOException.throwIt(SW_WRONG_LENGTH); 435 | 436 | // Check PW3 437 | if (!pw3.check(buffer, _0, (byte) in_received)) { 438 | ISOException 439 | .throwIt((short) (0x63C0 | pw3.getTriesRemaining())); 440 | } 441 | } else { 442 | ISOException.throwIt(SW_INCORRECT_P1P2); 443 | } 444 | } 445 | 446 | /** 447 | * Provide the CHANGE REFERENCE DATA command (INS 24) 448 | * 449 | * Change the password specified using mode: - 81: PW1 - 82: PW3 450 | * 451 | * @param apdu 452 | * @param mode 453 | * Password to be changed 454 | */ 455 | private void changeReferenceData(APDU apdu, byte mode) { 456 | if (mode == (byte) 0x81) { 457 | // Check length of the new password 458 | short new_length = (short) (in_received - pw1_length); 459 | if (new_length < PW1_MIN_LENGTH || new_length > PW1_MAX_LENGTH) 460 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 461 | 462 | if (!pw1.check(buffer, _0, pw1_length)) 463 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 464 | 465 | // Change PW1 466 | JCSystem.beginTransaction(); 467 | pw1.update(buffer, pw1_length, (byte) new_length); 468 | pw1_length = (byte) new_length; 469 | pw1_modes[PW1_MODE_NO81] = false; 470 | pw1_modes[PW1_MODE_NO82] = false; 471 | JCSystem.commitTransaction(); 472 | } else if (mode == (byte) 0x83) { 473 | // Check length of the new password 474 | short new_length = (short) (in_received - pw3_length); 475 | if (new_length < PW3_MIN_LENGTH || new_length > PW3_MAX_LENGTH) 476 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 477 | 478 | if (!pw3.check(buffer, _0, pw3_length)) 479 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 480 | 481 | // Change PW3 482 | JCSystem.beginTransaction(); 483 | pw3.update(buffer, pw3_length, (byte) new_length); 484 | pw3_length = (byte) new_length; 485 | JCSystem.commitTransaction(); 486 | } 487 | } 488 | 489 | /** 490 | * Provide the RESET RETRY COUNTER command (INS 2C) 491 | * 492 | * Reset PW1 either using the Resetting Code (mode = 00) or PW3 (mode = 02) 493 | * 494 | * @param apdu 495 | * @param mode 496 | * Mode used to reset PW1 497 | */ 498 | private void resetRetryCounter(APDU apdu, byte mode) { 499 | if (mode == (byte) 0x00) { 500 | // Authentication using RC 501 | if (rc_length == 0) 502 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 503 | 504 | short new_length = (short) (in_received - rc_length); 505 | if (new_length < PW1_MIN_LENGTH || new_length > PW1_MAX_LENGTH) 506 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 507 | 508 | if (!rc.check(buffer, _0, rc_length)) 509 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 510 | 511 | // Change PW1 512 | JCSystem.beginTransaction(); 513 | pw1.update(buffer, rc_length, (byte) new_length); 514 | pw1_length = (byte) new_length; 515 | JCSystem.commitTransaction(); 516 | } else if (mode == (byte) 0x02) { 517 | // Authentication using PW3 518 | if (!pw3.isValidated()) 519 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 520 | 521 | if (in_received < PW1_MIN_LENGTH || in_received > PW1_MAX_LENGTH) 522 | ISOException.throwIt(SW_WRONG_LENGTH); 523 | 524 | // Change PW1 525 | JCSystem.beginTransaction(); 526 | pw1.update(buffer, _0, (byte) in_received); 527 | pw1_length = (byte) in_received; 528 | JCSystem.commitTransaction(); 529 | } else { 530 | ISOException.throwIt(SW_WRONG_P1P2); 531 | } 532 | } 533 | 534 | /** 535 | * Provide the PSO: COMPUTE DIGITAL SIGNATURE command (INS 2A, P1P2 9E9A) 536 | * 537 | * Sign the data provided using the key for digital signatures. 538 | * 539 | * Before using this method PW1 has to be verified with mode No. 81. If the 540 | * first status byte of PW1 is 00, access condition PW1 with No. 81 is 541 | * reset. 542 | * 543 | * @param apdu 544 | * @return Length of data written in buffer 545 | */ 546 | private short computeDigitalSignature(APDU apdu) { 547 | if (!(pw1.isValidated() && pw1_modes[PW1_MODE_NO81])) 548 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); 549 | 550 | if (pw1_status == (byte) 0x00) 551 | pw1_modes[PW1_MODE_NO81] = false; 552 | 553 | if (!sig_key.getPrivate().isInitialized()) 554 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 555 | 556 | // Copy data to be signed to tmp 557 | short length = Util 558 | .arrayCopyNonAtomic(buffer, _0, tmp, _0, in_received); 559 | 560 | cipher.init(sig_key.getPrivate(), Cipher.MODE_ENCRYPT); 561 | increaseDSCounter(); 562 | 563 | return cipher.doFinal(tmp, _0, length, buffer, _0); 564 | } 565 | 566 | /** 567 | * Provide the PSO: DECIPHER command (INS 2A, P1P2 8086) 568 | * 569 | * Decrypt the data provided using the key for confidentiality. 570 | * 571 | * Before using this method PW1 has to be verified with mode No. 82. 572 | * 573 | * @param apdu 574 | * @return Length of data written in buffer 575 | */ 576 | private short decipher(APDU apdu) { 577 | // DECIPHER 578 | if (!(pw1.isValidated() && pw1_modes[PW1_MODE_NO82])) 579 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); 580 | if (!dec_key.getPrivate().isInitialized()) 581 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 582 | 583 | // Copy data to be decrypted to tmp, omit padding indicator 584 | short length = Util.arrayCopyNonAtomic(buffer, (short) 1, tmp, _0, 585 | (short) (in_received - 1)); 586 | 587 | cipher.init(dec_key.getPrivate(), Cipher.MODE_DECRYPT); 588 | 589 | return cipher.doFinal(tmp, _0, length, buffer, _0); 590 | } 591 | 592 | /** 593 | * Provide the INTERNAL AUTHENTICATE command (INS 88) 594 | * 595 | * Sign the data provided using the key for authentication. Before using 596 | * this method PW1 has to be verified with mode No. 82. 597 | * 598 | * @param apdu 599 | * @return Length of data written in buffer 600 | */ 601 | private short internalAuthenticate(APDU apdu) { 602 | if (!(pw1.isValidated() && pw1_modes[PW1_MODE_NO82])) 603 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); 604 | Util.arrayCopyNonAtomic(buffer, _0, tmp, _0, in_received); 605 | 606 | if (!auth_key.getPrivate().isInitialized()) 607 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); 608 | 609 | cipher.init(auth_key.getPrivate(), Cipher.MODE_ENCRYPT); 610 | return cipher.doFinal(tmp, _0, in_received, buffer, _0); 611 | } 612 | 613 | /** 614 | * Provide the GENERATE ASYMMETRIC KEY PAIR command (INS 47) 615 | * 616 | * For mode 80, generate a new key pair, specified in the first element of 617 | * buffer, and output the public key. 618 | * 619 | * For mode 81, output the public key specified in the first element of 620 | * buffer. 621 | * 622 | * Before using this method PW3 has to be verified. 623 | * 624 | * @param apdu 625 | * @param mode 626 | * Generate key pair (80) or read public key (81) 627 | * @return Length of data written in buffer 628 | */ 629 | private short genAsymKey(APDU apdu, byte mode) { 630 | PGPKey key = getKey(buffer[0]); 631 | 632 | if (mode == (byte) 0x80) { 633 | if (!pw3.isValidated()) 634 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); 635 | 636 | // TODO Usage of transaction resultsin SW 6F00 on new cards 637 | JCSystem.beginTransaction(); 638 | key.genKeyPair(); 639 | 640 | if (buffer[0] == (byte) 0xB6) { 641 | // Reset signature counter 642 | for(short i = 0; i < ds_counter.length; i++) { 643 | ds_counter[i] = (byte) 0; 644 | } 645 | } 646 | JCSystem.commitTransaction(); 647 | } 648 | 649 | // Output requested key 650 | return sendPublicKey(key); 651 | } 652 | 653 | /** 654 | * Provide the GET CHALLENGE command (INS 84) 655 | * 656 | * Generate a random number of the length given in len. 657 | * 658 | * @param apdu 659 | * @param len 660 | * Length of the requested challenge 661 | * @return Length of data written in buffer 662 | */ 663 | private short getChallenge(APDU apdu, short len) { 664 | if (len > CHALLENGES_MAX_LENGTH) 665 | ISOException.throwIt(SW_WRONG_LENGTH); 666 | 667 | random.generateData(buffer, _0, len); 668 | 669 | // Set the SSC used in Secure Messaging if the size of the requested 670 | // challenge is equal to the size of the SSC 671 | if(len == sm.getSSCSize()) { 672 | sm.setSSC(buffer, _0); 673 | } 674 | 675 | return len; 676 | } 677 | 678 | /** 679 | * Provide the GET DATA command (INS CA) 680 | * 681 | * Output the data specified with tag. 682 | * 683 | * @param apdu 684 | * @param tag 685 | * Tag of the requested data 686 | */ 687 | private short getData(short tag) { 688 | short offset = 0; 689 | 690 | switch (tag) { 691 | // 4F - Application identifier (AID) 692 | case (short) 0x004F: 693 | return JCSystem.getAID().getBytes(buffer, _0); 694 | 695 | // 5E - Login data 696 | case (short) 0x005E: 697 | return Util.arrayCopyNonAtomic(loginData, _0, buffer, _0, loginData_length); 698 | 699 | // 5F50 - URL 700 | case (short) 0x5F50: 701 | return Util.arrayCopyNonAtomic(url, _0, buffer, _0, url_length); 702 | 703 | // 5F52 - Historical bytes 704 | case (short) 0x5F52: 705 | return Util.arrayCopyNonAtomic(HISTORICAL, _0, buffer, _0, (short)HISTORICAL.length); 706 | 707 | // 65 - Cardholder Related Data 708 | case (short) 0x0065: 709 | buffer[offset++] = 0x65; 710 | buffer[offset++] = 0x00; 711 | 712 | // 5B - Name 713 | buffer[offset++] = 0x5B; 714 | buffer[offset++] = (byte) name_length; 715 | offset = Util.arrayCopyNonAtomic(name, _0, buffer, offset, 716 | name_length); 717 | 718 | // 5F2D - Language 719 | buffer[offset++] = 0x5F; 720 | buffer[offset++] = 0x2D; 721 | buffer[offset++] = (byte) lang_length; 722 | offset = Util.arrayCopyNonAtomic(lang, _0, buffer, offset, 723 | lang_length); 724 | 725 | // 5F35 - Sex 726 | buffer[offset++] = 0x5F; 727 | buffer[offset++] = 0x35; 728 | buffer[offset++] = 0x01; 729 | buffer[offset++] = sex; 730 | 731 | // Set length for combined data 732 | buffer[1] = (byte) (offset - 2); 733 | 734 | return offset; 735 | 736 | // 6E - Application Related Data 737 | case (short) 0x006E: 738 | buffer[offset++] = 0x6E; 739 | // Total length assumed to be >= 128 and < 256 740 | buffer[offset++] = (byte) 0x81; 741 | buffer[offset++] = 0; 742 | 743 | // 4F - AID 744 | buffer[offset++] = 0x4F; 745 | byte len = JCSystem.getAID().getBytes(buffer, (short)(offset + 1)); 746 | buffer[offset++] = len; 747 | offset += len; 748 | 749 | // 5F52 - Historical bytes 750 | buffer[offset++] = 0x5F; 751 | buffer[offset++] = 0x52; 752 | buffer[offset++] = (byte) HISTORICAL.length; 753 | offset = Util.arrayCopyNonAtomic(HISTORICAL, _0, buffer, offset, 754 | (short) HISTORICAL.length); 755 | 756 | // 73 - Discretionary data objects 757 | buffer[offset++] = 0x73; 758 | buffer[offset++] = 0x00; 759 | 760 | // C0 - Extended capabilities 761 | buffer[offset++] = (byte) 0xC0; 762 | buffer[offset++] = (byte) EXTENDED_CAP.length; 763 | offset = Util.arrayCopyNonAtomic(EXTENDED_CAP, _0, buffer, offset, 764 | (short) EXTENDED_CAP.length); 765 | 766 | // C1 - Algorithm attributes signature 767 | buffer[offset++] = (byte) 0xC1; 768 | buffer[offset++] = (byte) 0x06; 769 | offset = sig_key.getAttributes(buffer, offset); 770 | 771 | // C2 - Algorithm attributes decryption 772 | buffer[offset++] = (byte) 0xC2; 773 | buffer[offset++] = (byte) 0x06; 774 | offset = dec_key.getAttributes(buffer, offset); 775 | 776 | // C3 - Algorithm attributes authentication 777 | buffer[offset++] = (byte) 0xC3; 778 | buffer[offset++] = (byte) 0x06; 779 | offset = auth_key.getAttributes(buffer, offset); 780 | 781 | // C4 - PW1 Status bytes 782 | buffer[offset++] = (byte) 0xC4; 783 | buffer[offset++] = 0x07; 784 | buffer[offset++] = pw1_status; 785 | buffer[offset++] = PW1_MAX_LENGTH; 786 | buffer[offset++] = RC_MAX_LENGTH; 787 | buffer[offset++] = PW3_MAX_LENGTH; 788 | buffer[offset++] = pw1.getTriesRemaining(); 789 | buffer[offset++] = rc.getTriesRemaining(); 790 | buffer[offset++] = pw3.getTriesRemaining(); 791 | 792 | // C5 - Fingerprints sign, dec and auth keys 793 | buffer[offset++] = (byte) 0xC5; 794 | buffer[offset++] = (short) 60; 795 | offset = sig_key.getFingerprint(buffer, offset); 796 | offset = dec_key.getFingerprint(buffer, offset); 797 | offset = auth_key.getFingerprint(buffer, offset); 798 | 799 | // C6 - Fingerprints CA 1, 2 and 3 800 | buffer[offset++] = (byte) 0xC6; 801 | buffer[offset++] = (short) 60; 802 | offset = Util.arrayCopyNonAtomic(ca1_fp, _0, buffer, offset, 803 | (short) 20); 804 | offset = Util.arrayCopyNonAtomic(ca2_fp, _0, buffer, offset, 805 | (short) 20); 806 | offset = Util.arrayCopyNonAtomic(ca3_fp, _0, buffer, offset, 807 | (short) 20); 808 | 809 | // CD - Generation times of public key pair 810 | buffer[offset++] = (byte) 0xCD; 811 | buffer[offset++] = (short) 12; 812 | offset = sig_key.getTime(buffer, offset); 813 | offset = dec_key.getTime(buffer, offset); 814 | offset = auth_key.getTime(buffer, offset); 815 | 816 | // Set length of combined data 817 | buffer[2] = (byte) (offset - 3); 818 | 819 | return offset; 820 | 821 | // 7A - Security support template 822 | case (short) 0x007A: 823 | buffer[offset++] = 0x7A; 824 | buffer[offset++] = (byte) 0x05; 825 | 826 | // 93 - Digital signature counter 827 | buffer[offset++] = (byte) 0x93; 828 | buffer[offset++] = 0x03; 829 | offset = Util.arrayCopyNonAtomic(ds_counter, _0, buffer, offset, 830 | (short) 3); 831 | 832 | return offset; 833 | 834 | // 7F21 - Cardholder Certificate 835 | case (short) 0x7F21: 836 | // Use buffer since certificate may be longer than 837 | // RESPONSE_MAX_LENGTH 838 | buffer[offset++] = 0x7F; 839 | buffer[offset++] = 0x21; 840 | 841 | if (cert_length < 128) { 842 | buffer[offset++] = (byte) cert_length; 843 | } else if (cert_length < 256) { 844 | buffer[offset++] = (byte) 0x81; 845 | buffer[offset++] = (byte) cert_length; 846 | } else { 847 | buffer[offset++] = (byte) 0x82; 848 | Util.setShort(buffer, offset, cert_length); 849 | offset += 2; 850 | } 851 | 852 | offset = Util.arrayCopyNonAtomic(cert, _0, buffer, offset, 853 | cert_length); 854 | 855 | return offset; 856 | 857 | // C4 - PW Status Bytes 858 | case (short) 0x00C4: 859 | buffer[offset++] = pw1_status; 860 | buffer[offset++] = PW1_MAX_LENGTH; 861 | buffer[offset++] = RC_MAX_LENGTH; 862 | buffer[offset++] = PW3_MAX_LENGTH; 863 | buffer[offset++] = pw1.getTriesRemaining(); 864 | buffer[offset++] = rc.getTriesRemaining(); 865 | buffer[offset++] = pw3.getTriesRemaining(); 866 | 867 | return offset; 868 | 869 | default: 870 | ISOException.throwIt(SW_RECORD_NOT_FOUND); 871 | } 872 | 873 | return offset; 874 | } 875 | 876 | /** 877 | * Provide the PUT DATA command (INS DA) 878 | * 879 | * Write the data specified using tag. 880 | * 881 | * Before using this method PW3 has to be verified. 882 | * 883 | * @param apdu 884 | * @param tag 885 | * Tag of the requested data 886 | */ 887 | private void putData(short tag) { 888 | if (!pw3.isValidated()) 889 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); 890 | 891 | switch (tag) { 892 | // 5B - Name 893 | case (short) 0x005B: 894 | if (in_received > name.length) 895 | ISOException.throwIt(SW_WRONG_LENGTH); 896 | 897 | name_length = Util.arrayCopy(buffer, _0, name, _0, in_received); 898 | break; 899 | 900 | // 5E - Login data 901 | case (short) 0x005E: 902 | if (in_received > loginData.length) 903 | ISOException.throwIt(SW_WRONG_LENGTH); 904 | 905 | loginData_length = Util.arrayCopy(buffer, _0, loginData, _0, 906 | in_received); 907 | break; 908 | 909 | // 5F2D - Language preferences 910 | case (short) 0x5F2D: 911 | if (in_received > lang.length) 912 | ISOException.throwIt(SW_WRONG_LENGTH); 913 | 914 | lang_length = Util.arrayCopy(buffer, _0, lang, _0, in_received); 915 | break; 916 | 917 | // 5F35 - Sex 918 | case (short) 0x5F35: 919 | if (in_received != 1) 920 | ISOException.throwIt(SW_WRONG_LENGTH); 921 | 922 | // Check for valid values 923 | if (buffer[0] != (byte) 0x31 && buffer[0] != (byte) 0x32 924 | && buffer[0] != (byte) 0x39) 925 | ISOException.throwIt(SW_WRONG_DATA); 926 | 927 | sex = buffer[0]; 928 | break; 929 | 930 | // 5F50 - URL 931 | case (short) 0x5F50: 932 | if (in_received > url.length) 933 | ISOException.throwIt(SW_WRONG_LENGTH); 934 | 935 | url_length = Util.arrayCopy(buffer, _0, url, _0, in_received); 936 | break; 937 | 938 | // 7F21 - Cardholder certificate 939 | case (short) 0x7F21: 940 | if (in_received > cert.length) 941 | ISOException.throwIt(SW_WRONG_LENGTH); 942 | 943 | cert_length = Util.arrayCopy(buffer, _0, cert, _0, in_received); 944 | break; 945 | 946 | // C4 - PW Status Bytes 947 | case (short) 0x00C4: 948 | if (in_received != 1) 949 | ISOException.throwIt(SW_WRONG_LENGTH); 950 | 951 | // Check for valid values 952 | if (buffer[0] != (byte) 0x00 && buffer[0] != (byte) 0x01) 953 | ISOException.throwIt(SW_WRONG_DATA); 954 | 955 | pw1_status = buffer[0]; 956 | break; 957 | 958 | // C7 - Fingerprint signature key 959 | case (short) 0x00C7: 960 | if (in_received != PGPKey.FP_SIZE) 961 | ISOException.throwIt(SW_WRONG_LENGTH); 962 | 963 | sig_key.setFingerprint(buffer, _0); 964 | break; 965 | 966 | // C8 - Fingerprint decryption key 967 | case (short) 0x00C8: 968 | if (in_received != PGPKey.FP_SIZE) 969 | ISOException.throwIt(SW_WRONG_LENGTH); 970 | 971 | dec_key.setFingerprint(buffer, _0); 972 | break; 973 | 974 | // C9 - Fingerprint authentication key 975 | case (short) 0x00C9: 976 | if (in_received != PGPKey.FP_SIZE) 977 | ISOException.throwIt(SW_WRONG_LENGTH); 978 | 979 | auth_key.setFingerprint(buffer, _0); 980 | break; 981 | 982 | // CA - Fingerprint Certification Authority 1 983 | case (short) 0x00CA: 984 | if (in_received != ca1_fp.length) 985 | ISOException.throwIt(SW_WRONG_LENGTH); 986 | 987 | Util.arrayCopy(buffer, _0, ca1_fp, _0, in_received); 988 | break; 989 | 990 | // CB - Fingerprint Certification Authority 2 991 | case (short) 0x00CB: 992 | if (in_received != ca2_fp.length) 993 | ISOException.throwIt(SW_WRONG_LENGTH); 994 | 995 | Util.arrayCopy(buffer, _0, ca2_fp, _0, in_received); 996 | break; 997 | 998 | // CC - Fingerprint Certification Authority 3 999 | case (short) 0x00CC: 1000 | if (in_received != ca3_fp.length) 1001 | ISOException.throwIt(SW_WRONG_LENGTH); 1002 | 1003 | Util.arrayCopy(buffer, _0, ca3_fp, _0, in_received); 1004 | break; 1005 | 1006 | // CE - Signature key generation date/time 1007 | case (short) 0x00CE: 1008 | if (in_received != 4) 1009 | ISOException.throwIt(SW_WRONG_LENGTH); 1010 | 1011 | sig_key.setTime(buffer, _0); 1012 | break; 1013 | 1014 | // CF - Decryption key generation date/time 1015 | case (short) 0x00CF: 1016 | if (in_received != 4) 1017 | ISOException.throwIt(SW_WRONG_LENGTH); 1018 | 1019 | dec_key.setTime(buffer, _0); 1020 | break; 1021 | 1022 | // D0 - Authentication key generation date/time 1023 | case (short) 0x00D0: 1024 | if (in_received != 4) 1025 | ISOException.throwIt(SW_WRONG_LENGTH); 1026 | 1027 | auth_key.setTime(buffer, _0); 1028 | break; 1029 | 1030 | // D3 - Resetting Code 1031 | case (short) 0x00D3: 1032 | if (in_received == 0) { 1033 | rc_length = 0; 1034 | } else if (in_received >= RC_MIN_LENGTH 1035 | && in_received <= RC_MAX_LENGTH) { 1036 | JCSystem.beginTransaction(); 1037 | rc.update(buffer, _0, (byte) in_received); 1038 | rc_length = (byte) in_received; 1039 | JCSystem.commitTransaction(); 1040 | } else { 1041 | ISOException.throwIt(SW_WRONG_LENGTH); 1042 | } 1043 | break; 1044 | 1045 | // D1 - SM-Key-ENC 1046 | case (short) 0x00D1: 1047 | sm.setSessionKeyEncryption(buffer, _0); 1048 | break; 1049 | 1050 | // D2 - SM-Key-MAC 1051 | case (short) 0x00D2: 1052 | sm.setSessionKeyMAC(buffer, _0); 1053 | break; 1054 | 1055 | // F4 - SM-Key-Container 1056 | case (short) 0x00F4: 1057 | short offset = 0; 1058 | short key_len = 0; 1059 | // Set encryption key 1060 | if(buffer[offset++] == (byte)0xD1) { 1061 | key_len = (short)(buffer[offset++] & 0x7F); 1062 | sm.setSessionKeyEncryption(buffer, offset); 1063 | offset += key_len; 1064 | } 1065 | 1066 | // Set MAC key 1067 | if(buffer[offset++] == (byte)0xD2) { 1068 | key_len = (short)(buffer[offset++] & 0x7F); 1069 | sm.setSessionKeyMAC(buffer, offset); 1070 | offset += key_len; 1071 | } 1072 | break; 1073 | 1074 | default: 1075 | ISOException.throwIt(SW_RECORD_NOT_FOUND); 1076 | break; 1077 | } 1078 | } 1079 | 1080 | /** 1081 | * EXPERIMENTAL: Provide functionality for importing keys. 1082 | * 1083 | * @param apdu 1084 | */ 1085 | private void importKey(APDU apdu) { 1086 | // TODO The following code still has to be tested 1087 | if (!pw3.isValidated()) 1088 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); 1089 | 1090 | short offset = 0; 1091 | 1092 | // Check for tag 4D 1093 | if (buffer[offset++] != 0x4D) 1094 | ISOException.throwIt(SW_DATA_INVALID); 1095 | 1096 | // Length of 4D 1097 | offset += getLengthBytes(getLength(buffer, offset)); 1098 | 1099 | // Get key for Control Reference Template 1100 | PGPKey key = getKey(buffer[offset++]); 1101 | 1102 | // Skip empty length of CRT 1103 | offset++; 1104 | 1105 | // Check for tag 7F48 1106 | if (!(buffer[offset++] == 0x7F && buffer[offset++] == 0x48)) 1107 | ISOException.throwIt(SW_DATA_INVALID); 1108 | short len_template = getLength(buffer, offset); 1109 | offset += getLengthBytes(len_template); 1110 | 1111 | short offset_data = (short) (offset + len_template); 1112 | 1113 | if (buffer[offset++] != (byte) 0x91) 1114 | ISOException.throwIt(SW_DATA_INVALID); 1115 | short len_e = getLength(buffer, offset); 1116 | offset += getLengthBytes(len_e); 1117 | 1118 | if (buffer[offset++] != (byte) 0x92) 1119 | ISOException.throwIt(SW_DATA_INVALID); 1120 | short len_p = getLength(buffer, offset); 1121 | offset += getLengthBytes(len_p); 1122 | 1123 | if (buffer[offset++] != (byte) 0x93) 1124 | ISOException.throwIt(SW_DATA_INVALID); 1125 | short len_q = getLength(buffer, offset); 1126 | offset += getLengthBytes(len_q); 1127 | 1128 | if (buffer[offset++] != (byte) 0x94) 1129 | ISOException.throwIt(SW_DATA_INVALID); 1130 | short len_pq = getLength(buffer, offset); 1131 | offset += getLengthBytes(len_pq); 1132 | 1133 | if (buffer[offset++] != (byte) 0x95) 1134 | ISOException.throwIt(SW_DATA_INVALID); 1135 | short len_dp1 = getLength(buffer, offset); 1136 | offset += getLengthBytes(len_dp1); 1137 | 1138 | if (buffer[offset++] != (byte) 0x96) 1139 | ISOException.throwIt(SW_DATA_INVALID); 1140 | short len_dq1 = getLength(buffer, offset); 1141 | offset += getLengthBytes(len_dq1); 1142 | 1143 | if (!(buffer[offset_data++] == 0x5F && buffer[offset_data++] == 0x48)) 1144 | ISOException.throwIt(SW_DATA_INVALID); 1145 | offset_data += getLengthBytes(getLength(buffer, offset_data)); 1146 | 1147 | // TODO Check value of e 1148 | offset_data += len_e; 1149 | 1150 | key.setP(buffer, offset_data, len_p); 1151 | offset_data += len_p; 1152 | 1153 | key.setQ(buffer, offset_data, len_q); 1154 | offset_data += len_q; 1155 | 1156 | key.setPQ(buffer, offset_data, len_pq); 1157 | offset_data += len_pq; 1158 | 1159 | key.setDP1(buffer, offset_data, len_dp1); 1160 | offset_data += len_dp1; 1161 | 1162 | key.setDQ1(buffer, offset_data, len_dq1); 1163 | offset_data += len_dq1; 1164 | } 1165 | 1166 | /** 1167 | * Output the public key of the given key pair. 1168 | * 1169 | * @param apdu 1170 | * @param key 1171 | * Key pair containing public key to be output 1172 | */ 1173 | private short sendPublicKey(PGPKey key) { 1174 | RSAPublicKey pubkey = key.getPublic(); 1175 | 1176 | // Build message in tmp 1177 | short offset = 0; 1178 | 1179 | // 81 - Modulus 1180 | tmp[offset++] = (byte) 0x81; 1181 | 1182 | // Length of modulus is always greater than 128 bytes 1183 | if (key.getModulusLength() < 256) { 1184 | tmp[offset++] = (byte) 0x81; 1185 | tmp[offset++] = (byte) key.getModulusLength(); 1186 | } else { 1187 | tmp[offset++] = (byte) 0x82; 1188 | offset = Util.setShort(tmp, offset, key.getModulusLength()); 1189 | } 1190 | pubkey.getModulus(tmp, offset); 1191 | offset += key.getModulusLength(); 1192 | 1193 | // 82 - Exponent 1194 | tmp[offset++] = (byte) 0x82; 1195 | tmp[offset++] = (byte) key.getExponentLength(); 1196 | pubkey.getExponent(tmp, offset); 1197 | offset += key.getExponentLength(); 1198 | 1199 | short len = offset; 1200 | 1201 | offset = 0; 1202 | 1203 | buffer[offset++] = 0x7F; 1204 | buffer[offset++] = 0x49; 1205 | 1206 | if (len < 256) { 1207 | buffer[offset++] = (byte) 0x81; 1208 | buffer[offset++] = (byte) len; 1209 | } else { 1210 | buffer[offset++] = (byte) 0x82; 1211 | offset = Util.setShort(buffer, offset, len); 1212 | } 1213 | 1214 | offset = Util.arrayCopyNonAtomic(tmp, _0, buffer, offset, len); 1215 | 1216 | return offset; 1217 | } 1218 | 1219 | /** 1220 | * Send len bytes from buffer. If len is greater than RESPONSE_MAX_LENGTH, 1221 | * remaining data can be retrieved using GET RESPONSE. 1222 | * 1223 | * @param apdu 1224 | * @param len 1225 | * The byte length of the data to send 1226 | */ 1227 | private void sendBuffer(APDU apdu, short len) { 1228 | out_sent = 0; 1229 | out_left = len; 1230 | sendNext(apdu); 1231 | } 1232 | 1233 | /** 1234 | * Send provided status 1235 | * 1236 | * @param apdu 1237 | * @param status Status to send 1238 | */ 1239 | private void sendException(APDU apdu, short status) { 1240 | out_sent = 0; 1241 | out_left = 0; 1242 | sendNext(apdu, status); 1243 | } 1244 | 1245 | /** 1246 | * Send next block of data in buffer. Used for sending data in 1247 | * 1248 | * @param apdu 1249 | */ 1250 | private void sendNext(APDU apdu) { 1251 | sendNext(apdu, SW_NO_ERROR); 1252 | } 1253 | 1254 | /** 1255 | * Send next block of data in buffer. Used for sending data in 1256 | * 1257 | * @param apdu 1258 | * @param status Status to send 1259 | */ 1260 | private void sendNext(APDU apdu, short status) { 1261 | byte[] buf = APDU.getCurrentAPDUBuffer(); 1262 | apdu.setOutgoing(); 1263 | 1264 | // Determine maximum size of the messages 1265 | short max_length; 1266 | if(sm_success) { 1267 | max_length = RESPONSE_SM_MAX_LENGTH; 1268 | } 1269 | else { 1270 | max_length = RESPONSE_MAX_LENGTH; 1271 | } 1272 | 1273 | Util.arrayCopyNonAtomic(buffer, out_sent, buf, _0, max_length); 1274 | 1275 | short len = 0; 1276 | 1277 | if (out_left > max_length) { 1278 | len = max_length; 1279 | 1280 | // Compute byte left and sent 1281 | out_left -= max_length; 1282 | out_sent += max_length; 1283 | 1284 | // Determine new status word 1285 | if (out_left > max_length) { 1286 | status = (short) (SW_BYTES_REMAINING_00 | max_length); 1287 | } else { 1288 | status = (short) (SW_BYTES_REMAINING_00 | out_left); 1289 | } 1290 | } 1291 | else { 1292 | len = out_left; 1293 | 1294 | // Reset buffer 1295 | out_sent = 0; 1296 | out_left = 0; 1297 | } 1298 | 1299 | // If SM is used, wrap response 1300 | if(sm_success) { 1301 | len = sm.wrapResponseAPDU(buf, _0, len, status); 1302 | } 1303 | 1304 | // Send data in buffer 1305 | apdu.setOutgoingLength(len); 1306 | apdu.sendBytes(_0, len); 1307 | 1308 | // Send status word 1309 | if(status != SW_NO_ERROR) 1310 | ISOException.throwIt(status); 1311 | } 1312 | 1313 | /** 1314 | * Get length of TLV element. 1315 | * 1316 | * @param data 1317 | * Byte array 1318 | * @param offset 1319 | * Offset within byte array containing first byte 1320 | * @return Length of value 1321 | */ 1322 | private short getLength(byte[] data, short offset) { 1323 | short len = 0; 1324 | 1325 | if ((data[offset] & (byte) 0x80) == (byte) 0x00) { 1326 | len = data[offset]; 1327 | } else if ((data[offset] & (byte) 0x7F) == (byte) 0x01) { 1328 | len = (short)(0xFF & data[(short) (offset + 1)]); 1329 | } else if ((data[offset] & (byte) 0x7F) == (byte) 0x02) { 1330 | len = Util.makeShort(data[(short) (offset + 1)], data[(short) (offset + 2)]); 1331 | } else { 1332 | ISOException.throwIt(SW_UNKNOWN); 1333 | } 1334 | 1335 | return len; 1336 | } 1337 | 1338 | /** 1339 | * Get number of bytes needed to represent length for TLV element. 1340 | * 1341 | * @param length 1342 | * Length of value 1343 | * @return Number of bytes needed to represent length 1344 | */ 1345 | private short getLengthBytes(short length) { 1346 | if (length <= 127) 1347 | return 1; 1348 | else if (length <= 255) 1349 | return 2; 1350 | else 1351 | return 3; 1352 | } 1353 | 1354 | /** 1355 | * Return the key of the type requested: - B6: Digital signatures - B8: 1356 | * Confidentiality - A4: Authentication 1357 | * 1358 | * @param type 1359 | * Type of key to be returned 1360 | * @return Key of requested type 1361 | */ 1362 | private PGPKey getKey(byte type) { 1363 | PGPKey key = sig_key; 1364 | 1365 | if (type == (byte) 0xB6) 1366 | key = sig_key; 1367 | else if (type == (byte) 0xB8) 1368 | key = dec_key; 1369 | else if (type == (byte) 0xA4) 1370 | key = auth_key; 1371 | else 1372 | ISOException.throwIt(SW_UNKNOWN); 1373 | 1374 | return key; 1375 | } 1376 | 1377 | /** 1378 | * Increase the digital signature counter by one. In case of overflow 1379 | * SW_WARNING_STATE_UNCHANGED will be thrown and nothing will 1380 | * change. 1381 | */ 1382 | private void increaseDSCounter() { 1383 | for (short i = (short) (ds_counter.length - 1); i >= 0; i--) { 1384 | if ((short) (ds_counter[i] & 0xFF) >= 0xFF) { 1385 | if (i == 0) { 1386 | // Overflow 1387 | ISOException.throwIt(SW_WARNING_STATE_UNCHANGED); 1388 | } else { 1389 | ds_counter[i] = 0; 1390 | } 1391 | } else { 1392 | ds_counter[i]++; 1393 | break; 1394 | } 1395 | } 1396 | } 1397 | } 1398 | -------------------------------------------------------------------------------- /src/openpgpcard/OpenPGPSecureMessaging.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Java Card implementation of the OpenPGP card 3 | * 4 | * Copyright (C) 2011 Joeri de Ruiter 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License 8 | * as published by the Free Software Foundation; either version 2 9 | * of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | * 20 | * OpenPGPSecureMessaging.java is based on OVSecureMessaging.java which is part 21 | * of OVchip-ng 22 | * 23 | * Copyright (C) Pim Vullers, Radboud University Nijmegen, February 2011. 24 | */ 25 | 26 | package openpgpcard; 27 | 28 | import javacard.framework.APDU; 29 | import javacard.framework.ISO7816; 30 | import javacard.framework.ISOException; 31 | import javacard.framework.JCSystem; 32 | import javacard.framework.Util; 33 | import javacard.security.DESKey; 34 | import javacard.security.KeyBuilder; 35 | import javacard.security.Signature; 36 | import javacardx.crypto.Cipher; 37 | 38 | /** 39 | * OV secure messaging functionality. 40 | * 41 | *

OVSecureMessaging is based on PassportCrypto which is part of the 42 | * e-passport Java Card applet from the JMRTD project (http://jmrtd.org/). 43 | * 44 | * @author Pim Vullers 45 | * @version $Revision: 12 $ by $Author: joeridr $ 46 | * $LastChangedDate: 2012-02-23 15:31:33 +0100 (Thu, 23 Feb 2012) $ 47 | */ 48 | public class OpenPGPSecureMessaging { 49 | private static final short SW_INTERNAL_ERROR = (short) 0x6D66; 50 | private static final byte[] PAD_DATA = {(byte) 0x80, 0, 0, 0, 0, 0, 0, 0}; 51 | private static final short SSC_SIZE = 8; 52 | private static final short TMP_SIZE = 256; 53 | private static final short MAC_SIZE = 8; 54 | private static final short KEY_SIZE = 16; 55 | private static final byte[] EMPTY_KEY = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 56 | 57 | /** 58 | * The needed cryptographic functionality. 59 | */ 60 | private Signature signer; 61 | private Signature verifier; 62 | private Cipher cipher; 63 | private Cipher decipher; 64 | 65 | /** 66 | * The needed keys. 67 | */ 68 | private DESKey keyMAC; 69 | private DESKey keyENC; 70 | 71 | /** 72 | * The send sequence counter. 73 | */ 74 | private byte[] ssc; 75 | 76 | /** 77 | * Storage for temporary data. 78 | */ 79 | private byte[] tmp; 80 | 81 | private boolean[] ssc_set; 82 | 83 | /** 84 | * Construct a new secure messaging wrapper. 85 | */ 86 | public OpenPGPSecureMessaging() { 87 | ssc = JCSystem.makeTransientByteArray(SSC_SIZE, 88 | JCSystem.CLEAR_ON_DESELECT); 89 | tmp = JCSystem.makeTransientByteArray(TMP_SIZE, 90 | JCSystem.CLEAR_ON_DESELECT); 91 | signer = Signature.getInstance( 92 | Signature.ALG_DES_MAC8_ISO9797_1_M2_ALG3, false); 93 | verifier = Signature.getInstance( 94 | Signature.ALG_DES_MAC8_ISO9797_1_M2_ALG3, false); 95 | cipher = Cipher.getInstance( 96 | Cipher.ALG_DES_CBC_ISO9797_M2, false); 97 | decipher = Cipher.getInstance( 98 | Cipher.ALG_DES_CBC_ISO9797_M2, false); 99 | 100 | keyMAC = (DESKey) KeyBuilder.buildKey( 101 | KeyBuilder.TYPE_DES_TRANSIENT_DESELECT, 102 | KeyBuilder.LENGTH_DES3_2KEY, false); 103 | keyENC = (DESKey) KeyBuilder.buildKey( 104 | KeyBuilder.TYPE_DES_TRANSIENT_DESELECT, 105 | KeyBuilder.LENGTH_DES3_2KEY, false); 106 | 107 | ssc_set = JCSystem.makeTransientBooleanArray((short)1, JCSystem.CLEAR_ON_DESELECT); 108 | ssc_set[0] = false; 109 | } 110 | 111 | /** 112 | * Set the MAC and encryption (and decryption) session keys. Each key is a 113 | * 16 byte 3DES EDE key. This method may be called at any time and will 114 | * immediately replace the session key. 115 | * 116 | * @param buffer byte array containing the session keys. 117 | * @param offset location of the session keys in the buffer. 118 | */ 119 | public void setSessionKeys(byte[] buffer, short offset) { 120 | // Check for empty keys 121 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0 || 122 | Util.arrayCompare(buffer, KEY_SIZE, EMPTY_KEY, (short)0, KEY_SIZE) == 0) { 123 | keyMAC.clearKey(); 124 | keyENC.clearKey(); 125 | } 126 | else { 127 | keyMAC.setKey(buffer, offset); 128 | keyENC.setKey(buffer, (short) (offset + KEY_SIZE)); 129 | 130 | signer.init(keyMAC, Signature.MODE_SIGN); 131 | verifier.init(keyMAC, Signature.MODE_VERIFY); 132 | 133 | cipher.init(keyENC, Cipher.MODE_ENCRYPT); 134 | decipher.init(keyENC, Cipher.MODE_DECRYPT); 135 | } 136 | } 137 | 138 | /** 139 | * Set the MAC session key. Each key is a 16 byte 3DES EDE key. This method 140 | * may be called at any time and will immediately replace the session key. 141 | * 142 | * @param buffer byte array containing the session key. 143 | * @param offset location of the session key in the buffer. 144 | */ 145 | public void setSessionKeyMAC(byte[] buffer, short offset) { 146 | // Check for empty keys 147 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0) { 148 | keyMAC.clearKey(); 149 | keyENC.clearKey(); 150 | } 151 | else { 152 | keyMAC.setKey(buffer, offset); 153 | 154 | signer.init(keyMAC, Signature.MODE_SIGN); 155 | verifier.init(keyMAC, Signature.MODE_VERIFY); 156 | } 157 | } 158 | 159 | /** 160 | * Set the encryption session key. Each key is a 16 byte 3DES EDE key. This method 161 | * may be called at any time and will immediately replace the session key. 162 | * 163 | * @param buffer byte array containing the session key. 164 | * @param offset location of the session key in the buffer. 165 | */ 166 | public void setSessionKeyEncryption(byte[] buffer, short offset) { 167 | // Check for empty keys 168 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0) { 169 | keyMAC.clearKey(); 170 | keyENC.clearKey(); 171 | } 172 | else { 173 | keyENC.setKey(buffer, (short) (offset + KEY_SIZE)); 174 | 175 | cipher.init(keyENC, Cipher.MODE_ENCRYPT); 176 | decipher.init(keyENC, Cipher.MODE_DECRYPT); 177 | } 178 | } 179 | 180 | /** 181 | * Set the MAC and encryption (and decryption) 3DES session keys to zero. 182 | */ 183 | public void clearSessionKeys() { 184 | keyMAC.clearKey(); 185 | keyENC.clearKey(); 186 | } 187 | 188 | /** 189 | * Unwraps (verify and decrypt) the command APDU located in the APDU buffer. 190 | * The command buffer has to be filled by the APDU.setIncomingAndReceive() 191 | * method beforehand. The verified and decrypted command data get placed at 192 | * the start of the APDU buffer. 193 | * 194 | * @return the length value encoded by DO97, 0 if this object is missing. 195 | */ 196 | public short unwrapCommandAPDU() { 197 | byte[] buf = APDU.getCurrentAPDUBuffer(); 198 | short apdu_p = (short) (ISO7816.OFFSET_CDATA & 0xff); 199 | short start_p = apdu_p; 200 | short le = 0; 201 | short do87DataLen = 0; 202 | short do87Data_p = 0; 203 | short do87LenBytes = 0; 204 | short hdrLen = 4; 205 | short hdrPadLen = (short) (8 - hdrLen); 206 | 207 | incrementSSC(); 208 | 209 | if (buf[apdu_p] == (byte) 0x87) { 210 | apdu_p++; 211 | // do87 212 | if ((buf[apdu_p] & 0xff) > 0x80) { 213 | do87LenBytes = (short) (buf[apdu_p] & 0x7f); 214 | apdu_p++; 215 | } else { 216 | do87LenBytes = 1; 217 | } 218 | if (do87LenBytes > 2) { // sanity check 219 | ISOException.throwIt(SW_INTERNAL_ERROR); 220 | } 221 | for (short i = 0; i < do87LenBytes; i++) { 222 | do87DataLen += (short) ((buf[(short)(apdu_p + i)] & 0xff) << (short) ((do87LenBytes - 1 - i) * 8)); 223 | } 224 | apdu_p += do87LenBytes; 225 | 226 | if (buf[apdu_p] != 1) { 227 | ISOException.throwIt(SW_INTERNAL_ERROR); 228 | } 229 | // store pointer to data and defer decrypt to after mac check (do8e) 230 | do87Data_p = (short) (apdu_p + 1); 231 | apdu_p += do87DataLen; 232 | do87DataLen--; // compensate for 0x01 marker 233 | } 234 | 235 | if (buf[apdu_p] == (byte) 0x97) { 236 | // do97 237 | if (buf[++apdu_p] != 1) 238 | ISOException.throwIt(SW_INTERNAL_ERROR); 239 | le = (short) (buf[++apdu_p] & 0xff); 240 | apdu_p++; 241 | } 242 | 243 | // do8e 244 | if (buf[apdu_p] != (byte) 0x8e) { 245 | ISOException.throwIt(SW_INTERNAL_ERROR); 246 | } 247 | if (buf[++apdu_p] != 8) { 248 | ISOException.throwIt(ISO7816.SW_DATA_INVALID); 249 | } 250 | 251 | // verify mac 252 | verifier.update(ssc, (short)0, (short)ssc.length); 253 | verifier.update(buf, (short)0, hdrLen); 254 | verifier.update(PAD_DATA, (short)0, hdrPadLen); 255 | if (!verifier.verify(buf, start_p, (short) (apdu_p - 1 - start_p), buf, 256 | (short)(apdu_p + 1), MAC_SIZE)) { 257 | ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); 258 | } 259 | 260 | short lc = 0; 261 | if (do87DataLen != 0) { 262 | // decrypt data, and leave room for lc 263 | lc = decipher.doFinal(buf, do87Data_p, do87DataLen, buf, 264 | (short) (hdrLen + 1)); 265 | buf[hdrLen] = (byte) (lc & 0xff); 266 | } 267 | 268 | return le; 269 | } 270 | 271 | /** 272 | * Wraps (encrypts and build MAC) the response data and places it in the 273 | * APDU buffer starting at offset 0. The buffer can be any buffer including 274 | * the APDU buffer itself. If the length is zero the buffer will not be 275 | * addressed and no response data will be present in the wrapped output. 276 | * 277 | * @param buffer byte array containing the data which needs to be wrapped. 278 | * @param offset location of the data in the buffer. 279 | * @param length of the data in the buffer (in bytes). 280 | * @param status word which has to be wrapped in the response APDU. 281 | * @return the length of the wrapped data in the buffer 282 | */ 283 | public short wrapResponseAPDU(byte[] buffer, short offset, short length, 284 | short status) { 285 | byte[] apdu = APDU.getCurrentAPDUBuffer(); 286 | short apdu_p = 0; 287 | // smallest multiple of 8 strictly larger than plaintextLen (length + padding) 288 | short do87DataLen = (short) ((((short) (length + 8)) / 8) * 8); 289 | // for 0x01 marker (indicating padding is used) 290 | do87DataLen++; 291 | short do87DataLenBytes = (short)(do87DataLen > 0xff? 2 : 1); 292 | short do87HeaderBytes = getApduBufferOffset(length); 293 | short do87Bytes = (short)(do87HeaderBytes + do87DataLen - 1); // 0x01 is counted twice 294 | boolean hasDo87 = length > 0; 295 | 296 | incrementSSC(); 297 | 298 | short ciphertextLength=0; 299 | if(hasDo87) { 300 | // Copy the plain text to temporary buffer to avoid data corruption. 301 | Util.arrayCopyNonAtomic(buffer, offset, tmp, (short) 0, length); 302 | // Put the cipher text in the proper position. 303 | ciphertextLength = cipher.doFinal(tmp, (short) 0, length, apdu, 304 | do87HeaderBytes); 305 | } 306 | //sanity check 307 | //note that this check 308 | // (possiblyPaddedPlaintextLength != (short)(do87DataLen -1)) 309 | //does not always hold because some algs do the padding in the final, some in the init. 310 | if (hasDo87 && (((short) (do87DataLen - 1) != ciphertextLength))) 311 | ISOException.throwIt(SW_INTERNAL_ERROR); 312 | 313 | if (hasDo87) { 314 | // build do87 315 | apdu[apdu_p++] = (byte) 0x87; 316 | if(do87DataLen < 0x80) { 317 | apdu[apdu_p++] = (byte)do87DataLen; 318 | } else { 319 | apdu[apdu_p++] = (byte) (0x80 + do87DataLenBytes); 320 | for(short i = (short) (do87DataLenBytes - 1); i >= 0; i--) { 321 | apdu[apdu_p++] = (byte) ((do87DataLen >>> (i * 8)) & 0xff); 322 | } 323 | } 324 | apdu[apdu_p++] = 0x01; 325 | } 326 | 327 | if(hasDo87) { 328 | apdu_p = do87Bytes; 329 | } 330 | 331 | // build do99 332 | apdu[apdu_p++] = (byte) 0x99; 333 | apdu[apdu_p++] = 0x02; 334 | Util.setShort(apdu, apdu_p, status); 335 | apdu_p += 2; 336 | 337 | // calculate and write mac 338 | signer.update(ssc, (short) 0, (short) ssc.length); 339 | signer.sign(apdu, (short) 0, apdu_p, apdu, (short) (apdu_p + 2)); 340 | 341 | // write do8e 342 | apdu[apdu_p++] = (byte) 0x8e; 343 | apdu[apdu_p++] = 0x08; 344 | apdu_p += 8; // for mac written earlier 345 | 346 | return apdu_p; 347 | } 348 | 349 | /** 350 | * Increment the send sequence counter. 351 | */ 352 | private void incrementSSC() { 353 | if (ssc == null || ssc.length <= 0) { 354 | return; 355 | } 356 | 357 | for (short s = (short) (ssc.length - 1); s >= 0; s--) { 358 | if ((short) ((ssc[s] & 0xff) + 1) > 0xff) { 359 | ssc[s] = 0; 360 | } else { 361 | ssc[s]++; 362 | break; 363 | } 364 | } 365 | } 366 | 367 | /*** 368 | * Get the amount of space to reserve in the buffer when using secure 369 | * messaging. 370 | * 371 | * @param length length of plain text in which this offset depends. 372 | * @return the amount of space to reserve. 373 | */ 374 | private short getApduBufferOffset(short length) { 375 | short do87Bytes = 2; // 0x87 length data 0x01 376 | // smallest multiple of 8 strictly larger than plaintextLen + 1 377 | // byte is probably the length of the cipher text (including do87 0x01) 378 | short do87DataLen = (short) ((((short) (length + 8) / 8) * 8) + 1); 379 | 380 | if (do87DataLen < 0x80) { 381 | do87Bytes++; 382 | } else if (do87DataLen <= 0xff) { 383 | do87Bytes += 2; 384 | } else { 385 | do87Bytes += (short) (length > 0xff ? 2 : 1); 386 | } 387 | 388 | return do87Bytes; 389 | } 390 | 391 | /** 392 | * Set the SSC 393 | * 394 | * @param buffer byte array containing the SSC 395 | * @param offset location of the data in the buffer 396 | */ 397 | public void setSSC(byte[] buffer, short offset) { 398 | Util.arrayCopy(buffer, offset, ssc, (short)0, SSC_SIZE); 399 | ssc_set[0] = true; 400 | } 401 | 402 | /** 403 | * @return size in bytes of the SSC 404 | */ 405 | public short getSSCSize() { 406 | return SSC_SIZE; 407 | } 408 | 409 | /** 410 | * @return boolean indicating whether SSC has been set 411 | */ 412 | public boolean isSetSSC() { 413 | return ssc_set[0]; 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/openpgpcard/PGPKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Java Card implementation of the OpenPGP card 3 | * Copyright (C) 2011 Joeri de Ruiter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | package openpgpcard; 20 | 21 | import javacard.framework.*; 22 | import javacard.security.*; 23 | 24 | /** 25 | * @author Joeri de Ruiter (joeri@cs.ru.nl) 26 | * @version $Revision: 12 $ by $Author: joeridr $ 27 | * $LastChangedDate: 2012-02-23 15:31:33 +0100 (Thu, 23 Feb 2012) $ 28 | */ 29 | public class PGPKey implements ISO7816 { 30 | public static final short KEY_SIZE = 2048;// 2368; 31 | public static final short KEY_SIZE_BYTES = KEY_SIZE / 8; 32 | public static final short EXPONENT_SIZE = 17; 33 | public static final short EXPONENT_SIZE_BYTES = 3; 34 | public static final short FP_SIZE = 20; 35 | 36 | private KeyPair key; 37 | private byte[] fp; 38 | private byte[] time = { 0x00, 0x00, 0x00, 0x00 }; 39 | private byte[] attributes = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x02 }; 40 | 41 | public PGPKey() { 42 | key = new KeyPair(KeyPair.ALG_RSA_CRT, KEY_SIZE); 43 | 44 | fp = new byte[FP_SIZE]; 45 | Util.arrayFillNonAtomic(fp, (short) 0, (short) fp.length, (byte) 0); 46 | 47 | Util.setShort(attributes, (short) 1, KEY_SIZE); 48 | Util.setShort(attributes, (short) 3, EXPONENT_SIZE); 49 | } 50 | 51 | /** 52 | * Generate the key pair. 53 | */ 54 | public void genKeyPair() { 55 | key.genKeyPair(); 56 | } 57 | 58 | /** 59 | * Set the fingerprint for the public key. 60 | * 61 | * @param data 62 | * Byte array 63 | * @param offset 64 | * Offset within byte array containing first byte 65 | */ 66 | public void setFingerprint(byte[] data, short offset) { 67 | // Check whether there are enough bytes to copy 68 | if ((short) (offset + fp.length) > data.length) 69 | ISOException.throwIt(SW_UNKNOWN); 70 | 71 | Util.arrayCopy(data, offset, fp, (short) 0, (short) fp.length); 72 | } 73 | 74 | /** 75 | * Set the generation time for the key pair. 76 | * 77 | * @param data 78 | * Byte array 79 | * @param offset 80 | * Offset within byte array containing first byte 81 | */ 82 | public void setTime(byte[] data, short offset) { 83 | // Check whether there are enough bytes to copy 84 | if ((short) (offset + time.length) > data.length) 85 | ISOException.throwIt(SW_UNKNOWN); 86 | 87 | Util.arrayCopy(data, offset, time, (short) 0, (short) 4); 88 | } 89 | 90 | /** 91 | * Get the fingerprint for the public key. 92 | * 93 | * @param data 94 | * Byte array 95 | * @param offset 96 | * Offset within byte array indicating first byte 97 | */ 98 | public short getFingerprint(byte[] data, short offset) { 99 | Util.arrayCopyNonAtomic(fp, (short) 0, data, offset, (short) fp.length); 100 | return (short) (offset + fp.length); 101 | } 102 | 103 | /** 104 | * Get the generation time for the key pair. 105 | * 106 | * @param data 107 | * Byte array 108 | * @param offset 109 | * Offset within byte array indicating first byte 110 | */ 111 | public short getTime(byte[] data, short offset) { 112 | Util.arrayCopyNonAtomic(time, (short) 0, data, offset, 113 | (short) time.length); 114 | return (short) (offset + time.length); 115 | } 116 | 117 | /** 118 | * Get the algorithm attributes for the key pair. 119 | * 120 | * @param data 121 | * Byte array 122 | * @param offset 123 | * Offset within byte array indicating first byte 124 | */ 125 | public short getAttributes(byte[] data, short offset) { 126 | Util.arrayCopyNonAtomic(attributes, (short) 0, data, offset, 127 | (short) attributes.length); 128 | return (short) (offset + attributes.length); 129 | } 130 | 131 | /** 132 | * @return Public key of the key pair 133 | */ 134 | public RSAPublicKey getPublic() { 135 | return (RSAPublicKey) key.getPublic(); 136 | } 137 | 138 | /** 139 | * @return Private key of the key pair 140 | */ 141 | public RSAPrivateCrtKey getPrivate() { 142 | return (RSAPrivateCrtKey) key.getPrivate(); 143 | } 144 | 145 | /** 146 | * @return Length in bytes of the exponent 147 | */ 148 | public short getExponentLength() { 149 | // Fixed value of 65537 for exponent 150 | return EXPONENT_SIZE_BYTES; 151 | } 152 | 153 | /** 154 | * @return Length in bytes of the modulus 155 | */ 156 | public short getModulusLength() { 157 | return KEY_SIZE_BYTES; 158 | } 159 | 160 | /** 161 | * Sets the value of the DP1 parameter. The plain text data format is 162 | * big-endian and right-aligned (the least significant bit is the least 163 | * significant bit of last byte). Input DP1 parameter data is copied into 164 | * the internal representation. 165 | * 166 | * @param buffer 167 | * The input buffer 168 | * @param offset 169 | * The offset into the input buffer at which the parameter value 170 | * begins 171 | * @param length 172 | * The length of the parameter 173 | */ 174 | public void setDP1(byte[] buffer, short offset, short length) { 175 | ((RSAPrivateCrtKey) key.getPrivate()).setDP1(buffer, offset, length); 176 | } 177 | 178 | /** 179 | * Sets the value of the DQ1 parameter. The plain text data format is 180 | * big-endian and right-aligned (the least significant bit is the least 181 | * significant bit of last byte). Input DQ1 parameter data is copied into 182 | * the internal representation. 183 | * 184 | * @param buffer 185 | * The input buffer 186 | * @param offset 187 | * The offset into the input buffer at which the parameter value 188 | * begins 189 | * @param length 190 | * The length of the parameter 191 | */ 192 | public void setDQ1(byte[] buffer, short offset, short length) { 193 | ((RSAPrivateCrtKey) key.getPrivate()).setDQ1(buffer, offset, length); 194 | } 195 | 196 | /** 197 | * Sets the value of the P parameter. The plain text data format is 198 | * big-endian and right-aligned (the least significant bit is the least 199 | * significant bit of last byte). Input P parameter data is copied into the 200 | * internal representation. 201 | * 202 | * @param buffer 203 | * The input buffer 204 | * @param offset 205 | * The offset into the input buffer at which the parameter value 206 | * begins 207 | * @param length 208 | * The length of the parameter 209 | */ 210 | public void setP(byte[] buffer, short offset, short length) { 211 | ((RSAPrivateCrtKey) key.getPrivate()).setP(buffer, offset, length); 212 | } 213 | 214 | /** 215 | * Sets the value of the PQ parameter. The plain text data format is 216 | * big-endian and right-aligned (the least significant bit is the least 217 | * significant bit of last byte). Input PQ parameter data is copied into the 218 | * internal representation. 219 | * 220 | * @param buffer 221 | * The input buffer 222 | * @param offset 223 | * The offset into the input buffer at which the parameter value 224 | * begins 225 | * @param length 226 | * The length of the parameter 227 | */ 228 | public void setPQ(byte[] buffer, short offset, short length) { 229 | ((RSAPrivateCrtKey) key.getPrivate()).setPQ(buffer, offset, length); 230 | } 231 | 232 | /** 233 | * Sets the value of the Q parameter. The plain text data format is 234 | * big-endian and right-aligned (the least significant bit is the least 235 | * significant bit of last byte). Input Q parameter data is copied into the 236 | * internal representation. 237 | * 238 | * @param buffer 239 | * The input buffer 240 | * @param offset 241 | * The offset into the input buffer at which the parameter value 242 | * begins 243 | * @param length 244 | * The length of the parameter 245 | */ 246 | public void setQ(byte[] buffer, short offset, short length) { 247 | ((RSAPrivateCrtKey) key.getPrivate()).setQ(buffer, offset, length); 248 | } 249 | } 250 | --------------------------------------------------------------------------------