Initial commit
This commit is contained in:
commit
6635448be6
6
AUTHORS
Normal file
6
AUTHORS
Normal file
@ -0,0 +1,6 @@
|
||||
Maintainer: Arnaud Fontaine
|
||||
|
||||
Contributors:
|
||||
- Arnaud Fontaine
|
||||
- Pierre Chifflier (bin/*)
|
||||
|
339
LICENSE
Normal file
339
LICENSE
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
289
README.md
Normal file
289
README.md
Normal file
@ -0,0 +1,289 @@
|
||||
# SmartPGP applet
|
||||
|
||||
SmartPGP is a free and open source implementation of the [OpenPGP card
|
||||
3.x specification](http://www.g10code.com/docs/openpgp-card-3.0.pdf) in JavaCard.
|
||||
|
||||
The main improvement introduced in OpenPGP card 3.x specification from
|
||||
previous version is the support of elliptic curve cryptography with
|
||||
several existing curves (NIST P-256, NIST P-384, NIST P-521, brainpool
|
||||
p256r1, brainpool p384r1 and brainpool p512r1).
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
The following features are implemented at the applet level, but some
|
||||
of them depend on underlying hardware support and available
|
||||
(non-)volatile memory resources:
|
||||
|
||||
- RSA (>= 2048 bits modulus, 17 bits exponent) and ECC (NIST P-256,
|
||||
NIST P-384, NIST P-521, brainpool p256r1, brainpool p384r1 and
|
||||
brainpool p512r1) for signature, encryption and authentication;
|
||||
|
||||
- On-board key generation and external private key import;
|
||||
|
||||
- PIN codes (user, admin and resetting code) up to 127 characters;
|
||||
|
||||
- Certificate up to 1 kB (DER encoded) for each key;
|
||||
|
||||
- Login, URL, and private DOs up to 256 bytes;
|
||||
|
||||
- Command and response chaining;
|
||||
|
||||
- AES 128/256 bits deciphering primitive;
|
||||
|
||||
- Secure messaging (see below).
|
||||
|
||||
|
||||
## Default values
|
||||
|
||||
The SmartPGP applet is configured with the following default values:
|
||||
|
||||
- Admin PIN is 12345678;
|
||||
|
||||
- User PIN is 123456;
|
||||
|
||||
- No PUK (a.k.a. resetting code) is defined;
|
||||
|
||||
- RSA 2048 bits for PGP keys;
|
||||
|
||||
- NIST P-256 for the secure messaging key.
|
||||
|
||||
These values can be changed by modifying default values in the code
|
||||
(see the [Constants](src/fr/anssi/smartpgp/Constants.java)
|
||||
class).
|
||||
|
||||
When the applet is installed, one can use the `smartpgp-cli` utility
|
||||
given in the `bin` directory to change these values. Keep in mind that
|
||||
when you change the algorithm attributes of a PGP key or of the secure
|
||||
messaging key, the key and the corresponding certificate are
|
||||
erased. Also note that hard coded default values will be restored upon
|
||||
a factory reset.
|
||||
|
||||
|
||||
## Compliance with OpenPGP card 3.x specification
|
||||
|
||||
The SmartPGP applet implements the complete OpenPGP card 3.x
|
||||
specification, except the secure messaging related features:
|
||||
|
||||
- Commands and responses protection is not implemented as described in
|
||||
the specification. Motivation and implementation details are
|
||||
explained in the
|
||||
[secure messaging document](secure_messaging/smartpgp_sm.pdf);
|
||||
|
||||
- A command protected by secure messaging is not granted admin
|
||||
rights. Secure messaging can thus be used to protect communications
|
||||
only, especially when the token is used contactless;
|
||||
|
||||
- If and only if secure messaging static key and certificate have been
|
||||
provisioned, all commands containing sensitive data (e.g. PIN code,
|
||||
decrypted data, private key, ...) emitted through a contactless
|
||||
interface must be protected by secure messaging or they will be
|
||||
refused;
|
||||
|
||||
- The `ACTIVATE FILE` with P1 = P2 = 0, as described in the
|
||||
specification, resets everything except the secure messaging static
|
||||
key and certificate. Complete reset, including these elements, can
|
||||
be performed with `ACTIVATE FILE` with P1 = 0 and P2 = 1.
|
||||
|
||||
|
||||
|
||||
# Application support
|
||||
|
||||
Tokens following the OpenPGP card 3.x specification are not yet fully
|
||||
supported by most PGP applications.
|
||||
|
||||
## GnuPG
|
||||
|
||||
The support in [GnuPG](https://www.gnupg.org/) is ongoing work in the
|
||||
[master branch of 2.1 version](https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=shortlog;h=refs/heads/master).
|
||||
|
||||
The specific secure messaging of the SmartPGP applet is
|
||||
**not** supported at all at is not part of the OpenPGP card 3.x
|
||||
specification.
|
||||
|
||||
## OpenKeychain
|
||||
|
||||
The [patch](patches/open-keychain.patch) proposed for
|
||||
[OpenKeychain](https://www.openkeychain.org/) introduces the partial
|
||||
support (only NIST curves are supported in the original application)
|
||||
of elliptic curve cryptography with an OpenPGP card.
|
||||
|
||||
The specific secure messaging of the SmartPGP applet is fully
|
||||
implemented in this patch, but it requires to be setup correctly. See
|
||||
the section below for more information on the setup process.
|
||||
|
||||
|
||||
|
||||
# Content of the repository
|
||||
|
||||
The repository contains several directories:
|
||||
|
||||
- `patches` contains patch files applicable to existing applications
|
||||
to support OpenPGP card 3.x and/or SmartPGP secure messaging features;
|
||||
|
||||
- `bin` contains a Python library and command line tool called
|
||||
`smartpgp-cli` to interact with an OpenPGP card 3.x but also to deal
|
||||
with the specific secure messaging feature of the SmartPGP applet;
|
||||
|
||||
- `secure_messaging` contains documentation and example scripts to
|
||||
play with the secure messaging feature of SmartPGP;
|
||||
|
||||
- `src` contains the JavaCard source code of the SmartPGP applet.
|
||||
|
||||
|
||||
|
||||
|
||||
# Build and installation instructions
|
||||
|
||||
|
||||
## Prerequisites
|
||||
- JavaCard Development Kit 3.0.4 (or above) from
|
||||
[Oracle website](http://www.oracle.com/technetwork/java/embedded/javacard/downloads/index.html);
|
||||
|
||||
- The `ant` tool 1.9.4 (or above) from your Linux distribution or from
|
||||
[Apache Ant project website](http://ant.apache.org/);
|
||||
|
||||
- A device compliant with JavaCard 3.0.4 (or above) with enough
|
||||
available resources to hold the code (approximately 23 kB of
|
||||
non-volatile memory), persistent data (approximately 10 kB of
|
||||
non-volatile memory) and volatile data (approximately 2 kB of RAM).
|
||||
|
||||
## Reducing flash and/or RAM consumption
|
||||
|
||||
The applet allocates all its data structures to their maximal size
|
||||
at installation to avoid as much as possible runtime errors caused by
|
||||
memory allocation failure. If your device does not have enough flash
|
||||
and/or RAM available, or if you plan not to use some features
|
||||
(e.g. stored certificates), you can adjust the applet to reduce its
|
||||
resource consumption by tweaking the following variables:
|
||||
|
||||
- `Constants.INTERNAL_BUFFER_MAX_LENGTH`: the size in bytes of the
|
||||
internal RAM buffer used for input/output chaining. Chaining is
|
||||
especially used in case of long commands and responses such as those
|
||||
involved in private key import and certificate import/export.
|
||||
|
||||
- `Constants.EXTENDED_CAPABILITIES`, bytes 5 and 6: the maximal size
|
||||
in bytes of a certificate associated to a key. Following the OpenPGP
|
||||
card specification, a certificate can be stored for each of the
|
||||
three keys. In SmartPGP, a fourth certificate is stored for secure
|
||||
messaging.
|
||||
|
||||
|
||||
## Building the CAP file
|
||||
- Copy the `javacard.properties.example` file to a file named
|
||||
`javacard.properties`;
|
||||
|
||||
- Edit the `javacard.properties` file and set the path of your
|
||||
JavaCard Development Kit;
|
||||
|
||||
- (Optional) Edit the `build.xml` file and replace the `0xAF:0xAF`
|
||||
bytes in the `APPLET_AID` with your own manufacturer identifier (see
|
||||
section 4.2.1 of OpenPGP card specification);
|
||||
|
||||
- Execute `ant` with no parameter will produce the CAP file in
|
||||
`build/fr/anssi/smartpgp/javacard/smartpgp.cap`.
|
||||
|
||||
|
||||
## Installing the CAP file
|
||||
|
||||
The CAP file installation depends on your device, so you have to refer
|
||||
to the instructions given by your device manufacturer.
|
||||
|
||||
At installation, the applet expects to receive four bytes
|
||||
corresponding to the unique serial number of this OpenPGP card
|
||||
instance (see section 4.2.1 of OpenPGP card specification).
|
||||
|
||||
|
||||
|
||||
# Setting up secure messaging with OpenKeychain
|
||||
|
||||
The patch written for OpenKeychain permits to use the secure
|
||||
messaging feature with or without token authentication.
|
||||
|
||||
## Secure messaging without token authentication
|
||||
|
||||
Without token authentication, you are not protected against man-in-the
|
||||
middle attack as your device cannot ensure it is communicating
|
||||
directly with a trusted token. Nevertheles, the communications with
|
||||
the token are still protected in confidentiality against passive
|
||||
attacks (i.e. traffic capture).
|
||||
|
||||
If you want to test secure messaging without token authentication, you
|
||||
can use the following command to order the token to generate its
|
||||
secure messaging key on-board.
|
||||
|
||||
`./smartpgp-cli -r X -I generate-sm-key`
|
||||
|
||||
In this case, you have to deactivate the certificate verification in
|
||||
OpenKeychain: go to "Parameters" > "Experimental features" and
|
||||
deactivate the option called "SmartPGP verify certificate".
|
||||
|
||||
|
||||
## Secure messaging with token authentication
|
||||
|
||||
The `secure_messaging` directory contains a subdirectory called `pki`
|
||||
which contains two sample scripts to generate a certificate
|
||||
authority and token certificates.
|
||||
|
||||
The sample scripts are given **only** for test purposes of the secure
|
||||
messaging feature with certificate verification. They require
|
||||
`openssl` to be installed on your system.
|
||||
|
||||
If you want to use your own PKI, you have to generate a specific
|
||||
intermediate certificate authority to sign the certificates of your
|
||||
token(s). Then, you have to provision the complete certificate chain
|
||||
from this new intermediate CA to your root CA in OpenKeychain because
|
||||
the certificate verification implemented in the given patch does not
|
||||
rely on the system keystore.
|
||||
|
||||
### Generate a sample CA key and certificate
|
||||
|
||||
Change your current directory to the `pki` directory and execute the
|
||||
script `./generate_token.sh`. It will produce a sample CA key in
|
||||
`PKI/private/ca.key.pem` and the corresponding certificate in
|
||||
`PKI/certs/ca.cert.pem`.
|
||||
|
||||
### Generate a sample token key and certificate
|
||||
|
||||
Change your current directory to the `pki` directory and execute the
|
||||
script
|
||||
|
||||
`./generate_token.sh mycard1`
|
||||
|
||||
where `mycard1` is some unique identifier for the token. It will
|
||||
produce a sample token key in `PKI/private/mycard1.key.pem` and the
|
||||
corresponding certificate in `PKI/certs/mycard1.cert.pem`.
|
||||
|
||||
### Provision the token with its sample key and certificate
|
||||
|
||||
Change your current directory to the `bin` directory and execute the
|
||||
|
||||
following commands after replacing the reader number `X` by the number
|
||||
of the reader that contains your token, and the path to the `pki`
|
||||
directory used in previous sections.
|
||||
|
||||
The following command imports the token key in the token.
|
||||
|
||||
`./smartpgp-cli -r X -I -i path_to_the_pki_dir/PKI/private/mycard1.key.der put-sm-key`
|
||||
|
||||
The following command imports the token certificate in the token.
|
||||
|
||||
`./smartpgp-cli -r X -I -i path_to_the_pki_dir/PKI/certs/mycard1.cert.der put-sm-certificate`
|
||||
|
||||
These commands have to be executed in this order because the key
|
||||
import clears any previously stored certificate.
|
||||
|
||||
Once the token key is imported, you should remove the token private
|
||||
key from you system as there is no need to keep it outside of your
|
||||
token.
|
||||
|
||||
### Install the CA in OpenKeychain
|
||||
|
||||
- Upload the CA certificate `PKI/certs/ca.cert.pem` on your phone;
|
||||
|
||||
- Go to "Parameters" > "Experimental features" and activate the option called "SmartPGP verify certificate`;
|
||||
|
||||
- Click on "SmartPGP trusted authorities", and then on "+" at the top left;
|
||||
|
||||
- Set a name for this authority and select the file you uploaded.
|
||||
|
12
bin/example-set-mixed-crypto.py
Executable file
12
bin/example-set-mixed-crypto.py
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from smartpgp.highlevel import *
|
||||
|
||||
ctx = CardConnectionContext()
|
||||
|
||||
ctx.connect()
|
||||
ctx.verify_admin_pin()
|
||||
|
||||
ctx.cmd_switch_crypto("P-521","auth")
|
||||
ctx.cmd_switch_crypto("RSA","dec")
|
||||
ctx.cmd_switch_crypto("brainpoolP512r1","sig")
|
100
bin/smartpgp-cli
Executable file
100
bin/smartpgp-cli
Executable file
@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
# https://github.com/ANSSI-FR/smartpgp
|
||||
# Copyright (C) 2016 ANSSI
|
||||
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from getpass import getpass
|
||||
|
||||
from smartpgp.highlevel import *
|
||||
|
||||
VALID_COMMANDS={
|
||||
'list-readers':CardConnectionContext.cmd_list_readers,
|
||||
'full-reset': CardConnectionContext.cmd_full_reset,
|
||||
'reset': CardConnectionContext.cmd_reset,
|
||||
'switch-rsa': CardConnectionContext.cmd_switch_rsa2048,
|
||||
'switch-bp256':CardConnectionContext.cmd_switch_bp256,
|
||||
'switch-bp384':CardConnectionContext.cmd_switch_bp384,
|
||||
'switch-bp512':CardConnectionContext.cmd_switch_bp512,
|
||||
'switch-p256': CardConnectionContext.cmd_switch_p256,
|
||||
'switch-p384': CardConnectionContext.cmd_switch_p384,
|
||||
'switch-p521': CardConnectionContext.cmd_switch_p521,
|
||||
'generate-sm-key': CardConnectionContext.cmd_generate_sm_key,
|
||||
'set-resetting-code': CardConnectionContext.cmd_set_resetting_code,
|
||||
'unblock-pin': CardConnectionContext.cmd_unblock_pin,
|
||||
'put-sm-key': CardConnectionContext.cmd_put_sm_key,
|
||||
'put-sm-certificate': CardConnectionContext.cmd_put_sm_certificate,
|
||||
'get-sm-certificate': CardConnectionContext.cmd_get_sm_certificate,
|
||||
}
|
||||
|
||||
def read_pin_interactive(name):
|
||||
pw = getpass("Enter %s PIN: " % name)
|
||||
return pw
|
||||
|
||||
def parse_args(ctx):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("command", help="The command. Valid commands are: %s" % ', '.join([c for c in VALID_COMMANDS.keys()]))
|
||||
parser.add_argument("-r", "--reader", type=int,
|
||||
help="Select reader index (default: 0)")
|
||||
parser.add_argument("-i", "--input", type=str,
|
||||
help="Input file for commands requiring input data (other than PIN codes)")
|
||||
parser.add_argument("-o", "--output", type=str,
|
||||
help="Output file for commands emitting output data")
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("-p", "--pin", type=str,
|
||||
help="Admin PIN (default: 12345678). Use ENV:VARNAME to read from an environment variable")
|
||||
group.add_argument("-I", "--interactive", action='store_true',
|
||||
help="Ask Admin PIN interactively")
|
||||
args = parser.parse_args()
|
||||
# option -r
|
||||
ctx.reader_index = args.reader or 0
|
||||
# option -p
|
||||
if args.pin is not None:
|
||||
if args.pin.startswith('ENV:'):
|
||||
varname = args.pin[4:]
|
||||
try:
|
||||
ctx.admin_pin=os.environ[varname]
|
||||
except KeyError,e:
|
||||
print "Environment variable %s not found" % varname
|
||||
sys.exit(1)
|
||||
else:
|
||||
ctx.admin_pin = args.pin
|
||||
# option -I
|
||||
if args.interactive:
|
||||
ctx.set_pin_read_function(read_pin_interactive)
|
||||
# option -i
|
||||
ctx.input = args.input
|
||||
# option -O
|
||||
ctx.output = args.output
|
||||
return ctx,args
|
||||
|
||||
def main():
|
||||
ctx = CardConnectionContext()
|
||||
ctx,args = parse_args(ctx)
|
||||
if args.command in VALID_COMMANDS:
|
||||
VALID_COMMANDS[args.command](ctx)
|
||||
else:
|
||||
print "Unknown command '%s'" % args.command
|
||||
print "Run '%s -h' for help" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
0
bin/smartpgp/__init__.py
Normal file
0
bin/smartpgp/__init__.py
Normal file
284
bin/smartpgp/commands.py
Normal file
284
bin/smartpgp/commands.py
Normal file
@ -0,0 +1,284 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
# https://github.com/ANSSI-FR/smartpgp
|
||||
# Copyright (C) 2016 ANSSI
|
||||
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
from smartcard.Exceptions import NoCardException
|
||||
from smartcard.System import readers
|
||||
from smartcard.util import toHexString
|
||||
|
||||
import struct
|
||||
|
||||
SELECT = [0x00, 0xA4, 0x04, 0x00,
|
||||
0x06,
|
||||
0xD2, 0x76, 0x00, 0x01, 0x24, 0x01,
|
||||
0x00]
|
||||
|
||||
VERIFY = [0x00, 0x20, 0x00, 0x83]
|
||||
TERMINATE = [0x00, 0xe6, 0x00, 0x00]
|
||||
ACTIVATE = [0x00, 0x44, 0x00, 0x00]
|
||||
ACTIVATE_FULL = [0x00, 0x44, 0x00, 0x01]
|
||||
GET_SM_CURVE_OID = [0x00, 0xca, 0x00, 0xd4]
|
||||
GENERATE_ASYMETRIC_KEYPAIR = [0x00, 0x47, 0x80, 0x00]
|
||||
|
||||
ALGS_ALIASES = {
|
||||
'ansix9p256r1': 'ansix9p256r1',
|
||||
'P256': 'ansix9p256r1',
|
||||
'P-256': 'ansix9p256r1',
|
||||
'NIST-P256': 'ansix9p256r1',
|
||||
'ansix9p384r1': 'ansix9p384r1',
|
||||
'P384': 'ansix9p384r1',
|
||||
'P-384': 'ansix9p384r1',
|
||||
'NIST-P384': 'ansix9p384r1',
|
||||
'ansix9p521r1': 'ansix9p521r1',
|
||||
'P521': 'ansix9p521r1',
|
||||
'P-521': 'ansix9p521r1',
|
||||
'NIST-P521': 'ansix9p521r1',
|
||||
|
||||
'brainpoolP256r1': 'brainpoolP256r1',
|
||||
'BP256': 'brainpoolP256r1',
|
||||
'BP-256': 'brainpoolP256r1',
|
||||
'brainpool256': 'brainpoolP256r1',
|
||||
'brainpoolP384r1': 'brainpoolP384r1',
|
||||
'BP384': 'brainpoolP384r1',
|
||||
'BP-384': 'brainpoolP384r1',
|
||||
'brainpool384': 'brainpoolP384r1',
|
||||
'brainpoolP512r1': 'brainpoolP512r1',
|
||||
'BP512': 'brainpoolP512r1',
|
||||
'BP-512': 'brainpoolP512r1',
|
||||
'brainpool512': 'brainpoolP512r1',
|
||||
}
|
||||
|
||||
OID_ALGS = {
|
||||
'ansix9p256r1': [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
|
||||
'ansix9p384r1': [0x2B, 0x81, 0x04, 0x00, 0x22],
|
||||
'ansix9p521r1': [0x2B, 0x81, 0x04, 0x00, 0x23],
|
||||
'brainpoolP256r1': [0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07],
|
||||
'brainpoolP384r1': [0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B],
|
||||
'brainpoolP512r1': [0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D],
|
||||
}
|
||||
|
||||
class WrongKeyRole(Exception):
|
||||
pass
|
||||
|
||||
class WrongAlgo(Exception):
|
||||
pass
|
||||
|
||||
def ascii_encode_pin(pin):
|
||||
return [ord(c) for c in pin]
|
||||
|
||||
def assemble_with_len(prefix,data):
|
||||
return prefix + [len(data)] + data
|
||||
|
||||
def asOctets(bs):
|
||||
l = len(bs)
|
||||
if l%8 is not 0:
|
||||
raise "BitString length is not a multiple of 8"
|
||||
result = []
|
||||
i = 0
|
||||
while i < l:
|
||||
byte = 0
|
||||
for x in range(8):
|
||||
byte |= bs[i + x] << (7 - x)
|
||||
result.append(byte)
|
||||
i += 8
|
||||
return result
|
||||
|
||||
def encode_len(data):
|
||||
l = len(data)
|
||||
if l > 0xff:
|
||||
l = [0x82, (l >> 8) & 0xff, l & 0xff]
|
||||
elif l > 0x7f:
|
||||
l = [0x81, l & 0xff]
|
||||
else:
|
||||
l = [l & 0xff]
|
||||
return l
|
||||
|
||||
def _raw_send_apdu(connection, text, apdu):
|
||||
print "%s" % text
|
||||
data, sw1, sw2 = connection.transmit(apdu)
|
||||
print data
|
||||
print "%s: %02X %02X" % (text, sw1, sw2)
|
||||
return (data,sw1,sw2)
|
||||
|
||||
def list_readers():
|
||||
for reader in readers():
|
||||
try:
|
||||
connection = reader.createConnection()
|
||||
connection.connect()
|
||||
print(reader, toHexString(connection.getATR()))
|
||||
except NoCardException:
|
||||
print(reader, 'no card inserted')
|
||||
|
||||
def select_reader(reader_index):
|
||||
reader_list = readers()
|
||||
r = reader_list[reader_index]
|
||||
conn = r.createConnection()
|
||||
conn.connect()
|
||||
return conn
|
||||
|
||||
def select_applet(connection):
|
||||
return _raw_send_apdu(connection,"Select OpenPGP Applet",SELECT)
|
||||
|
||||
def verif_admin_pin(connection, admin_pin):
|
||||
verif_apdu = assemble_with_len(VERIFY,ascii_encode_pin(admin_pin))
|
||||
return _raw_send_apdu(connection,"Verify Admin PIN",verif_apdu)
|
||||
|
||||
def full_reset_card(connection):
|
||||
_raw_send_apdu(connection,"Terminate",TERMINATE)
|
||||
_raw_send_apdu(connection,"Activate",ACTIVATE_FULL)
|
||||
|
||||
def reset_card(connection):
|
||||
_raw_send_apdu(connection,"Terminate",TERMINATE)
|
||||
_raw_send_apdu(connection,"Activate",ACTIVATE)
|
||||
|
||||
def switch_crypto_rsa(connection,key_role):
|
||||
data = [
|
||||
0x01, # RSA
|
||||
0x08, 0x00, # 2048 bits modulus
|
||||
0x00, 0x11, # 65537 - 17 bits public exponent
|
||||
0x03] # crt form with modulus
|
||||
if key_role == 'sig':
|
||||
role = 0xc1
|
||||
elif key_role == 'dec':
|
||||
role = 0xc2
|
||||
elif key_role == 'auth':
|
||||
role = 0xc3
|
||||
elif key_role == 'sm':
|
||||
role = 0xd4
|
||||
else:
|
||||
raise WrongKeyRole
|
||||
prefix = [0x00, 0xDA, 0x00] + [role]
|
||||
apdu = assemble_with_len(prefix, data)
|
||||
_raw_send_apdu(connection,"Switch to RSA2048 (%s)" % (key_role,),apdu)
|
||||
|
||||
def switch_crypto(connection,crypto,key_role):
|
||||
alg_name = None
|
||||
role = None
|
||||
# treat RSA differently
|
||||
if crypto=='rsa2048' or crypto=='RSA2048' or crypto=='rsa' or crypto=='RSA':
|
||||
return switch_crypto_rsa(connection,key_role)
|
||||
# this code is only for elliptic curves
|
||||
try:
|
||||
alg_name = ALGS_ALIASES[crypto]
|
||||
except KeyError:
|
||||
raise WrongAlgo
|
||||
data = OID_ALGS[alg_name]
|
||||
byte1 = 0x12
|
||||
if key_role == 'sig':
|
||||
role = 0xc1
|
||||
byte1 = 0x13
|
||||
elif key_role == 'dec':
|
||||
role = 0xc2
|
||||
elif key_role == 'auth':
|
||||
role = 0xc3
|
||||
elif key_role == 'sm':
|
||||
role = 0xd4
|
||||
else:
|
||||
raise WrongKeyRole
|
||||
prefix = [0x00, 0xDA, 0x00] + [role]
|
||||
apdu = assemble_with_len(prefix, [byte1] + data + [0xff])
|
||||
_raw_send_apdu(connection,"Switch to %s (%s)" % (crypto,key_role),apdu)
|
||||
|
||||
def generate_sm_key(connection):
|
||||
apdu = assemble_with_len(GENERATE_ASYMETRIC_KEYPAIR, [0xA6, 0x00])
|
||||
apdu = apdu + [0x00]
|
||||
return _raw_send_apdu(connection,"Generate SM key",apdu)
|
||||
|
||||
def set_resetting_code(connection, resetting_code):
|
||||
apdu = assemble_with_len([0x00, 0xDA, 0x00, 0xD3], ascii_encode_pin(resetting_code))
|
||||
_raw_send_apdu(connection,"Define the resetting code (PUK)",apdu)
|
||||
|
||||
def unblock_pin(connection, resetting_code, new_user_pin):
|
||||
data = ascii_encode_pin(resetting_code)+ascii_encode_pin(new_user_pin)
|
||||
apdu = assemble_with_len([0x00, 0x2C, 0x00, 0x81], data)
|
||||
_raw_send_apdu(connection,"Unblock user PIN with resetting code",apdu)
|
||||
|
||||
def put_sm_key(connection, pubkey, privkey):
|
||||
ins_p1_p2 = [0xDB, 0x3F, 0xFF]
|
||||
cdata = [0x92] + encode_len(privkey) + [0x99] + encode_len(pubkey)
|
||||
cdata = [0xA6, 0x00, 0x7F, 0x48] + encode_len(cdata) + cdata
|
||||
cdata = cdata + [0x5F, 0x48] + encode_len(privkey + pubkey) + privkey + pubkey
|
||||
cdata = [0x4D] + encode_len(cdata) + cdata
|
||||
i = 0
|
||||
cl = 255
|
||||
l = len(cdata)
|
||||
while i < l:
|
||||
if (l - i) <= cl:
|
||||
cla = 0x00
|
||||
data = cdata[i:]
|
||||
i = l
|
||||
else:
|
||||
cla = 0x10
|
||||
data = cdata[i:i+cl]
|
||||
i = i + cl
|
||||
apdu = assemble_with_len([cla] + ins_p1_p2, data)
|
||||
_raw_send_apdu(connection,"Sending SM key chunk",apdu)
|
||||
|
||||
def put_sm_certificate(connection, cert):
|
||||
prefix = [0x00, 0xA5, 0x03, 0x04]
|
||||
data = [0x60, 0x04, 0x5C, 0x02, 0x7F, 0x21]
|
||||
apdu = assemble_with_len(prefix, data)
|
||||
_raw_send_apdu(connection,"Selecting SM certificate",apdu)
|
||||
ins_p1_p2 = [0xDA, 0x7F, 0x21]
|
||||
i = 0
|
||||
cl = 255
|
||||
l = len(cert)
|
||||
while i < l:
|
||||
if (l - i) <= cl:
|
||||
cla = 0x00
|
||||
data = cert[i:]
|
||||
i = l
|
||||
else:
|
||||
cla = 0x10
|
||||
data = cert[i:i+cl]
|
||||
i = i + cl
|
||||
apdu = assemble_with_len([cla] + ins_p1_p2, data)
|
||||
_raw_send_apdu(connection,"Sending SM certificate chunk",apdu)
|
||||
|
||||
def get_sm_certificate(connection):
|
||||
prefix = [0x00, 0xA5, 0x03, 0x04]
|
||||
data = [0x60, 0x04, 0x5C, 0x02, 0x7F, 0x21]
|
||||
apdu = assemble_with_len(prefix, data)
|
||||
_raw_send_apdu(connection,"Selecting SM certificate",apdu)
|
||||
apdu = [0x00, 0xCA, 0x7F, 0x21, 0x00]
|
||||
(data,sw1,sw2) = _raw_send_apdu(connection,"Receiving SM certificate chunk",apdu)
|
||||
while sw1 == 0x61:
|
||||
apdu = [0x00, 0xC0, 0x00, 0x00, 0x00]
|
||||
(ndata,sw1,sw2) = _raw_send_apdu(connection,"Receiving SM certificate chunk",apdu)
|
||||
data = data + ndata
|
||||
return (data,sw1,sw2)
|
||||
|
||||
def get_sm_curve_oid(connection):
|
||||
""" Get Curve OID for Secure Messaging
|
||||
Return Curve OID (DER-encoded)
|
||||
"""
|
||||
apdu = GET_SM_CURVE_OID + [0x00]
|
||||
(data,sw1,sw2) = _raw_send_apdu(connection,"SM Curve OID",apdu)
|
||||
b = bytearray(data)
|
||||
assert(b[0]==0xd4)
|
||||
curve_len = b[1]
|
||||
curve = b[2:]
|
||||
assert(curve_len == len(curve))
|
||||
assert(curve[0])==0x12
|
||||
curve = curve[1:]
|
||||
if curve[-1] == 0xff:
|
||||
curve.pop()
|
||||
#print ' '.join('{:02X}'.format(c) for c in curve)
|
||||
# Add DER OID header manually ...
|
||||
return '\x06' + struct.pack('B',len(curve)) + curve
|
234
bin/smartpgp/highlevel.py
Normal file
234
bin/smartpgp/highlevel.py
Normal file
@ -0,0 +1,234 @@
|
||||
|
||||
# SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
# https://github.com/ANSSI-FR/smartpgp
|
||||
# Copyright (C) 2016 ANSSI
|
||||
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
from commands import *
|
||||
|
||||
import binascii
|
||||
import pyasn1
|
||||
from pyasn1.type import univ
|
||||
from pyasn1.codec.der import encoder as der_encoder,decoder as der_decoder
|
||||
|
||||
class ConnectionFailed(Exception):
|
||||
pass
|
||||
|
||||
class AdminPINFailed(Exception):
|
||||
pass
|
||||
|
||||
class CardConnectionContext:
|
||||
|
||||
def __init__(self):
|
||||
self.reader_index = 0
|
||||
self.admin_pin = "12345678"
|
||||
self.connection = None
|
||||
self.read_pin = self._default_pin_read_function
|
||||
self.connected = False
|
||||
self.verified = False
|
||||
self.input = None
|
||||
|
||||
def _default_pin_read_function(self, pin_type):
|
||||
return self.admin_pin
|
||||
|
||||
def set_pin_read_function(self, fun):
|
||||
self.read_pin = fun
|
||||
|
||||
def verify_admin_pin(self):
|
||||
if self.verified:
|
||||
return
|
||||
admin_pin = self.read_pin("Admin")
|
||||
(_,sw1,sw2)=verif_admin_pin(self.connection, admin_pin)
|
||||
if sw1==0x90 and sw2==0x00:
|
||||
self.verified = True
|
||||
else:
|
||||
raise AdminPINFailed
|
||||
|
||||
def connect(self):
|
||||
if self.connected:
|
||||
return
|
||||
self.connection = select_reader(self.reader_index)
|
||||
(_,sw1,sw2)=select_applet(self.connection)
|
||||
if sw1==0x90 and sw2==0x00:
|
||||
self.connected = True
|
||||
else:
|
||||
raise ConnectionFailed
|
||||
|
||||
def cmd_list_readers(self):
|
||||
list_readers()
|
||||
|
||||
def cmd_full_reset(self):
|
||||
# ignore errors
|
||||
self.connection = select_reader(self.reader_index)
|
||||
select_applet(self.connection)
|
||||
# do not use self.verify_admin_pin(), we want to force sending the APDUs
|
||||
verif_admin_pin(self.connection, self.admin_pin)
|
||||
verif_admin_pin(self.connection, self.admin_pin)
|
||||
verif_admin_pin(self.connection, self.admin_pin)
|
||||
full_reset_card(self.connection)
|
||||
# force re-entering admin PIN
|
||||
self.verified = False
|
||||
|
||||
def cmd_reset(self):
|
||||
# ignore errors
|
||||
self.connection = select_reader(self.reader_index)
|
||||
select_applet(self.connection)
|
||||
# do not use self.verify_admin_pin(), we want to force sending the APDUs
|
||||
verif_admin_pin(self.connection, self.admin_pin)
|
||||
verif_admin_pin(self.connection, self.admin_pin)
|
||||
verif_admin_pin(self.connection, self.admin_pin)
|
||||
reset_card(self.connection)
|
||||
# force re-entering admin PIN
|
||||
self.verified = False
|
||||
|
||||
def cmd_switch_crypto(self,alg_name,key_role):
|
||||
self.connect()
|
||||
self.verify_admin_pin()
|
||||
switch_crypto(self.connection,alg_name,key_role)
|
||||
|
||||
def cmd_switch_all_crypto(self,alg_name):
|
||||
self.connect()
|
||||
self.verify_admin_pin()
|
||||
switch_crypto(self.connection,alg_name,'sig')
|
||||
switch_crypto(self.connection,alg_name,'dec')
|
||||
switch_crypto(self.connection,alg_name,'auth')
|
||||
|
||||
def cmd_switch_bp256(self):
|
||||
self.cmd_switch_all_crypto('brainpoolP256r1')
|
||||
|
||||
def cmd_switch_bp384(self):
|
||||
self.cmd_switch_all_crypto('brainpoolP384r1')
|
||||
|
||||
def cmd_switch_bp512(self):
|
||||
self.cmd_switch_all_crypto('brainpoolP512r1')
|
||||
|
||||
def cmd_switch_p256(self):
|
||||
self.cmd_switch_all_crypto('P-256')
|
||||
|
||||
def cmd_switch_p384(self):
|
||||
self.cmd_switch_all_crypto('P-384')
|
||||
|
||||
def cmd_switch_p521(self):
|
||||
self.cmd_switch_all_crypto('P-521')
|
||||
|
||||
def cmd_switch_rsa2048(self):
|
||||
self.cmd_switch_all_crypto('rsa2048')
|
||||
|
||||
def cmd_generate_sm_key(self):
|
||||
if not self.output:
|
||||
print "Missing output file name"
|
||||
return
|
||||
self.connect()
|
||||
self.verify_admin_pin()
|
||||
(data,sw1,sw2) = generate_sm_key(self.connection)
|
||||
if sw1!=0x90 or sw2!=0x00:
|
||||
print "generate_sm_key failed"
|
||||
return
|
||||
if len(data) < 4 or data[0]!=0x7f or data[1]!=0x49:
|
||||
print "Strange reply for get_sm_certificate"
|
||||
return
|
||||
blob_len = data[2]
|
||||
blob = data[3:]
|
||||
assert(blob_len == len(blob))
|
||||
if blob[0]!=0x86:
|
||||
print "get_sm_certificate return something not a public key"
|
||||
return
|
||||
assert(blob[1]==len(blob[2:]))
|
||||
pubkey = blob[2:]
|
||||
# get curve OID
|
||||
curve_oid_der = get_sm_curve_oid(self.connection)
|
||||
if not curve_oid_der:
|
||||
print "Error getting SM curve OID"
|
||||
return
|
||||
(curve_oid,_) = der_decoder.decode(str(curve_oid_der))
|
||||
# now format it to DER [RFC5480]
|
||||
s = univ.Sequence()
|
||||
oid_elliptic_curve_pubkey = univ.ObjectIdentifier('1.2.840.10045.2.1')
|
||||
s.setComponentByPosition(0,oid_elliptic_curve_pubkey)
|
||||
s.setComponentByPosition(1,curve_oid)
|
||||
bs = univ.BitString("'%s'H" % binascii.hexlify(bytearray(pubkey)))
|
||||
s2 = univ.Sequence()
|
||||
s2.setComponentByPosition(0,s)
|
||||
s2.setComponentByPosition(1,bs)
|
||||
pubkey_der = der_encoder.encode(s2)
|
||||
print binascii.hexlify(pubkey_der)
|
||||
# and write result
|
||||
with open(self.output,"wb") as f:
|
||||
f.write(pubkey_der)
|
||||
f.close()
|
||||
|
||||
def cmd_put_sm_key(self):
|
||||
if self.input is None:
|
||||
print "No input key file"
|
||||
return
|
||||
f = open(self.input, 'r')
|
||||
fstr = f.read()
|
||||
f.close()
|
||||
(der,_) = der_decoder.decode(fstr)
|
||||
privkey = [ord(c) for c in der[1].asOctets()]
|
||||
oid = bytearray(der_encoder.encode(der[2]))
|
||||
pubkey = asOctets(der[3])
|
||||
if oid[0] == 0xa0:
|
||||
oid = oid[2:]
|
||||
oid_len = oid[1]
|
||||
oid = oid[2:]
|
||||
assert(oid_len == len(oid))
|
||||
curve = None
|
||||
for k,v in OID_ALGS.items():
|
||||
if bytearray(v) == oid:
|
||||
curve = k
|
||||
if curve is None:
|
||||
print "Curve not supported (%s)" % der[2]
|
||||
return
|
||||
self.connect()
|
||||
self.verify_admin_pin()
|
||||
switch_crypto(self.connection, curve, 'sm')
|
||||
put_sm_key(self.connection, pubkey, privkey)
|
||||
|
||||
def cmd_set_resetting_code(self):
|
||||
self.connect()
|
||||
self.verify_admin_pin()
|
||||
resetting_code = self.read_pin("PUK")
|
||||
set_resetting_code(self.connection, resetting_code)
|
||||
|
||||
def cmd_unblock_pin(self):
|
||||
self.connect()
|
||||
resetting_code = self.read_pin("PUK")
|
||||
new_user_pin = self.read_pin("new user")
|
||||
unlock_pin(self.connection, resetting_code, new_user_pin)
|
||||
|
||||
def cmd_put_sm_certificate(self):
|
||||
if self.input is None:
|
||||
print "No input certificate file"
|
||||
return
|
||||
f = open(self.input, 'r')
|
||||
cert = f.read()
|
||||
cert = [ord(c) for c in cert]
|
||||
f.close()
|
||||
self.connect()
|
||||
self.verify_admin_pin()
|
||||
put_sm_certificate(self.connection,cert)
|
||||
|
||||
def cmd_get_sm_certificate(self):
|
||||
if self.output is None:
|
||||
print "No output file"
|
||||
return
|
||||
self.connect()
|
||||
(cert,_,_) = get_sm_certificate(self.connection)
|
||||
cert = "".join([chr(c) for c in cert])
|
||||
with open(self.output, 'w') as f:
|
||||
f.write(cert)
|
||||
f.close()
|
39
build.xml
Normal file
39
build.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="smartpgp" default="convert" basedir=".">
|
||||
<description>Ant build for SmartPGP applet</description>
|
||||
<property name="src" location="src"/>
|
||||
<property name="build" location="build"/>
|
||||
<property file="javacard.properties"/>
|
||||
<property name="JAVA_PACKAGE" value="fr.anssi.smartpgp"/>
|
||||
<property name="JAVA_PACKAGE_DIR" value="fr/anssi/smartpgp/"/>
|
||||
<property name="APPLET_NAME" value="SmartPGPApplet"/>
|
||||
<property name="PACKAGE_AID" value="0xd2:0x76:0x00:0x01:0x24:0x01"/>
|
||||
<property name="APPLET_AID" value="0xd2:0x76:0x00:0x01:0x24:0x01:0x03:0x02:0xAF:0xAF"/>
|
||||
<property name="VERSION" value="1.0"/>
|
||||
<target name="init">
|
||||
<mkdir dir="${build}"/>
|
||||
</target>
|
||||
<target name="compile" depends="init" description="compile the source">
|
||||
<javac srcdir="${src}" destdir="${build}" includeantruntime="false" source="1.6" target="1.6">
|
||||
<classpath>
|
||||
<pathelement path="${JAVACARD_HOME}/lib/api_classic.jar"/>
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
<target depends="compile" name="convert" description="convert to .cap">
|
||||
<java classname="com.sun.javacard.converter.Main" fork="true" failonerror="true">
|
||||
<arg line="-classdir ${build}"/>
|
||||
<arg line="-verbose"/>
|
||||
<arg line="-exportpath ${JAVACARD_HOME}/api_export_files"/>
|
||||
<arg line="-out CAP JCA EXP"/>
|
||||
<arg line="-applet ${APPLET_AID} ${APPLET_NAME}"/>
|
||||
<arg line="${JAVA_PACKAGE} ${PACKAGE_AID} ${VERSION}"/>
|
||||
<classpath>
|
||||
<pathelement location="${JAVACARD_HOME}/lib/tools.jar"/>
|
||||
</classpath>
|
||||
</java>
|
||||
</target>
|
||||
<target name="clean" description="clean up">
|
||||
<delete dir="${build}"/>
|
||||
</target>
|
||||
</project>
|
1
javacard.properties.example
Normal file
1
javacard.properties.example
Normal file
@ -0,0 +1 @@
|
||||
JAVACARD_HOME=path_to_your_jdk_3_0_4
|
4117
patches/open-keychain.patch
Normal file
4117
patches/open-keychain.patch
Normal file
File diff suppressed because it is too large
Load Diff
29
secure_messaging/pki/generate_ca.sh
Executable file
29
secure_messaging/pki/generate_ca.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
CURVE=secp521r1
|
||||
DAYS=1825
|
||||
|
||||
######
|
||||
|
||||
DIR=PKI
|
||||
|
||||
######
|
||||
|
||||
set -e -u
|
||||
|
||||
if [[ -e "$DIR/private/ca.key.pem" ]] ; then
|
||||
echo "CA already exists, please remove it manually if you want to generate a new one" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$DIR/private" "$DIR/certs"
|
||||
|
||||
openssl ecparam -name "$CURVE" -genkey -check -noout -outform pem -out "$DIR/private/ca.key.pem"
|
||||
|
||||
openssl req -config openssl.cnf -extensions v3_ca -days $DAYS -new -x509 -sha256 -keyform pem -key "$DIR/private/ca.key.pem" -outform pem -out "$DIR/certs/ca.cert.pem"
|
||||
|
||||
touch $DIR/index.txt
|
||||
|
||||
echo 1000 > $DIR/serial
|
||||
|
||||
echo 1000 > $DIR/crlnumber
|
42
secure_messaging/pki/generate_token.sh
Executable file
42
secure_messaging/pki/generate_token.sh
Executable file
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
CURVE=secp256r1
|
||||
DAYS=730
|
||||
|
||||
######
|
||||
|
||||
DIR=PKI
|
||||
|
||||
######
|
||||
|
||||
set -e -u
|
||||
|
||||
if [[ $# -lt 1 ]] ; then
|
||||
echo "Missing card certificate identifier" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ $# -gt 1 ]] ; then
|
||||
echo "Too many parameters" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -e "$DIR/private/ca.key.pem" ]] ; then
|
||||
echo "Missing CA (please execute generate_ca.sh)" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
|
||||
NAME="$1"
|
||||
|
||||
mkdir -p "$DIR/csr"
|
||||
|
||||
openssl ecparam -name "$CURVE" -genkey -check -noout -outform der -out "$DIR/private/$NAME.key.der"
|
||||
|
||||
openssl req -config openssl.cnf -new -sha256 -keyform der -key "$DIR/private/$NAME.key.der" -outform pem -out "$DIR/csr/$NAME.csr.pem"
|
||||
|
||||
openssl ca -config openssl.cnf -extensions card_cert -days $DAYS -md sha256 -in "$DIR/csr/$NAME.csr.pem" -out "$DIR/certs/$NAME.cert.pem"
|
||||
|
||||
openssl x509 -inform pem -in "$DIR/certs/$NAME.cert.pem" -outform der -out "$DIR/certs/$NAME.cert.der"
|
||||
|
||||
rm "$DIR/certs/$NAME.cert.pem"
|
||||
|
91
secure_messaging/pki/openssl.cnf
Normal file
91
secure_messaging/pki/openssl.cnf
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
[ ca ]
|
||||
default_ca = CA_default
|
||||
|
||||
[ CA_default ]
|
||||
# Directory and file locations.
|
||||
dir = ./PKI/
|
||||
certs = $dir/certs
|
||||
crl_dir = $dir/crl
|
||||
new_certs_dir = $dir/certs
|
||||
database = $dir/index.txt
|
||||
serial = $dir/serial
|
||||
RANDFILE = $dir/private/.rand
|
||||
|
||||
# The root key and root certificate.
|
||||
private_key = $dir/private/ca.key.pem
|
||||
certificate = $dir/certs/ca.cert.pem
|
||||
|
||||
# For certificate revocation lists.
|
||||
crlnumber = $dir/crlnumber
|
||||
crl = $dir/crl/ca.crl.pem
|
||||
crl_extensions = crl_ext
|
||||
default_crl_days = 30
|
||||
|
||||
# SHA-1 is deprecated, so use SHA-2 instead.
|
||||
default_md = sha256
|
||||
|
||||
name_opt = ca_default
|
||||
cert_opt = ca_default
|
||||
default_days = 730
|
||||
preserve = no
|
||||
policy = policy_loose
|
||||
|
||||
[ policy_loose ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
[ req ]
|
||||
# Options for the `req` tool (`man req`).
|
||||
default_bits = 2048
|
||||
distinguished_name = req_distinguished_name
|
||||
string_mask = utf8only
|
||||
|
||||
# SHA-1 is deprecated, so use SHA-2 instead.
|
||||
default_md = sha256
|
||||
|
||||
# Extension to add when the -x509 option is used.
|
||||
x509_extensions = v3_ca
|
||||
|
||||
[ req_distinguished_name ]
|
||||
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
|
||||
countryName = Country Name (2 letter code)
|
||||
stateOrProvinceName = State or Province Name
|
||||
localityName = Locality Name
|
||||
0.organizationName = Organization Name
|
||||
organizationalUnitName = Organizational Unit Name
|
||||
commonName = Common Name
|
||||
emailAddress = Email Address
|
||||
|
||||
# Optionally, specify some defaults.
|
||||
countryName_default =
|
||||
stateOrProvinceName_default =
|
||||
localityName_default =
|
||||
0.organizationName_default =
|
||||
organizationalUnitName_default =
|
||||
emailAddress_default =
|
||||
|
||||
[ v3_ca ]
|
||||
# Extensions for a typical CA (`man x509v3_config`).
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer
|
||||
basicConstraints = critical, CA:true, pathlen:1
|
||||
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||
|
||||
[ card_cert ]
|
||||
# Extensions for client certificates (`man x509v3_config`).
|
||||
basicConstraints = CA:FALSE
|
||||
nsCertType = client
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer
|
||||
keyUsage = critical, nonRepudiation
|
||||
|
||||
[ crl_ext ]
|
||||
# Extension for CRLs (`man x509v3_config`).
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
BIN
secure_messaging/smartpgp_sm.pdf
Normal file
BIN
secure_messaging/smartpgp_sm.pdf
Normal file
Binary file not shown.
81
src/fr/anssi/smartpgp/CmacKey.java
Normal file
81
src/fr/anssi/smartpgp/CmacKey.java
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class CmacKey {
|
||||
|
||||
protected final AESKey key;
|
||||
protected final byte[] k1;
|
||||
protected final byte[] k2;
|
||||
|
||||
protected CmacKey() {
|
||||
key = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT,
|
||||
(short)(Constants.aesKeyLength() * 8),
|
||||
false);
|
||||
|
||||
k1 = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE, JCSystem.CLEAR_ON_DESELECT);
|
||||
|
||||
k2 = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE, JCSystem.CLEAR_ON_DESELECT);
|
||||
}
|
||||
|
||||
protected final boolean isInitialized() {
|
||||
return key.isInitialized();
|
||||
}
|
||||
|
||||
protected final void clearKey() {
|
||||
key.clearKey();
|
||||
Util.arrayFillNonAtomic(k1, (short)0, (short)k1.length, (byte)0);
|
||||
Util.arrayFillNonAtomic(k2, (short)0, (short)k2.length, (byte)0);
|
||||
}
|
||||
|
||||
protected final void setKey(final byte[] buf, final short bufOff) {
|
||||
key.setKey(buf, bufOff);
|
||||
|
||||
final Cipher cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
|
||||
|
||||
cipher.init(key, Cipher.MODE_ENCRYPT);
|
||||
|
||||
Util.arrayFillNonAtomic(k2, (short)0, Constants.AES_BLOCK_SIZE, (byte)0);
|
||||
cipher.doFinal(k2, (short)0, Constants.AES_BLOCK_SIZE,
|
||||
k1, (short)0);
|
||||
|
||||
final boolean mark = ((k1[0] & (byte)0x80) != (byte)0);
|
||||
Common.arrayLeftShift(k1, (short)0,
|
||||
k1, (short)0,
|
||||
Constants.AES_BLOCK_SIZE);
|
||||
if(mark) {
|
||||
k1[(short)(Constants.AES_BLOCK_SIZE - 1)] = (byte)(k1[(short)(Constants.AES_BLOCK_SIZE - 1)] ^ (byte)0x87);
|
||||
}
|
||||
|
||||
Common.arrayLeftShift(k1, (short)0,
|
||||
k2, (short)0,
|
||||
Constants.AES_BLOCK_SIZE);
|
||||
if((k1[0] & (byte)0x80) != (byte)0) {
|
||||
k2[(short)(Constants.AES_BLOCK_SIZE - 1)] = (byte)(k2[(short)(Constants.AES_BLOCK_SIZE - 1)] ^ (byte)0x87);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
229
src/fr/anssi/smartpgp/CmacSignature.java
Normal file
229
src/fr/anssi/smartpgp/CmacSignature.java
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class CmacSignature {
|
||||
|
||||
private CmacKey key;
|
||||
|
||||
private final Cipher cipher;
|
||||
|
||||
private final byte[] block_prev;
|
||||
private final byte[] block;
|
||||
|
||||
private final byte[] bytes;
|
||||
private static final byte BYTE_OFFSET_BLOCK_LEN = 0;
|
||||
private static final byte BYTES_SIZE = BYTE_OFFSET_BLOCK_LEN + 1;
|
||||
|
||||
|
||||
protected CmacSignature() {
|
||||
key = null;
|
||||
|
||||
cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
|
||||
|
||||
block_prev = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE, JCSystem.CLEAR_ON_DESELECT);
|
||||
block = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE, JCSystem.CLEAR_ON_DESELECT);
|
||||
|
||||
bytes = JCSystem.makeTransientByteArray(BYTES_SIZE, JCSystem.CLEAR_ON_DESELECT);
|
||||
}
|
||||
|
||||
protected final void clear() {
|
||||
key = null;
|
||||
}
|
||||
|
||||
private final byte blockLen() {
|
||||
return bytes[BYTE_OFFSET_BLOCK_LEN];
|
||||
}
|
||||
|
||||
private final void setBlockLen(final byte len) {
|
||||
bytes[BYTE_OFFSET_BLOCK_LEN] = len;
|
||||
}
|
||||
|
||||
|
||||
protected final boolean isInitialized() {
|
||||
return (key != null)
|
||||
&& key.isInitialized();
|
||||
}
|
||||
|
||||
private final void initBlock() {
|
||||
Util.arrayFillNonAtomic(block_prev, (short)0, (short)block_prev.length, (byte)0);
|
||||
Util.arrayFillNonAtomic(block, (short)0, (short)block.length, (byte)0);
|
||||
Util.arrayFillNonAtomic(bytes, (short)0, (short)bytes.length, (byte)0);
|
||||
}
|
||||
|
||||
protected final void init(final CmacKey key) {
|
||||
if((key == null) || !key.isInitialized()) {
|
||||
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
this.key = key;
|
||||
|
||||
cipher.init(key.key, Cipher.MODE_ENCRYPT);
|
||||
|
||||
initBlock();
|
||||
}
|
||||
|
||||
private final void commitBlock() {
|
||||
setBlockLen((byte)0);
|
||||
|
||||
Common.arrayXor(block_prev, (short)0,
|
||||
block, (short)0,
|
||||
block, (short)0,
|
||||
Constants.AES_BLOCK_SIZE);
|
||||
|
||||
cipher.doFinal(block, (short)0, Constants.AES_BLOCK_SIZE,
|
||||
block_prev, (short)0);
|
||||
}
|
||||
|
||||
protected final void update(final byte[] inBuf, short inOff, short inLen) {
|
||||
|
||||
if(!isInitialized()) {
|
||||
CryptoException.throwIt(CryptoException.INVALID_INIT);
|
||||
return;
|
||||
}
|
||||
|
||||
if(inLen <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
short bl = (short)blockLen();
|
||||
|
||||
short remLen = (short)(Constants.AES_BLOCK_SIZE - bl);
|
||||
|
||||
while(inLen >= remLen) {
|
||||
Util.arrayCopyNonAtomic(inBuf, inOff,
|
||||
block, bl,
|
||||
remLen);
|
||||
commitBlock();
|
||||
|
||||
inLen -= remLen;
|
||||
inOff += remLen;
|
||||
|
||||
remLen = Constants.AES_BLOCK_SIZE;
|
||||
bl = (short)0;
|
||||
}
|
||||
|
||||
if(inLen > 0) {
|
||||
Util.arrayCopyNonAtomic(inBuf, inOff,
|
||||
block, bl,
|
||||
inLen);
|
||||
|
||||
bl = (short)(bl + inLen);
|
||||
}
|
||||
|
||||
setBlockLen((byte)bl);
|
||||
}
|
||||
|
||||
protected final void updateByte(final byte b) {
|
||||
if(!isInitialized()) {
|
||||
CryptoException.throwIt(CryptoException.INVALID_INIT);
|
||||
return;
|
||||
}
|
||||
|
||||
short bl = blockLen();
|
||||
|
||||
block[bl++] = b;
|
||||
|
||||
if(bl == Constants.AES_BLOCK_SIZE) {
|
||||
commitBlock();
|
||||
} else {
|
||||
setBlockLen((byte)bl);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void updateShort(final short s) {
|
||||
updateByte((byte)((s >> 8) & (byte)0xff));
|
||||
updateByte((byte)(s & (byte)0xff));
|
||||
}
|
||||
|
||||
private final void compute(final byte[] inBuf, short inOff, short inLen) {
|
||||
if(!isInitialized()) {
|
||||
CryptoException.throwIt(CryptoException.INVALID_INIT);
|
||||
return;
|
||||
}
|
||||
|
||||
if(inLen < 0) {
|
||||
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
|
||||
return;
|
||||
}
|
||||
|
||||
short bl = blockLen();
|
||||
|
||||
if(inLen > 0) {
|
||||
final short il = (short)(inLen - 1);
|
||||
|
||||
update(inBuf, inOff, il);
|
||||
|
||||
bl = blockLen();
|
||||
|
||||
block[bl++] = inBuf[(short)(inOff + il)];
|
||||
|
||||
setBlockLen((byte)bl);
|
||||
}
|
||||
|
||||
if(bl == Constants.AES_BLOCK_SIZE) {
|
||||
Common.arrayXor(key.k1, (short)0,
|
||||
block, (short)0,
|
||||
block, (short)0,
|
||||
Constants.AES_BLOCK_SIZE);
|
||||
} else {
|
||||
block[bl++] = (byte)0x80;
|
||||
Util.arrayFillNonAtomic(block, bl, (short)(Constants.AES_BLOCK_SIZE - bl), (byte)0);
|
||||
Common.arrayXor(key.k2, (short)0,
|
||||
block, (short)0,
|
||||
block, (short)0,
|
||||
Constants.AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
commitBlock();
|
||||
}
|
||||
|
||||
protected final short sign(final byte[] inBuf, short inOff, short inLen,
|
||||
final byte[] sigBuf, final short sigOff, final short sigLen) {
|
||||
|
||||
if(!isInitialized()) {
|
||||
CryptoException.throwIt(CryptoException.INVALID_INIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if((sigLen < 0) || (sigLen > Constants.AES_BLOCK_SIZE)) {
|
||||
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
compute(inBuf, inOff, inLen);
|
||||
|
||||
Util.arrayCopyNonAtomic(block_prev, (short)0,
|
||||
sigBuf, sigOff,
|
||||
sigLen);
|
||||
|
||||
init(key);
|
||||
|
||||
return sigLen;
|
||||
}
|
||||
|
||||
}
|
138
src/fr/anssi/smartpgp/Common.java
Normal file
138
src/fr/anssi/smartpgp/Common.java
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class Common {
|
||||
|
||||
protected static final short writeLength(final byte[] buf, short off, final short len) {
|
||||
if(len > 0xff) {
|
||||
buf[off] = (byte)0x82;
|
||||
return Util.setShort(buf, (short)(off+1), len);
|
||||
}
|
||||
|
||||
if(len > 0x7f) {
|
||||
buf[off++] = (byte)0x81;
|
||||
buf[off++] = (byte)(len & 0xff);
|
||||
return off;
|
||||
}
|
||||
|
||||
buf[off++] = (byte)(len & 0x7f);
|
||||
return off;
|
||||
}
|
||||
|
||||
protected static final short skipLength(final byte[] buf, final short off, final short len) {
|
||||
|
||||
if(len < 1) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return off;
|
||||
}
|
||||
|
||||
if((buf[off] & (byte)0x80) == 0) {
|
||||
return (short)(off + 1);
|
||||
}
|
||||
|
||||
switch(buf[off]) {
|
||||
case (byte)0x81:
|
||||
if(len < 2) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return off;
|
||||
}
|
||||
return (short)(off + 2);
|
||||
|
||||
case (byte)0x82:
|
||||
if(len < 3) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return off;
|
||||
}
|
||||
return (short)(off + 3);
|
||||
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return off;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final short readLength(final byte[] buf, final short off, final short len) {
|
||||
|
||||
if(len < 1) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return (short)0;
|
||||
}
|
||||
|
||||
if((buf[off] & (byte)0x80) == 0) {
|
||||
return Util.makeShort((byte)0, buf[off]);
|
||||
}
|
||||
|
||||
switch(buf[off]) {
|
||||
case (byte)0x81:
|
||||
if(len < 2) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return (short)0;
|
||||
}
|
||||
return Util.makeShort((byte)0, buf[(short)(off + 1)]);
|
||||
|
||||
case (byte)0x82:
|
||||
if(len < 3) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return (short)0;
|
||||
}
|
||||
return Util.getShort(buf, (short)(off + 1));
|
||||
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return (short)0;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final short bitsToBytes(final short bits) {
|
||||
return (short)((bits / 8) + (short)(((bits % 8) == 0) ? 0 : 1));
|
||||
}
|
||||
|
||||
|
||||
protected static final void arrayLeftShift(final byte[] inBuf, short inOff,
|
||||
final byte[] outBuf, short outOff,
|
||||
final short len) {
|
||||
if(len > 0) {
|
||||
outBuf[outOff++] = (byte)(inBuf[inOff++] << 1);
|
||||
for(short i = 1; i < len; ++i) {
|
||||
if((inBuf[inOff] & (byte)0x80) != (byte)0) {
|
||||
outBuf[(short)(outOff - 1)] |= (byte)0x01;
|
||||
}
|
||||
outBuf[outOff++] = (byte)(inBuf[inOff++] << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static final void arrayXor(final byte[] inBuf1, short inOff1,
|
||||
final byte[] inBuf2, short inOff2,
|
||||
final byte[] outBuf, short outOff,
|
||||
final short len) {
|
||||
for(short i = 0; i < len; ++i) {
|
||||
outBuf[outOff++] = (byte)(inBuf1[inOff1++] ^ inBuf2[inOff2++]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
240
src/fr/anssi/smartpgp/Constants.java
Normal file
240
src/fr/anssi/smartpgp/Constants.java
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class Constants {
|
||||
|
||||
protected static final short INTERNAL_BUFFER_MAX_LENGTH =
|
||||
(short)((short)0x500);
|
||||
|
||||
protected static final short APDU_MAX_LENGTH = (short)256;
|
||||
|
||||
|
||||
protected static final byte USER_PIN_RETRY_COUNT = 3;
|
||||
protected static final byte USER_PIN_MIN_SIZE = 0x06;
|
||||
protected static final byte USER_PIN_MAX_SIZE = 0x7f; /* max is 0x7f because PIN format 2 */
|
||||
protected static final byte[] USER_PIN_DEFAULT = {
|
||||
0x31, 0x32, 0x33,0x34, 0x35, 0x36
|
||||
};
|
||||
|
||||
protected static final boolean USER_PIN_DEFAULT_FORCE_VERIFY_SIGNATURE = true;
|
||||
|
||||
protected static final byte USER_PUK_RETRY_COUNT = 3;
|
||||
protected static final byte USER_PUK_MIN_SIZE = 0x08;
|
||||
protected static final byte USER_PUK_MAX_SIZE = 0x7f; /* max is 0x7f because PIN format 2 */
|
||||
|
||||
protected static final byte ADMIN_PIN_RETRY_COUNT = 3;
|
||||
protected static final byte ADMIN_PIN_MIN_SIZE = 0x08;
|
||||
protected static final byte ADMIN_PIN_MAX_SIZE = 0x7f; /* max is 0x7f because PIN format 2 */
|
||||
protected static final byte[] ADMIN_PIN_DEFAULT = {
|
||||
0x31, 0x32, 0x33,0x34, 0x35, 0x36, 0x37, 0x38
|
||||
};
|
||||
|
||||
protected static final byte FINGERPRINT_SIZE = 20;
|
||||
protected static final byte GENERATION_DATE_SIZE = 4;
|
||||
|
||||
protected static final byte NAME_MAX_LENGTH = 39;
|
||||
protected static final byte LANG_MIN_LENGTH = 2;
|
||||
protected static final byte LANG_MAX_LENGTH = 8;
|
||||
protected static final byte[] LANG_DEFAULT = { (byte)0x65, (byte)0x6e };
|
||||
|
||||
protected static final byte SEX_MALE = (byte)0x31;
|
||||
protected static final byte SEX_FEMALE = (byte)0x32;
|
||||
protected static final byte SEX_NOT_ANNOUNCED = (byte)0x39;
|
||||
|
||||
protected static final short TAG_AID = (short)0x004f;
|
||||
protected static final short TAG_LOGIN = (short)0x005e;
|
||||
protected static final short TAG_URL = (short)0x5f50;
|
||||
protected static final short TAG_HISTORICAL_BYTES_CARD_SERVICE_CARD_CAPABILITIES = (short)0x5f52;
|
||||
protected static final short TAG_CARDHOLDER_RELATED_DATA = (short)0x0065;
|
||||
protected static final short TAG_APPLICATION_RELATED_DATA = (short)0x006e;
|
||||
protected static final short TAG_SECURITY_SUPPORT_TEMPLATE = (short)0x007a;
|
||||
protected static final short TAG_CARDHOLDER_CERTIFICATE = (short)0x7f21;
|
||||
protected static final short TAG_NAME = (short)0x005b;
|
||||
protected static final short TAG_LANG = (short)0x5f2d;
|
||||
protected static final short TAG_SEX = (short)0x5f35;
|
||||
protected static final short TAG_ALGORITHM_ATTRIBUTES_SIG = (short)0x00c1;
|
||||
protected static final short TAG_ALGORITHM_ATTRIBUTES_DEC = (short)0x00c2;
|
||||
protected static final short TAG_ALGORITHM_ATTRIBUTES_AUT = (short)0x00c3;
|
||||
protected static final short TAG_ALGORITHM_ATTRIBUTES_SM = (short)0x00d4;
|
||||
protected static final short TAG_PW_STATUS = (short)0x00c4;
|
||||
protected static final short TAG_KEY_FINGERPRINTS = (short)0x00c5;
|
||||
protected static final short TAG_CA_FINGERPRINTS = (short)0x00c6;
|
||||
protected static final short TAG_FINGERPRINT_SIG = (short)0x00c7;
|
||||
protected static final short TAG_FINGERPRINT_DEC = (short)0x00c8;
|
||||
protected static final short TAG_FINGERPRINT_AUT = (short)0x00c9;
|
||||
protected static final short TAG_FINGERPRINT_CA = (short)0x00ca;
|
||||
protected static final short TAG_FINGERPRINT_CB = (short)0x00cb;
|
||||
protected static final short TAG_FINGERPRINT_CC = (short)0x00cc;
|
||||
protected static final short TAG_KEY_GENERATION_DATES = (short)0x00cd;
|
||||
protected static final short TAG_GENERATION_DATE_SIG = (short)0x00ce;
|
||||
protected static final short TAG_GENERATION_DATE_DEC = (short)0x00cf;
|
||||
protected static final short TAG_GENERATION_DATE_AUT = (short)0x00d0;
|
||||
protected static final short TAG_RESETTING_CODE = (short)0x00d3;
|
||||
protected static final short TAG_EXTENDED_LENGTH_INFORMATION = (short)0x7f66;
|
||||
protected static final short TAG_PRIVATE_DO_0101 = (short)0x0101;
|
||||
protected static final short TAG_PRIVATE_DO_0102 = (short)0x0102;
|
||||
protected static final short TAG_PRIVATE_DO_0103 = (short)0x0103;
|
||||
protected static final short TAG_PRIVATE_DO_0104 = (short)0x0104;
|
||||
protected static final short TAG_AES_KEY = (short)0x00d5;
|
||||
|
||||
protected static final short CRT_AUTHENTICATION_KEY = (short)0xa400;
|
||||
protected static final short CRT_SECURE_MESSAGING_KEY = (short)0xa600;
|
||||
protected static final short CRT_SIGNATURE_KEY = (short)0xb600;
|
||||
protected static final short CRT_DECRYPTION_KEY = (short)0xb800;
|
||||
|
||||
protected static final byte CLA_MASK_CHAINING = (byte)0x10;
|
||||
protected static final byte CLA_MASK_SECURE_MESSAGING = (byte)0x04;
|
||||
|
||||
|
||||
protected static final byte INS_SELECT_DATA = (byte)0xA5;
|
||||
protected static final byte INS_GET_DATA = (byte)0xCA;
|
||||
protected static final byte INS_GET_NEXT_DATA = (byte)0xCC;
|
||||
protected static final byte INS_VERIFY = (byte)0x20;
|
||||
protected static final byte INS_CHANGE_REFERENCE_DATA = (byte)0x24;
|
||||
protected static final byte INS_RESET_RETRY_COUNTER = (byte)0x2C;
|
||||
protected static final byte INS_PUT_DATA_DA = (byte)0xDA;
|
||||
protected static final byte INS_PUT_DATA_DB = (byte)0xDB;
|
||||
protected static final byte INS_GENERATE_ASYMMETRIC_KEY_PAIR = (byte)0x47;
|
||||
protected static final byte INS_PERFORM_SECURITY_OPERATION = (byte)0x2A;
|
||||
protected static final byte INS_INTERNAL_AUTHENTICATE = (byte)0x88;
|
||||
protected static final byte INS_GET_RESPONSE = (byte)0xC0;
|
||||
protected static final byte INS_GET_CHALLENGE = (byte)0x84;
|
||||
protected static final byte INS_TERMINATE_DF = (byte)0xE6;
|
||||
protected static final byte INS_ACTIVATE_FILE = (byte)0x44;
|
||||
|
||||
|
||||
protected static final short SW_TERMINATED = (short)0x6285;
|
||||
protected static final short SW_MEMORY_FAILURE = (short)0x6581;
|
||||
protected static final short SW_CHAINING_ERROR = (short)0x6883;
|
||||
protected static final short SW_REFERENCE_DATA_NOT_FOUND = (short)0x6A88;
|
||||
|
||||
|
||||
|
||||
protected static final byte[] HISTORICAL_BYTES = {
|
||||
(byte)0x00, /* category indicator byte */
|
||||
|
||||
(byte)0x31, /* card service data */
|
||||
(byte)0xC5, /* ... */
|
||||
|
||||
(byte)0x73, /* card capabilities */
|
||||
(byte)0xC0, /* 1st byte: "methods supported" see ISO 7816-4 */
|
||||
(byte)0x01, /* 2nd byte: "data coding byte" idem */
|
||||
(byte)0x80, /* 3rd byte: command chaining (not extended length by default as all readers do not support them...) */
|
||||
|
||||
(byte)0x05, /* status indicator byte : operational state */
|
||||
(byte)0x90, /* SW1 */
|
||||
(byte)0x00 /* SW2 */
|
||||
};
|
||||
|
||||
protected static final byte[] EXTENDED_CAPABILITIES = {
|
||||
(byte)(0x80 | /* support secure messaging */
|
||||
0x40 | /* support get challenge */
|
||||
0x20 | /* support key import */
|
||||
0x10 | /* support pw status changes */
|
||||
0x08 | /* support private DOs (0101-0104) */
|
||||
0x04 | /* support algorithm attributes changes */
|
||||
0x02), /* support PSO:DEC AES */
|
||||
(byte)0x01, /* SM 0x01 = 128 bits, 0x02 = 256 bits */
|
||||
(byte)0x00, (byte)0x20, /* max length get challenge */
|
||||
(byte)0x04, (byte)0x80, /* max length of carholder certificate */
|
||||
(byte)0x00, (byte)0xff, /* max length of special DOs (private, login, url) */
|
||||
(byte)0x00, /* PIN format 2 not supported */
|
||||
(byte)0x00 /* RFU */
|
||||
};
|
||||
|
||||
protected static final short aesKeyLength() {
|
||||
return (short)(16 * EXTENDED_CAPABILITIES[1]);
|
||||
}
|
||||
|
||||
protected static final short challengeMaxLength() {
|
||||
return Util.getShort(EXTENDED_CAPABILITIES, (short)2);
|
||||
}
|
||||
|
||||
protected static final short cardholderCertificateMaxLength() {
|
||||
return Util.getShort(EXTENDED_CAPABILITIES, (short)4);
|
||||
}
|
||||
|
||||
protected static final short specialDoMaxLength() {
|
||||
return Util.getShort(EXTENDED_CAPABILITIES, (short)6);
|
||||
}
|
||||
|
||||
|
||||
protected static final byte[] DSI_SHA224_HEADER = {
|
||||
(byte)0x30, (byte)0x2D,
|
||||
(byte)0x30, (byte)0x0D,
|
||||
(byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x04,
|
||||
(byte)0x05, (byte)0x00,
|
||||
(byte)0x04, (byte)0x1C
|
||||
};
|
||||
|
||||
protected static final byte[] DSI_SHA256_HEADER = {
|
||||
(byte)0x30, (byte)0x31,
|
||||
(byte)0x30, (byte)0x0D,
|
||||
(byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x01,
|
||||
(byte)0x05, (byte)0x00,
|
||||
(byte)0x04, (byte)0x20
|
||||
};
|
||||
|
||||
protected static final byte[] DSI_SHA384_HEADER = {
|
||||
(byte)0x30, (byte)0x41,
|
||||
(byte)0x30, (byte)0x0D,
|
||||
(byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x02,
|
||||
(byte)0x05, (byte)0x00,
|
||||
(byte)0x04, (byte)0x30
|
||||
};
|
||||
|
||||
protected static final byte[] DSI_SHA512_HEADER = {
|
||||
(byte)0x30, (byte)0x51,
|
||||
(byte)0x30, (byte)0x0D,
|
||||
(byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x03,
|
||||
(byte)0x05, (byte)0x00,
|
||||
(byte)0x04, (byte)0x40
|
||||
};
|
||||
|
||||
|
||||
protected static final byte ALGORITHM_ATTRIBUTES_MIN_LENGTH = 6;
|
||||
protected static final byte ALGORITHM_ATTRIBUTES_MAX_LENGTH = 13;
|
||||
|
||||
protected static final byte[] ALGORITHM_ATTRIBUTES_DEFAULT = {
|
||||
(byte)0x01, /* RSA */
|
||||
(byte)0x08, (byte)0x00, /* 2048 bits modulus */
|
||||
(byte)0x00, (byte)0x11, /* 65537 = 17 bits public exponent */
|
||||
(byte)0x03 /* crt form with modulus */
|
||||
};
|
||||
|
||||
protected static final byte[] ALGORITHM_ATTRIBUTES_DEFAULT_SECURE_MESSAGING = {
|
||||
(byte)0x12, /* ECDH */
|
||||
(byte)0x2A, (byte)0x86, (byte)0x48, (byte)0xCE, (byte)0x3D, (byte)0x03, (byte)0x01, (byte)0x07, /* ansix9p256r1 */
|
||||
(byte)0xFF /* with public key */
|
||||
};
|
||||
|
||||
protected static final byte[] RSA_EXPONENT = { (byte)0x01, (byte)0x00, (byte)0x01 };
|
||||
|
||||
|
||||
protected static final short AES_BLOCK_SIZE = (short)16;
|
||||
|
||||
}
|
605
src/fr/anssi/smartpgp/ECConstants.java
Normal file
605
src/fr/anssi/smartpgp/ECConstants.java
Normal file
@ -0,0 +1,605 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
public final class ECConstants {
|
||||
|
||||
protected static final byte[] ansix9p256r1_oid =
|
||||
{ (byte)0x2A, (byte)0x86, (byte)0x48, (byte)0xCE, (byte)0x3D, (byte)0x03, (byte)0x01, (byte)0x07 };
|
||||
|
||||
protected static final byte[] ansix9p256r1_field = {
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p256r1_a = {
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFC
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p256r1_b = {
|
||||
(byte)0x5A, (byte)0xC6, (byte)0x35, (byte)0xD8,
|
||||
(byte)0xAA, (byte)0x3A, (byte)0x93, (byte)0xE7,
|
||||
(byte)0xB3, (byte)0xEB, (byte)0xBD, (byte)0x55,
|
||||
(byte)0x76, (byte)0x98, (byte)0x86, (byte)0xBC,
|
||||
(byte)0x65, (byte)0x1D, (byte)0x06, (byte)0xB0,
|
||||
(byte)0xCC, (byte)0x53, (byte)0xB0, (byte)0xF6,
|
||||
(byte)0x3B, (byte)0xCE, (byte)0x3C, (byte)0x3E,
|
||||
(byte)0x27, (byte)0xD2, (byte)0x60, (byte)0x4B
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p256r1_g = {
|
||||
(byte)0x04,
|
||||
(byte)0x6B, (byte)0x17, (byte)0xD1, (byte)0xF2,
|
||||
(byte)0xE1, (byte)0x2C, (byte)0x42, (byte)0x47,
|
||||
(byte)0xF8, (byte)0xBC, (byte)0xE6, (byte)0xE5,
|
||||
(byte)0x63, (byte)0xA4, (byte)0x40, (byte)0xF2,
|
||||
(byte)0x77, (byte)0x03, (byte)0x7D, (byte)0x81,
|
||||
(byte)0x2D, (byte)0xEB, (byte)0x33, (byte)0xA0,
|
||||
(byte)0xF4, (byte)0xA1, (byte)0x39, (byte)0x45,
|
||||
(byte)0xD8, (byte)0x98, (byte)0xC2, (byte)0x96,
|
||||
(byte)0x4F, (byte)0xE3, (byte)0x42, (byte)0xE2,
|
||||
(byte)0xFE, (byte)0x1A, (byte)0x7F, (byte)0x9B,
|
||||
(byte)0x8E, (byte)0xE7, (byte)0xEB, (byte)0x4A,
|
||||
(byte)0x7C, (byte)0x0F, (byte)0x9E, (byte)0x16,
|
||||
(byte)0x2B, (byte)0xCE, (byte)0x33, (byte)0x57,
|
||||
(byte)0x6B, (byte)0x31, (byte)0x5E, (byte)0xCE,
|
||||
(byte)0xCB, (byte)0xB6, (byte)0x40, (byte)0x68,
|
||||
(byte)0x37, (byte)0xBF, (byte)0x51, (byte)0xF5
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p256r1_r = {
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xBC, (byte)0xE6, (byte)0xFA, (byte)0xAD,
|
||||
(byte)0xA7, (byte)0x17, (byte)0x9E, (byte)0x84,
|
||||
(byte)0xF3, (byte)0xB9, (byte)0xCA, (byte)0xC2,
|
||||
(byte)0xFC, (byte)0x63, (byte)0x25, (byte)0x51
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected static final byte[] ansix9p384r1_oid =
|
||||
{ (byte)0x2B, (byte)0x81, (byte)0x04, (byte)0x00, (byte)0x22 };
|
||||
|
||||
protected static final byte[] ansix9p384r1_field = {
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFE,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p384r1_a = {
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFE,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFC
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p384r1_b = {
|
||||
(byte)0xB3, (byte)0x31, (byte)0x2F, (byte)0xA7,
|
||||
(byte)0xE2, (byte)0x3E, (byte)0xE7, (byte)0xE4,
|
||||
(byte)0x98, (byte)0x8E, (byte)0x05, (byte)0x6B,
|
||||
(byte)0xE3, (byte)0xF8, (byte)0x2D, (byte)0x19,
|
||||
(byte)0x18, (byte)0x1D, (byte)0x9C, (byte)0x6E,
|
||||
(byte)0xFE, (byte)0x81, (byte)0x41, (byte)0x12,
|
||||
(byte)0x03, (byte)0x14, (byte)0x08, (byte)0x8F,
|
||||
(byte)0x50, (byte)0x13, (byte)0x87, (byte)0x5A,
|
||||
(byte)0xC6, (byte)0x56, (byte)0x39, (byte)0x8D,
|
||||
(byte)0x8A, (byte)0x2E, (byte)0xD1, (byte)0x9D,
|
||||
(byte)0x2A, (byte)0x85, (byte)0xC8, (byte)0xED,
|
||||
(byte)0xD3, (byte)0xEC, (byte)0x2A, (byte)0xEF
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p384r1_g = {
|
||||
(byte)0x04,
|
||||
(byte)0xAA, (byte)0x87, (byte)0xCA, (byte)0x22,
|
||||
(byte)0xBE, (byte)0x8B, (byte)0x05, (byte)0x37,
|
||||
(byte)0x8E, (byte)0xB1, (byte)0xC7, (byte)0x1E,
|
||||
(byte)0xF3, (byte)0x20, (byte)0xAD, (byte)0x74,
|
||||
(byte)0x6E, (byte)0x1D, (byte)0x3B, (byte)0x62,
|
||||
(byte)0x8B, (byte)0xA7, (byte)0x9B, (byte)0x98,
|
||||
(byte)0x59, (byte)0xF7, (byte)0x41, (byte)0xE0,
|
||||
(byte)0x82, (byte)0x54, (byte)0x2A, (byte)0x38,
|
||||
(byte)0x55, (byte)0x02, (byte)0xF2, (byte)0x5D,
|
||||
(byte)0xBF, (byte)0x55, (byte)0x29, (byte)0x6C,
|
||||
(byte)0x3A, (byte)0x54, (byte)0x5E, (byte)0x38,
|
||||
(byte)0x72, (byte)0x76, (byte)0x0A, (byte)0xB7,
|
||||
(byte)0x36, (byte)0x17, (byte)0xDE, (byte)0x4A,
|
||||
(byte)0x96, (byte)0x26, (byte)0x2C, (byte)0x6F,
|
||||
(byte)0x5D, (byte)0x9E, (byte)0x98, (byte)0xBF,
|
||||
(byte)0x92, (byte)0x92, (byte)0xDC, (byte)0x29,
|
||||
(byte)0xF8, (byte)0xF4, (byte)0x1D, (byte)0xBD,
|
||||
(byte)0x28, (byte)0x9A, (byte)0x14, (byte)0x7C,
|
||||
(byte)0xE9, (byte)0xDA, (byte)0x31, (byte)0x13,
|
||||
(byte)0xB5, (byte)0xF0, (byte)0xB8, (byte)0xC0,
|
||||
(byte)0x0A, (byte)0x60, (byte)0xB1, (byte)0xCE,
|
||||
(byte)0x1D, (byte)0x7E, (byte)0x81, (byte)0x9D,
|
||||
(byte)0x7A, (byte)0x43, (byte)0x1D, (byte)0x7C,
|
||||
(byte)0x90, (byte)0xEA, (byte)0x0E, (byte)0x5F
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p384r1_r = {
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xC7, (byte)0x63, (byte)0x4D, (byte)0x81,
|
||||
(byte)0xF4, (byte)0x37, (byte)0x2D, (byte)0xDF,
|
||||
(byte)0x58, (byte)0x1A, (byte)0x0D, (byte)0xB2,
|
||||
(byte)0x48, (byte)0xB0, (byte)0xA7, (byte)0x7A,
|
||||
(byte)0xEC, (byte)0xEC, (byte)0x19, (byte)0x6A,
|
||||
(byte)0xCC, (byte)0xC5, (byte)0x29, (byte)0x73
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected static final byte[] ansix9p521r1_oid =
|
||||
{ (byte)0x2B, (byte)0x81, (byte)0x04, (byte)0x00, (byte)0x23 };
|
||||
|
||||
protected static final byte[] ansix9p521r1_field = {
|
||||
(byte)0x01, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p521r1_a = {
|
||||
(byte)0x01, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFC
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p521r1_b = {
|
||||
(byte)0x00, (byte)0x51, (byte)0x95, (byte)0x3E,
|
||||
(byte)0xB9, (byte)0x61, (byte)0x8E, (byte)0x1C,
|
||||
(byte)0x9A, (byte)0x1F, (byte)0x92, (byte)0x9A,
|
||||
(byte)0x21, (byte)0xA0, (byte)0xB6, (byte)0x85,
|
||||
(byte)0x40, (byte)0xEE, (byte)0xA2, (byte)0xDA,
|
||||
(byte)0x72, (byte)0x5B, (byte)0x99, (byte)0xB3,
|
||||
(byte)0x15, (byte)0xF3, (byte)0xB8, (byte)0xB4,
|
||||
(byte)0x89, (byte)0x91, (byte)0x8E, (byte)0xF1,
|
||||
(byte)0x09, (byte)0xE1, (byte)0x56, (byte)0x19,
|
||||
(byte)0x39, (byte)0x51, (byte)0xEC, (byte)0x7E,
|
||||
(byte)0x93, (byte)0x7B, (byte)0x16, (byte)0x52,
|
||||
(byte)0xC0, (byte)0xBD, (byte)0x3B, (byte)0xB1,
|
||||
(byte)0xBF, (byte)0x07, (byte)0x35, (byte)0x73,
|
||||
(byte)0xDF, (byte)0x88, (byte)0x3D, (byte)0x2C,
|
||||
(byte)0x34, (byte)0xF1, (byte)0xEF, (byte)0x45,
|
||||
(byte)0x1F, (byte)0xD4, (byte)0x6B, (byte)0x50,
|
||||
(byte)0x3F, (byte)0x00
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p521r1_g = {
|
||||
(byte)0x04,
|
||||
(byte)0x00, (byte)0xC6, (byte)0x85, (byte)0x8E,
|
||||
(byte)0x06, (byte)0xB7, (byte)0x04, (byte)0x04,
|
||||
(byte)0xE9, (byte)0xCD, (byte)0x9E, (byte)0x3E,
|
||||
(byte)0xCB, (byte)0x66, (byte)0x23, (byte)0x95,
|
||||
(byte)0xB4, (byte)0x42, (byte)0x9C, (byte)0x64,
|
||||
(byte)0x81, (byte)0x39, (byte)0x05, (byte)0x3F,
|
||||
(byte)0xB5, (byte)0x21, (byte)0xF8, (byte)0x28,
|
||||
(byte)0xAF, (byte)0x60, (byte)0x6B, (byte)0x4D,
|
||||
(byte)0x3D, (byte)0xBA, (byte)0xA1, (byte)0x4B,
|
||||
(byte)0x5E, (byte)0x77, (byte)0xEF, (byte)0xE7,
|
||||
(byte)0x59, (byte)0x28, (byte)0xFE, (byte)0x1D,
|
||||
(byte)0xC1, (byte)0x27, (byte)0xA2, (byte)0xFF,
|
||||
(byte)0xA8, (byte)0xDE, (byte)0x33, (byte)0x48,
|
||||
(byte)0xB3, (byte)0xC1, (byte)0x85, (byte)0x6A,
|
||||
(byte)0x42, (byte)0x9B, (byte)0xF9, (byte)0x7E,
|
||||
(byte)0x7E, (byte)0x31, (byte)0xC2, (byte)0xE5,
|
||||
(byte)0xBD, (byte)0x66,
|
||||
(byte)0x01, (byte)0x18, (byte)0x39, (byte)0x29,
|
||||
(byte)0x6A, (byte)0x78, (byte)0x9A, (byte)0x3B,
|
||||
(byte)0xC0, (byte)0x04, (byte)0x5C, (byte)0x8A,
|
||||
(byte)0x5F, (byte)0xB4, (byte)0x2C, (byte)0x7D,
|
||||
(byte)0x1B, (byte)0xD9, (byte)0x98, (byte)0xF5,
|
||||
(byte)0x44, (byte)0x49, (byte)0x57, (byte)0x9B,
|
||||
(byte)0x44, (byte)0x68, (byte)0x17, (byte)0xAF,
|
||||
(byte)0xBD, (byte)0x17, (byte)0x27, (byte)0x3E,
|
||||
(byte)0x66, (byte)0x2C, (byte)0x97, (byte)0xEE,
|
||||
(byte)0x72, (byte)0x99, (byte)0x5E, (byte)0xF4,
|
||||
(byte)0x26, (byte)0x40, (byte)0xC5, (byte)0x50,
|
||||
(byte)0xB9, (byte)0x01, (byte)0x3F, (byte)0xAD,
|
||||
(byte)0x07, (byte)0x61, (byte)0x35, (byte)0x3C,
|
||||
(byte)0x70, (byte)0x86, (byte)0xA2, (byte)0x72,
|
||||
(byte)0xC2, (byte)0x40, (byte)0x88, (byte)0xBE,
|
||||
(byte)0x94, (byte)0x76, (byte)0x9F, (byte)0xD1,
|
||||
(byte)0x66, (byte)0x50
|
||||
};
|
||||
|
||||
protected static final byte[] ansix9p521r1_r = {
|
||||
(byte)0x01, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
|
||||
(byte)0xFF, (byte)0xFA, (byte)0x51, (byte)0x86,
|
||||
(byte)0x87, (byte)0x83, (byte)0xBF, (byte)0x2F,
|
||||
(byte)0x96, (byte)0x6B, (byte)0x7F, (byte)0xCC,
|
||||
(byte)0x01, (byte)0x48, (byte)0xF7, (byte)0x09,
|
||||
(byte)0xA5, (byte)0xD0, (byte)0x3B, (byte)0xB5,
|
||||
(byte)0xC9, (byte)0xB8, (byte)0x89, (byte)0x9C,
|
||||
(byte)0x47, (byte)0xAE, (byte)0xBB, (byte)0x6F,
|
||||
(byte)0xB7, (byte)0x1E, (byte)0x91, (byte)0x38,
|
||||
(byte)0x64, (byte)0x09
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected static final byte[] brainpoolP256r1_oid =
|
||||
{ (byte)0x2B, (byte)0x24, (byte)0x03, (byte)0x03, (byte)0x02, (byte)0x08, (byte)0x01, (byte)0x01, (byte)0x07 };
|
||||
|
||||
protected static final byte[] brainpoolP256r1_field = {
|
||||
(byte)0xA9, (byte)0xFB, (byte)0x57, (byte)0xDB,
|
||||
(byte)0xA1, (byte)0xEE, (byte)0xA9, (byte)0xBC,
|
||||
(byte)0x3E, (byte)0x66, (byte)0x0A, (byte)0x90,
|
||||
(byte)0x9D, (byte)0x83, (byte)0x8D, (byte)0x72,
|
||||
(byte)0x6E, (byte)0x3B, (byte)0xF6, (byte)0x23,
|
||||
(byte)0xD5, (byte)0x26, (byte)0x20, (byte)0x28,
|
||||
(byte)0x20, (byte)0x13, (byte)0x48, (byte)0x1D,
|
||||
(byte)0x1F, (byte)0x6E, (byte)0x53, (byte)0x77
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP256r1_a = {
|
||||
(byte)0x7D, (byte)0x5A, (byte)0x09, (byte)0x75,
|
||||
(byte)0xFC, (byte)0x2C, (byte)0x30, (byte)0x57,
|
||||
(byte)0xEE, (byte)0xF6, (byte)0x75, (byte)0x30,
|
||||
(byte)0x41, (byte)0x7A, (byte)0xFF, (byte)0xE7,
|
||||
(byte)0xFB, (byte)0x80, (byte)0x55, (byte)0xC1,
|
||||
(byte)0x26, (byte)0xDC, (byte)0x5C, (byte)0x6C,
|
||||
(byte)0xE9, (byte)0x4A, (byte)0x4B, (byte)0x44,
|
||||
(byte)0xF3, (byte)0x30, (byte)0xB5, (byte)0xD9
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP256r1_b = {
|
||||
(byte)0x26, (byte)0xDC, (byte)0x5C, (byte)0x6C,
|
||||
(byte)0xE9, (byte)0x4A, (byte)0x4B, (byte)0x44,
|
||||
(byte)0xF3, (byte)0x30, (byte)0xB5, (byte)0xD9,
|
||||
(byte)0xBB, (byte)0xD7, (byte)0x7C, (byte)0xBF,
|
||||
(byte)0x95, (byte)0x84, (byte)0x16, (byte)0x29,
|
||||
(byte)0x5C, (byte)0xF7, (byte)0xE1, (byte)0xCE,
|
||||
(byte)0x6B, (byte)0xCC, (byte)0xDC, (byte)0x18,
|
||||
(byte)0xFF, (byte)0x8C, (byte)0x07, (byte)0xB6
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP256r1_g = {
|
||||
(byte)0x04,
|
||||
(byte)0x8B, (byte)0xD2, (byte)0xAE, (byte)0xB9,
|
||||
(byte)0xCB, (byte)0x7E, (byte)0x57, (byte)0xCB,
|
||||
(byte)0x2C, (byte)0x4B, (byte)0x48, (byte)0x2F,
|
||||
(byte)0xFC, (byte)0x81, (byte)0xB7, (byte)0xAF,
|
||||
(byte)0xB9, (byte)0xDE, (byte)0x27, (byte)0xE1,
|
||||
(byte)0xE3, (byte)0xBD, (byte)0x23, (byte)0xC2,
|
||||
(byte)0x3A, (byte)0x44, (byte)0x53, (byte)0xBD,
|
||||
(byte)0x9A, (byte)0xCE, (byte)0x32, (byte)0x62,
|
||||
(byte)0x54, (byte)0x7E, (byte)0xF8, (byte)0x35,
|
||||
(byte)0xC3, (byte)0xDA, (byte)0xC4, (byte)0xFD,
|
||||
(byte)0x97, (byte)0xF8, (byte)0x46, (byte)0x1A,
|
||||
(byte)0x14, (byte)0x61, (byte)0x1D, (byte)0xC9,
|
||||
(byte)0xC2, (byte)0x77, (byte)0x45, (byte)0x13,
|
||||
(byte)0x2D, (byte)0xED, (byte)0x8E, (byte)0x54,
|
||||
(byte)0x5C, (byte)0x1D, (byte)0x54, (byte)0xC7,
|
||||
(byte)0x2F, (byte)0x04, (byte)0x69, (byte)0x97
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP256r1_r = {
|
||||
(byte)0xA9, (byte)0xFB, (byte)0x57, (byte)0xDB,
|
||||
(byte)0xA1, (byte)0xEE, (byte)0xA9, (byte)0xBC,
|
||||
(byte)0x3E, (byte)0x66, (byte)0x0A, (byte)0x90,
|
||||
(byte)0x9D, (byte)0x83, (byte)0x8D, (byte)0x71,
|
||||
(byte)0x8C, (byte)0x39, (byte)0x7A, (byte)0xA3,
|
||||
(byte)0xB5, (byte)0x61, (byte)0xA6, (byte)0xF7,
|
||||
(byte)0x90, (byte)0x1E, (byte)0x0E, (byte)0x82,
|
||||
(byte)0x97, (byte)0x48, (byte)0x56, (byte)0xA7
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected static final byte[] brainpoolP384r1_oid =
|
||||
{ (byte)0x2B, (byte)0x24, (byte)0x03, (byte)0x03, (byte)0x02, (byte)0x08, (byte)0x01, (byte)0x01, (byte)0x0B };
|
||||
|
||||
protected static final byte[] brainpoolP384r1_field = {
|
||||
(byte)0x8C, (byte)0xB9, (byte)0x1E, (byte)0x82,
|
||||
(byte)0xA3, (byte)0x38, (byte)0x6D, (byte)0x28,
|
||||
(byte)0x0F, (byte)0x5D, (byte)0x6F, (byte)0x7E,
|
||||
(byte)0x50, (byte)0xE6, (byte)0x41, (byte)0xDF,
|
||||
(byte)0x15, (byte)0x2F, (byte)0x71, (byte)0x09,
|
||||
(byte)0xED, (byte)0x54, (byte)0x56, (byte)0xB4,
|
||||
(byte)0x12, (byte)0xB1, (byte)0xDA, (byte)0x19,
|
||||
(byte)0x7F, (byte)0xB7, (byte)0x11, (byte)0x23,
|
||||
(byte)0xAC, (byte)0xD3, (byte)0xA7, (byte)0x29,
|
||||
(byte)0x90, (byte)0x1D, (byte)0x1A, (byte)0x71,
|
||||
(byte)0x87, (byte)0x47, (byte)0x00, (byte)0x13,
|
||||
(byte)0x31, (byte)0x07, (byte)0xEC, (byte)0x53
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP384r1_a = {
|
||||
(byte)0x7B, (byte)0xC3, (byte)0x82, (byte)0xC6,
|
||||
(byte)0x3D, (byte)0x8C, (byte)0x15, (byte)0x0C,
|
||||
(byte)0x3C, (byte)0x72, (byte)0x08, (byte)0x0A,
|
||||
(byte)0xCE, (byte)0x05, (byte)0xAF, (byte)0xA0,
|
||||
(byte)0xC2, (byte)0xBE, (byte)0xA2, (byte)0x8E,
|
||||
(byte)0x4F, (byte)0xB2, (byte)0x27, (byte)0x87,
|
||||
(byte)0x13, (byte)0x91, (byte)0x65, (byte)0xEF,
|
||||
(byte)0xBA, (byte)0x91, (byte)0xF9, (byte)0x0F,
|
||||
(byte)0x8A, (byte)0xA5, (byte)0x81, (byte)0x4A,
|
||||
(byte)0x50, (byte)0x3A, (byte)0xD4, (byte)0xEB,
|
||||
(byte)0x04, (byte)0xA8, (byte)0xC7, (byte)0xDD,
|
||||
(byte)0x22, (byte)0xCE, (byte)0x28, (byte)0x26
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP384r1_b = {
|
||||
(byte)0x04, (byte)0xA8, (byte)0xC7, (byte)0xDD,
|
||||
(byte)0x22, (byte)0xCE, (byte)0x28, (byte)0x26,
|
||||
(byte)0x8B, (byte)0x39, (byte)0xB5, (byte)0x54,
|
||||
(byte)0x16, (byte)0xF0, (byte)0x44, (byte)0x7C,
|
||||
(byte)0x2F, (byte)0xB7, (byte)0x7D, (byte)0xE1,
|
||||
(byte)0x07, (byte)0xDC, (byte)0xD2, (byte)0xA6,
|
||||
(byte)0x2E, (byte)0x88, (byte)0x0E, (byte)0xA5,
|
||||
(byte)0x3E, (byte)0xEB, (byte)0x62, (byte)0xD5,
|
||||
(byte)0x7C, (byte)0xB4, (byte)0x39, (byte)0x02,
|
||||
(byte)0x95, (byte)0xDB, (byte)0xC9, (byte)0x94,
|
||||
(byte)0x3A, (byte)0xB7, (byte)0x86, (byte)0x96,
|
||||
(byte)0xFA, (byte)0x50, (byte)0x4C, (byte)0x11
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP384r1_g = {
|
||||
(byte)0x04,
|
||||
(byte)0x1D, (byte)0x1C, (byte)0x64, (byte)0xF0,
|
||||
(byte)0x68, (byte)0xCF, (byte)0x45, (byte)0xFF,
|
||||
(byte)0xA2, (byte)0xA6, (byte)0x3A, (byte)0x81,
|
||||
(byte)0xB7, (byte)0xC1, (byte)0x3F, (byte)0x6B,
|
||||
(byte)0x88, (byte)0x47, (byte)0xA3, (byte)0xE7,
|
||||
(byte)0x7E, (byte)0xF1, (byte)0x4F, (byte)0xE3,
|
||||
(byte)0xDB, (byte)0x7F, (byte)0xCA, (byte)0xFE,
|
||||
(byte)0x0C, (byte)0xBD, (byte)0x10, (byte)0xE8,
|
||||
(byte)0xE8, (byte)0x26, (byte)0xE0, (byte)0x34,
|
||||
(byte)0x36, (byte)0xD6, (byte)0x46, (byte)0xAA,
|
||||
(byte)0xEF, (byte)0x87, (byte)0xB2, (byte)0xE2,
|
||||
(byte)0x47, (byte)0xD4, (byte)0xAF, (byte)0x1E,
|
||||
(byte)0x8A, (byte)0xBE, (byte)0x1D, (byte)0x75,
|
||||
(byte)0x20, (byte)0xF9, (byte)0xC2, (byte)0xA4,
|
||||
(byte)0x5C, (byte)0xB1, (byte)0xEB, (byte)0x8E,
|
||||
(byte)0x95, (byte)0xCF, (byte)0xD5, (byte)0x52,
|
||||
(byte)0x62, (byte)0xB7, (byte)0x0B, (byte)0x29,
|
||||
(byte)0xFE, (byte)0xEC, (byte)0x58, (byte)0x64,
|
||||
(byte)0xE1, (byte)0x9C, (byte)0x05, (byte)0x4F,
|
||||
(byte)0xF9, (byte)0x91, (byte)0x29, (byte)0x28,
|
||||
(byte)0x0E, (byte)0x46, (byte)0x46, (byte)0x21,
|
||||
(byte)0x77, (byte)0x91, (byte)0x81, (byte)0x11,
|
||||
(byte)0x42, (byte)0x82, (byte)0x03, (byte)0x41,
|
||||
(byte)0x26, (byte)0x3C, (byte)0x53, (byte)0x15
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP384r1_r = {
|
||||
(byte)0x8C, (byte)0xB9, (byte)0x1E, (byte)0x82,
|
||||
(byte)0xA3, (byte)0x38, (byte)0x6D, (byte)0x28,
|
||||
(byte)0x0F, (byte)0x5D, (byte)0x6F, (byte)0x7E,
|
||||
(byte)0x50, (byte)0xE6, (byte)0x41, (byte)0xDF,
|
||||
(byte)0x15, (byte)0x2F, (byte)0x71, (byte)0x09,
|
||||
(byte)0xED, (byte)0x54, (byte)0x56, (byte)0xB3,
|
||||
(byte)0x1F, (byte)0x16, (byte)0x6E, (byte)0x6C,
|
||||
(byte)0xAC, (byte)0x04, (byte)0x25, (byte)0xA7,
|
||||
(byte)0xCF, (byte)0x3A, (byte)0xB6, (byte)0xAF,
|
||||
(byte)0x6B, (byte)0x7F, (byte)0xC3, (byte)0x10,
|
||||
(byte)0x3B, (byte)0x88, (byte)0x32, (byte)0x02,
|
||||
(byte)0xE9, (byte)0x04, (byte)0x65, (byte)0x65
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected static final byte[] brainpoolP512r1_oid =
|
||||
{ (byte)0x2B, (byte)0x24, (byte)0x03, (byte)0x03, (byte)0x02, (byte)0x08, (byte)0x01, (byte)0x01, (byte)0x0D };
|
||||
|
||||
protected static final byte[] brainpoolP512r1_field = {
|
||||
(byte)0xAA, (byte)0xDD, (byte)0x9D, (byte)0xB8,
|
||||
(byte)0xDB, (byte)0xE9, (byte)0xC4, (byte)0x8B,
|
||||
(byte)0x3F, (byte)0xD4, (byte)0xE6, (byte)0xAE,
|
||||
(byte)0x33, (byte)0xC9, (byte)0xFC, (byte)0x07,
|
||||
(byte)0xCB, (byte)0x30, (byte)0x8D, (byte)0xB3,
|
||||
(byte)0xB3, (byte)0xC9, (byte)0xD2, (byte)0x0E,
|
||||
(byte)0xD6, (byte)0x63, (byte)0x9C, (byte)0xCA,
|
||||
(byte)0x70, (byte)0x33, (byte)0x08, (byte)0x71,
|
||||
(byte)0x7D, (byte)0x4D, (byte)0x9B, (byte)0x00,
|
||||
(byte)0x9B, (byte)0xC6, (byte)0x68, (byte)0x42,
|
||||
(byte)0xAE, (byte)0xCD, (byte)0xA1, (byte)0x2A,
|
||||
(byte)0xE6, (byte)0xA3, (byte)0x80, (byte)0xE6,
|
||||
(byte)0x28, (byte)0x81, (byte)0xFF, (byte)0x2F,
|
||||
(byte)0x2D, (byte)0x82, (byte)0xC6, (byte)0x85,
|
||||
(byte)0x28, (byte)0xAA, (byte)0x60, (byte)0x56,
|
||||
(byte)0x58, (byte)0x3A, (byte)0x48, (byte)0xF3
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP512r1_a = {
|
||||
(byte)0x78, (byte)0x30, (byte)0xA3, (byte)0x31,
|
||||
(byte)0x8B, (byte)0x60, (byte)0x3B, (byte)0x89,
|
||||
(byte)0xE2, (byte)0x32, (byte)0x71, (byte)0x45,
|
||||
(byte)0xAC, (byte)0x23, (byte)0x4C, (byte)0xC5,
|
||||
(byte)0x94, (byte)0xCB, (byte)0xDD, (byte)0x8D,
|
||||
(byte)0x3D, (byte)0xF9, (byte)0x16, (byte)0x10,
|
||||
(byte)0xA8, (byte)0x34, (byte)0x41, (byte)0xCA,
|
||||
(byte)0xEA, (byte)0x98, (byte)0x63, (byte)0xBC,
|
||||
(byte)0x2D, (byte)0xED, (byte)0x5D, (byte)0x5A,
|
||||
(byte)0xA8, (byte)0x25, (byte)0x3A, (byte)0xA1,
|
||||
(byte)0x0A, (byte)0x2E, (byte)0xF1, (byte)0xC9,
|
||||
(byte)0x8B, (byte)0x9A, (byte)0xC8, (byte)0xB5,
|
||||
(byte)0x7F, (byte)0x11, (byte)0x17, (byte)0xA7,
|
||||
(byte)0x2B, (byte)0xF2, (byte)0xC7, (byte)0xB9,
|
||||
(byte)0xE7, (byte)0xC1, (byte)0xAC, (byte)0x4D,
|
||||
(byte)0x77, (byte)0xFC, (byte)0x94, (byte)0xCA
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP512r1_b = {
|
||||
(byte)0x3D, (byte)0xF9, (byte)0x16, (byte)0x10,
|
||||
(byte)0xA8, (byte)0x34, (byte)0x41, (byte)0xCA,
|
||||
(byte)0xEA, (byte)0x98, (byte)0x63, (byte)0xBC,
|
||||
(byte)0x2D, (byte)0xED, (byte)0x5D, (byte)0x5A,
|
||||
(byte)0xA8, (byte)0x25, (byte)0x3A, (byte)0xA1,
|
||||
(byte)0x0A, (byte)0x2E, (byte)0xF1, (byte)0xC9,
|
||||
(byte)0x8B, (byte)0x9A, (byte)0xC8, (byte)0xB5,
|
||||
(byte)0x7F, (byte)0x11, (byte)0x17, (byte)0xA7,
|
||||
(byte)0x2B, (byte)0xF2, (byte)0xC7, (byte)0xB9,
|
||||
(byte)0xE7, (byte)0xC1, (byte)0xAC, (byte)0x4D,
|
||||
(byte)0x77, (byte)0xFC, (byte)0x94, (byte)0xCA,
|
||||
(byte)0xDC, (byte)0x08, (byte)0x3E, (byte)0x67,
|
||||
(byte)0x98, (byte)0x40, (byte)0x50, (byte)0xB7,
|
||||
(byte)0x5E, (byte)0xBA, (byte)0xE5, (byte)0xDD,
|
||||
(byte)0x28, (byte)0x09, (byte)0xBD, (byte)0x63,
|
||||
(byte)0x80, (byte)0x16, (byte)0xF7, (byte)0x23
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP512r1_g = {
|
||||
(byte)0x04,
|
||||
(byte)0x81, (byte)0xAE, (byte)0xE4, (byte)0xBD,
|
||||
(byte)0xD8, (byte)0x2E, (byte)0xD9, (byte)0x64,
|
||||
(byte)0x5A, (byte)0x21, (byte)0x32, (byte)0x2E,
|
||||
(byte)0x9C, (byte)0x4C, (byte)0x6A, (byte)0x93,
|
||||
(byte)0x85, (byte)0xED, (byte)0x9F, (byte)0x70,
|
||||
(byte)0xB5, (byte)0xD9, (byte)0x16, (byte)0xC1,
|
||||
(byte)0xB4, (byte)0x3B, (byte)0x62, (byte)0xEE,
|
||||
(byte)0xF4, (byte)0xD0, (byte)0x09, (byte)0x8E,
|
||||
(byte)0xFF, (byte)0x3B, (byte)0x1F, (byte)0x78,
|
||||
(byte)0xE2, (byte)0xD0, (byte)0xD4, (byte)0x8D,
|
||||
(byte)0x50, (byte)0xD1, (byte)0x68, (byte)0x7B,
|
||||
(byte)0x93, (byte)0xB9, (byte)0x7D, (byte)0x5F,
|
||||
(byte)0x7C, (byte)0x6D, (byte)0x50, (byte)0x47,
|
||||
(byte)0x40, (byte)0x6A, (byte)0x5E, (byte)0x68,
|
||||
(byte)0x8B, (byte)0x35, (byte)0x22, (byte)0x09,
|
||||
(byte)0xBC, (byte)0xB9, (byte)0xF8, (byte)0x22,
|
||||
(byte)0x7D, (byte)0xDE, (byte)0x38, (byte)0x5D,
|
||||
(byte)0x56, (byte)0x63, (byte)0x32, (byte)0xEC,
|
||||
(byte)0xC0, (byte)0xEA, (byte)0xBF, (byte)0xA9,
|
||||
(byte)0xCF, (byte)0x78, (byte)0x22, (byte)0xFD,
|
||||
(byte)0xF2, (byte)0x09, (byte)0xF7, (byte)0x00,
|
||||
(byte)0x24, (byte)0xA5, (byte)0x7B, (byte)0x1A,
|
||||
(byte)0xA0, (byte)0x00, (byte)0xC5, (byte)0x5B,
|
||||
(byte)0x88, (byte)0x1F, (byte)0x81, (byte)0x11,
|
||||
(byte)0xB2, (byte)0xDC, (byte)0xDE, (byte)0x49,
|
||||
(byte)0x4A, (byte)0x5F, (byte)0x48, (byte)0x5E,
|
||||
(byte)0x5B, (byte)0xCA, (byte)0x4B, (byte)0xD8,
|
||||
(byte)0x8A, (byte)0x27, (byte)0x63, (byte)0xAE,
|
||||
(byte)0xD1, (byte)0xCA, (byte)0x2B, (byte)0x2F,
|
||||
(byte)0xA8, (byte)0xF0, (byte)0x54, (byte)0x06,
|
||||
(byte)0x78, (byte)0xCD, (byte)0x1E, (byte)0x0F,
|
||||
(byte)0x3A, (byte)0xD8, (byte)0x08, (byte)0x92
|
||||
};
|
||||
|
||||
protected static final byte[] brainpoolP512r1_r = {
|
||||
(byte)0xAA, (byte)0xDD, (byte)0x9D, (byte)0xB8,
|
||||
(byte)0xDB, (byte)0xE9, (byte)0xC4, (byte)0x8B,
|
||||
(byte)0x3F, (byte)0xD4, (byte)0xE6, (byte)0xAE,
|
||||
(byte)0x33, (byte)0xC9, (byte)0xFC, (byte)0x07,
|
||||
(byte)0xCB, (byte)0x30, (byte)0x8D, (byte)0xB3,
|
||||
(byte)0xB3, (byte)0xC9, (byte)0xD2, (byte)0x0E,
|
||||
(byte)0xD6, (byte)0x63, (byte)0x9C, (byte)0xCA,
|
||||
(byte)0x70, (byte)0x33, (byte)0x08, (byte)0x70,
|
||||
(byte)0x55, (byte)0x3E, (byte)0x5C, (byte)0x41,
|
||||
(byte)0x4C, (byte)0xA9, (byte)0x26, (byte)0x19,
|
||||
(byte)0x41, (byte)0x86, (byte)0x61, (byte)0x19,
|
||||
(byte)0x7F, (byte)0xAC, (byte)0x10, (byte)0x47,
|
||||
(byte)0x1D, (byte)0xB1, (byte)0xD3, (byte)0x81,
|
||||
(byte)0x08, (byte)0x5D, (byte)0xDA, (byte)0xDD,
|
||||
(byte)0xB5, (byte)0x87, (byte)0x96, (byte)0x82,
|
||||
(byte)0x9C, (byte)0xA9, (byte)0x00, (byte)0x69
|
||||
};
|
||||
|
||||
|
||||
}
|
116
src/fr/anssi/smartpgp/ECCurves.java
Normal file
116
src/fr/anssi/smartpgp/ECCurves.java
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class ECCurves {
|
||||
|
||||
protected final ECParams[] curves;
|
||||
|
||||
protected ECCurves() {
|
||||
final ECParams ansix9p256r1 =
|
||||
new ECParams((short)256,
|
||||
ECConstants.ansix9p256r1_oid,
|
||||
ECConstants.ansix9p256r1_field,
|
||||
ECConstants.ansix9p256r1_a,
|
||||
ECConstants.ansix9p256r1_b,
|
||||
ECConstants.ansix9p256r1_g,
|
||||
ECConstants.ansix9p256r1_r,
|
||||
(short)1);
|
||||
|
||||
final ECParams ansix9p384r1 =
|
||||
new ECParams((short)384,
|
||||
ECConstants.ansix9p384r1_oid,
|
||||
ECConstants.ansix9p384r1_field,
|
||||
ECConstants.ansix9p384r1_a,
|
||||
ECConstants.ansix9p384r1_b,
|
||||
ECConstants.ansix9p384r1_g,
|
||||
ECConstants.ansix9p384r1_r,
|
||||
(short)1);
|
||||
|
||||
final ECParams ansix9p521r1 =
|
||||
new ECParams((short)521,
|
||||
ECConstants.ansix9p521r1_oid,
|
||||
ECConstants.ansix9p521r1_field,
|
||||
ECConstants.ansix9p521r1_a,
|
||||
ECConstants.ansix9p521r1_b,
|
||||
ECConstants.ansix9p521r1_g,
|
||||
ECConstants.ansix9p521r1_r,
|
||||
(short)1);
|
||||
|
||||
final ECParams brainpoolP256r1 =
|
||||
new ECParams((short)256,
|
||||
ECConstants.brainpoolP256r1_oid,
|
||||
ECConstants.brainpoolP256r1_field,
|
||||
ECConstants.brainpoolP256r1_a,
|
||||
ECConstants.brainpoolP256r1_b,
|
||||
ECConstants.brainpoolP256r1_g,
|
||||
ECConstants.brainpoolP256r1_r,
|
||||
(short)1);
|
||||
|
||||
final ECParams brainpoolP384r1 =
|
||||
new ECParams((short)384,
|
||||
ECConstants.brainpoolP384r1_oid,
|
||||
ECConstants.brainpoolP384r1_field,
|
||||
ECConstants.brainpoolP384r1_a,
|
||||
ECConstants.brainpoolP384r1_b,
|
||||
ECConstants.brainpoolP384r1_g,
|
||||
ECConstants.brainpoolP384r1_r,
|
||||
(short)1);
|
||||
|
||||
final ECParams brainpoolP512r1 =
|
||||
new ECParams((short)512,
|
||||
ECConstants.brainpoolP512r1_oid,
|
||||
ECConstants.brainpoolP512r1_field,
|
||||
ECConstants.brainpoolP512r1_a,
|
||||
ECConstants.brainpoolP512r1_b,
|
||||
ECConstants.brainpoolP512r1_g,
|
||||
ECConstants.brainpoolP512r1_r,
|
||||
(short)1);
|
||||
|
||||
curves = new ECParams[]{
|
||||
ansix9p256r1,
|
||||
ansix9p384r1,
|
||||
ansix9p521r1,
|
||||
brainpoolP256r1,
|
||||
brainpoolP384r1,
|
||||
brainpoolP512r1
|
||||
};
|
||||
}
|
||||
|
||||
protected final ECParams findByOid(final byte[] buf,
|
||||
final short off,
|
||||
final byte len) {
|
||||
byte i = 0;
|
||||
while(i < curves.length) {
|
||||
if(curves[i].matchOid(buf, off, len)) {
|
||||
return curves[i];
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
67
src/fr/anssi/smartpgp/ECParams.java
Normal file
67
src/fr/anssi/smartpgp/ECParams.java
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
|
||||
public final class ECParams {
|
||||
|
||||
protected final short nb_bits;
|
||||
protected final byte[] oid;
|
||||
protected final byte[] field, a, b, g, r;
|
||||
protected final short k;
|
||||
|
||||
protected ECParams(final short nb_bits,
|
||||
final byte[] oid,
|
||||
final byte[] field, /* p */
|
||||
final byte[] a,
|
||||
final byte[] b,
|
||||
final byte[] g,
|
||||
final byte[] r, /* n */
|
||||
final short k) /* h */ {
|
||||
this.nb_bits = nb_bits;
|
||||
this.oid = oid;
|
||||
this.field = field;
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.g = g;
|
||||
this.r = r;
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
|
||||
protected final boolean matchOid(final byte[] buf, final short off, final short len) {
|
||||
return (len == (short)oid.length) && (Util.arrayCompare(buf, off, oid, (short)0, len) == 0);
|
||||
}
|
||||
|
||||
protected final void setParams(final ECKey key) {
|
||||
key.setFieldFP(field, (short)0, (short)field.length);
|
||||
key.setA(a, (short)0, (short)a.length);
|
||||
key.setB(b, (short)0, (short)b.length);
|
||||
key.setG(g, (short)0, (short)g.length);
|
||||
key.setR(r, (short)0, (short)r.length);
|
||||
key.setK(k);
|
||||
}
|
||||
}
|
50
src/fr/anssi/smartpgp/Fingerprint.java
Normal file
50
src/fr/anssi/smartpgp/Fingerprint.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class Fingerprint {
|
||||
protected final byte[] data;
|
||||
|
||||
protected Fingerprint() {
|
||||
data = new byte[Constants.FINGERPRINT_SIZE];
|
||||
}
|
||||
|
||||
protected final void reset() {
|
||||
Util.arrayFillNonAtomic(data, (short)0, Constants.FINGERPRINT_SIZE, (byte)0);
|
||||
}
|
||||
|
||||
protected final void set(final byte[] buf, final short off, final short len) {
|
||||
if(len != Constants.FINGERPRINT_SIZE) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
Util.arrayCopy(buf, off, data, (short)0, len);
|
||||
}
|
||||
|
||||
protected final short write(final byte[] buf, final short off) {
|
||||
return Util.arrayCopyNonAtomic(data, (short)0, buf, off, Constants.FINGERPRINT_SIZE);
|
||||
}
|
||||
}
|
848
src/fr/anssi/smartpgp/PGPKey.java
Normal file
848
src/fr/anssi/smartpgp/PGPKey.java
Normal file
@ -0,0 +1,848 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class PGPKey {
|
||||
|
||||
protected final Fingerprint fingerprint;
|
||||
|
||||
protected final byte[] generation_date;
|
||||
|
||||
protected final byte[] certificate;
|
||||
protected short certificate_length;
|
||||
|
||||
protected final byte[] attributes;
|
||||
protected byte attributes_length;
|
||||
|
||||
protected final boolean is_secure_messaging_key;
|
||||
|
||||
private KeyPair keys;
|
||||
|
||||
|
||||
protected PGPKey(final boolean for_secure_messaging) {
|
||||
|
||||
is_secure_messaging_key = for_secure_messaging;
|
||||
|
||||
if(is_secure_messaging_key) {
|
||||
fingerprint = null;
|
||||
generation_date = null;
|
||||
} else {
|
||||
fingerprint = new Fingerprint();
|
||||
generation_date = new byte[Constants.GENERATION_DATE_SIZE];
|
||||
}
|
||||
|
||||
certificate = new byte[Constants.cardholderCertificateMaxLength()];
|
||||
certificate_length = 0;
|
||||
|
||||
attributes = new byte[Constants.ALGORITHM_ATTRIBUTES_MAX_LENGTH];
|
||||
attributes_length = 0;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
private final void resetKeys() {
|
||||
if(keys != null) {
|
||||
keys.getPrivate().clearKey();
|
||||
keys.getPublic().clearKey();
|
||||
keys = null;
|
||||
}
|
||||
|
||||
if(certificate_length > 0) {
|
||||
certificate_length = (short)0;
|
||||
Util.arrayFillNonAtomic(certificate, (short)0, certificate_length, (byte)0);
|
||||
}
|
||||
|
||||
if(!is_secure_messaging_key) {
|
||||
fingerprint.reset();
|
||||
|
||||
Util.arrayFillNonAtomic(generation_date, (short)0, Constants.GENERATION_DATE_SIZE, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void reset() {
|
||||
resetKeys();
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(attributes_length > 0) {
|
||||
Util.arrayFillNonAtomic(attributes, (short)0, attributes_length, (byte)0);
|
||||
attributes_length = (byte)0;
|
||||
}
|
||||
|
||||
if(is_secure_messaging_key) {
|
||||
Util.arrayCopyNonAtomic(Constants.ALGORITHM_ATTRIBUTES_DEFAULT_SECURE_MESSAGING, (short)0,
|
||||
attributes, (short)0,
|
||||
(short)Constants.ALGORITHM_ATTRIBUTES_DEFAULT_SECURE_MESSAGING.length);
|
||||
attributes_length = (byte)Constants.ALGORITHM_ATTRIBUTES_DEFAULT_SECURE_MESSAGING.length;
|
||||
} else {
|
||||
Util.arrayCopyNonAtomic(Constants.ALGORITHM_ATTRIBUTES_DEFAULT, (short)0,
|
||||
attributes, (short)0,
|
||||
(short)Constants.ALGORITHM_ATTRIBUTES_DEFAULT.length);
|
||||
attributes_length = (byte)Constants.ALGORITHM_ATTRIBUTES_DEFAULT.length;
|
||||
}
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
protected final boolean isInitialized() {
|
||||
return (keys != null) && keys.getPrivate().isInitialized() && keys.getPublic().isInitialized();
|
||||
}
|
||||
|
||||
protected final void setCertificate(final byte[] buf, final short off, final short len) {
|
||||
if((len < 0) ||
|
||||
(len > Constants.cardholderCertificateMaxLength())) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(certificate_length > 0) {
|
||||
Util.arrayFillNonAtomic(certificate, (short)0, certificate_length, (byte)0);
|
||||
}
|
||||
Util.arrayCopyNonAtomic(buf, off, certificate, (short)0, len);
|
||||
certificate_length = len;
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
protected final void setGenerationDate(final byte[] buf, final short off, final short len) {
|
||||
if(len != Constants.GENERATION_DATE_SIZE) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
Util.arrayCopy(buf, off, generation_date, (short)0, len);
|
||||
}
|
||||
|
||||
protected final void setAttributes(final ECCurves ec,
|
||||
final byte[] buf, final short off, final short len) {
|
||||
if((len < Constants.ALGORITHM_ATTRIBUTES_MIN_LENGTH) ||
|
||||
(len > Constants.ALGORITHM_ATTRIBUTES_MAX_LENGTH)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(buf[0]) {
|
||||
case 0x01:
|
||||
if((len != 6) || is_secure_messaging_key) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
if((Util.getShort(buf, (short)1) < 2048) ||
|
||||
(Util.getShort(buf, (short)3) != 0x11) ||
|
||||
(buf[5] < 0) || (buf[5] > 3)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
if(len < 2) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
final byte delta = (buf[(short)(len - 1)] == (byte)0xff) ? (byte)1 : (byte)0;
|
||||
final ECParams params = ec.findByOid(buf, (short)(off + 1), (byte)(len - 1 - delta));
|
||||
if(params == null) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
if(is_secure_messaging_key) {
|
||||
if((buf[0] != 0x12) ||
|
||||
(((short)(Constants.aesKeyLength()* 8) > (short)128) &&
|
||||
(params.nb_bits < 512))) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
resetKeys();
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(attributes_length > 0) {
|
||||
Util.arrayFillNonAtomic(attributes, (short)0, attributes_length, (byte)0);
|
||||
}
|
||||
Util.arrayCopyNonAtomic(buf, off, attributes, (short)0, len);
|
||||
attributes_length = (byte)len;
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
|
||||
protected final boolean isRsa() {
|
||||
return (attributes[0] == 1);
|
||||
}
|
||||
|
||||
protected final short rsaModulusBitSize() {
|
||||
return Util.getShort(attributes, (short)1);
|
||||
}
|
||||
|
||||
protected final short rsaExponentBitSize() {
|
||||
return Util.getShort(attributes, (short)3);
|
||||
}
|
||||
|
||||
protected final boolean isEc() {
|
||||
return ((attributes[0] == (byte)0x12) ||
|
||||
(attributes[0] == (byte)0x13));
|
||||
}
|
||||
|
||||
protected final ECParams ecParams(final ECCurves ec) {
|
||||
final byte delta = (attributes[(short)(attributes_length - 1)] == (byte)0xff) ? (byte)1 : (byte)0;
|
||||
return ec.findByOid(attributes, (short)1, (byte)(attributes_length - 1 - delta));
|
||||
}
|
||||
|
||||
|
||||
private final KeyPair generateRSA() {
|
||||
final PrivateKey priv = (PrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, rsaModulusBitSize(), false);
|
||||
final RSAPublicKey pub = (RSAPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, rsaModulusBitSize(), false);
|
||||
|
||||
if((priv == null) || (pub == null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub.setExponent(Constants.RSA_EXPONENT, (short)0, (byte)Constants.RSA_EXPONENT.length);
|
||||
|
||||
return new KeyPair(pub, priv);
|
||||
}
|
||||
|
||||
|
||||
private final KeyPair generateEC(final ECCurves ec) {
|
||||
|
||||
final ECParams params = ecParams(ec);
|
||||
|
||||
final ECPrivateKey priv = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, params.nb_bits, false);
|
||||
final ECPublicKey pub = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, params.nb_bits, false);
|
||||
|
||||
if((priv == null) || (pub == null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
params.setParams(priv);
|
||||
params.setParams(pub);
|
||||
|
||||
return new KeyPair(pub, priv);
|
||||
}
|
||||
|
||||
|
||||
protected final void generate(final ECCurves ec) {
|
||||
|
||||
KeyPair nkeys = null;
|
||||
|
||||
if(isRsa()) {
|
||||
nkeys = generateRSA();
|
||||
} else if(isEc()) {
|
||||
nkeys = generateEC(ec);
|
||||
}
|
||||
|
||||
if(nkeys == null) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
nkeys.genKeyPair();
|
||||
|
||||
if(!nkeys.getPublic().isInitialized() || !nkeys.getPrivate().isInitialized()) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
resetKeys();
|
||||
|
||||
keys = nkeys;
|
||||
}
|
||||
|
||||
|
||||
private final KeyPair importRSAKey(final byte[] buf,
|
||||
final short boff, final short len,
|
||||
final byte tag_count, final byte[] tag_val, final short[] tag_len) {
|
||||
|
||||
final short attr_modulus_bit_size = rsaModulusBitSize();
|
||||
final short attr_modulus_byte_size = Common.bitsToBytes(attr_modulus_bit_size);
|
||||
|
||||
final RSAPrivateCrtKey priv = (RSAPrivateCrtKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, attr_modulus_bit_size, false);
|
||||
final RSAPublicKey pub = (RSAPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, attr_modulus_bit_size, false);
|
||||
|
||||
if((priv == null) || (pub == null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
short off = boff;
|
||||
byte i = 0;
|
||||
while(i < tag_count) {
|
||||
|
||||
if((short)((short)(off - boff) + tag_len[i]) > len) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return null;
|
||||
}
|
||||
|
||||
switch(tag_val[i]) {
|
||||
case (byte)0x91:
|
||||
if(tag_len[i] != Common.bitsToBytes(rsaExponentBitSize())) {
|
||||
return null;
|
||||
}
|
||||
pub.setExponent(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
case (byte)0x92:
|
||||
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||||
return null;
|
||||
}
|
||||
priv.setP(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
case (byte)0x93:
|
||||
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||||
return null;
|
||||
}
|
||||
priv.setQ(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
case (byte)0x94:
|
||||
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||||
return null;
|
||||
}
|
||||
priv.setPQ(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
case (byte)0x95:
|
||||
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||||
return null;
|
||||
}
|
||||
priv.setDP1(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
case (byte)0x96:
|
||||
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||||
return null;
|
||||
}
|
||||
priv.setDQ1(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
case (byte)0x97:
|
||||
if(tag_len[i] != attr_modulus_byte_size) {
|
||||
return null;
|
||||
}
|
||||
pub.setModulus(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
off += tag_len[i];
|
||||
++i;
|
||||
}
|
||||
|
||||
if(!priv.isInitialized() || !pub.isInitialized()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new KeyPair(pub, priv);
|
||||
}
|
||||
|
||||
private final KeyPair importECKey(final ECCurves ec,
|
||||
final byte[] buf,
|
||||
final short boff, final short len,
|
||||
final byte tag_count, final byte[] tag_val, final short[] tag_len) {
|
||||
final ECParams params = ecParams(ec);
|
||||
|
||||
final ECPrivateKey priv = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE,
|
||||
params.nb_bits,
|
||||
false);
|
||||
final ECPublicKey pub = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC,
|
||||
params.nb_bits,
|
||||
false);
|
||||
|
||||
if((priv == null) || (pub == null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
params.setParams(priv);
|
||||
params.setParams(pub);
|
||||
|
||||
short off = boff;
|
||||
byte i = 0;
|
||||
while(i < tag_count) {
|
||||
|
||||
if((short)((short)(off - boff) + tag_len[i]) > len) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return null;
|
||||
}
|
||||
|
||||
switch(tag_val[i]) {
|
||||
case (byte)0x92:
|
||||
if(tag_len[i] > Common.bitsToBytes(params.nb_bits)) {
|
||||
return null;
|
||||
}
|
||||
priv.setS(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
case (byte)0x99:
|
||||
if(tag_len[i] > (short)(2 * Common.bitsToBytes(params.nb_bits) + 1)) {
|
||||
return null;
|
||||
}
|
||||
if(((byte)(tag_len[i] - 1) & (byte)0x1) != 0) {
|
||||
return null;
|
||||
}
|
||||
pub.setW(buf, off, tag_len[i]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
off += tag_len[i];
|
||||
++i;
|
||||
}
|
||||
|
||||
if(!priv.isInitialized() || !pub.isInitialized()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new KeyPair(pub, priv);
|
||||
}
|
||||
|
||||
protected final void importKey(final ECCurves ec,
|
||||
final byte[] buf, final short boff, final short len) {
|
||||
|
||||
short off = boff;
|
||||
|
||||
short template_len = 0;
|
||||
short template_off = 0;
|
||||
|
||||
short data_len = 0;
|
||||
short data_off = 0;
|
||||
|
||||
byte data_tag_count = 0;
|
||||
byte[] data_tag_val = new byte[7];
|
||||
short[] data_tag_len = new short[7];
|
||||
|
||||
while((short)(len - (short)(off - boff)) > 2) {
|
||||
switch(Util.getShort(buf, off)) {
|
||||
|
||||
case (short)0x7f48:
|
||||
off += 2;
|
||||
template_len = Common.readLength(buf, off, (short)(len - (short)(off - boff)));
|
||||
off = Common.skipLength(buf, off, (short)(len - (short)(off - boff)));
|
||||
template_off = off;
|
||||
|
||||
if(template_len > (short)(len - ((short)off - boff))) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
while((short)(template_len - (short)(off - template_off)) > 1) {
|
||||
if((buf[off] < (byte)0x91) ||
|
||||
(buf[off] > (byte)0x99)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
if(data_tag_count >= data_tag_val.length) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
data_tag_val[data_tag_count] = buf[off];
|
||||
++off;
|
||||
|
||||
data_tag_len[data_tag_count] = Common.readLength(buf, off, (short)(template_len - (short)(off - template_off)));
|
||||
off = Common.skipLength(buf, off, (short)(template_len - (short)(off - template_off)));
|
||||
|
||||
++data_tag_count;
|
||||
}
|
||||
break;
|
||||
|
||||
case (short)0x5f48:
|
||||
off += 2;
|
||||
data_len = Common.readLength(buf, off, (short)(len - (short)(off - boff)));
|
||||
off = Common.skipLength(buf, off, (short)(len - (short)(off - boff)));
|
||||
data_off = off;
|
||||
|
||||
if(data_len > (short)(len - ((short)off - boff))) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
off += data_len;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
KeyPair nkeys = null;
|
||||
|
||||
if(isRsa()) {
|
||||
nkeys = importRSAKey(buf, data_off, data_len, data_tag_count, data_tag_val, data_tag_len);
|
||||
} else if(isEc()) {
|
||||
nkeys = importECKey(ec, buf, data_off, data_len, data_tag_count, data_tag_val, data_tag_len);
|
||||
}
|
||||
|
||||
if(nkeys == null) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!nkeys.getPrivate().isInitialized() || !nkeys.getPublic().isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetKeys();
|
||||
keys = nkeys;
|
||||
}
|
||||
|
||||
|
||||
protected final short writePublicKeyDo(final byte[] buf, short off) {
|
||||
|
||||
if(!isInitialized()) {
|
||||
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final PublicKey pub = keys.getPublic();
|
||||
|
||||
off = Util.setShort(buf, off, (short)0x7f49);
|
||||
|
||||
if(isRsa()) {
|
||||
|
||||
final RSAPublicKey rsapub = (RSAPublicKey)pub;
|
||||
final short modulus_size = Common.bitsToBytes(rsaModulusBitSize());
|
||||
final short exponent_size = Common.bitsToBytes(rsaExponentBitSize());
|
||||
|
||||
final short mlensize = (short)((modulus_size > (short)0xff) ? 3 : 2);
|
||||
|
||||
final short flen =
|
||||
(short)(1 + mlensize + modulus_size +
|
||||
1 + 1 + exponent_size);
|
||||
|
||||
off = Common.writeLength(buf, off, flen);
|
||||
|
||||
buf[off++] = (byte)0x81;
|
||||
off = Common.writeLength(buf, off, modulus_size);
|
||||
off += rsapub.getModulus(buf, off);
|
||||
|
||||
buf[off++] = (byte)0x82;
|
||||
off = Common.writeLength(buf, off, exponent_size);
|
||||
off += rsapub.getExponent(buf, off);
|
||||
|
||||
return off;
|
||||
|
||||
} else if(isEc()) {
|
||||
|
||||
final ECPublicKey ecpub = (ECPublicKey)pub;
|
||||
final short qsize = (short)(1 + 2 * (short)((ecpub.getSize() / 8) + (((ecpub.getSize() % 8) == 0) ? 0 : 1)));
|
||||
short rsize = (short)(1 + qsize);
|
||||
|
||||
if(qsize > 0x7f) {
|
||||
rsize = (short)(rsize + 2);
|
||||
} else {
|
||||
rsize = (short)(rsize + 1);
|
||||
}
|
||||
|
||||
off = Common.writeLength(buf, off, rsize);
|
||||
|
||||
buf[off++] = (byte)0x86;
|
||||
|
||||
off = Common.writeLength(buf, off, qsize);
|
||||
|
||||
off += ecpub.getW(buf, off);
|
||||
|
||||
return off;
|
||||
|
||||
}
|
||||
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return off;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected final short sign(final byte[] buf, final short lc,
|
||||
final boolean forAuth) {
|
||||
|
||||
if(!isInitialized()) {
|
||||
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final PrivateKey priv = keys.getPrivate();
|
||||
|
||||
short off = 0;
|
||||
|
||||
byte[] sha_header = null;
|
||||
|
||||
if(isRsa()) {
|
||||
|
||||
if(!forAuth) {
|
||||
if(lc == (short)(2 + Constants.DSI_SHA224_HEADER[1])) {
|
||||
sha_header = Constants.DSI_SHA224_HEADER;
|
||||
} else if(lc == (short)(2 + Constants.DSI_SHA256_HEADER[1])) {
|
||||
sha_header = Constants.DSI_SHA256_HEADER;
|
||||
} else if(lc == (short)(2 + Constants.DSI_SHA384_HEADER[1])) {
|
||||
sha_header = Constants.DSI_SHA384_HEADER;
|
||||
} else if(lc == (short)(2 + Constants.DSI_SHA512_HEADER[1])) {
|
||||
sha_header = Constants.DSI_SHA512_HEADER;
|
||||
} else {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Util.arrayCompare(buf, (short)0, sha_header, (short)0, (byte)sha_header.length) != 0) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(lc > (short)(((short)(Common.bitsToBytes(rsaModulusBitSize()) * 2)) / 5)) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final Cipher cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
|
||||
cipher.init(priv, Cipher.MODE_ENCRYPT);
|
||||
|
||||
off = cipher.doFinal(buf, (short)0, lc,
|
||||
buf, (short)lc);
|
||||
|
||||
return Util.arrayCopyNonAtomic(buf, (short)lc,
|
||||
buf, (short)0,
|
||||
off);
|
||||
|
||||
} else if(isEc()) {
|
||||
|
||||
byte alg;
|
||||
|
||||
if(lc == MessageDigest.LENGTH_SHA) {
|
||||
alg = Signature.ALG_ECDSA_SHA;
|
||||
} else if(lc == MessageDigest.LENGTH_SHA_224) {
|
||||
alg = Signature.ALG_ECDSA_SHA_224;
|
||||
} else if(lc == MessageDigest.LENGTH_SHA_256) {
|
||||
alg = Signature.ALG_ECDSA_SHA_256;
|
||||
} else if(lc == MessageDigest.LENGTH_SHA_384) {
|
||||
alg = Signature.ALG_ECDSA_SHA_384;
|
||||
} else if(lc == MessageDigest.LENGTH_SHA_512) {
|
||||
alg = Signature.ALG_ECDSA_SHA_512;
|
||||
} else {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final Signature sig = Signature.getInstance(alg, false);
|
||||
sig.init(priv, Signature.MODE_SIGN);
|
||||
|
||||
final short sig_size = sig.signPreComputedHash(buf, (short)0, lc,
|
||||
buf, lc);
|
||||
|
||||
off = (short)(lc + 1);
|
||||
if((buf[off] & (byte)0x80) != (byte)0) {
|
||||
++off;
|
||||
}
|
||||
++off;
|
||||
|
||||
if((buf[off++] != (byte)0x02)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if((buf[off] & (byte)0x80) != (byte)0) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final short r_size = Util.makeShort((byte)0, buf[off++]);
|
||||
final short r_off = off;
|
||||
|
||||
off += r_size;
|
||||
|
||||
if((buf[off++] != (byte)0x02)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if((buf[off] & (byte)0x80) != (byte)0) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final short s_size = Util.makeShort((byte)0, buf[off++]);
|
||||
final short s_off = off;
|
||||
|
||||
off = (short)(lc + sig_size);
|
||||
|
||||
if(r_size < s_size) {
|
||||
off = Util.arrayFillNonAtomic(buf, off, (short)(s_size - r_size), (byte)0);
|
||||
}
|
||||
|
||||
off = Util.arrayCopyNonAtomic(buf, r_off,
|
||||
buf, off, r_size);
|
||||
|
||||
if(s_size < r_size) {
|
||||
off = Util.arrayFillNonAtomic(buf, off, (short)(r_size - s_size), (byte)0);
|
||||
}
|
||||
|
||||
off = Util.arrayCopyNonAtomic(buf, s_off,
|
||||
buf, off, s_size);
|
||||
|
||||
off = Util.arrayCopyNonAtomic(buf, (short)(lc + sig_size),
|
||||
buf, (short)0,
|
||||
(short)(off - lc - sig_size));
|
||||
|
||||
Util.arrayFillNonAtomic(buf, off, (short)(lc + sig_size - off), (byte)0);
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
protected final short decipher(final ECCurves ec, final byte[] buf, final short lc) {
|
||||
|
||||
if(!isInitialized()) {
|
||||
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final PrivateKey priv = keys.getPrivate();
|
||||
|
||||
short off = 0;
|
||||
|
||||
if(isRsa()) {
|
||||
final short modulus_size = Common.bitsToBytes(rsaModulusBitSize());
|
||||
|
||||
if(lc != (short)(modulus_size + 1)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(buf[0] != (byte)0) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final Cipher cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
|
||||
cipher.init(priv, Cipher.MODE_DECRYPT);
|
||||
|
||||
final short len = cipher.doFinal(buf, (short)1, (short)(lc - 1),
|
||||
buf, (short)lc);
|
||||
|
||||
off = Util.arrayCopyNonAtomic(buf, lc,
|
||||
buf, (short)0,
|
||||
len);
|
||||
|
||||
Util.arrayFillNonAtomic(buf, lc, len, (byte)0);
|
||||
|
||||
return off;
|
||||
|
||||
} else if(isEc()) {
|
||||
|
||||
final ECParams params = ecParams(ec);
|
||||
|
||||
if((lc <= 7) || (lc > (short)(7 + 1 + (short)(2 * Common.bitsToBytes(params.nb_bits))))) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(buf[off] != (byte)0xA6) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
++off;
|
||||
|
||||
if(buf[off] != (byte)(lc - off - 1)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
++off;
|
||||
|
||||
if(Util.getShort(buf, off) != (short)(0x7f49)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
off += 2;
|
||||
|
||||
if(buf[off] != (byte)(lc - off - 1)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
++off;
|
||||
|
||||
if(buf[off] != (byte)0x86) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
++off;
|
||||
|
||||
if(buf[off] != (byte)(lc - off - 1)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
++off;
|
||||
|
||||
final KeyAgreement ka = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DHC_PLAIN, false);
|
||||
ka.init(priv);
|
||||
|
||||
final short len = ka.generateSecret(buf, off, (short)(lc - off),
|
||||
buf, lc);
|
||||
|
||||
off = Util.arrayCopyNonAtomic(buf, lc,
|
||||
buf, (short)0,
|
||||
len);
|
||||
|
||||
Util.arrayFillNonAtomic(buf, lc, len, (byte)0);
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected final void initSignature(final Signature sign) {
|
||||
if(!isInitialized()) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
sign.init(keys.getPrivate(), Signature.MODE_SIGN);
|
||||
}
|
||||
|
||||
protected final void initKeyAgreement(final KeyAgreement ka) {
|
||||
if(!isInitialized()) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
ka.init(keys.getPrivate());
|
||||
}
|
||||
|
||||
}
|
233
src/fr/anssi/smartpgp/Persistent.java
Normal file
233
src/fr/anssi/smartpgp/Persistent.java
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class Persistent {
|
||||
|
||||
protected boolean isTerminated;
|
||||
|
||||
protected final byte[] login;
|
||||
protected short login_length;
|
||||
|
||||
protected final byte[] url;
|
||||
protected short url_length;
|
||||
|
||||
protected final PGPKey[] pgp_keys;
|
||||
protected static final byte PGP_KEYS_OFFSET_SIG = 0;
|
||||
protected static final byte PGP_KEYS_OFFSET_DEC = PGP_KEYS_OFFSET_SIG + 1;
|
||||
protected static final byte PGP_KEYS_OFFSET_AUT = PGP_KEYS_OFFSET_DEC + 1;
|
||||
private static final byte PGP_KEYS_LENGTH = PGP_KEYS_OFFSET_AUT + 1;
|
||||
|
||||
protected final Fingerprint[] fingerprints;
|
||||
protected static final byte FINGERPRINTS_OFFSET_CA = 0;
|
||||
protected static final byte FINGERPRINTS_OFFSET_CB = FINGERPRINTS_OFFSET_CA + 1;
|
||||
protected static final byte FINGERPRINTS_OFFSET_CC = FINGERPRINTS_OFFSET_CB + 1;
|
||||
private static final byte FINGERPRINTS_LENGTH = FINGERPRINTS_OFFSET_CC + 1;
|
||||
|
||||
protected final byte[] name;
|
||||
protected byte name_length;
|
||||
|
||||
protected final byte[] lang;
|
||||
protected byte lang_length;
|
||||
|
||||
protected byte sex;
|
||||
|
||||
|
||||
protected byte[] digital_signature_counter;
|
||||
|
||||
|
||||
protected byte[] do_0101;
|
||||
protected short do_0101_length;
|
||||
|
||||
protected byte[] do_0102;
|
||||
protected short do_0102_length;
|
||||
|
||||
protected byte[] do_0103;
|
||||
protected short do_0103_length;
|
||||
|
||||
protected byte[] do_0104;
|
||||
protected short do_0104_length;
|
||||
|
||||
protected AESKey aes_key;
|
||||
|
||||
|
||||
|
||||
protected final OwnerPIN user_pin; /* PW1 */
|
||||
protected byte user_pin_length;
|
||||
protected boolean user_pin_force_verify_signature;
|
||||
|
||||
protected final OwnerPIN user_puk; /* resetting code */
|
||||
protected byte user_puk_length;
|
||||
|
||||
protected final OwnerPIN admin_pin; /* PW3 */
|
||||
protected byte admin_pin_length;
|
||||
|
||||
|
||||
|
||||
protected Persistent() {
|
||||
login = new byte[Constants.specialDoMaxLength()];
|
||||
login_length = 0;
|
||||
|
||||
url = new byte[Constants.specialDoMaxLength()];
|
||||
url_length = 0;
|
||||
|
||||
fingerprints = new Fingerprint[FINGERPRINTS_LENGTH];
|
||||
for(byte i = 0; i < fingerprints.length; ++i) {
|
||||
fingerprints[i] = new Fingerprint();
|
||||
}
|
||||
|
||||
name = new byte[Constants.NAME_MAX_LENGTH];
|
||||
name_length = 0;
|
||||
|
||||
lang = new byte[Constants.LANG_MAX_LENGTH];
|
||||
lang_length = 0;
|
||||
|
||||
digital_signature_counter = new byte[3];
|
||||
|
||||
do_0101 = new byte[Constants.specialDoMaxLength()];
|
||||
do_0101_length = 0;
|
||||
|
||||
do_0102 = new byte[Constants.specialDoMaxLength()];
|
||||
do_0101_length = 0;
|
||||
|
||||
do_0103 = new byte[Constants.specialDoMaxLength()];
|
||||
do_0103_length = 0;
|
||||
|
||||
do_0104 = new byte[Constants.specialDoMaxLength()];
|
||||
do_0104_length = 0;
|
||||
|
||||
aes_key = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES,
|
||||
(short)(Constants.aesKeyLength() * 8),
|
||||
false);
|
||||
|
||||
pgp_keys = new PGPKey[PGP_KEYS_LENGTH];
|
||||
for(byte i = 0; i < pgp_keys.length; ++i) {
|
||||
pgp_keys[i] = new PGPKey(false);
|
||||
}
|
||||
|
||||
user_pin = new OwnerPIN(Constants.USER_PIN_RETRY_COUNT, Constants.USER_PIN_MAX_SIZE);
|
||||
user_puk = new OwnerPIN(Constants.USER_PUK_RETRY_COUNT, Constants.USER_PUK_MAX_SIZE);
|
||||
admin_pin = new OwnerPIN(Constants.ADMIN_PIN_RETRY_COUNT, Constants.ADMIN_PIN_MAX_SIZE);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
protected void reset() {
|
||||
for(byte i = 0; i < pgp_keys.length; ++i) {
|
||||
pgp_keys[i].reset();
|
||||
}
|
||||
|
||||
if(login_length > 0) {
|
||||
JCSystem.beginTransaction();
|
||||
Util.arrayFillNonAtomic(login, (short)0, login_length, (byte)0);
|
||||
login_length = (short)0;
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
if(url_length > 0) {
|
||||
JCSystem.beginTransaction();
|
||||
Util.arrayFillNonAtomic(url, (short)0, url_length, (byte)0);
|
||||
url_length = (short)0;
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
for(byte i = 0; i < fingerprints.length; ++i) {
|
||||
fingerprints[i].reset();
|
||||
}
|
||||
|
||||
if(name_length > 0) {
|
||||
JCSystem.beginTransaction();
|
||||
Util.arrayFillNonAtomic(name, (short)0, name_length, (byte)0);
|
||||
name_length = (byte)0;
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(lang_length > 0) {
|
||||
Util.arrayFillNonAtomic(lang, (short)0, lang_length, (byte)0);
|
||||
}
|
||||
Util.arrayCopyNonAtomic(Constants.LANG_DEFAULT, (short)0, lang, (short)0, (short)Constants.LANG_DEFAULT.length);
|
||||
lang_length = (byte)Constants.LANG_DEFAULT.length;
|
||||
JCSystem.commitTransaction();
|
||||
|
||||
sex = Constants.SEX_NOT_ANNOUNCED;
|
||||
|
||||
Util.arrayFillNonAtomic(digital_signature_counter, (short)0,
|
||||
(short)digital_signature_counter.length, (byte)0);
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(do_0101_length > 0) {
|
||||
Util.arrayFillNonAtomic(do_0101, (short)0,
|
||||
(short)do_0101.length, (byte)0);
|
||||
do_0101_length = 0;
|
||||
}
|
||||
JCSystem.commitTransaction();
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(do_0102_length > 0) {
|
||||
Util.arrayFillNonAtomic(do_0102, (short)0,
|
||||
(short)do_0102.length, (byte)0);
|
||||
do_0102_length = 0;
|
||||
}
|
||||
JCSystem.commitTransaction();
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(do_0103_length > 0) {
|
||||
Util.arrayFillNonAtomic(do_0103, (short)0,
|
||||
(short)do_0103.length, (byte)0);
|
||||
do_0103_length = 0;
|
||||
}
|
||||
JCSystem.commitTransaction();
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
if(do_0104_length > 0) {
|
||||
Util.arrayFillNonAtomic(do_0104, (short)0,
|
||||
(short)do_0104.length, (byte)0);
|
||||
do_0104_length = 0;
|
||||
}
|
||||
JCSystem.commitTransaction();
|
||||
|
||||
aes_key.clearKey();
|
||||
|
||||
user_pin_force_verify_signature = Constants.USER_PIN_DEFAULT_FORCE_VERIFY_SIGNATURE;
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
user_pin_length = (byte)Constants.USER_PIN_DEFAULT.length;
|
||||
user_pin.update(Constants.USER_PIN_DEFAULT, (short)0, user_pin_length);
|
||||
user_pin.resetAndUnblock();
|
||||
JCSystem.commitTransaction();
|
||||
|
||||
user_puk_length = 0;
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
admin_pin_length = (byte)Constants.ADMIN_PIN_DEFAULT.length;
|
||||
admin_pin.update(Constants.ADMIN_PIN_DEFAULT, (short)0, admin_pin_length);
|
||||
admin_pin.resetAndUnblock();
|
||||
JCSystem.commitTransaction();
|
||||
|
||||
isTerminated = false;
|
||||
}
|
||||
}
|
440
src/fr/anssi/smartpgp/SecureMessaging.java
Normal file
440
src/fr/anssi/smartpgp/SecureMessaging.java
Normal file
@ -0,0 +1,440 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
import javacardx.apdu.*;
|
||||
import javacardx.crypto.*;
|
||||
|
||||
public final class SecureMessaging {
|
||||
|
||||
public static final short MAC_LENGTH = (short)(Constants.AES_BLOCK_SIZE / (short)2);
|
||||
|
||||
|
||||
protected static final byte[] PADDING_BLOCK = {
|
||||
(byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
|
||||
};
|
||||
|
||||
private final byte[] iv;
|
||||
private final byte[] mac_chaining;
|
||||
|
||||
private final Cipher cipher;
|
||||
private final AESKey senc;
|
||||
|
||||
private final CmacSignature macer;
|
||||
private final CmacKey smac;
|
||||
private final CmacKey srmac;
|
||||
private final CmacKey sreceiptmac;
|
||||
|
||||
protected final PGPKey static_key;
|
||||
|
||||
protected SecureMessaging(final Transients transients) {
|
||||
cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
|
||||
|
||||
macer = new CmacSignature();
|
||||
|
||||
iv = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE,
|
||||
JCSystem.CLEAR_ON_DESELECT);
|
||||
|
||||
mac_chaining = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE,
|
||||
JCSystem.CLEAR_ON_DESELECT);
|
||||
|
||||
senc = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT,
|
||||
(short)(Constants.aesKeyLength() * 8),
|
||||
false);
|
||||
smac = new CmacKey();
|
||||
srmac = new CmacKey();
|
||||
sreceiptmac = new CmacKey();
|
||||
|
||||
static_key = new PGPKey(true);
|
||||
|
||||
reset(transients);
|
||||
}
|
||||
|
||||
protected final void clearSession(final Transients transients) {
|
||||
senc.clearKey();
|
||||
smac.clearKey();
|
||||
srmac.clearKey();
|
||||
sreceiptmac.clearKey();
|
||||
macer.clear();
|
||||
transients.setSecureMessagingEncryptionCounter((short)0);
|
||||
Util.arrayFillNonAtomic(iv, (short)0, (short)iv.length, (byte)0);
|
||||
Util.arrayFillNonAtomic(mac_chaining, (short)0, (short)mac_chaining.length, (byte)0);
|
||||
}
|
||||
|
||||
protected final void reset(final Transients transients) {
|
||||
clearSession(transients);
|
||||
static_key.reset();
|
||||
}
|
||||
|
||||
protected final boolean isInitialized() {
|
||||
return static_key.isInitialized();
|
||||
}
|
||||
|
||||
protected final boolean isSessionAvailable() {
|
||||
return isInitialized()
|
||||
&& senc.isInitialized()
|
||||
&& smac.isInitialized()
|
||||
&& srmac.isInitialized();
|
||||
}
|
||||
|
||||
private final short scp11b(final ECParams params,
|
||||
final byte[] buf, final short len) {
|
||||
|
||||
final byte[] crt = new byte[]{ (byte)0xA6, (byte)0x0D,
|
||||
(byte)0x90, (byte)0x02, (byte)0x11, (byte)0x00,
|
||||
(byte)0x95, (byte)0x01, (byte)0x3C,
|
||||
(byte)0x80, (byte)0x01, (byte)0x88,
|
||||
(byte)0x81, (byte)0x01 };
|
||||
|
||||
if(len <= (short)((short)crt.length + 4)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Util.arrayCompare(crt, (short)0,
|
||||
buf, (short)0,
|
||||
(short)crt.length) != (byte)0) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
short off = (short)crt.length;
|
||||
|
||||
if(buf[off] != Constants.aesKeyLength()) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
++off;
|
||||
|
||||
if(Util.getShort(buf, off) != (short)0x5F49) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
off += 2;
|
||||
|
||||
final short keylen = Common.readLength(buf, off, len);
|
||||
|
||||
off = Common.skipLength(buf, off, len);
|
||||
|
||||
if((short)(off + keylen) > len) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(keylen != (short)(2 * Common.bitsToBytes(params.nb_bits) + 1)) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final ECPrivateKey eskcard = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE,
|
||||
params.nb_bits,
|
||||
false);
|
||||
final ECPublicKey epkcard = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC,
|
||||
params.nb_bits,
|
||||
false);
|
||||
|
||||
params.setParams(eskcard);
|
||||
params.setParams(epkcard);
|
||||
|
||||
final KeyPair ekcard = new KeyPair(epkcard, eskcard);
|
||||
|
||||
ekcard.genKeyPair();
|
||||
|
||||
if(!eskcard.isInitialized() ||
|
||||
!epkcard.isInitialized()) {
|
||||
ISOException.throwIt(Constants.SW_MEMORY_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final KeyAgreement ka = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DHC_PLAIN, false);
|
||||
|
||||
ka.init(eskcard);
|
||||
|
||||
short msglen = 0;
|
||||
|
||||
msglen += ka.generateSecret(buf, off, keylen, buf, len);
|
||||
eskcard.clearKey();
|
||||
|
||||
static_key.initKeyAgreement(ka);
|
||||
msglen += ka.generateSecret(buf, off, keylen, buf, (short)(len + msglen));
|
||||
|
||||
Util.setShort(buf, (short)(len + msglen), (short)0);
|
||||
msglen += 2;
|
||||
|
||||
short counter = 1;
|
||||
off = (short)(len + msglen);
|
||||
msglen += 2;
|
||||
|
||||
buf[(short)(len + msglen)] = crt[(short)8];
|
||||
++msglen;
|
||||
buf[(short)(len + msglen)] = crt[(short)11];
|
||||
++msglen;
|
||||
buf[(short)(len + msglen)] = buf[crt.length];
|
||||
++msglen;
|
||||
|
||||
short keydata_len = 0;
|
||||
|
||||
final MessageDigest digest = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
|
||||
|
||||
while(keydata_len < (short)(4 * buf[crt.length])) {
|
||||
Util.setShort(buf, off, counter);
|
||||
++counter;
|
||||
|
||||
keydata_len += digest.doFinal(buf, len, msglen,
|
||||
buf, (short)(len + msglen + keydata_len));
|
||||
}
|
||||
|
||||
sreceiptmac.setKey(buf, (short)(len + msglen));
|
||||
senc.setKey(buf, (short)(len + msglen + Constants.aesKeyLength()));
|
||||
smac.setKey(buf, (short)(len + msglen + 2 * Constants.aesKeyLength()));
|
||||
srmac.setKey(buf, (short)(len + msglen + 3 * Constants.aesKeyLength()));
|
||||
|
||||
Util.arrayFillNonAtomic(buf, len, (short)(msglen + keydata_len), (byte)0);
|
||||
|
||||
off = len;
|
||||
Util.setShort(buf, off, (short)0x5F49);
|
||||
off += 2;
|
||||
off = Common.writeLength(buf, off, (short)(2 * Common.bitsToBytes(params.nb_bits) + 1));
|
||||
off += epkcard.getW(buf, off);
|
||||
msglen = off;
|
||||
|
||||
epkcard.clearKey();
|
||||
|
||||
buf[off++] = (byte)0x86;
|
||||
buf[off++] = (byte)Constants.AES_BLOCK_SIZE;
|
||||
|
||||
macer.init(sreceiptmac);
|
||||
macer.sign(buf, (short)0, msglen,
|
||||
buf, off, Constants.AES_BLOCK_SIZE);
|
||||
sreceiptmac.clearKey();
|
||||
macer.clear();
|
||||
|
||||
Util.arrayCopy(buf, off, mac_chaining, (short)0, Constants.AES_BLOCK_SIZE);
|
||||
|
||||
off += Constants.AES_BLOCK_SIZE;
|
||||
|
||||
msglen = (short)(off - len);
|
||||
|
||||
Util.arrayCopy(buf, len, buf, (short)0, msglen);
|
||||
|
||||
return msglen;
|
||||
}
|
||||
|
||||
|
||||
protected final short establish(final Transients transients,
|
||||
final ECCurves ec,
|
||||
final byte[] buf, final short len) {
|
||||
|
||||
clearSession(transients);
|
||||
|
||||
if(isInitialized() && static_key.isEc()) {
|
||||
final ECParams params = static_key.ecParams(ec);
|
||||
|
||||
if(params != null) {
|
||||
if(((short)(Constants.aesKeyLength() * 8) == (short)128) ||
|
||||
(params.nb_bits >= 512)) {
|
||||
return scp11b(params, buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private final void incrementEncryptionCounter(final Transients transients) {
|
||||
final short pval = transients.secureMessagingEncryptionCounter();
|
||||
final short nval = (short)(pval + 1);
|
||||
|
||||
if(nval <= pval) {
|
||||
clearSession(transients);
|
||||
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
transients.setSecureMessagingEncryptionCounter(nval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected final short verifyAndDecryptCommand(final Transients transients,
|
||||
short dataLen, short dataWithHeaderLen) {
|
||||
|
||||
if(!isSessionAvailable()) {
|
||||
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
incrementEncryptionCounter(transients);
|
||||
|
||||
if(dataLen < MAC_LENGTH) {
|
||||
clearSession(transients);
|
||||
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final byte[] buf = transients.buffer;
|
||||
|
||||
macer.init(smac);
|
||||
macer.update(mac_chaining, (short)0, Constants.AES_BLOCK_SIZE);
|
||||
macer.update(buf, dataLen, (short)(dataWithHeaderLen - dataLen));
|
||||
macer.sign(buf, (short)0, (short)(dataLen - MAC_LENGTH),
|
||||
buf, dataLen, Constants.AES_BLOCK_SIZE);
|
||||
|
||||
if(Util.arrayCompare(buf, (short)(dataLen - MAC_LENGTH),
|
||||
buf, dataLen,
|
||||
MAC_LENGTH) != (byte)0) {
|
||||
clearSession(transients);
|
||||
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Util.arrayCopyNonAtomic(buf, dataLen,
|
||||
mac_chaining, (short)0,
|
||||
Constants.AES_BLOCK_SIZE);
|
||||
|
||||
dataLen -= MAC_LENGTH;
|
||||
|
||||
if(dataLen > 0) {
|
||||
Util.arrayFillNonAtomic(buf, dataLen, Constants.AES_BLOCK_SIZE, (byte)0);
|
||||
Util.setShort(buf, (short)(dataLen + Constants.AES_BLOCK_SIZE - 2),
|
||||
transients.secureMessagingEncryptionCounter());
|
||||
|
||||
cipher.init(senc, Cipher.MODE_ENCRYPT);
|
||||
cipher.doFinal(buf, dataLen, Constants.AES_BLOCK_SIZE,
|
||||
iv, (short)0);
|
||||
|
||||
cipher.init(senc, Cipher.MODE_DECRYPT,
|
||||
iv, (short)0, Constants.AES_BLOCK_SIZE);
|
||||
|
||||
|
||||
short tmp = (short)(Constants.INTERNAL_BUFFER_MAX_LENGTH - dataLen);
|
||||
if(tmp < Constants.AES_BLOCK_SIZE) {
|
||||
ISOException.throwIt(Constants.SW_MEMORY_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Util.arrayCopyNonAtomic(buf, (short)0,
|
||||
buf, tmp,
|
||||
dataLen);
|
||||
dataLen = 0;
|
||||
while(tmp < Constants.INTERNAL_BUFFER_MAX_LENGTH) {
|
||||
if((short)(Constants.INTERNAL_BUFFER_MAX_LENGTH - tmp) <= Constants.AES_BLOCK_SIZE) {
|
||||
dataLen += cipher.doFinal(buf, tmp, (short)(Constants.INTERNAL_BUFFER_MAX_LENGTH - tmp),
|
||||
buf, dataLen);
|
||||
tmp = Constants.INTERNAL_BUFFER_MAX_LENGTH;
|
||||
} else {
|
||||
dataLen += cipher.update(buf, tmp, Constants.AES_BLOCK_SIZE,
|
||||
buf, dataLen);
|
||||
tmp += Constants.AES_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
Util.arrayFillNonAtomic(iv, (short)0, (short)iv.length, (byte)0);
|
||||
|
||||
--dataLen;
|
||||
while((dataLen > 0) && buf[dataLen] == (byte)0)
|
||||
--dataLen;
|
||||
|
||||
if((dataLen <= 0) || (buf[dataLen] != (byte)0x80)) {
|
||||
clearSession(transients);
|
||||
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected final short encryptAndSign(final Transients transients,
|
||||
short dataLen,
|
||||
final short sw) {
|
||||
|
||||
if(!isSessionAvailable()) {
|
||||
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
final byte[] buf = transients.buffer;
|
||||
|
||||
if(dataLen > 0) {
|
||||
Util.arrayFillNonAtomic(buf, dataLen, Constants.AES_BLOCK_SIZE, (byte)0);
|
||||
buf[dataLen] = (byte)0x80;
|
||||
Util.setShort(buf, (short)(dataLen + Constants.AES_BLOCK_SIZE - 2),
|
||||
transients.secureMessagingEncryptionCounter());
|
||||
|
||||
cipher.init(senc, Cipher.MODE_ENCRYPT);
|
||||
|
||||
cipher.doFinal(buf, dataLen, Constants.AES_BLOCK_SIZE,
|
||||
iv, (short)0);
|
||||
|
||||
cipher.init(senc, Cipher.MODE_ENCRYPT,
|
||||
iv, (short)0, Constants.AES_BLOCK_SIZE);
|
||||
|
||||
short tmp = (short)(Constants.INTERNAL_BUFFER_MAX_LENGTH - dataLen);
|
||||
if(tmp < Constants.AES_BLOCK_SIZE) {
|
||||
ISOException.throwIt(Constants.SW_MEMORY_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Util.arrayCopyNonAtomic(buf, (short)0,
|
||||
buf, tmp,
|
||||
dataLen);
|
||||
dataLen = 0;
|
||||
while(tmp < Constants.INTERNAL_BUFFER_MAX_LENGTH) {
|
||||
if((short)(Constants.INTERNAL_BUFFER_MAX_LENGTH - tmp) <= Constants.AES_BLOCK_SIZE) {
|
||||
dataLen += cipher.doFinal(buf, tmp, (short)(Constants.INTERNAL_BUFFER_MAX_LENGTH - tmp),
|
||||
buf, dataLen);
|
||||
tmp = Constants.INTERNAL_BUFFER_MAX_LENGTH;
|
||||
} else {
|
||||
dataLen += cipher.update(buf, tmp, Constants.AES_BLOCK_SIZE,
|
||||
buf, dataLen);
|
||||
tmp += Constants.AES_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
Util.arrayFillNonAtomic(iv, (short)0, (short)iv.length, (byte)0);
|
||||
}
|
||||
|
||||
macer.init(srmac);
|
||||
|
||||
macer.update(mac_chaining, (short)0, Constants.AES_BLOCK_SIZE);
|
||||
if(dataLen > 0) {
|
||||
macer.update(buf, (short)0, dataLen);
|
||||
}
|
||||
macer.updateShort(sw);
|
||||
dataLen += macer.sign(null, (short)0, (short)0,
|
||||
buf, dataLen, MAC_LENGTH);
|
||||
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
}
|
1516
src/fr/anssi/smartpgp/SmartPGPApplet.java
Normal file
1516
src/fr/anssi/smartpgp/SmartPGPApplet.java
Normal file
File diff suppressed because it is too large
Load Diff
188
src/fr/anssi/smartpgp/Transients.java
Normal file
188
src/fr/anssi/smartpgp/Transients.java
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||||
https://github.com/ANSSI-FR/smartpgp
|
||||
Copyright (C) 2016 ANSSI
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package fr.anssi.smartpgp;
|
||||
|
||||
import javacard.framework.*;
|
||||
|
||||
public final class Transients {
|
||||
|
||||
protected final byte[] buffer;
|
||||
|
||||
private final short[] shorts;
|
||||
private static final byte SHORT_OFFSET_CURRENT_TAG = 0;
|
||||
private static final byte SHORT_OFFSET_OUTPUT_START = SHORT_OFFSET_CURRENT_TAG + 1;
|
||||
private static final byte SHORT_OFFSET_OUTPUT_LENGTH = SHORT_OFFSET_OUTPUT_START + 1;
|
||||
private static final byte SHORT_OFFSET_CHAINING_INPUT_LENGTH = SHORT_OFFSET_OUTPUT_LENGTH + 1;
|
||||
private static final byte SHORT_OFFSET_SECURE_MESSAGING_ENCRYPTION_COUNTER = SHORT_OFFSET_CHAINING_INPUT_LENGTH + 1;
|
||||
private static final byte SHORTS_SIZE = SHORT_OFFSET_SECURE_MESSAGING_ENCRYPTION_COUNTER + 1;
|
||||
|
||||
private final byte[] bytes;
|
||||
private static final byte BYTE_OFFSET_CHAINING_INPUT_INS = 0;
|
||||
private static final byte BYTE_OFFSET_CHAINING_INPUT_P1 = BYTE_OFFSET_CHAINING_INPUT_INS + 1;
|
||||
private static final byte BYTE_OFFSET_CHAINING_INPUT_P2 = BYTE_OFFSET_CHAINING_INPUT_P1 + 1;
|
||||
private static final byte BYTE_OFFSET_CURRENT_TAG_OCCURRENCE = BYTE_OFFSET_CHAINING_INPUT_P2 + 1;
|
||||
private static final byte BYTES_SIZE = BYTE_OFFSET_CURRENT_TAG_OCCURRENCE + 1;
|
||||
|
||||
private final boolean[] booleans;
|
||||
private static final byte BOOLEAN_OFFSET_CHAINING_OUTPUT = 0;
|
||||
private static final byte BOOLEAN_OFFSET_CHAINING_INPUT = BOOLEAN_OFFSET_CHAINING_OUTPUT + 1;
|
||||
private static final byte BOOLEAN_OFFSET_USER_PIN_MODE_81 = BOOLEAN_OFFSET_CHAINING_INPUT + 1;
|
||||
private static final byte BOOLEAN_OFFSET_USER_PIN_MODE_82 = BOOLEAN_OFFSET_USER_PIN_MODE_81 + 1;
|
||||
private static final byte BOOLEAN_OFFSET_SECURE_MESSAGING_OK = BOOLEAN_OFFSET_USER_PIN_MODE_82 + 1;
|
||||
private static final byte BOOLEANS_SIZE = BOOLEAN_OFFSET_SECURE_MESSAGING_OK + 1;
|
||||
|
||||
|
||||
protected Transients() {
|
||||
buffer = JCSystem.makeTransientByteArray(Constants.INTERNAL_BUFFER_MAX_LENGTH,
|
||||
JCSystem.CLEAR_ON_DESELECT);
|
||||
shorts = JCSystem.makeTransientShortArray(SHORTS_SIZE,
|
||||
JCSystem.CLEAR_ON_DESELECT);
|
||||
bytes = JCSystem.makeTransientByteArray(BYTES_SIZE,
|
||||
JCSystem.CLEAR_ON_DESELECT);
|
||||
booleans = JCSystem.makeTransientBooleanArray(BOOLEANS_SIZE,
|
||||
JCSystem.CLEAR_ON_DESELECT);
|
||||
}
|
||||
|
||||
protected final void clear() {
|
||||
for(byte i = 0; i < shorts.length; ++i) {
|
||||
shorts[i] = (short)0;
|
||||
}
|
||||
for(byte i = 0; i < bytes.length; ++i) {
|
||||
bytes[i] = (byte)0;
|
||||
}
|
||||
for(byte i = 0; i < booleans.length; ++i) {
|
||||
booleans[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected final void setCurrentTag(final short tag) {
|
||||
shorts[SHORT_OFFSET_CURRENT_TAG] = tag;
|
||||
}
|
||||
|
||||
protected final short currentTag() {
|
||||
return shorts[SHORT_OFFSET_CURRENT_TAG];
|
||||
}
|
||||
|
||||
protected final void setChainingInputLength(final short len) {
|
||||
shorts[SHORT_OFFSET_CHAINING_INPUT_LENGTH] = len;
|
||||
}
|
||||
|
||||
protected final short chainingInputLength() {
|
||||
return shorts[SHORT_OFFSET_CHAINING_INPUT_LENGTH];
|
||||
}
|
||||
|
||||
protected final void setSecureMessagingEncryptionCounter(final short val) {
|
||||
shorts[SHORT_OFFSET_SECURE_MESSAGING_ENCRYPTION_COUNTER] = val;
|
||||
}
|
||||
|
||||
protected final short secureMessagingEncryptionCounter() {
|
||||
return shorts[SHORT_OFFSET_SECURE_MESSAGING_ENCRYPTION_COUNTER];
|
||||
}
|
||||
|
||||
protected final void setOutputStart(final short off) {
|
||||
shorts[SHORT_OFFSET_OUTPUT_START] = off;
|
||||
}
|
||||
|
||||
protected final short outputStart() {
|
||||
return shorts[SHORT_OFFSET_OUTPUT_START];
|
||||
}
|
||||
|
||||
protected final void setOutputLength(final short len) {
|
||||
shorts[SHORT_OFFSET_OUTPUT_LENGTH] = len;
|
||||
}
|
||||
|
||||
protected final short outputLength() {
|
||||
return shorts[SHORT_OFFSET_OUTPUT_LENGTH];
|
||||
}
|
||||
|
||||
protected final void setChainingInputIns(final byte ins) {
|
||||
bytes[BYTE_OFFSET_CHAINING_INPUT_INS] = ins;
|
||||
}
|
||||
|
||||
protected final byte chainingInputIns() {
|
||||
return bytes[BYTE_OFFSET_CHAINING_INPUT_INS];
|
||||
}
|
||||
|
||||
protected final void setChainingInputP1(final byte p1) {
|
||||
bytes[BYTE_OFFSET_CHAINING_INPUT_P1] = p1;
|
||||
}
|
||||
|
||||
protected final byte chainingInputP1() {
|
||||
return bytes[BYTE_OFFSET_CHAINING_INPUT_P1];
|
||||
}
|
||||
|
||||
protected final void setChainingInputP2(final byte p2) {
|
||||
bytes[BYTE_OFFSET_CHAINING_INPUT_P2] = p2;
|
||||
}
|
||||
|
||||
protected final byte chainingInputP2() {
|
||||
return bytes[BYTE_OFFSET_CHAINING_INPUT_P2];
|
||||
}
|
||||
|
||||
protected final void setCurrentTagOccurrence(final byte occ) {
|
||||
bytes[BYTE_OFFSET_CURRENT_TAG_OCCURRENCE] = occ;
|
||||
}
|
||||
|
||||
protected final byte currentTagOccurrence() {
|
||||
return bytes[BYTE_OFFSET_CURRENT_TAG_OCCURRENCE];
|
||||
}
|
||||
|
||||
protected final void setChainingOutput(final boolean chaining) {
|
||||
booleans[BOOLEAN_OFFSET_CHAINING_OUTPUT] = chaining;
|
||||
}
|
||||
|
||||
protected final boolean chainingOutput() {
|
||||
return booleans[BOOLEAN_OFFSET_CHAINING_OUTPUT];
|
||||
}
|
||||
|
||||
protected final void setChainingInput(final boolean chaining) {
|
||||
booleans[BOOLEAN_OFFSET_CHAINING_INPUT] = chaining;
|
||||
}
|
||||
|
||||
protected final boolean chainingInput() {
|
||||
return booleans[BOOLEAN_OFFSET_CHAINING_INPUT];
|
||||
}
|
||||
|
||||
protected final void setUserPinMode81(final boolean is81) {
|
||||
booleans[BOOLEAN_OFFSET_USER_PIN_MODE_81] = is81;
|
||||
}
|
||||
|
||||
protected final boolean userPinMode81() {
|
||||
return booleans[BOOLEAN_OFFSET_USER_PIN_MODE_81];
|
||||
}
|
||||
|
||||
protected final void setUserPinMode82(final boolean is82) {
|
||||
booleans[BOOLEAN_OFFSET_USER_PIN_MODE_82] = is82;
|
||||
}
|
||||
|
||||
protected final boolean userPinMode82() {
|
||||
return booleans[BOOLEAN_OFFSET_USER_PIN_MODE_82];
|
||||
}
|
||||
|
||||
protected final void setSecureMessagingOk(final boolean ok) {
|
||||
booleans[BOOLEAN_OFFSET_SECURE_MESSAGING_OK] = ok;
|
||||
}
|
||||
|
||||
protected final boolean secureMessagingOk() {
|
||||
return booleans[BOOLEAN_OFFSET_SECURE_MESSAGING_OK];
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user