diff --git a/WebContent/submitIdentity.jsp b/WebContent/submitIdentity.jsp index 853cdde5..94340744 100644 --- a/WebContent/submitIdentity.jsp +++ b/WebContent/submitIdentity.jsp @@ -63,7 +63,7 @@

- +
<%-- This is where the actual identity generation takes place --%> diff --git a/build.xml b/build.xml index 06b0a81c..327279bd 100644 --- a/build.xml +++ b/build.xml @@ -124,6 +124,7 @@ + @@ -133,10 +134,17 @@ - + + + + + + + + - + @@ -177,6 +185,13 @@ + + + + + + + @@ -220,7 +235,7 @@ - + diff --git a/doc/techdoc.txt b/doc/techdoc.txt index c6dba710..03d519fa 100644 --- a/doc/techdoc.txt +++ b/doc/techdoc.txt @@ -607,6 +607,11 @@ The file format is: mailapi.jar Part of JavaMail src/SevenZip/ An LZMA implementation from http://www.7-zip.org/sdk.html src/net/sf/ntru An NTRU implementation from http://sf.net/projects/ntru/ + de/flexiprovider and codec/ A stripped down and patched version of the FlexiProvider + sources (http://www.flexiprovider.de/), containing only the + classes needed for GMSS. + The patch consists of a modified de.flexiprovider.core.CoreRegistry + so there are no dependencies on classes not related to GMSS. src/com/lambdaworks/crypto/ An scrypt implementation from https://github.com/wg/scrypt/ diff --git a/plugin/webapps.config b/plugin/webapps.config index 0ac4e2c4..59837156 100644 --- a/plugin/webapps.config +++ b/plugin/webapps.config @@ -1 +1 @@ -webapps.i2pbote.classpath=$I2P/lib/jstl.jar,$I2P/lib/standard.jar,$PLUGIN/lib/i2pbote.jar,$PLUGIN/lib/mailapi.jar,$PLUGIN/lib/bcprov-ecc-jdk16-145.jar +webapps.i2pbote.classpath=$I2P/lib/jstl.jar,$I2P/lib/standard.jar,$PLUGIN/lib/i2pbote.jar,$PLUGIN/lib/mailapi.jar,$PLUGIN/lib/bcprov-ecc-jdk16-145.jar,$PLUGIN/lib/flexi-gmss-1.7p1.jar diff --git a/src/codec/CorruptedCodeException.java b/src/codec/CorruptedCodeException.java new file mode 100644 index 00000000..88f3c277 --- /dev/null +++ b/src/codec/CorruptedCodeException.java @@ -0,0 +1,106 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec; + +/** + * Thrown if a code is recognized as being corrupted for instance due to + * transmission errors. + * + * @author Volker Roth + * @version $Id: CorruptedCodeException.java,v 1.3 2005/04/06 09:24:51 flautens + * Exp $ + * @see Base64 + */ +public class CorruptedCodeException extends Exception { + /** + * Creates an exception instance with no particular message. + */ + public CorruptedCodeException() { + super(); + } + + /** + * Creates an exception instance with the given message. + * + * @param message + * The message string describing the reason for this + * exception. + */ + public CorruptedCodeException(String message) { + super(message); + } +} diff --git a/src/codec/InconsistentStateException.java b/src/codec/InconsistentStateException.java new file mode 100644 index 00000000..36c78a8b --- /dev/null +++ b/src/codec/InconsistentStateException.java @@ -0,0 +1,162 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec; + +/** + * Signals an inconsistent object state. Objects can enter inconsistent states + * for instance if an exception is caught that shouldn't happen. Such exceptions + * can occur due to unexpected exceptions from other code or internal errors. + *

+ * A typical example is when an application knows that a specific error + * condition cannot occur because the preconditions required for a successful + * completion of a method call are met, but an exception is thrown nevertheless. + * In this case, the exception can be caught and wrapped into an exception of + * this class in order to pass on the original cause of the exception to code + * further up the calling stack. + * + * @author Volker Roth + * @version "$Id: InconsistentStateException.java,v 1.3 2005/04/06 09:33:26 + * flautens Exp $" + */ +public class InconsistentStateException extends RuntimeException { + /** + * The wrapped exception + */ + private Exception e_; + + /** + * Creates an instance. + */ + public InconsistentStateException() { + } + + /** + * Creates an instance with the given message. + * + * @param message + * The message. + */ + public InconsistentStateException(String message) { + super(message); + } + + /** + * Creates an exception that wraps around the given exception. The message + * of this exception is set to the one of the given exception. The given + * exception must not be null. + * + * @param e + * The exception that shall be wrapped in this one. + * @throws NullPointerException + * if the given exception is null. + */ + public InconsistentStateException(Exception e) { + super(e.getMessage()); + e_ = e; + } + + /** + * Returns the wrapped exception or this if no exception is + * wrapped in this one. + * + * @return The wrapped exception or this if there is no + * exception wrapped by this one. + */ + public Exception getException() { + if (e_ == null) { + return this; + } + + return e_; + } + + /** + * Prints the stack trace of this exception. If this exception has a wrapped + * exception then the stack trace of the wrapped exception is printed + * instead of the one of this exception. Since this exception was created in + * the course of catching the wrapped exception, the location where this + * exception was thrown is included in the stack trace of the wrapped + * exception. + */ + public void printStackTrace() { + if (e_ == null) { + super.printStackTrace(); + } else { + e_.printStackTrace(); + } + } +} diff --git a/src/codec/asn1/ASN1.java b/src/codec/asn1/ASN1.java new file mode 100644 index 00000000..4789c068 --- /dev/null +++ b/src/codec/asn1/ASN1.java @@ -0,0 +1,369 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * Defines various constants used with ASN.1 such as the tag and type class + * identifiers. The classes in this package are modelled along the lines of + * ITU-T Recommendations X.680, X.681, X.682, X.690, and X.691. From now on we + * assume the reader is familiar with ASN.1, BER, and DER. + *

+ * + * This package defines a number of primitive types as specified by the basic + * syntax in X.680. Based on these primitive types more complex types can be + * created. We refer to these types as compound types or structures. + * Below, we discuss how such types are constructed, encoded and decoded using + * the classes in this package. + *

+ * + * For instance the type PrivateKeyInfo is defined in PKCS#8 as + * follows using ASN.1:

+ * + *
+ * PrivateKeyInfo ::= SEQUENCE (
+ *   version Version,
+ *   privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ *   privateKey PrivateKey,
+ *   attributes [0] IMPLICIT Attributes OPTIONAL
+ * }
+ * Version ::= INTEGER
+ * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+ * PrivateKey ::= OCTET STRING
+ * Attributes ::= SET OF Attribute
+ * 
+ * + *
+ * + * This type can be created as follows based on the classes in this package: + *
+ * + *
+ * public class PrivateKeyInfo extends ASN1Sequence
+ *  {
+ *    public PrivateKeyInfo()
+ *     {
+ *       add(new ASN1Integer());
+ *       add(new AlgorithmIdentifier()); // Detailed below
+ *       add(new ASN1OctetString());
+ *       add(new ASN1TaggedType(
+ *         0, new ASN1SetOf(Attribute.class), false, true);
+ *     }
+ *    ...
+ *  }
+ * 
+ * + *
The {@link ASN1TaggedType tagged type} allows to define types + * of the ASN.1 tag class UNIVERSAL, CONTEXT SPECIFIC, APPLICATION, and PRIVATE. + * The constructor shown above is a convenience constructor that assumes the + * class is CONTEXT SPECIFIC. The third parameter specifies that the tagging is + * not EXPLICIT (hence IMPLICIT as required by the definition above), and the + * fourth parameter specifies that attributes is OPTIONAL. + *

+ * + * The interface {@link ASN1Type ASN1Type} specifies a number of methods to + * declare types as OPTIONAL, IMPLICIT or EXPLICIT. In principle, ASN.1 + * structures can be modelled almost one to one using the classes in this + * package. + *

+ * + * Individual types also offer setter methods and constructors that allow to + * preset certain values as the values of the ASN.1 types from native Java types + * such as String, int, byte[] etc. + *

+ * + * Once, a primitive type or a compound type x has been defined and + * initialized it can be encoded in a number of ways. The first step is to + * choose an {@link Encoder encoder}. One example encoder is the + * {@link DEREncoder DEREncoder}. This encoder encodes types according to the + * Distinguished Encoding Rules, a subset of the Basic Encoding Rules defined in + * the ITU-T Recommendations. Encoding to a file is simple, the following code + * snippet shows how to do it:

+ * + *
+ * DEREncoder enc;
+ * enc = new DEREncoder(new FileOutputStream("code.der"));
+ * enc.writeType(x);
+ * enc.close();
+ * 
+ * + *
There is nothing more to it. However, only types not declared + * as OPTIONAL are written to the stream. Hence, the type + * attributes + * mentioned above is not written. + *

+ * + * Why then is it declared as OPTIONAL? Answer, by convention in this package + * (and by reason) the default constructor is meant to initialize a type for + * decoding. In a code the + * attributes type can be encountered. The + * decoder clears the OPTIONAL flag of types it encounters during decoding so it + * is clear which types were present and which were not. If a type is only used + * for encoding and not for decoding then the OPTIONAL types not required can be + * omitted. + * + *

+ * AlgorithmIdentifier  ::= SEQUENCE{
+ *   algorithm  OBJECT IDENTIFIER,
+ *   parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ * 
+ * + * @author Volker Roth + * @version "$Id: ASN1.java,v 1.4 2005/03/22 16:04:37 flautens Exp $" + */ +public final class ASN1 extends Object { + /** + * DOCUMENT ME! + */ + public static final int TAG_EOC = 0; + + /** + * DOCUMENT ME! + */ + public static final int TAG_BOOLEAN = 1; + + /** + * DOCUMENT ME! + */ + public static final int TAG_INTEGER = 2; + + /** + * DOCUMENT ME! + */ + public static final int TAG_BITSTRING = 3; + + /** + * DOCUMENT ME! + */ + public static final int TAG_OCTETSTRING = 4; + + /** + * DOCUMENT ME! + */ + public static final int TAG_NULL = 5; + + /** + * DOCUMENT ME! + */ + public static final int TAG_OID = 6; + + /** + * DOCUMENT ME! + */ + public static final int TAG_REAL = 9; + + /** + * DOCUMENT ME! + */ + public static final int TAG_ENUMERATED = 10; + + /** + * DOCUMENT ME! + */ + public static final int TAG_UTF8STRING = 12; + + /** + * DOCUMENT ME! + */ + public static final int TAG_SEQUENCE = 16; + + /** + * DOCUMENT ME! + */ + public static final int TAG_SET = 17; + + /** + * DOCUMENT ME! + */ + public static final int TAG_NUMERICSTRING = 18; + + /** + * DOCUMENT ME! + */ + public static final int TAG_PRINTABLESTRING = 19; + + /** + * DOCUMENT ME! + */ + public static final int TAG_T61STRING = 20; + + /** + * DOCUMENT ME! + */ + public static final int TAG_VIDEOTEXTSTRING = 21; + + /** + * DOCUMENT ME! + */ + public static final int TAG_IA5STRING = 22; + + /** + * DOCUMENT ME! + */ + public static final int TAG_UTCTIME = 23; + + /** + * DOCUMENT ME! + */ + public static final int TAG_GENERALIZEDTIME = 24; + + /** + * DOCUMENT ME! + */ + public static final int TAG_GRAPHICSTRING = 25; + + /** + * DOCUMENT ME! + */ + public static final int TAG_VISIBLESTRING = 26; + + /** + * DOCUMENT ME! + */ + public static final int TAG_GENERALSTRING = 27; + + /** + * DOCUMENT ME! + */ + public static final int TAG_UNIVERSALSTRING = 28; + + /** + * DOCUMENT ME! + */ + public static final int TAG_BMPSTRING = 30; + + /** + * DOCUMENT ME! + */ + public static final int TAG_MASK = 0x1f; + + /** + * DOCUMENT ME! + */ + public static final int TAG_LONGFORM = 0x1f; + + /** + * DOCUMENT ME! + */ + public static final int CLASS_UNIVERSAL = 0x00; + + /** + * DOCUMENT ME! + */ + public static final int CLASS_APPLICATION = 0x40; + + /** + * DOCUMENT ME! + */ + public static final int CLASS_CONTEXT = 0x80; + + /** + * DOCUMENT ME! + */ + public static final int CLASS_PRIVATE = 0xc0; + + /** + * DOCUMENT ME! + */ + public static final int CLASS_MASK = 0xc0; + + /** + * DOCUMENT ME! + */ + public static final int PRIMITIVE = 0x00; + + /** + * DOCUMENT ME! + */ + public static final int CONSTRUCTED = 0x20; + + /** + * DOCUMENT ME! + */ + public static final int LENGTH_LONGFORM = 0x80; + + /** + * DOCUMENT ME! + */ + public static final int LENGTH_MASK = 0x7f; + + /** + * No-one can instantiate this class. + */ + private ASN1() { + } +} diff --git a/src/codec/asn1/ASN1AbstractCollection.java b/src/codec/asn1/ASN1AbstractCollection.java new file mode 100644 index 00000000..d99dda0e --- /dev/null +++ b/src/codec/asn1/ASN1AbstractCollection.java @@ -0,0 +1,412 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * Represents an abstract collection of ASN.1 types such as a SEQUENCE or a SET. + * Since this class inherits from the Collection framework class ArrayList, + * ASN.1 types may be added conveniently just as object instances are added to a + * list. + *

+ * + * Please note that constraints of collections are validated before encoding and + * after decoding. Invalid modification of a collection type can be detected on + * importing and exporting abstract collections. On DER encoding a collection + * its constraint is validated twice since the DER encoding is a two-pass + * process. + * + * @author Volker Roth + * @version "$Id: ASN1AbstractCollection.java,v 1.5 2005/03/22 16:12:45 flautens + * Exp $" + */ +public abstract class ASN1AbstractCollection extends ArrayList implements + ASN1Collection, Cloneable, Externalizable { + /** + * DOCUMENT ME! + */ + private boolean optional_ = false; + + /** + * DOCUMENT ME! + */ + private boolean explicit_ = true; + + /** + * DOCUMENT ME! + */ + private Constraint constraint_; + + /** + * Abstract method declarations. + * + * @return corresponding ASN1 tag + */ + public abstract int getTag(); + + /** + * Method declarations with default implementation. + */ + public ASN1AbstractCollection() { + super(); + } + + /** + * Creates an instance with the given capacity. + * + * @param capacity + * The capacity. + */ + public ASN1AbstractCollection(int capacity) { + super(capacity); + } + + /** + * Returns the Java type that corresponds to this ASN.1 type. + * + * @return The collection used internally for storing the elements in this + * constructed ASN.1 type. + */ + public Object getValue() { + return this; + } + + /** + * Returns the Java type that corresponds to this ASN.1 type. + * + * @return The collection used internally for storing the elements in this + * constructed ASN.1 type. + */ + public Collection getCollection() { + return this; + } + + /** + * Optional types may be present in an encoding but they need not be. + * + * @param optional + * true iff this type is optional. + */ + public void setOptional(boolean optional) { + optional_ = optional; + } + + /** + * @return true iff this type is optional. + */ + public boolean isOptional() { + return optional_; + } + + /** + * This default implementation returns {@link ASN1#CLASS_UNIVERSAL + * UNIVERSAL}. + * + * @return The class of the ASN.1 tag. + */ + public int getTagClass() { + return ASN1.CLASS_UNIVERSAL; + } + + /** + * Sets the tagging of this type as either EXPLICIT or IMPLICIT. The default + * is EXPLICIT. Encoders skip the encoding of identifier octets for types + * that are declared as IMPLICIT. + * + * @param explicit + * true if this type shall be tagged EXPLICIT + * and false if it shall be encoded IMPLICIT. + */ + public void setExplicit(boolean explicit) { + explicit_ = explicit; + } + + /** + * + * @return true if this type is tagged EXPLICIT and + * false if it is tagged IMPLICIT. + */ + public boolean isExplicit() { + return explicit_; + } + + /** + * Sets the {@link Constraint Constraint} of this type. For instance an + * ASN.1 INTEGER might be constrained to a certain range such as INTEGER + * (0..99). null can be passed as a constraint which disables + * constraint checking. + * + * @param constraint + * The {@link Constraint Constraint} of this type. + */ + public void setConstraint(Constraint constraint) { + constraint_ = constraint; + } + + /** + * Returns the {@link Constraint Constraint} of this type or + * null if there is none. + * + * @return The Constraint or null. + */ + public Constraint getConstraint() { + return constraint_; + } + + /** + * Checks the constraint on this type if it is set. Otherwise this method + * returns silently. + * + * @throws ConstraintException + * if this type is not in the appropriate range of values. + */ + public void checkConstraints() throws ConstraintException { + if (constraint_ != null) { + constraint_.constrain(this); + } + } + + /** + * Returns true if the given tag and tag class matches the + * tag and tag class of this instance. This method is used primarily by + * decoders and variable types such as {@link ASN1Choice ASN1Choice} and + * {@link ASN1OpenType ASN1OpenType}. It enables decoders to query a + * variable type whether a decoded type is accepted. + *

+ * + * This method provides a default implementation that matches the given tag + * and tag class against the values returned by {@link #getTag getTag} and + * {@link #getTagClass getTagClass} respectively. + * + * @param tag + * The tag to compare with. + * @param tagclass + * The tag class to compare with. + * @return true if the given tag and tag class matches this + * type and false otherwise. + */ + public boolean isType(int tag, int tagclass) { + if ((getTag() == tag) && (getTagClass() == tagclass)) { + return true; + } + + return false; + } + + /** + * Writes this collection to the given {@link Encoder encoder}. + * + * @param enc + * The encoder to write this type to. + */ + public void encode(Encoder enc) throws ASN1Exception, IOException { + checkConstraints(); + enc.writeCollection(this); + } + + /** + * Reads this collection from the given {@link Decoder Decoder}. This type + * is initialized with the decoded data. The components of the decoded + * collection must match the components of this collection. If they do then + * the components are also initialized with the decoded values. Otherwise an + * exception is thrown. + * + * @param dec + * The decoder to read from. + */ + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readCollection(this); + checkConstraints(); + } + + /** + * Prints this collection. This default implementation derives a descriptive + * name from the name of the fully qualified name of this class (or that of + * the respective subclass). The last component of the class name is + * extracted and a prefix of "ASN1" is removed from it. Then the + * elements contained in this collection are printed. + * + * @return The string representation of this ASN.1 collection. + */ + public String toString() { + StringBuffer buf; + Iterator i; + String s; + + s = removePackageName(getClass()); + + buf = new StringBuffer(); + buf.append(s); + + if (isOptional()) { + buf.append(" OPTIONAL"); + } + + if (this instanceof ASN1CollectionOf) { + buf.append(" SEQUENCE OF " + + removePackageName(((ASN1CollectionOf) this) + .getElementType())); + } else { + buf.append(" SEQUENCE "); + } + buf.append(" {\n"); + + for (i = iterator(); i.hasNext();) { + buf.append(i.next().toString()); + buf.append("\n"); + } + buf.append("}"); + return buf.toString(); + } + + /** + * This method removes the package information from the qualified class + * name. If the remaining class name starts with 'ASN1' then this prefix is + * also removed. + * + * @param clazz + * The class to handle + * @return the shortened class name + */ + private String removePackageName(Class clazz) { + String s = clazz.getName(); + int n = s.lastIndexOf('.'); + + if (n < 0) { + n = -1; + } + + s = s.substring(n + 1); + if (s.startsWith("ASN1")) { + s = s.substring(4); + } + return s; + } + + /** + * The writeExternal and readExternal methods of the Externalizable + * interface are implemented by a class to give the serializable class + * complete control over the format and contents of the stream for an object + * and its supertypes. + * + * @param out - + * the stream to write the object to + * @see java.io.Externalizable + */ + public void writeExternal(ObjectOutput out) throws IOException { + byte[] res = null; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + try { + encode(new DEREncoder(baos)); + res = baos.toByteArray(); + baos.close(); + out.write(res); + } catch (ASN1Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * The writeExternal and readExternal methods of the Externalizable + * interface are implemented by a class to give the serializable class + * complete control over the format and contents of the stream for an object + * and its supertypes. + * + * @param in - + * the stream to read data from in order to restore the + * object + * @see java.io.Externalizable + */ + public void readExternal(ObjectInput in) throws IOException { + try { + decode(new DERDecoder((ObjectInputStream) in)); + } catch (ASN1Exception e) { + throw new RuntimeException(e.toString()); + } + } +} diff --git a/src/codec/asn1/ASN1AbstractString.java b/src/codec/asn1/ASN1AbstractString.java new file mode 100644 index 00000000..869b07fc --- /dev/null +++ b/src/codec/asn1/ASN1AbstractString.java @@ -0,0 +1,298 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * The root class of all ASN.1 string types including but not limited to + * IA5String, VisibleString, PrintableString, UTCTime, and GeneralizedTime. + *

+ * + * Each string type is encoded as if it is declared as [UNIVERSAL x] IMPLICIT OCTET STRING + * where x is the tag number of the respective string type (see ITU-T + * Rec. X.690, paragraph 8.20.3). + *

+ * + * There are 8 restructed string types of which 4 do not allow escape sequences, + * namely NumericString, PrintableString, VisibleString (ISO646String) and + * IA5String. TeletexString (T61String), VideotextString, GraphicString, and + * GeneralString allow the use of escape sequences. However, the srings must be + * encoded such as to use the minimum number of octets possible. All these + * strings use 1-octet representations; IA5String uses 2-octet representations + * for special characters. + *

+ * + * Two unrestricted string types are defined in X.680, namely BMPString and + * UniversalString. BMPString uses a 2-octet representation per character and + * UniversalString uses a 4-octet representation. + *

+ * + * Each string type represented in this package handles octets to character and + * character to octets conversion according to the general coding scheme of the + * particular string, but not neccessarily restriction to a particular character + * set. This is to be implemented through {@link Constraint constraints} that + * are added to the respective types on creation (in the constructors). + * Restriction of character sets is thus done on the Unicode character set used + * by Java. + *

+ * + * This class implements plain 1-octet to character conversion by default. Class + * {@link ASN1BMPString ASN1BMPString} handles 2-octet conversion and class + * {@link ASN1UniversalString ASN1UniversalString} handles 4-octets conversion. + * Without reference to ISO defined character encodings these implementations + * assume that the n-octet tuples represent the least significant bits + * of the Unicode characters with the corresponding bits set to zero. + * + * + * @author Volker Roth + * @version "$Id: ASN1AbstractString.java,v 1.4 2004/08/26 15:08:21 pebinger Exp $" + */ +public abstract class ASN1AbstractString extends ASN1AbstractType implements + ASN1String { + + private static final String DEFAULT_VALUE = ""; + + private String value_ = DEFAULT_VALUE; + + public ASN1AbstractString() { + super(); + } + + /** + * Creates an instance with the given string value. + * + * This constructor calls {@link #setString setString} to set the string + * value. + * + * @param s + * The string value. + */ + public ASN1AbstractString(String s) { + setString0(s); + } + + /** + * Returns the represented string value. + * + * @return The string value of this type. + */ + public Object getValue() { + return value_; + } + + /** + * Returns the represented string value. + * + * @return The string value of this type. + */ + public String getString() { + return value_; + } + + /** + * Sets the string value. + * + * @param s + * The string value. + * @throws ConstraintException + * if the given string does not match the constraint set for + * this instance. + */ + public void setString(String s) throws ConstraintException { + setString0(s); + checkConstraints(); + } + + protected void setString0(String s) { + if (s == null) + throw new NullPointerException("Need a string!"); + + value_ = s; + } + + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeString(this); + } + + public void decode(Decoder enc) throws ASN1Exception, IOException { + enc.readString(this); + checkConstraints(); + } + + /** + * Converts the given byte array to a string by filling up each consecutive + * byte with 0's to the size of the Unicode characters. + * + * @param b + * The byte array to convert. + * @throws ASN1Exception + * never, only for compliance with the {@link ASN1String} + * interface. + */ + public String convert(byte[] b) throws ASN1Exception { + if (b == null) + throw new NullPointerException("Cannot convert null array!"); + + char[] c = new char[b.length]; + for (int i = 0; i < b.length; i++) + c[i] = (char) (b[i] & 0xff); + + return String.valueOf(c); + } + + /** + * Converts the given string to a byte array by chopping away all but the + * least significant byte of each character. + * + * @param s + * The string to convert. + * @throws ASN1Exception + * never, only for compliance with the {@link ASN1String} + * interface. + */ + public byte[] convert(String s) throws ASN1Exception { + if (s == null) + throw new NullPointerException("Cannot convert null string!"); + + char[] c = s.toCharArray(); + byte[] b = new byte[c.length]; + + for (int i = 0; i < c.length; i++) + b[i] = (byte) (c[i] & 0xff); + + return b; + } + + /** + * Returns the number of bytes required to store the converted string. + * + * @param s + * The string. + * @throws ASN1Exception + * never, only for compliance with the {@link ASN1String} + * interface. + */ + public int convertedLength(String s) throws ASN1Exception { + return s.length(); + } + + public String toString() { + String s; + int n; + + s = getClass().getName(); + n = s.lastIndexOf('.'); + + if (n < 0) + n = -1; + + s = s.substring(n + 1); + if (s.startsWith("ASN1")) + s = s.substring(4); + + return s + " \"" + value_ + "\""; + } + + /** + * Indicates whether some other ASN.1 string is "equal to" this one. + * + * @param s + * the reference string with which to compare. + * @return true if this string is the same as the s argument; false + * otherwise. + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object s) { + if (this.getClass().equals(s.getClass())) { + return value_.equals(((ASN1AbstractString) s).getString()); + } + return false; + } + + /** + * Returns a hash code value for the object calculated from the contained + * String. + * + * @return a hash code value for this object. + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return (this.getClass().hashCode() + value_.hashCode()) / 2; + } + +} diff --git a/src/codec/asn1/ASN1AbstractType.java b/src/codec/asn1/ASN1AbstractType.java new file mode 100644 index 00000000..478527b7 --- /dev/null +++ b/src/codec/asn1/ASN1AbstractType.java @@ -0,0 +1,299 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; + +/** + * The basic interface for Java objects representing primitive ASN.1 types + * according to ITU-T Recommendation X.680. + * + * @author Volker Roth + * @version "$Id: ASN1AbstractType.java,v 1.4 2005/03/23 12:45:51 flautens Exp $" + */ +public abstract class ASN1AbstractType extends Object implements ASN1Type, + Cloneable, Externalizable { + /** + * Holds the ASN.1 optional state. Optional types may be present in an + * encoding but they need not be. + */ + private boolean optional_ = false; + + /** + * This flag defines the tagging variant which should be used by the + * decoder/encoder while processing this type. + */ + private boolean explicit_ = true; + + /** + * Holds the constraint which should be checked if the internal value of a + * sub class is modified. + */ + private Constraint constraint_; + + /** + * This abstract method should return the value wrapped by the ASN1Type. + * + * @return the internal value + */ + public abstract Object getValue(); + + /** + * Returns the corresponding ASN.1 tag. + * + * @return the corresponding ASN.1 tag + */ + public abstract int getTag(); + + public abstract void encode(Encoder enc) throws ASN1Exception, IOException; + + public abstract void decode(Decoder dec) throws ASN1Exception, IOException; + + /* + * Method declarations with default implementation. + */ + public ASN1AbstractType() { + super(); + } + + /** + * Optional types may be present in an encoding but they need not be. + * + * @param optional + * true iff this type is optional. + */ + public void setOptional(boolean optional) { + optional_ = optional; + } + + /** + * @return true if this type is optional. + */ + public boolean isOptional() { + return optional_; + } + + /** + * This default implementation returns {@link ASN1#CLASS_UNIVERSAL + * UNIVERSAL}. + * + * @return The class of the ASN.1 tag. + */ + public int getTagClass() { + return ASN1.CLASS_UNIVERSAL; + } + + /** + * Sets the tagging of this type as either EXPLICIT or IMPLICIT. The default + * is EXPLICIT. Encoders skip the encoding of identifier octets for types + * that are declared as IMPLICIT. + * + * @param explicit + * true if this type shall be tagged EXPLICIT + * and false if it shall be encoded IMPLICIT. + */ + public void setExplicit(boolean explicit) { + explicit_ = explicit; + } + + /** + * Returns code>true if this type is tagged EXPLICIT and false + * otherwise. + * + * @return true if this type is tagged EXPLICIT and false + * if it is tagged IMPLICIT. + */ + public boolean isExplicit() { + return explicit_; + } + + /** + * Returns true if the given tag and tag class matches the + * tag and tag class of this instance. This method is used primarily by + * decoders and variable types such as {@link ASN1Choice ASN1Choice} and + * {@link ASN1OpenType ASN1OpenType}. It enables decoders to query a + * variable type whether a decoded type is accepted. + *

+ * + * This method provides a default implementation that matches the given tag + * and tag class against the values returned by {@link #getTag getTag} and + * {@link #getTagClass getTagClass} respectively. + * + * @param tag + * The tag to compare with. + * @param tagclass + * The tag class to compare with. + * @return true if the given tag and tag class matches this + * type and false otherwise. + */ + public boolean isType(int tag, int tagclass) { + if ((getTag() == tag) && (getTagClass() == tagclass)) { + return true; + } + + return false; + } + + /** + * Sets the {@link Constraint Constraint} of this type. For instance an + * ASN.1 INTEGER might be constrained to a certain range such as INTEGER + * (0..99). null can be passed as a constraint which disables + * constraint checking. + * + * @param constraint + * The {@link Constraint Constraint} of this type. + */ + public void setConstraint(Constraint constraint) { + constraint_ = constraint; + } + + /** + * Returns the {@link Constraint Constraint} of this type or + * null if there is none. + * + * @return The Constraint or null. + */ + public Constraint getConstraint() { + return constraint_; + } + + /** + * Checks the constraint on this type if it is set. Otherwise this method + * returns silently. + * + * @throws ConstraintException + * if this type is not in the appropriate range of values. + */ + public void checkConstraints() throws ConstraintException { + if (constraint_ != null) { + constraint_.constrain(this); + } + } + + /** + * The writeExternal and readExternal methods of the Externalizable + * interface are implemented by a class to give the serializable class + * complete control over the format and contents of the stream for an object + * and its supertypes. + * + * @param out - + * the stream to write the object to + * @throws IOException + * if an I/0 error has occured + * @see java.io.Externalizable + */ + public void writeExternal(ObjectOutput out) throws IOException { + byte[] res = null; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + try { + encode(new DEREncoder(baos)); + res = baos.toByteArray(); + baos.close(); + out.write(res); + } catch (ASN1Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * The writeExternal and readExternal methods of the Externalizable + * interface are implemented by a class to give the serializable class + * complete control over the format and contents of the stream for an object + * and its supertypes. + * + * @param in - + * the stream to read data from in order to restore the + * object + * @throws IOException + * if an I/0 error has occured + * @see java.io.Externalizable + */ + public void readExternal(ObjectInput in) throws IOException { + try { + decode(new DERDecoder((ObjectInputStream) in)); + } catch (ASN1Exception e) { + throw new RuntimeException(e.toString()); + } + } +} diff --git a/src/codec/asn1/ASN1BitString.java b/src/codec/asn1/ASN1BitString.java new file mode 100644 index 00000000..1222e37a --- /dev/null +++ b/src/codec/asn1/ASN1BitString.java @@ -0,0 +1,510 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Represents an ASN.1 BIT STRING type. The corresponding Java type is + * boolean[]. + *

+ * + * This class has to modes of initialization which play a crucial role in + * standards compliant DER encoding. One mode initializes instances of this + * class as a representation of "named bits". The second mode + * initializes it as a plain bitstring. + *

+ * + * An ASN.1 structure with named bits looks e.g., as follows: + * + *

+ * Rights ::= BIT STRING { read(0), write(1), execute(2)}
+ * 
+ * + * Such bitstrings have a special canonical encoding. The mode (and defaults) + * are specified in the documentation of the constructors of this class. + * Basically, in named bits mode, trailing zeroes are truncated from the + * internal representation of the bitstring. + * + * @author Volker Roth + * @version "$Id: ASN1BitString.java,v 1.6 2005/03/23 13:01:20 flautens Exp $" + */ +public class ASN1BitString extends ASN1AbstractType { + + private static final byte[] DEFAULT_VALUE = new byte[0]; + + private static final byte[] MASK = { (byte) 0x80, (byte) 0x40, (byte) 0x20, + (byte) 0x10, (byte) 0x08, (byte) 0x04, (byte) 0x02, (byte) 0x01 }; + + private static final byte[] TRAIL_MASK = { (byte) 0xff, (byte) 0xfe, + (byte) 0xfc, (byte) 0xf8, (byte) 0xf0, (byte) 0xe0, (byte) 0xc0, + (byte) 0x80 }; + + private int pad_ = 0; + + private byte[] value_ = DEFAULT_VALUE; + + /** + * Says whether this instance is a "named bits" bitstring. The + * default is false. + */ + private boolean namedBits_ = false; + + /** + * Initializes an instance for decoding. This initializes this instance to + * be decoded as a plain bitstring (no named bits). Use this for bitstrings + * with named bits as well. It does not make a difference for the + * application, but it ensures that bitstrings that have been decoded are + * encoded in the same way again. + */ + public ASN1BitString() { + } + + /** + * Initializes the instance for encoding with the given bits. The index of + * each bit corresponds to its number in the ASN.1 definition. This + * constructor initializes the instance as a bitstring with named bits. The + * array is not copied into this instance and can be modified subsequently + * without causing side effects. + * + * @param b + * The string of bits to be encoded. + */ + public ASN1BitString(boolean[] b) { + setBits0(b); + } + + /** + * Creates an instance with the given contents. Use of this constructor + * copies the given byte array by reference and may cause side effects. The + * mode of this bitstring is plain (no named bits are assumed). + * + * @param b + * The left aligned contents bits. + * @param pad + * The number of pad bits. + */ + public ASN1BitString(byte[] b, int pad) { + setBits0(b, pad); + } + + /** + * This method calls {@link #getBits getBits()}. + * + * @return The contents bits as a boolean array. + */ + public Object getValue() { + return getBits(); + } + + /** + * Returns the contents bits of this instance. No side effects occur when + * the returned array is modified. + * + * @return The contents bits. + */ + public boolean[] getBits() { + int n, i; + boolean[] b; + + if (value_.length == 0) { + return new boolean[0]; + } + b = new boolean[(value_.length * 8) - pad_]; + + for (n = 0, i = 0; i < b.length; i++) { + if ((value_[n] & MASK[i & 0x07]) != 0) { + b[i] = true; + } else { + b[i] = false; + } + if ((i & 0x07) == 0x07) { + n++; + } + } + return b; + } + + /** + * Sets the contents bits of this instance. This method does not cause side + * effects. It switches to the named bits mode. + * + * @param bits + * The contents bits that are set. + * @throws ConstraintException + * if the given bits violates the specified constraint. + */ + public void setBits(boolean[] bits) throws ConstraintException { + setBits0(bits); + checkConstraints(); + } + + /** + * Sets the contents bits of this instance. This method does not cause side + * effects. It switches to named bits mode. + * + * @param bits + * The contents bits that are set. + */ + protected void setBits0(boolean[] bits) { + namedBits_ = true; + + if ((bits == null) || (bits.length == 0)) { + value_ = DEFAULT_VALUE; + pad_ = 0; + + return; + } + int i; + int j; + int n; + int k; + byte m; + byte[] b; + + /* + * We skip trailing zero bits. Trailing bits are to the "right" in ASN.1 + * terms (bits with high numbers). + */ + for (k = bits.length - 1; k >= 0; k--) { + if (bits[k]) { + break; + } + } + /* + * If all bits are zero then we set the default zero and we are done. + */ + if (k < 0) { + value_ = DEFAULT_VALUE; + pad_ = 0; + + return; + } + k++; + + /* + * If required we truncate trailing bits. This is compliant to the + * X.690/DER encoding rules and saves us trouble in the encoder. + */ + b = new byte[(k + 7) / 8]; + m = 0; + + for (n = 0, i = 0; i < k; i++) { + j = i & 0x07; + + if (bits[i]) { + m = (byte) (m | MASK[j]); + } + if (j == 7) { + b[n++] = m; + m = 0; + } + } + j = i & 0x07; + + if (j != 0) { + b[n] = m; + } + value_ = b; + pad_ = (8 - j) & 0x07; + } + + /** + * Sets the bit string from the given byte aray and pad count. Bit 0 is the + * most significant bit in the first byte of the array and bit n is + * bit 7-(n&0x07) in byte floor(n/8). + * The length of the bit string is b.length*8-pad. The pad + * value be in the range of [0..7]. In other words the bits in the byte + * array are left aligned. + *

+ * + * The given byte array may be copied by reference. Subsequent modification + * of it can cause side effects. The mode is set not to represent named + * bits. + * + * @param b + * The bits encoded into a byte array. + * @param pad + * The number of pad bits after the actual bits in the array. + * @throws IllegalArgumentException + * if the pad value is out of range. + * @throws ConstraintException + * if the given bits violates the + */ + public void setBits(byte[] b, int pad) throws ConstraintException { + setBits0(b, pad); + checkConstraints(); + } + + /** + * Sets the bits and number of trailing pad bits from the given byte array. + * The given instance is copied by reference. Therefor side effects can + * occur if the given byte array is modified subsequently. The + * initialization mode is plain (no named bits). + * + * @param b + * The minimum number of bytes to hold the left aligned + * contents bits. Any unused trailing bits (as defined by the + * pad count) MUST be zero! + * @param p + * The number of trailing padding bits. + */ + protected void setBits0(byte[] b, int p) { + int n; + + if ((p < 0) || (p > 7)) { + throw new IllegalArgumentException("Illegal pad value (" + p + ")"); + } + namedBits_ = false; + + /* + * We first skip all leading zero bytes. + */ + for (n = b.length - 1; n >= 0; n--) { + if (b[n] != 0) { + break; + } + } + /* + * If all bytes are zero then we encode a zero bit string and are done. + */ + if (n < 0) { + if (p != 0) { + throw new IllegalArgumentException( + "Zero length bit strings can't have pad bits!"); + } + value_ = DEFAULT_VALUE; + pad_ = 0; + + return; + } + /* + * We test whether the trailing pad bits are really zeroes. + */ + if ((b[b.length - 1] & ~TRAIL_MASK[p]) != 0) { + throw new IllegalArgumentException( + "trailing pad bits are not zero!"); + } + value_ = b; + pad_ = p; + } + + /** + * Returns the contents octets of this instance. The bits are left aligned + * and the most significant bit is a one (or else the bitstring is zero and + * an empty array is returned). The returned byte array is the one used + * internally. Modifying it causes side effects which may result in + * erroneous encoding. So, don't modify it! + *

+ * + * Please also note that the bits in the bytes are left aligned. In other + * words, the bits are shifted to the left by the amount of pad bits. Bit X + * in the byte array corresponds to the logical bit with the number X minus + * pad count. + * + * @return The bits left aligned in a byte array with no trailing zeroes. + */ + public byte[] getBytes() { + return value_; + } + + /** + * @return The number of unused bits in the last byte of the bitstring's + * byte array representation. This number is always in the range + * zero to seven. + */ + public int getPadCount() { + return pad_; + } + + /** + * @return The number of bytes of the bitstring's byte array representation. + */ + public int byteCount() { + return value_.length; + } + + /** + * Returns the number of bits of the bitstring representation. Bits are + * counted only to the most significant bit that is a one. Trailing zeroes + * are neither present in the internal representation nor are they counted. + * + * @return The number of bits of the bitstring representation. + */ + public int bitCount() { + return (value_.length * 8) - pad_; + } + + /** + * @return true iff this instance has named bits. + */ + public boolean isNamedBits() { + return namedBits_; + } + + /** + * Returns true if the bit string contains no bits that are + * 1. Otherwise, false is returned. This method is used by + * the {@link DEREncoder DEREncoder} in order to determine cases in which + * special encoding is to be used. If no bits of a BIT STRING are 1 then it + * is encoded as 0x03 0x01 0x00 + * even if the BIT STRING has + * hundreds of bits in length. + * + * @return true if all bits are zero. + */ + public boolean isZero() { + return (value_.length == 0); + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public int getTag() { + return ASN1.TAG_BITSTRING; + } + + /** + * DOCUMENT ME! + * + * @param enc + * The encoder which is used to encode + * + * @throws ASN1Exception + * DOCUMENT ME! + * @throws IOException + * if an I/O error occures + */ + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeBitString(this); + } + + /** + * Decodes this instance. After decoding, this method restores the mode + * (named bits vs no named bits) of this instance to the one it had before + * decoding. This is to maintain the original mode while assuring that + * encoded values are identical to decoded ones even if the encoding. + * + * @param dec + * The decoder which is used to decode + * @throws IOException + * if an I/O error occures + */ + public void decode(Decoder dec) throws ASN1Exception, IOException { + boolean tmp; + + tmp = namedBits_; + + try { + dec.readBitString(this); + } finally { + namedBits_ = tmp; + } + + // Redudant, also called by setBits(byte[], int) + // checkConstraints(); + } + + /** + * Returns the string representation of this ASN1BitString. + * + * @return the string representation of this ASN1BitString + */ + public String toString() { + StringBuffer buf; + boolean[] bits; + int i; + + bits = getBits(); + buf = new StringBuffer(12 + bits.length); + + buf.append("BitString '"); + + for (i = 0; i < bits.length; i++) { + if (bits[i]) { + buf.append("1"); + } else { + buf.append("0"); + } + } + buf.append("'"); + + return buf.toString(); + } +} diff --git a/src/codec/asn1/ASN1Boolean.java b/src/codec/asn1/ASN1Boolean.java new file mode 100644 index 00000000..d12305c2 --- /dev/null +++ b/src/codec/asn1/ASN1Boolean.java @@ -0,0 +1,127 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Represents an ASN.1 BOOEAN type. The corresponding Java type is + * boolean. This type does not support constraint checking. + * + * @see Constraint + * @author Volker Roth + * @version "$Id: ASN1Boolean.java,v 1.2 2000/12/06 17:47:23 vroth Exp $" + */ +public class ASN1Boolean extends ASN1AbstractType { + + private boolean value_ = true; + + public ASN1Boolean() { + } + + public ASN1Boolean(boolean t) { + setTrue(t); + } + + public Object getValue() { + return new Boolean(value_); + } + + public boolean isTrue() { + return value_; + } + + public void setTrue(boolean b) { + value_ = b; + } + + public int getTag() { + return ASN1.TAG_BOOLEAN; + } + + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeBoolean(this); + } + + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readBoolean(this); + } + + public String toString() { + return "BOOLEAN " + value_; + } +} diff --git a/src/codec/asn1/ASN1Choice.java b/src/codec/asn1/ASN1Choice.java new file mode 100644 index 00000000..e848b8e0 --- /dev/null +++ b/src/codec/asn1/ASN1Choice.java @@ -0,0 +1,420 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * This type represents the ASN.1 CHOICE type as specified in ITU-T + * Recommendation X.680. On decoding, the decoder must be able to decide + * umambiguously which alternative choice it has to decode. For this reason all + * elements in a CHOICE type must have distinctive tags. + *

+ * + * This class does not enforce the distinctive tag rule. Instead, the + * alternative with the first matching tag should be chosen by decoders. The + * application that builds the CHOICE type must take care not to produce + * ambiguous sets of alternatives. + *

+ * + * This class distinguishes alternative choices and an inner type. Upon + * decoding, the inner type is selected from the list of choices based on the + * identifier octets encountered in the encoded stream. This type is then + * {@link #setInnerType set as the inner type} of this instance. Unless an inner + * type is set (either explicitly or by means of decoding) the state of the + * choice is undefined. + *

+ * + * This instance always mimicks its inner type. The methods + * {@link ASN1Type#getTag getTag}, {@link ASN1Type#getTagClass getTagClass}, + * {@link ASN1Type#getValue getValue} all return the appropriate results of the + * corresponding method of the inner type. On encoding an instance of this class + * the inner type is encoded. + *

+ * + * No nested CHOICE classes are supported. In principle this is easily supported + * but it is not good style to build such structures. + * + * @author Volker Roth + * @version "$Id: ASN1Choice.java,v 1.3 2004/08/06 15:01:20 flautens Exp $" + */ +public class ASN1Choice extends ASN1AbstractType { + private static final String NO_INNER = "No inner type defined!"; + + private ASN1Type inner_; + + private ArrayList choices_; + + /** + * Creates an instance with an initial capacity of 2. + */ + public ASN1Choice() { + choices_ = new ArrayList(2); + } + + /** + * Creates an instance with the given initial capacity. The capacity + * determines the number of choices to store. This instance is backed by an + * ArrayList, hence the capacity is increased dynamically as required. Use + * the {@link #trimToSize() trimToSize()} method to trim the internal list + * to the number of stored choices in order to reclaim memory. + * + * @param capacity + * The initial capacity for storing choices. + * @throws IllegalArgumentException + * if the capacity is less than 1. + */ + public ASN1Choice(int capacity) { + if (capacity < 1) + throw new IllegalArgumentException( + "capacity must be greater than zero!"); + + choices_ = new ArrayList(capacity); + } + + /** + * Adds the given type as an alternative choice to the collection of + * choices. The caller has to take care that no ambiguous choices are added. + * Each added type must have a distinctive tag. + *

+ * + * CHOICE elements must neither be OPTIONAL nor tagged IMPLICIT. For safety, + * this method calls {@link ASN1Type#setOptional setOptional}(false) and + * {@link ASN1Type#setExplicit setExplicit}(true) on the given type. + * Callers must not alter this setting after adding a type to this choice. + * However, the CHOICE itself can be declared OPTIONAL. + * + * @param t + * The ASN.1 type to add as a choice. + * @throws NullPointerException + * if the given type is null. + * @throws IllegalArgumentException + * if the given type is a ASN1Choice type. + */ + public void addType(ASN1Type t) { + if (t == null) + throw new NullPointerException("Choice is null!"); + + if (t instanceof ASN1Choice) + throw new IllegalArgumentException( + "No nested CHOICE types are allowed!"); + + t.setOptional(false); + t.setExplicit(true); + + choices_.add(t); + } + + /** + * Returns the choice with the given tag and tagclass if it exists, + * otherwise null is returned. This method is called by the + * decoder in order to determine the appropriate type to decode. The + * returned type is set up as the inner type by the decoder. + * + * @param tag + * The tag of the type encountered in the encoded stream. The + * tags of the various primitive ASN.1 types are defined in + * class {@link ASN1 ASN1}. + * @param tagclass + * The tag class of the type encountered in the encoded + * stream. The tag class identifiers are defined in class + * {@link ASN1 ASN1}. See for instance + * {@link ASN1#CLASS_UNIVERSAL CLASS_UNIVERSAL}. + * @return The choice with matching tag and tag class or null + * if no matching choice is found. + */ + public ASN1Type getType(int tag, int tagclass) { + Iterator i; + ASN1Type t; + + for (i = choices_.iterator(); i.hasNext();) { + t = (ASN1Type) i.next(); + if (t.getTag() != tag) + continue; + if (t.getTagClass() == tagclass) + return t; + } + return null; + } + + public boolean isType(int tag, int tagclass) { + if (getType(tag, tagclass) != null) + return true; + + return false; + } + + /** + * Trims the internal list of choices to the actual number of choices stored + * in it. + */ + public void trimToSize() { + choices_.trimToSize(); + } + + /** + * Clears the internal list of choices. The inner type remains unaffected if + * it is already set. + * + * number of choices stored in it. + */ + public void clear() { + choices_.clear(); + } + + /** + * Returns the inner ASN.1 type. + * + * @return The inner ASN.1 type. + */ + public ASN1Type getInnerType() { + return inner_; + } + + /** + * Sets the inner type. + * + * @param t + * The type to set as the inner type. + * @throws NullPointerException + * if the given type is null. + */ + public void setInnerType(ASN1Type t) { + if (t == null) + throw new NullPointerException("No type given!"); + + inner_ = t; + } + + /** + * Returns the tag of the inner type. + * + * @return The tag of the inner type. + * @throws IllegalStateException + * if the inner type is not set. + */ + public int getTag() { + if (inner_ == null) + throw new IllegalStateException(NO_INNER); + + return inner_.getTag(); + } + + /** + * Returns the tag class of the inner type. + * + * @return The tag class of the inner type. + * @throws IllegalStateException + * if the inner type is not set. + */ + public int getTagClass() { + if (inner_ == null) + throw new IllegalStateException(NO_INNER); + + return inner_.getTagClass(); + } + + /** + * Returns the value of the inner type. The default inner type is + * {@link ASN1Null ASN1Null}. This method calls + * {@link ASN1Type#getValue getValue} on the inner type and returns the + * result. + * + * @return The value of the inner type. + * @throws IllegalStateException + * if the inner type is not set. + */ + public Object getValue() { + if (inner_ == null) + throw new IllegalStateException(NO_INNER); + + return inner_.getValue(); + } + + /** + * Sets the tagging of the inner type as either EXPLICIT or IMPLICIT. The + * default is EXPLICIT. Encoders skip the encoding of identifier octets for + * types that are declared as IMPLICIT. + * + * @param explicit + * true if this type shall be tagged EXPLICIT + * and false if it shall be encoded IMPLICIT. + * @throws IllegalStateException + * if the inner type is not set. + */ + public void setExplicit(boolean explicit) { + if (!explicit) + throw new IllegalArgumentException( + "CHOICE types must be tagged EXPLICIT!"); + } + + /** + * Returns the tagging of the inner type. + * + * @return true if the inner type is tagged EXPLICIT and + * false if it is tagged IMPLICIT. + * @throws IllegalStateException + * if the inner type is not set. + */ + public boolean isExplicit() { + return true; + } + + /** + * Sets the {@link Constraint Constraint} of the inner type. For instance an + * ASN.1 INTEGER might be constrained to a certain range such as INTEGER + * (0..99). null can be passed as a constraint which disables + * constraint checking. + * + * @param constraint + * The {@link Constraint Constraint} of this type. + * @throws IllegalStateException + * if the inner type is not set. + */ + public void setConstraint(Constraint constraint) { + if (inner_ == null) + throw new IllegalStateException(NO_INNER); + + inner_.setConstraint(constraint); + } + + /** + * Checks the constraint on the inner type if it is set. Otherwise this + * method returns silently. + * + * @throws ConstraintException + * if this type is not in the appropriate range of values. + * @throws IllegalStateException + * if the inner type is not set. + */ + public void checkConstraints() throws ConstraintException { + if (inner_ == null) + throw new IllegalStateException(NO_INNER); + + inner_.checkConstraints(); + } + + /** + * Encodes this type to the given encoder. Before this method is called, the + * inner type must be set. Otherwise an IllegalStateException is thrown. + *

+ * + * If this method is declared OPTIONAL then still an exception is thrown. + * The OPTIONAL flag is checked only by {@link Encoder encoders} and + * {@link Decoder decoders}. Transparent handling of CHOICE types can be + * achieved by calling {@link Encoder#writeType + * writeType}(ASN1Choice choice) + * on the encoder. The encoder's method checks if its argument is OPTIONAL. + * + * @param enc + * The {@link Encoder Encoder} to use for encoding. + * @throws IllegalStateException + * if the inner type is not set. + */ + public void encode(Encoder enc) throws ASN1Exception, IOException { + if (inner_ == null) + throw new IllegalStateException(NO_INNER); + + enc.writeType(inner_); + } + + /** + * Decodes the inner type to the given {@link Decoder decoder}. + * + * @param dec + * The decoder to decode to. + * @throws IllegalStateException + * if the open type cannot be resolved on runtime. + */ + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readChoice(this); + } + + /** + * Returns a string representation of this type. + * + * @return The string representation. + */ + public String toString() { + if (inner_ == null) + return "CHOICE "; + + return "(CHOICE) " + inner_.toString(); + } +} diff --git a/src/codec/asn1/ASN1Collection.java b/src/codec/asn1/ASN1Collection.java new file mode 100644 index 00000000..37f60aca --- /dev/null +++ b/src/codec/asn1/ASN1Collection.java @@ -0,0 +1,98 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.util.Collection; + +/** + * The basic interface for Java objects representing a constructed ASN.1 type + * such as a SEQUENCE or SET as specified in ITU-T Recommendation X.680. + * + * @author Volker Roth + * @version "$Id: ASN1Collection.java,v 1.2 2000/12/06 17:47:23 vroth Exp $" + */ +public interface ASN1Collection extends ASN1Type, Collection { + + /** + * Returns the Java Collection that is used to maintain the embedded ASN.1 + * types. + * + * @return The collection used internally for storing the elements in this + * constructed ASN.1 type. + */ + public Collection getCollection(); +} diff --git a/src/codec/asn1/ASN1CollectionOf.java b/src/codec/asn1/ASN1CollectionOf.java new file mode 100644 index 00000000..2ee1e58a --- /dev/null +++ b/src/codec/asn1/ASN1CollectionOf.java @@ -0,0 +1,116 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * The basic interface for Java objects representing a constructed ASN.1 type + * such as a SEQUENCE or SET as specified in ITU-T Recommendation X.680. + * + * @author Volker Roth + * @version "$Id: ASN1CollectionOf.java,v 1.2 2000/12/06 17:47:24 vroth Exp $" + */ +public interface ASN1CollectionOf extends ASN1Collection { + + /** + * Returns the Java class representing the ASN.1 type of the elements in + * this collection. + * + * @return The ASN.1 type of the elements in this collection. + */ + public Class getElementType(); + + /** + * Creates and returns a new instance of the class passed to the constructor + * of this instance. The freshly created instance is added to this instance + * automatically. + *

+ * + * If no new instance can be created then an IllegalStateException is + * thrown. + *

+ * + * {@link Decoder Decoders} should call this method in order to create + * additional elements on decoding. Subclasses may use this method to + * keep track on elements added to them. + * + * @return A new instance of the element type of this sequence. + * @throws IllegalStateException + * if no new instance could be created. + */ + public ASN1Type newElement(); + +} diff --git a/src/codec/asn1/ASN1Exception.java b/src/codec/asn1/ASN1Exception.java new file mode 100644 index 00000000..e809242c --- /dev/null +++ b/src/codec/asn1/ASN1Exception.java @@ -0,0 +1,93 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * + * + * @author Volker Roth + * @version "$Id: ASN1Exception.java,v 1.2 2000/12/06 17:47:24 vroth Exp $" + */ +public class ASN1Exception extends Exception { + public ASN1Exception() { + } + + public ASN1Exception(String message) { + super(message); + } + +} diff --git a/src/codec/asn1/ASN1IA5String.java b/src/codec/asn1/ASN1IA5String.java new file mode 100644 index 00000000..025e24c4 --- /dev/null +++ b/src/codec/asn1/ASN1IA5String.java @@ -0,0 +1,120 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * This class represents an ASN.1 IA5String as described in ITU-T Recommendation + * X.680. + * + * @author Volker Roth + * @version "$Id: ASN1IA5String.java,v 1.3 2004/08/06 15:04:44 flautens Exp $" + */ +public class ASN1IA5String extends ASN1AbstractString { + + /** + * Constructor declaration. + * + */ + public ASN1IA5String() { + super(); + } + + /** + * Creates an instance with the given string value. No constraints can be + * set yet so none are checked. + * + * @param s + * The string value. + */ + public ASN1IA5String(String s) { + super(s); + } + + /** + * Method declaration. + * + * + * @return The tag value + */ + public int getTag() { + return ASN1.TAG_IA5STRING; + } + +} + +/*--- formatting done in "SeMoA Java Convention" style on 05-17-2000 ---*/ + diff --git a/src/codec/asn1/ASN1Integer.java b/src/codec/asn1/ASN1Integer.java new file mode 100644 index 00000000..26134110 --- /dev/null +++ b/src/codec/asn1/ASN1Integer.java @@ -0,0 +1,248 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * Represents an ASN.1 INTEGER type. The corresponding Java type is + * java.math.BigInteger. + * + * @author Volker Roth + * @version "$Id: ASN1Integer.java,v 1.2 2000/12/06 17:47:24 vroth Exp $" + */ +public class ASN1Integer extends ASN1AbstractType { + /** + * Holds the octets per integer count. This value is computed by means of + * static initializer code below. + */ + private static int opi_; + + /** + * The value of this ASN.1 INTEGER. + */ + private BigInteger value_; + + /* + * Initializes the opi_ field above. + */ + static { + int n; + int i; + + for (n = 1, i = 0; n != 0; n = n << 8) { + i++; + } + opi_ = i; + } + + /** + * Creates a new instance ready for parsing. The value of this instance is + * set to 0. + */ + public ASN1Integer() { + value_ = BigInteger.ZERO; + } + + /** + * Creates a new instance with the given BigInteger as its initial value. + * + * @param val + * The value. + */ + public ASN1Integer(BigInteger val) { + if (val == null) + throw new NullPointerException("Need a number!"); + + value_ = val; + } + + /** + * Creates an ASN.1 INTEGER from the given string representation. + * + * This method calls the equivalent constructor of class + * {@link java.math.BigInteger java.math.BigInteger}. + * + * @param val + * The string representation of the multiple precision + * integer. + * @throws NumberFormatException + * if the string could not be parsed successfully. + */ + public ASN1Integer(String val) throws NumberFormatException { + value_ = new BigInteger(val); + } + + /** + * Creates a new instance from the given byte array. The byte array contains + * the two's-complement binary representation of a BigInteger. The input + * array is assumed to be in big endian byte-order. The most + * significant byte is in the zeroth element. + * + * This method calls the equivalent constructor of class + * {@link java.math.BigInteger java.math.BigInteger}. + * + * @param val + * The two's-complement input number in big endian + * byte-order. + * @throws NumberFormatException + * if val is zero bytes long. + */ + public ASN1Integer(byte[] val) throws NumberFormatException { + value_ = new BigInteger(val); + } + + /** + * Translates the sign-magnitude representation of a BigInteger into an + * ASN.1 INTEGER. The sign is represented as an integer signum value: -1 for + * negative, 0 for zero, or 1 for positive. The magnitude is a byte array in + * big-endian byte-order: the most significant byte is in the zeroth + * element. A zero-length magnitude array is permissible, and will result in + * in a BigInteger value of 0, whether signum is -1, 0 or 1. + *

+ * + * This method calls the equivalent constructor of class + * {@link java.math.BigInteger java.math.BigInteger}. + * + * @param signum + * signum of the number (-1 for negative, 0 for zero, 1 for + * positive). + * @param magnitude + * The big endian binary representation of the magnitude of + * the number. + * @throws NumberFormatException + * signum is not one of the three legal values (-1, 0, and + * 1), or signum is 0 and magnitude contains one or more + * non-zero bytes. + */ + public ASN1Integer(int signum, byte[] magnitude) + throws NumberFormatException { + value_ = new BigInteger(signum, magnitude); + } + + /** + * Creates an instance with the given int value. + * + * @param n + * The integer to initialize with. + */ + public ASN1Integer(int n) { + byte[] b; + int i; + int m; + + b = new byte[opi_]; + m = n; + + for (i = opi_ - 1; i >= 0; i--) { + b[i] = (byte) (m & 0xff); + m = m >>> 8; + } + value_ = new BigInteger(b); + } + + public Object getValue() { + return value_; + } + + public BigInteger getBigInteger() { + return value_; + } + + public void setBigInteger(BigInteger n) throws ConstraintException { + value_ = n; + checkConstraints(); + } + + public int getTag() { + return ASN1.TAG_INTEGER; + } + + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeInteger(this); + } + + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readInteger(this); + checkConstraints(); + } + + public String toString() { + return "Integer " + value_.toString(); + } +} diff --git a/src/codec/asn1/ASN1Null.java b/src/codec/asn1/ASN1Null.java new file mode 100644 index 00000000..10cfd1a6 --- /dev/null +++ b/src/codec/asn1/ASN1Null.java @@ -0,0 +1,108 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Represents the ASN.1 NULL type. + * + * @author Volker Roth + * @version "$Id: ASN1Null.java,v 1.2 2000/12/06 17:47:24 vroth Exp $" + */ +public class ASN1Null extends ASN1AbstractType implements Cloneable { + + public Object getValue() { + return null; + } + + public int getTag() { + return ASN1.TAG_NULL; + } + + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeNull(this); + } + + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readNull(this); + } + + public String toString() { + return "NULL"; + } +} diff --git a/src/codec/asn1/ASN1ObjectIdentifier.java b/src/codec/asn1/ASN1ObjectIdentifier.java new file mode 100644 index 00000000..94644b4c --- /dev/null +++ b/src/codec/asn1/ASN1ObjectIdentifier.java @@ -0,0 +1,436 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; +import java.util.StringTokenizer; + +/** + * Represents an ASN.1 OBJECT IDENTIFIER type. The corresponding Java type is + * int[]. Constraints are checked for this type only at the end + * of method {@link #decode decode}. + * + * @author Volker Roth + * @version "$Id: ASN1ObjectIdentifier.java,v 1.4 2005/03/22 14:57:58 flautens + * Exp $" + */ +public class ASN1ObjectIdentifier extends ASN1AbstractType implements + Cloneable, Comparable { + /** + * holds the int[] representation of the OID + */ + private int[] value_ = new int[2]; + + /** + * Creates a new ASN1ObjectIdentifier object. + */ + public ASN1ObjectIdentifier() { + super(); + } + + /** + * Creates an instance with the given array of integers as elements. No + * constraints are checked by this constructor. + * + * @param oid + * The array of consecutive integers of the OID. + * @throws NullPointerException + * if the given oid + * is null. + * @throws IllegalArgumentException + * if the given + * oid is not well-formed. For + * instance, a bad oid might have a value + * greater than 2 as its first element. + */ + public ASN1ObjectIdentifier(int[] oid) { + set0(oid); + } + + /** + * Creates an ASN.1 OBJECT IDENTIFIER instance initialized from the given + * OID string representation. The format must be either OID.1.2.3.4, + * oid.1.2.3.4, or 1.2.3.4 for the initiliser to work properly. Trailing + * dots are ignored. + * + * @param s + * string representation of oid + * @throws NumberFormatException + * if some element of the OID string is not an integer + * number. + * @throws IllegalArgumentException + * if the string is not a well-formed OID. + */ + public ASN1ObjectIdentifier(String s) throws NumberFormatException { + int n; + int[] oid; + String t; + StringTokenizer tok; + + oid = new int[16]; + + if (s.startsWith("OID.") || s.startsWith("oid.")) { + s = s.substring(4); + } + + tok = new StringTokenizer(s, "."); + + if (tok.countTokens() >= oid.length) { + throw new IllegalArgumentException("OID has too many elements!"); + } + + n = 0; + + while (tok.hasMoreTokens()) { + t = tok.nextToken(); + oid[n++] = Integer.parseInt(t); + } + + int[] buf = new int[n]; + System.arraycopy(oid, 0, buf, 0, n); + set0(buf); + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public Object getValue() { + return value_.clone(); + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public int[] getOID() { + return (int[]) value_.clone(); + } + + /** + * DOCUMENT ME! + * + * @param oid + * DOCUMENT ME! + * + * @throws ConstraintException + * DOCUMENT ME! + */ + public void setOID(int[] oid) throws ConstraintException { + set0(oid); + checkConstraints(); + } + + /** + * Checks if the given oid is valid and stores the oid. If the oid is not + * valid the method throws an IllegalArgumentException. + * + * @param oid + * The oid to be checked + */ + private void set0(int[] oid) { + int n; + + if (oid == null) { + throw new NullPointerException("Need an OID!"); + } + + n = oid.length; + + if (n < 2) { + throw new IllegalArgumentException( + "OID must have at least 2 elements!"); + } + + if ((oid[0] < 0) || (oid[0] > 2)) { + throw new IllegalArgumentException("OID[0] must be 0, 1, or 2!"); + } + + if ((oid[1] < 0) || (oid[1] > 39)) { + throw new IllegalArgumentException( + "OID[1] must be in the range 0,..,39!"); + } + + value_ = new int[n]; + System.arraycopy(oid, 0, value_, 0, n); + } + + /** + * Returns the number of elements of the oid. + * + * @return the number of elements + */ + public int elementCount() { + return value_.length; + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public int getTag() { + return ASN1.TAG_OID; + } + + /** + * Encodes this ASN1ObjectIdentifier. + * + * @param enc + * The encoder to encode to. + * + * @throws ASN1Exception + * @throws IOException + */ + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeObjectIdentifier(this); + } + + /** + * Decodes to this ASN1ObjectIdentifier. + * + * @param dec + * DOCUMENT ME! + * + * @throws ASN1Exception + * @throws IOException + */ + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readObjectIdentifier(this); + checkConstraints(); + } + + /** + * Returns the string representation of this OID. The string consists of the + * numerical elements of the OID separated by periods. + * + * @return The string representation of the OID. + */ + public String toString() { + StringBuffer buf; + int i; + + buf = new StringBuffer(); + + for (i = 0; i < value_.length; i++) { + buf.append(value_[i] + "."); + } + + if (value_.length > 0) { + buf.setLength(buf.length() - 1); + } + + return buf.toString(); + } + + /** + * Compares two OIDs for equality. Two OIDs are equal if the have the same + * number of elements and all corresponding elements are equal. + * + * @param o + * The object to compare to. + * @return true iff the given object is an + * ASN1ObjectIdentifier and iff it equals this one. + */ + public boolean equals(Object o) { + int i; + ASN1ObjectIdentifier oid; + + if (!(o instanceof ASN1ObjectIdentifier)) { + return false; + } + + oid = (ASN1ObjectIdentifier) o; + if (oid.value_.length != value_.length) { + return false; + } + + for (i = 0; i < value_.length; i++) { + if (value_[i] != oid.value_[i]) { + return false; + } + } + + return true; + } + + /** + * This method computes the hash code of this instance. The hash code of + * this instance is defined as a hash function of the underlying integer + * array. + * + * @return the hash code of this instance. + */ + public int hashCode() { + int i; + int h; + + h = 23; + for (i = 0; i < value_.length; i++) { + h = (h * 7) + value_[i]; + } + + return h; + } + + /** + * This method compares two OID and returns -1, 0, 1 if this OID is less + * than, equal or greater than the given one. OID are interpreted as strings + * of numbers. An OID that is a prefix of another is always smaller than the + * other. + * + * @param o + * The OID to compare to. + * @return -1, 0, 1 if this OID is smaller than, equal to, or greater than + * the given OID. + * @throws ClassCastException + * iff o is not an ASN1ObjectIdentifier. + */ + public int compareTo(Object o) { + int n; + int i; + int[] oid; + + oid = ((ASN1ObjectIdentifier) o).value_; + + n = Math.min(value_.length, oid.length); + for (i = 0; i < n; i++) { + if (value_[i] < oid[i]) { + return -1; + } else if (value_[i] > oid[i]) { + return 1; + } + } + if (value_.length > n) { + return 1; + } + + if (oid.length > n) { + return -1; + } + + return 0; + } + + /** + * This method determines whether the given OID is part of the OID family + * defined by this OID prefix. In other words, this method returns + * true if this OID is a prefix of the given one. + * + * @param o + * the oid to check + * @return true if this OID is a prefix of the given one. + */ + public boolean isPrefixOf(ASN1ObjectIdentifier o) { + int i; + + i = value_.length; + if (o.value_.length < i) { + return false; + } + + while (i > 0) { + i--; + if (value_[i] != o.value_[i]) { + return false; + } + } + return true; + } + + /** + * Returns a clone of this instance. This method is not thread safe. The + * constraints are copied by reference. + * + * @return The clone. + */ + public Object clone() { + int[] m; + ASN1ObjectIdentifier oid; + + oid = new ASN1ObjectIdentifier(); + m = new int[value_.length]; + System.arraycopy(value_, 0, m, 0, m.length); + oid.value_ = m; + + oid.setConstraint(getConstraint()); + + return oid; + } +} diff --git a/src/codec/asn1/ASN1OctetString.java b/src/codec/asn1/ASN1OctetString.java new file mode 100644 index 00000000..55705ec3 --- /dev/null +++ b/src/codec/asn1/ASN1OctetString.java @@ -0,0 +1,211 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Represents an ASN.1 OCTET STRING type. The corresponding Java type is + * byte[]. + * + * @author Volker Roth + * @version "$Id: ASN1OctetString.java,v 1.3 2001/01/21 13:46:55 vroth Exp $" + */ +public class ASN1OctetString extends ASN1AbstractType { + private static final byte[] DEFAULT_VALUE = new byte[0]; + + private byte[] value_ = DEFAULT_VALUE; + + public ASN1OctetString() { + } + + /** + * Creates an instance with side effects. The given array is copied by + * reference. + * + * @param b + * The byte array that is set as contents. + */ + public ASN1OctetString(byte[] b) { + setByteArray0(b); + } + + public Object getValue() { + return value_; + } + + /** + * Returns the contents octets as a byte array. The returned byte array is + * is the instance used internally. Do not modify it, otherwise side effects + * occur. + * + * @return The contents octets as a byte array. + */ + public byte[] getByteArray() { + return (byte[]) value_.clone(); + } + + /** + * Sets the given bytes. The given byte array is copied by reference. Be + * careful, side effects can occur if the array is modified subsequent to + * calling this method. Constraints are checked after setting the bytes. + * + * @param b + * The byte array that is set. + * @throws ConstraintException + * if the constraint is not met by the given byte array. + */ + public void setByteArray(byte[] b) throws ConstraintException { + setByteArray0(b); + checkConstraints(); + } + + /** + * Sets the given bytes. The given byte array is copied by reference. Be + * careful, side effects can occur if the array is modified subsequent to + * calling this method. + * + * @param b + * The byte array that is set. + */ + private void setByteArray0(byte[] b) { + if (b == null) + value_ = DEFAULT_VALUE; + else + value_ = b; + } + + public int byteCount() { + return value_.length; + } + + public int getTag() { + return ASN1.TAG_OCTETSTRING; + } + + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeOctetString(this); + } + + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readOctetString(this); + checkConstraints(); + } + + public String toString() { + StringBuffer buf; + String octet; + int i; + + buf = new StringBuffer("Octet String"); + + for (i = 0; i < value_.length; i++) { + octet = Integer.toHexString(value_[i] & 0xff); + + buf.append(' '); + + if (octet.length() == 1) { + buf.append('0'); + } + buf.append(octet); + } + return buf.toString(); + } + + /** + * Returns a clone. The clone is a deep copy of this instance with the + * exception of constraints. Constraints are copied by reference. + * + * @return The clone. + */ + public Object clone() { + ASN1OctetString o; + + try { + o = (ASN1OctetString) super.clone(); + } catch (CloneNotSupportedException e) { + /* + * This cannot, cannot, cannot, CANNOT happen ! If it does, put back + * the import statement for 'Cloneable' into ASN1AbstractType ! + */ + throw new Error("Internal, clone support mismatch!"); + } + o.value_ = (byte[]) value_.clone(); + + return o; + } +} diff --git a/src/codec/asn1/ASN1Opaque.java b/src/codec/asn1/ASN1Opaque.java new file mode 100644 index 00000000..13e7ac7b --- /dev/null +++ b/src/codec/asn1/ASN1Opaque.java @@ -0,0 +1,266 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Represents an opaque type. An opaque type merely decodes the tag and tag + * class and stores the contents octets in an OCTET STRING. The opaque type is + * represented in ASN.1 as

+ * [UNIVERSAL x] IMPLICIT OCTET STRING + *
+ * where x is the tag. + *

+ * + * The opaque type is comparable to an {@link ASN1OpenType open type} in that it + * matches any type (just like the deprecated ANY type) on decoding. The + * encoding can be reconstructed easily. This type is used whenever decoding of + * a structure should be deferred to a later point in time. For instance an + * AlgorithmIdentifier implementation can use an opaque type in order to decode + * algorithm parameters. The encoding of the algorithm parameters is then done + * by JCA/JCE classes later on. + *

+ * + * One drawback of the opaque type is that special handling by the encoders and + * decoders is rquired to make it work properly. The main problem is that the + * opaque type does not store whether the underlying type is constructed or + * primitive. This decision must be made by the encoder. + *

+ * + * Due to this limitation the opaque type can be used only for decoding types of + * class UNIVERSAL. + * + * @author Volker Roth + * @version "$Id: ASN1Opaque.java,v 1.2 2000/12/06 17:47:25 vroth Exp $" + */ +public class ASN1Opaque extends ASN1TaggedType { + /** + * Creates an instance. On decoding, opaque types pretend to be of a + * particular type and read the actual type's encoding into an OCTET STRING + * from which it can be retrieved later. + * + */ + public ASN1Opaque() { + super(-1, ASN1.CLASS_UNIVERSAL, new ASN1OctetString(), false); + } + + /** + * Creates an instance that stores the given encoding. The encoding must be + * a valid DER encoding as specified in X.690. This constructor uses a + * {@link DERDecoder DERDecoder} in order to decode the identifier octets in + * the given encoding. + *

+ * + * Note: If the given encoding contains the concatenation of + * multiple encodings then only the first one will be stored. All others + * will be lost. + * + * @throws ASN1Exception + * if the given code cannot be decoded. + */ + public ASN1Opaque(byte[] code) throws ASN1Exception { + super(-1, ASN1.CLASS_UNIVERSAL, new ASN1OctetString(), false); + + ByteArrayInputStream bis; + DERDecoder dec; + + try { + bis = new ByteArrayInputStream(code); + dec = new DERDecoder(bis); + decode(dec); + dec.close(); + } catch (IOException e) { + throw new ASN1Exception("Internal, caught IOException!"); + } + } + + /** + * Creates an instance with the given type, class, and inner type. Be + * careful, the given octet string must contain the valid DER encoding + * of the contents octets of a type that matches the tag and tag class. + * Otherwise coding exceptions are most probably thrown subsequently. + * + * @param tag + * The ASN.1 tag of the opaque type. + * @param tagclass + * The tag class of the opaque type. + * @param b + * The DER compliant encoding of the contents octets of the + * opaque type. + * @throws NullPointerException + * if the given byte array is null. + */ + public ASN1Opaque(int tag, int tagclass, byte[] b) { + super(tag, tagclass, new ASN1OctetString((byte[]) b.clone()), false); + } + + /** + * This method adopts the given tag and tag class if this instance is not + * yet initialized with a tag or tag class. In that case true + * is returned. + *

+ * + * If a tag or tag class is already set then this method calls its super + * method. + * + * @param tag + * The tag to compare with. + * @param tagclass + * The tag class to compare with. + */ + public boolean isType(int tag, int tagclass) { + if (tagclass != ASN1.CLASS_UNIVERSAL) + return false; + + if (getTag() == -1) { + setTag(tag); + return true; + } + return super.isType(tag, tagclass); + } + + /** + * This method is a convenience method in order to encode this type with + * DER. It uses a {@link DEREncoder DEREncoder} in order to encode this type + * to a byte array which is returned. + *

+ * + * @return The DER encoding of this type. + */ + public byte[] getEncoded() throws ASN1Exception { + ByteArrayOutputStream bos; + DEREncoder enc; + byte[] code; + + try { + bos = new ByteArrayOutputStream(); + enc = new DEREncoder(bos); + encode(enc); + code = bos.toByteArray(); + enc.close(); + + return code; + } catch (IOException e) { + throw new ASN1Exception("Internal, caught IOException!"); + } + } + + /** + * Sets the inner type of this opaque type. The given type must be + * {@link ASN1OctetString ASN1OctetString} or a ClassCastException is + * thrown. + * + * @param t + * The type to set as the inner type. + * @throws NullPointerException + * if the given type is null. + * @throws ClassCastException + * if the given type is not an ASN1OctetString. + */ + public void setInnerType(ASN1Type t) { + super.setInnerType(t); + } + + /** + * Returns a clone. The clone is a deep copy of this instance except the + * constraints. Constraints are copied by reference. + * + * @return The clone. + */ + public Object clone() { + ASN1OctetString b; + ASN1Opaque o; + + try { + o = (ASN1Opaque) super.clone(); + b = (ASN1OctetString) o.getInnerType(); + + o.setInnerType((ASN1OctetString) b.clone()); + } catch (CloneNotSupportedException e) { + /* + * This cannot, cannot, cannot, CANNOT happen ! If it does, put back + * the import statement for 'Cloneable' into ASN1AbstractType ! + */ + throw new Error("Internal, clone support mismatch!"); + } + return o; + } + +} diff --git a/src/codec/asn1/ASN1OpenType.java b/src/codec/asn1/ASN1OpenType.java new file mode 100644 index 00000000..30cc8df0 --- /dev/null +++ b/src/codec/asn1/ASN1OpenType.java @@ -0,0 +1,385 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * This type represents what was formerly called the ASN.1 ANY type. The ANY and + * ANY DEFINED BY types are superseded as of ITU-T Recommendation X.680 version + * current December 1997 by the ability to define type classes. Modelling type + * classes is beyond the scope of this ASN.1 package although the package can be + * enhanced accordingly. ASN.1 type classes can contain components whose type is + * unspecified. Such components are called "open types". This class + * mimics an open type insofar as it decodes any type encountered in an encoded + * stream of ASN.1 types. On encoding the proper type is encoded in place of the + * open type. Decoding an open type that was not properly initialized either by + * a call to a creator with an argument or by decoding it from a valid ASN.1 + * encoding results in an {@link ASN1Null ASN1Null} being decoded. + *

+ * + * This class enforces as an invariant that inner types have the same tagging as + * the type itself. For instance:

+ * + *
+ * ASN1OpenType ot;
+ * ASN1Integer n;
+ * n = new Integer("42");
+ * n.setExplicit(true);
+ * ot = new OpenType(new FooResolver());
+ * ot.setExplicit(false);
+ * ot.setInnerType(n);
+ * 
+ * + *
will cause the tagging method of n to be changed + * into EXPLICIT upon the call to ot.setInnerType(). + * + * @author Volker Roth + * @version "$Id: ASN1OpenType.java,v 1.4 2004/08/06 15:13:01 flautens Exp $" + */ +public class ASN1OpenType extends ASN1AbstractType { + private static final String NO_INNER = "No inner type defined!"; + + private ASN1Type inner_; + + protected Resolver resolver_; + + public ASN1OpenType() { + super(); + } + + /** + * Creates an instance that attempts to resolve the actual type on decoding + * using the given {@link Resolver Resolver}. + * + * @param resolver + * The instance that is asked to deliver the type to decode. + */ + public ASN1OpenType(Resolver resolver) { + resolver_ = resolver; + } + + /** + * This constructor corresponds to the superseded ANY DEFINED BY type. The + * open type attempts to resolve the type to decode right before decoding by + * a call to the given registry with the given OID as the argument. The + * exact OID instance is used that is passed to this method as the argument. + * If this instance is decoded before the open type is decoded (because the + * OID is encountered earlier in a decoded stream) then the open type can + * determine the exact type to decode by a call to the registry. + * + * @param oid + * The OID that is passed to the given registry on resolving. + */ + public ASN1OpenType(OIDRegistry registry, ASN1ObjectIdentifier oid) { + resolver_ = new DefinedByResolver(registry, oid); + } + + public ASN1OpenType(ASN1ObjectIdentifier oid) { + resolver_ = new DefinedByResolver(oid); + } + + /** + * Returns the inner ASN.1 type. If the inner type is not set and a + * {@link Resolver Resolver} is set then the Resolver is asked to resolve + * the inner type. The resulting type is then returned. + *

+ * + * This method may return null if the resolver cannot + * determine the inner type of the open type. In particular, if the Resolver + * is null and no inner type is already set then + * null is returned. + * + * @return The inner ASN.1 type. + */ + public ASN1Type getInnerType() throws ResolverException { + if (inner_ != null) { + return inner_; + } + if (resolver_ == null) { + return null; + } + inner_ = resolver_.resolve(this); + + return inner_; + } + + /** + * Sets the inner type. The inner type inherits the tagging of this type. + * + * @param t + * The type to set as the inner type. + * @throws NullPointerException + * if the given type is null. + */ + protected void setInnerType(ASN1Type t) { + inner_ = t; + inner_.setExplicit(isExplicit()); + } + + /** + * Returns the tag of the inner type. + * + * @return The tag of the inner type. + * @throws IllegalStateException + * if the inner type is not yet initialized. + */ + public int getTag() { + if (inner_ == null) { + throw new IllegalStateException(NO_INNER); + } + return inner_.getTag(); + } + + /** + * Returns the tag class of the inner type. + * + * @return The tag class of the inner type. + * @throws IllegalStateException + * if the inner type is not yet initialized. + */ + public int getTagClass() { + if (inner_ == null) { + throw new IllegalStateException(NO_INNER); + } + return inner_.getTagClass(); + } + + /** + * Returns the value of the inner type. The default inner type is + * {@link ASN1Null ASN1Null}. This method calls + * {@link ASN1Type#getValue getValue} on the inner type and returns the + * result. + * + * @return The value of the inner type. + * @throws IllegalStateException + * if the inner type is not yet initialized. + */ + public Object getValue() { + if (inner_ == null) { + throw new IllegalStateException(NO_INNER); + } + return inner_.getValue(); + } + + /** + * Sets the tagging to either EXPLICIT or IMPLICIT. If this type already has + * an inner type set then the tagging of the inner type is set to the same + * tagging. + * + * @param explicit + * true if this type shall be tagged EXPLICIT + * and false if it shall be encoded IMPLICIT. + * @throws IllegalStateException + * if the inner type is not yet initialized. + */ + public void setExplicit(boolean explicit) { + super.setExplicit(explicit); + + if (inner_ != null) { + inner_.setExplicit(explicit); + } + } + + /** + * Sets the {@link Constraint Constraint} of the inner type. For instance an + * ASN.1 INTEGER might be constrained to a certain range such as INTEGER + * (0..99). null can be passed as a constraint which disables + * constraint checking. + * + * @param constraint + * The {@link Constraint Constraint} of this type. + * @throws IllegalStateException + * if the inner type is not yet initialized. + */ + public void setConstraint(Constraint constraint) { + if (inner_ == null) { + throw new IllegalStateException(NO_INNER); + } + inner_.setConstraint(constraint); + } + + /** + * Checks the constraint on the inner type if it is set. Otherwise this + * method returns silently. + * + * @throws ConstraintException + * if this type is not in the appropriate range of values. + * @throws IllegalStateException + * if the inner type is not yet initialized. + */ + public void checkConstraints() throws ConstraintException { + if (inner_ == null) { + throw new IllegalStateException(NO_INNER); + } + inner_.checkConstraints(); + } + + /** + * This method compares the given tag and tag class with the tag and tag + * class of the resolved type. + *

+ * If an exception is thrown by the {@link Resolver Resolver} upon resolving + * the inner type of this type then false is returned in + * order to provoke a decoding error. + *

+ * + * If no inner type can be resolved then true + * is returned. In + * that case this type behaves like the ANY type known from previous ASN.1 + * versions. + * + * @param tag + * The tag to match. + * @param tagclass + * The tag class to match. + * @return true iff the given tag and tag class match one of + * the alternative types represented by this variable type. + */ + public boolean isType(int tag, int tagclass) { + if (inner_ != null) { + return inner_.isType(tag, tagclass); + } + try { + if (resolver_ != null) { + inner_ = resolver_.resolve(this); + } + } catch (ResolverException e) { + return false; + } + + /* + * If no inner type could be resolved then we behave like the ANY type. + */ + if (inner_ == null) { + return true; + } + return inner_.isType(tag, tagclass); + } + + /** + * Encodes the inner typeof this open type using the given + * {@link Encoder Encoder}. If the inner type is not yet initialized then + * an exception is thrown. + * + * @param enc + * The {@link Encoder Encoder} to use for encoding the inner + * type. + * @throws IllegalStateException + * if the inner type is not yet initialized. + */ + public void encode(Encoder enc) throws ASN1Exception, IOException { + if (inner_ == null) { + throw new IllegalStateException(NO_INNER); + } + enc.writeType(inner_); + } + + /** + * Decodes the inner type to the given {@link Decoder decoder}. If a + * {@link Resolver resolver} was specified then it is asked to provide an + * ASN.1 type to decode. + * + * @param dec + * The decoder to decode to. + * @throws IllegalStateException + * if the open type cannot be resolved on runtime. + */ + public void decode(Decoder dec) throws ASN1Exception, IOException { + if (resolver_ != null && inner_ == null) { + inner_ = resolver_.resolve(this); + } + if (inner_ == null) { + inner_ = dec.readType(); + } else { + inner_.decode(dec); + } + inner_.setExplicit(isExplicit()); + } + + /** + * Returns the string representation of this instance. + * + * @return The string representation of this instance. + */ + public String toString() { + if (inner_ == null) { + return "Open Type "; + } + return "(Open Type) " + inner_.toString(); + } +} diff --git a/src/codec/asn1/ASN1Permission.java b/src/codec/asn1/ASN1Permission.java new file mode 100644 index 00000000..9f86eb52 --- /dev/null +++ b/src/codec/asn1/ASN1Permission.java @@ -0,0 +1,122 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.security.BasicPermission; + +/** + * This permission is for controlling access to functionality of the ASN.1 + * package. In particular, registering and unregistering {@link OIDRegistry + * ASN.1 Object Identifier Registries} is protected using this permission. + * Malicious code may not access the global OIDRegistries, nor register new ones + * or remove registered ones. + *

+ * + * This permission is a simple named permission. The names distinguished are: + *

    + *
  • OIDRegistry.add + *
  • OIDRegistry.remove + *
+ * + * @author Volker Roth + * @version "$Id: ASN1Permission.java,v 1.2 2000/12/06 17:47:25 vroth Exp $" + */ +public class ASN1Permission extends BasicPermission { + + /** + * Creates an instance with the given name and actions list. + * + * @param name + * The name of the permission. + * @param actions + * The actions (not used). + */ + public ASN1Permission(String name, String actions) { + super(name, actions); + } + + /** + * Creates an instance with the given name and actions list. + * + * @param name + * The name of the permission. + */ + public ASN1Permission(String name) { + super(name); + } + +} diff --git a/src/codec/asn1/ASN1RegisteredType.java b/src/codec/asn1/ASN1RegisteredType.java new file mode 100644 index 00000000..5aa8d614 --- /dev/null +++ b/src/codec/asn1/ASN1RegisteredType.java @@ -0,0 +1,94 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * This class represents an ASN.1 type that is officially registered. In other + * words, this type is associated with a unique OID. + * + * @author Volker Roth + * @version "$Id: ASN1RegisteredType.java,v 1.3 2004/08/06 15:14:52 flautens Exp $" + */ +public interface ASN1RegisteredType extends ASN1Type { + /** + * This method returns the official OID that identifies this ASN.1 type. + * + * @return The official ASN.1 OID of this type. + */ + public ASN1ObjectIdentifier getOID(); + +} diff --git a/src/codec/asn1/ASN1Sequence.java b/src/codec/asn1/ASN1Sequence.java new file mode 100644 index 00000000..7cf9851d --- /dev/null +++ b/src/codec/asn1/ASN1Sequence.java @@ -0,0 +1,110 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * Represents an ASN.1 SEQUENCE type as specified in ITU-T Recommendation X.680. + * + * @author Volker Roth + * @version "$Id: ASN1Sequence.java,v 1.2 2000/12/06 17:47:25 vroth Exp $" + */ +public class ASN1Sequence extends ASN1AbstractCollection { + + public ASN1Sequence() { + super(); + } + + /** + * Creates an instance with the given capacity. + * + * @param capacity + * The capacity. + */ + public ASN1Sequence(int capacity) { + super(capacity); + } + + /** + * Returns the {@link ASN1#TAG_SEQUENCE SEQUENCE} tag. + * + * @return The {@link ASN1#TAG_SEQUENCE SEQUENCE} tag. + */ + public int getTag() { + return ASN1.TAG_SEQUENCE; + } + +} diff --git a/src/codec/asn1/ASN1SequenceOf.java b/src/codec/asn1/ASN1SequenceOf.java new file mode 100644 index 00000000..aedc5c9e --- /dev/null +++ b/src/codec/asn1/ASN1SequenceOf.java @@ -0,0 +1,227 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Represents an ASN.1 SEQUENCE OF type as specified in ITU-T Recommendation + * X.680. The SequenceOf and SetOf types do not have default constructors in + * contrast to all the other ASN1Types. The reason is that these types are never + * created directly on decoding ASN.1 structures. The decoding process always + * decodes Sequence and Set types because creating the appropriate SequenceOf or + * SetOf type requires explicit knowledge of the syntactic structure definition. + * On the other hand, if an explicit structure is given for decoding then the + * SequenceOf and SetOf types are decoded properly (because they do not have to + * be created and hence the decoder need not know the component type). + *

+ * + * Constraints are checked only after decoding by a call to method + * {@link #decode decode}. + * + * @author Volker Roth + * @version "$Id: ASN1SequenceOf.java,v 1.3 2004/08/06 15:16:08 flautens Exp $" + */ +public class ASN1SequenceOf extends ASN1Sequence implements ASN1CollectionOf { + /** + * The {@link ASN1Type ASN1Type} from which the component types of this + * collection are created. + */ + private Resolver resolver_; + + /** + * Creates an instance with the given capacity. This constructor is provided + * for subclasses that wish to handle creation of new elements themselves + * and do not rely on an application-provided element type. + * + * @param capacity + * The initial capacity of the set. + */ + protected ASN1SequenceOf(int capacity) { + super(capacity); + } + + /** + * Creates an instance that keeps elements of the given type. The type must + * be a valid {@link ASN1Type ASN1Type}. The given class must be public and + * it must have a public default constructor. + * + * @param type + * The class that represents the component type of this SET + * OF. + * @throws IllegalArgumentException + * if the given class does not implement ASN1Type. + * @throws NullPointerException + * if type is null. + */ + public ASN1SequenceOf(Class type) { + if (type == null) + throw new NullPointerException("Need a class!"); + + resolver_ = new ClassInstanceResolver(type); + } + + /** + * Creates an instance with the given capacity. + * + * @param capacity + * The capacity. + */ + public ASN1SequenceOf(Class type, int capacity) { + super(capacity); + + if (type == null) + throw new NullPointerException("Need a class!"); + + resolver_ = new ClassInstanceResolver(type); + } + + /** + * Creates an instance that uses the given {@link Resolver Resolver} to + * create new elements. + * + * @param resolver + * The resolver to use for generating elements. + */ + public ASN1SequenceOf(Resolver resolver) { + if (resolver == null) { + throw new NullPointerException("Need a resolver!"); + } + resolver_ = resolver; + } + + /** + * Returns the Java class representing the ASN.1 type of the elements in + * this collection or ASN1Type.class + * if the type cannot be + * determined. + * + * @return The ASN.1 type of the elements in this collection. + */ + public Class getElementType() { + if (resolver_ instanceof ClassInstanceResolver) { + return ((ClassInstanceResolver) resolver_).getFactoryClass(); + } + return ASN1Type.class; + } + + /** + * Creates and returns a new instance of the element type of this instance. + * The freshly created instance is added to this instance automatically. + *

+ * + * New instances are created by invoking the Resolver + * instance set in this instance. + *

+ * + * If no new instance can be created then an IllegalStateException is + * thrown. + *

+ * + * {@link Decoder Decoders} should call this method in order to create + * additional elements on decoding. Subclasses may use this method to + * keep track on elements added to them. + * + * @return A new instance of the element type of this set. + * @throws IllegalStateException + * if no new instance could be created. + */ + public ASN1Type newElement() { + try { + ASN1Type o; + + o = resolver_.resolve(this); + add(o); + + return o; + } catch (Exception e) { + throw new IllegalStateException("Caught " + e.getClass().getName() + + "(\"" + e.getMessage() + "\")"); + } + } + + /** + * Reads this collection from the given {@link Decoder Decoder}. + * + * @param dec + * The decoder to read from. + */ + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readCollectionOf(this); + checkConstraints(); + } +} diff --git a/src/codec/asn1/ASN1Set.java b/src/codec/asn1/ASN1Set.java new file mode 100644 index 00000000..643d6f7c --- /dev/null +++ b/src/codec/asn1/ASN1Set.java @@ -0,0 +1,115 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * Represents an ASN.1 SET type as specified in ITU-T Recommendation X.680. + *

+ * + * This implementation does not sort the elements according to their encodings + * as required (in principle) by the standard. Upon decoding, all decoded + * elements are kept in the order they appeared in the encoded stream. + * + * @author Volker Roth + * @version "$Id: ASN1Set.java,v 1.2 2000/12/06 17:47:26 vroth Exp $" + */ +public class ASN1Set extends ASN1AbstractCollection { + + public ASN1Set() { + super(); + } + + /** + * Creates an instance with the given capacity. + * + * @param capacity + * The capacity. + */ + public ASN1Set(int capacity) { + super(capacity); + } + + /** + * Returns the {@link ASN1#TAG_SET SET} tag. + * + * @return The {@link ASN1#TAG_SET SET} tag. + */ + public int getTag() { + return ASN1.TAG_SET; + } + +} diff --git a/src/codec/asn1/ASN1SetOf.java b/src/codec/asn1/ASN1SetOf.java new file mode 100644 index 00000000..7db5ed4e --- /dev/null +++ b/src/codec/asn1/ASN1SetOf.java @@ -0,0 +1,253 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Represents an ASN.1 SEQUENCE OF type as specified in ITU-T Recommendation + * X.680. The SequenceOf and SetOf types do not have default constructors in + * contrast to all the other ASN1Types. The reason is that these types are never + * created directly on decoding ASN.1 structures. The decoding process always + * decodes Sequence and Set types because creating the appropriate SequenceOf or + * SetOf type requires explicit knowledge of the syntactic structure definition. + * On the other hand, if an explicit structure is given for decoding then the + * SequenceOf and SetOf types are decoded properly (because they do not have to + * be created and hence the decoder need not know the component type). + *

+ * + * This implementation does not sort the elements according to their encodings + * as required (in principle) by the standard. Upon decoding, all decoded + * elements are kept in the order they appeared in the encoded stream. + *

+ * + * Constraints are checked after decoding instances of this type. + * + * @author Volker Roth + * @version "$Id: ASN1SetOf.java,v 1.3 2004/08/09 06:59:23 flautens Exp $" + */ +public class ASN1SetOf extends ASN1Set implements ASN1CollectionOf { + /** + * The {@link ASN1Type ASN1Type} from which the component types of this + * collection are created. + */ + private Resolver resolver_; + + /** + * Creates an instance with the given capacity. This constructor is provided + * for subclasses that wish to handle creation of new elements themselves + * and do not rely on an application-provided element type. + * + * @param capacity + * The initial capacity of the set. + */ + protected ASN1SetOf(int capacity) { + super(capacity); + } + + /** + * Creates an instance that keeps elements of the given type. The type must + * be a valid {@link ASN1Type ASN1Type}. The given class must be public and + * it must have a public default constructor. + * + * @param type + * The class that represents the component type of this SET + * OF. + * @throws IllegalArgumentException + * if the given class does not implement ASN1Type. + * @throws NullPointerException + * if type is null. + */ + public ASN1SetOf(Class type) { + if (type == null) + throw new NullPointerException("Need a class!"); + + resolver_ = new ClassInstanceResolver(type); + } + + /** + * Creates an instance with the given capacity. + * + * @param capacity + * The capacity. + */ + public ASN1SetOf(Class type, int capacity) { + super(capacity); + + if (type == null) + throw new NullPointerException("Need a class!"); + + resolver_ = new ClassInstanceResolver(type); + } + + /** + * Creates an instance that uses the given {@link Resolver Resolver} to + * create new elements. + * + * @param resolver + * The resolver to use for generating elements. + */ + public ASN1SetOf(Resolver resolver) { + if (resolver == null) { + throw new NullPointerException("Need a resolver!"); + } + resolver_ = resolver; + } + + /** + * Creates an instance that uses the given {@link Resolver Resolver} to + * create new elements. + * + * @param resolver + * The resolver to use for generating elements. + * @param capacity + * The capacity. + */ + public ASN1SetOf(Resolver resolver, int capacity) { + /* + * This method is used by the updated codec.x501.Name class. I inserted + * this method. --volker roth + */ + super(capacity); + + if (resolver == null) { + throw new NullPointerException("Need a resolver!"); + } + resolver_ = resolver; + } + + /** + * Returns the Java class representing the ASN.1 type of the elements in + * this collection or ASN1Type.class + * if the type cannot be + * determined. + * + * @return The ASN.1 type of the elements in this collection. + */ + public Class getElementType() { + if (resolver_ instanceof ClassInstanceResolver) { + return ((ClassInstanceResolver) resolver_).getFactoryClass(); + } + return ASN1Type.class; + } + + /** + * Creates and returns a new instance of the element type of this instance. + * The freshly created instance is added to this instance automatically. + *

+ * + * New instances are created by invoking the Resolver + * instance set in this instance. + *

+ * + * If no new instance can be created then an IllegalStateException is + * thrown. + *

+ * + * {@link Decoder Decoders} should call this method in order to create + * additional elements on decoding. Subclasses may use this method to + * keep track on elements added to them. + * + * @return A new instance of the element type of this set. + * @throws IllegalStateException + * if no new instance could be created. + */ + public ASN1Type newElement() { + try { + ASN1Type o; + + o = resolver_.resolve(this); + add(o); + + return o; + } catch (Exception e) { + throw new IllegalStateException("Caught " + e.getClass().getName() + + "(\"" + e.getMessage() + "\")"); + } + } + + /** + * Reads this collection from the given {@link Decoder Decoder}. + * + * @param dec + * The decoder to read from. + */ + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readCollectionOf(this); + checkConstraints(); + } +} diff --git a/src/codec/asn1/ASN1String.java b/src/codec/asn1/ASN1String.java new file mode 100644 index 00000000..1f7f6b16 --- /dev/null +++ b/src/codec/asn1/ASN1String.java @@ -0,0 +1,143 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * The common interface of all ASN.1 string types. This interface specifies + * setter and getter methods for string values and methods for string to octet + * and octet to string conversion. See {@link ASN1AbstractString + * ASN1AbstractString} for details on strings. + * + * @author Volker Roth + * @version "$Id: ASN1String.java,v 1.2 2000/12/06 17:47:26 vroth Exp $" + * @see ASN1AbstractString + */ +public interface ASN1String extends ASN1Type { + /** + * Returns the represented string value. + * + * @return The string value of this type. + */ + public String getString(); + + /** + * Sets the string value. + * + * @param s + * The string value. + */ + public void setString(String s) throws ConstraintException; + + /** + * Converts the given byte array to a string. + * + * @param b + * The byte array to convert. + */ + public String convert(byte[] b) throws ASN1Exception; + + /** + * Converts the given string to a byte array. + * + * @param s + * The string to convert. + */ + public byte[] convert(String s) throws ASN1Exception; + + /** + * Returns the number of octets required to encode the given string + * according to the basic encoding scheme of this type. For restricted + * string types this likely equals the number of characters in the string + * unless special characters or escape sequences are allowed. For + * {@link ASN1BMPString BMPStrings} this is twice the number of characters + * and for {@link ASN1UniversalString UniversalStrings} it is four times the + * number of characters in the string. + *

+ * + * The number returned must equal the number returned by the method call + * {@link #convert(java.lang.String) convert(s)}. This method is required + * for DER encoding of string types in order to determine the number of + * octets required for encoding the given string. For BER encoding this + * method is not and the encoding of the string may be broken up into + * consecutive OCTET STRINGS. + * + * @param s + * The string whose encoding length is determined. + */ + public int convertedLength(String s) throws ASN1Exception; + +} diff --git a/src/codec/asn1/ASN1TagComparator.java b/src/codec/asn1/ASN1TagComparator.java new file mode 100644 index 00000000..d2707a09 --- /dev/null +++ b/src/codec/asn1/ASN1TagComparator.java @@ -0,0 +1,110 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.util.Comparator; + +/** + * ASN1 Comparator, used for Sorting an ASN1 Set. First TagClasses of the + * ASN1Types are compared. If they are equal, the Tags of the ASN1Types are + * compared. result is : 1, 0, -1 for a1 > a2, a1 = a2, a1 < a2 + */ + +public class ASN1TagComparator implements Comparator { + + public int compare(Object o1, Object o2) { + + ASN1Type a1 = (ASN1Type) o1; + ASN1Type a2 = (ASN1Type) o2; + + if (a1.getTagClass() > a2.getTagClass()) + return 1; + if (a1.getTagClass() == a2.getTagClass()) { + if (a1.getTag() > a2.getTag()) { + return 1; + } + if (a1.getTag() == a2.getTag()) { + return 0; + } + // only this option, a1.Tag() < a2.Tag() left + return -1; + } + // only this option, a1.TagClass() < a2.TagClass() left + return -1; + } + +} \ No newline at end of file diff --git a/src/codec/asn1/ASN1TaggedType.java b/src/codec/asn1/ASN1TaggedType.java new file mode 100644 index 00000000..bbde8cce --- /dev/null +++ b/src/codec/asn1/ASN1TaggedType.java @@ -0,0 +1,328 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Represents an ASN.1 tagged type. Tagged types are types that modify the tag + * of an underlying type. The ASN.1 type classes + * {@link ASN1#CLASS_CONTEXT CONTEXT}, {@link ASN1#CLASS_PRIVATE PRIVATE}, and + * {@link ASN1#CLASS_APPLICATION APPLICATION} specify tagged types. + * + * @author Volker Roth + * @version "$Id: ASN1TaggedType.java,v 1.4 2004/08/09 07:45:30 flautens Exp $" + */ +public class ASN1TaggedType extends ASN1AbstractType { + private int tag_; + private int cls_ = ASN1.CLASS_CONTEXT; + + private ASN1Type inner_; + + /** + * Creates an instance with the given tag, tag class, and inner type. The + * tagging method is EXPLICIT if + * explicit is + * true and IMPLICIT otherwise. + * + * @param tag + * The tag of this type. + * @param cls + * The tag class of this type, for instance + * {@link ASN1#CLASS_CONTEXT CONTEXT SPECIFIC}. + * @param inner + * The inner type of this tagged type. + * @param explicit + * true if EXPLICIT tagging shall be used and + * false if the tagging method shall be + * IMPLICIT. + * @throws NullPointerException + * if the given inner type is null. + */ + public ASN1TaggedType(int tag, int cls, ASN1Type inner, boolean explicit) { + setTag(tag); + setTagClass(cls); + setInnerType(inner); + inner_.setExplicit(explicit); + } + + /** + * Creates an instance with the given tag and inner type. The tagging method + * is EXPLICIT if + * explicit is true and + * IMPLICIT otherwise. The tag class is set to {@link ASN1#CLASS_CONTEXT + * CONTEXT SPECIFIC}. + * + * @param tag + * The tag of this type. + * @param inner + * The inner type of this tagged type. + * @param explicit + * true if EXPLICIT tagging shall be used and + * false if the tagging method shall be + * IMPLICIT. + * @throws NullPointerException + * if the given inner type is null. + */ + public ASN1TaggedType(int tag, ASN1Type inner, boolean explicit) { + setTag(tag); + setTagClass(ASN1.CLASS_CONTEXT); + setInnerType(inner); + inner_.setExplicit(explicit); + } + + /** + * Creates an instance with the given tag, tag class, and inner type. The + * tagging method is EXPLICIT if + * explicit is + * true and IMPLICIT otherwise. The tag class is set to + * {@link ASN1#CLASS_CONTEXT CONTEXT SPECIFIC}. If + * optional + * is true then this type is declared OPTIONAL. + * + * @param tag + * The tag of this type. + * @param inner + * The inner type of this tagged type. + * @param explicit + * true if EXPLICIT tagging shall be used and + * false if the tagging method shall be + * IMPLICIT. + * @param optional + * true declares this type as OPTIONAL. + * @throws NullPointerException + * if the given inner type is null. + */ + public ASN1TaggedType(int tag, ASN1Type inner, boolean explicit, + boolean optional) { + setTag(tag); + setTagClass(ASN1.CLASS_CONTEXT); + setInnerType(inner); + inner_.setExplicit(explicit); + setOptional(optional); + } + + /** + * Returns the underlying ASN.1 type. Please note that OPTIONAL modifiers of + * (for instance) context-specific types in compound ASN.1 types refer to + * the outer type and not to the inner type. Types are declared OPTIONAL by + * calling their {@link ASN1Type#setOptional setOptional} method. + * + * @return The underlying ASN.1 type. + */ + public ASN1Type getInnerType() { + return inner_; + } + + /** + * Returns the value of the inner type. The default inner type is + * {@link ASN1Null ASN1Null}. This method calls + * {@link ASN1Type#getValue getValue} on the inner type and returns the + * result. + * + * @return The value of the inner type. + */ + public Object getValue() { + return inner_.getValue(); + } + + /** + * Sets the inner type of this CONTEXT SPECIFIC type. + * + * @param t + * The type to set as the inner type. + * @throws NullPointerException + * if the given type is null. + */ + public void setInnerType(ASN1Type t) { + if (t == null) { + throw new NullPointerException("Type is NULL!"); + } + inner_ = t; + } + + /** + * Sets the tag of this type. + * + * @param tag + * The tag. + */ + public void setTag(int tag) { + tag_ = tag; + } + + /** + * Returns the tag of this type. + * + * @return The tag of this type. + */ + public int getTag() { + return tag_; + } + + /** + * Sets the tag class of this type. This tag class may be one of + * {@link ASN1#CLASS_UNIVERSAL UNIVERSAL}, {@link ASN1#CLASS_CONTEXT + * CONTEXT SPECIFIC}, {@link ASN1#CLASS_PRIVATE PRIVATE}, or {@link + * ASN1#CLASS_APPLICATION APPLICATION}. + * + * @param cls + * The tag class. + */ + public void setTagClass(int cls) { + cls_ = cls; + } + + /** + * Returns the tag class of this type. The default class of this instance is + * {@link ASN1#CLASS_CONTEXT CONTEXT SPECIFIC}. + * + * @return The class of this ASN.1 tag. + */ + public int getTagClass() { + return cls_; + } + + /** + * Tagged types themselves are always tagged EXPLICIT. The inner type can be + * tagged either EXPLICIT or IMPLICIT. IMPLICIT types are isomorphic to the + * underlying type except that the tag and tag class is distinct (with + * regard to encoding). + * + * @return true, tagged types themselves are always tagged + * EXPLICIT. + */ + public boolean isExplicit() { + return true; + } + + /** + * Throws an exception if the give tagging type is not EXPLICIT (true). + * Tagged types themselves are always EXPLICIT; re-tagging tagged types is + * very bad style! + * + * @param explicit + * The tagging method of the tagged (outer) type. This should + * not be mixed with the tagging method of the inner type + * which can be tagged either EXPLICIT or IMPLICIT. + */ + public void setExplicit(boolean explicit) { + if (!explicit) + throw new IllegalArgumentException( + "Tagget types are never IMPLICIT!"); + } + + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeTaggedType(this); + } + + public void decode(Decoder dec) throws ASN1Exception, IOException { + dec.readTaggedType(this); + } + + public String toString() { + StringBuffer buf; + + buf = new StringBuffer(); + buf.append("["); + + switch (cls_) { + case ASN1.CLASS_CONTEXT: + buf.append("CONTEXT SPECIFIC "); + break; + case ASN1.CLASS_UNIVERSAL: + buf.append("UNIVERSAL "); + break; + case ASN1.CLASS_APPLICATION: + buf.append("APPLICATION "); + break; + case ASN1.CLASS_PRIVATE: + buf.append("PRIVATE "); + break; + } + buf.append(tag_ + "] "); + + if (inner_.isExplicit()) + buf.append("EXPLICIT "); + else + buf.append("IMPLICIT "); + + buf.append(inner_.toString()); + return buf.toString(); + } + +} diff --git a/src/codec/asn1/ASN1Time.java b/src/codec/asn1/ASN1Time.java new file mode 100644 index 00000000..9bed0d13 --- /dev/null +++ b/src/codec/asn1/ASN1Time.java @@ -0,0 +1,502 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** + * This is the root class of all ASN.1 time types. In principle, the known time + * types are all of type VisibleString. + * + * @author Volker Roth + * @version "$Id: ASN1Time.java,v 1.7 2004/09/20 15:22:31 pebinger Exp $" + */ +abstract public class ASN1Time extends ASN1VisibleString { + /** + * The TimeZone representing universal coordinated time + * (UTC). + */ + private static final TimeZone TZ = TimeZone.getTimeZone("GMT"); + + /** + * Used to fill with zeroes. + */ + protected static final String ZEROES = "0000"; + + /** + * The internal storage of the date. + */ + protected Date date_; + + /** + * Returns a Java Date instance representing the time in this ASN.1 time + * type. + * + * @return The time as a Java Date instance. + */ + public Date getDate() { + return (Date) date_.clone(); + } + + /** + * Returns a Java long representing the time in milliseconds since January + * 1, 1970, 00:00:00 GMT in this ASN.1 time type. + * + * @return The number of milliseconds since January 1, 1970, 00:00:00 GMT. + */ + public long getTime() { + return date_.getTime(); + } + + /** + * Sets the time from the given Calendar. + * + * @param calendar + * The Calendar with the date that shall be + * set. + */ + public void setDate(Calendar calendar) { + if (calendar == null) { + throw new NullPointerException("calendar"); + } + date_ = calendar.getTime(); + + setString0(toString(date_)); + } + + /** + * Sets the time from the given Date instance. + * + * @param date + * The Date. + */ + public void setDate(Date date) { + if (date == null) { + throw new NullPointerException("date"); + } + date_ = (Date) date.clone(); + + setString0(toString(date_)); + } + + /** + * Sets the time from the given time in milliseconds since January 1, 1970, + * 00:00:00 GMT. + * + * @param time + * The number of milliseconds since January 1, 1970, 00:00:00 + * GMT. + */ + public void setDate(long time) { + date_ = new Date(time); + + setString0(toString(date_)); + } + + /** + * Sets the date to the one represented by the given string. The internal + * string representation is normalized and complies to DER. The date string + * is thus converted to GMT. + * + * @param date + * The date as a X.680 date string. + * @throws IllegalArgumentException + * if the string is not well-formed. + * @throws StringIndexOutOfBoundsException + * if the string is not well-formed. + */ + public void setDate(String date) { + if (date == null) { + throw new NullPointerException("date string"); + } + date_ = toDate(date); + + setString0(toString(date_)); + } + + /** + * Sets the string value. + * + * @param s + * The string value. + */ + public void setString(String s) { + date_ = toDate(s); + + /* + * The value must be set literally because this method is called by the + * decoders. This ensures that the encoding is bitwise identical to the + * decoding. + */ + setString0(s); + } + + public void encode(Encoder enc) throws ASN1Exception, IOException { + enc.writeTime(this); + } + + public void decode(Decoder enc) throws ASN1Exception, IOException { + enc.readTime(this); + } + + abstract protected int[] getFields(); + + abstract protected int[] getFieldLengths(); + + abstract protected int[] getFieldCorrections(); + + /** + * Converts the given Date into a string representation + * according to DER as described in X.690. + * + * @param date + * The Date that is converted. + * @return The string with the date. + */ + protected String toString(Date date) { + StringBuffer buf; + Calendar cal; + String s; + int[] lengths; + int[] correct; + int[] fields; + int len; + int w; + int n; + int v; + int lastzero; + + if (date == null) { + throw new NullPointerException("date"); + } + cal = new GregorianCalendar(TZ); + fields = getFields(); + correct = getFieldCorrections(); + lengths = getFieldLengths(); + buf = new StringBuffer(20); + + /* + * Date is UTC time (most of the time ;-) and we set Calendar to UTC. + */ + cal.setTime(date); + + for (n = 0; n < fields.length; n++) { + v = cal.get(fields[n]) - correct[n]; + s = String.valueOf(v); + len = s.length(); + + /* + * If the target length is zero then we truncate to the left, and + * take only the hundreds if they are greater than zero. Hence, only + * one digit is printed. In summary, we handle the case of + * milliseconds. + */ + w = lengths[n]; + + if (w == 0) { + if (v > 0) { + + buf.append("."); + // add leading 0s if necessary + s = ZEROES.substring(0, 3 - s.length()) + s; + + if (s.charAt(s.length() - 1) != '0') { + buf.append(s); + } else { + lastzero = s.length() - 1; + while ((lastzero > 0) + && (s.charAt(lastzero - 1) == '0')) { + lastzero--; + } + buf.append(s.substring(0, lastzero)); + } + } + continue; + } + /* + * If we have to fill up then we fill zeroes to the left. This + * accounts for days as well as hours and minutes. + */ + if (w < 0) { + w = -w; + } + if (len < w) { + buf.append(ZEROES.substring(0, w - len)); + buf.append(s); + } + /* + * If we must truncate then we take the rightmost characters. This + * accounts for truncated years e.g. "98" instead of "1998". + */ + else if (len > w) { + buf.append(s.substring(len - w)); + } + /* + * Everything is fine, we got the length we need. + */ + else { + buf.append(s); + } + } + buf.append('Z'); + + return buf.toString(); + } + + /** + * Converts the given string to a Date object. + * + * @param code + * The string encoding of the date to be converted. + * @return The Date object. + * @throws IllegalArgumentException + * if the given string is not a valid BER encoding of a + * date. + */ + protected Date toDate(String code) { + Calendar cal; + Calendar res; + TimeZone tz; + int[] lengths; + int[] correct; + int[] fields; + String s; + int pos; + int len; + int n; + int w; + int v; + int c; + + if (code == null) { + throw new NullPointerException("code"); + } + cal = new GregorianCalendar(TZ); + cal.setTime(new Date(0)); + fields = getFields(); + correct = getFieldCorrections(); + lengths = getFieldLengths(); + len = code.length(); + + for (pos = 0, n = 0; n < fields.length; n++) { + /* + * If the field length is zero then we handle milliseconds. In + * particular, we test whether the milliseconds are present. + */ + w = lengths[n]; + + if (w == 0) { + /* + * No character, no period or comma, therefor no milliseconds + * either. + */ + if (pos >= len) { + continue; + } + c = code.charAt(pos); + + /* + * No period or comma but another character presumably means + * that there are no millis but a time zone offset - or a bad + * code. + */ + if (c != '.' && c != ',') { + continue; + } + pos++; + + /* + * We have millis, and now we're gonna read them! + */ + for (v = 0; (v < 3 && pos < len); v++) { + if (!Character.isDigit(code.charAt(pos))) { + break; + } + pos++; + } + /* + * If we did not consume at least one digit then we have a bad + * encoding. + */ + if (v == 0) { + throw new IllegalArgumentException( + "Milliseconds format error!"); + } + s = code.substring(pos - v, pos); + + if (v < 3) { + s = s + ZEROES.substring(0, 3 - v); + } + v = Integer.parseInt(s); + v = v + correct[n]; + + cal.set(fields[n], v); + + continue; + } + /* + * Here we deal with optional digit fields such as seconds in BER. + */ + if (w < 0) { + w = -w; + + if (pos >= len || !Character.isDigit(code.charAt(pos))) { + continue; + } + } + /* + * We fetch the required number of characters and try to decode + * them. + */ + s = code.substring(pos, pos + w); + v = Integer.parseInt(s); + v = v + correct[n]; + pos = pos + w; + + /* + * Special case for UTCTime: we have to correct for years before + * 1970. + */ + if (fields[n] == Calendar.YEAR && lengths[n] == 2) { + v = v + ((v < 70) ? 2000 : 1900); + } + cal.set(fields[n], v); + } + /* + * We still have to deal with time zone offsets and time zone + * specifications - nasty stuff. + */ + if (pos < len) { + c = code.charAt(pos); + + /* + * If there is a '+' or '-' then we have a time differential to GMT + * and no trailing 'Z'. + */ + if (c == '+' || c == '-') { + s = code.substring(pos, pos + 5); + tz = TimeZone.getTimeZone("GMT" + s); + pos = pos + 5; + } + /* + * No time differential means we either have a 'Z' or a bad + * encoding. + */ + else if (code.charAt(pos) != 'Z') { + throw new IllegalArgumentException( + "Illegal char in place of 'Z' (" + pos + ")"); + } + /* + * We got the 'Z', thus we have GMT. + */ + else { + tz = TimeZone.getTimeZone("GMT"); + pos++; + } + } + /* + * We reached the end of the string without encountering a time + * differential or a 'Z', therefor we use the local time zone. This + * should rarely happen unless someone screws up. Nevertheless, it's a + * valid code. + */ + else { + tz = TimeZone.getDefault(); + } + if (pos != len) { + throw new IllegalArgumentException( + "Trailing characters after encoding! (" + pos + ")"); + } + /* + * we now have a Calendar calibrated to GMT and a time zone in tz. Now + * we merge both together in order to get the correct time according to + * GMT. + */ + res = Calendar.getInstance(tz); + res.setTime(new Date(0)); + + for (n = 0; n < fields.length; n++) { + res.set(fields[n], cal.get(fields[n])); + } + return res.getTime(); + } + +} diff --git a/src/codec/asn1/ASN1Type.java b/src/codec/asn1/ASN1Type.java new file mode 100644 index 00000000..d657c7f6 --- /dev/null +++ b/src/codec/asn1/ASN1Type.java @@ -0,0 +1,168 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * The basic interface for Java objects representing primitive ASN.1 types + * according to ITU-T Recommendation X.680. A special feature are + * {@link Constraint constraints}. With constraints the range of valid values + * of an ASN.1 type can be limited. Constraints are validated for most types in + * the setter methods allowing initialization with Java types. + *

+ * + * An abstract implementation of most of the methods declared in this interface + * can be found in {@link ASN1AbstractType ASN1AbstractType}. + * + * @author Volker Roth + * @version "$Id: ASN1Type.java,v 1.2 2000/12/06 17:47:26 vroth Exp $" + */ +public interface ASN1Type { + + public Object getValue(); + + public void setOptional(boolean optional); + + public boolean isOptional(); + + public int getTag(); + + public int getTagClass(); + + public void setExplicit(boolean explicit); + + public boolean isExplicit(); + + /** + * Returns true if this type matches the given tag and + * tagclass. This method is primarily used by decoders in order to verify + * the tag and tag class of a decoded type. Basic types need not implement + * this method since {@link ASN1AbstractType ASN1AbstractType} provides a + * default implementation. Certain variable types such as {@link ASN1Choice + * ASN1Choice} and {@link ASN1OpenType ASN1OpenType} implement this method. + * This helps decoders to determine if a decoded type matches a given ASN.1 + * structure. + * + * @param tag + * The tag to match. + * @param tagclass + * The tag class to match. + * @return true if this type matches the given tag and tag + * class. + */ + public boolean isType(int tag, int tagclass); + + public void encode(Encoder enc) throws ASN1Exception, IOException; + + public void decode(Decoder dec) throws ASN1Exception, IOException; + + /** + * Sets a {@link Constraint constraint} for this type. Constraints are + * checked by setter methods and as the last operation of a call to the + * {@link ASN1Type#decode decode()} method. + * + * A number of constraints can be defined in ASN.1; one example is the SIZE + * constraint on string types. For instance, + * foo IA5String (SIZE 10..20) means the string foo + * can be 10 to 20 characters long. Strings can also be constrained with + * regard to the character sets. The constraint model of this package allows + * to add arbitrary constraints on types. + *

+ * + * @param o + * The constraint to set. + */ + public void setConstraint(Constraint o); + + /** + * Returns the {@link Constraint Constraint} of this type or + * null if there is none. + * + * @return The Constraint or null. + */ + public Constraint getConstraint(); + + /** + * Checks the {@link Constraint constraints} registered with this instance. + * + * @see Constraint + * @see ConstraintCollection + */ + public void checkConstraints() throws ConstraintException; + +} diff --git a/src/codec/asn1/ASN1VisibleString.java b/src/codec/asn1/ASN1VisibleString.java new file mode 100644 index 00000000..974ddad2 --- /dev/null +++ b/src/codec/asn1/ASN1VisibleString.java @@ -0,0 +1,105 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * This class represents an ASN.1 T61String as described in ITU-T Recommendation + * X.680. Note that no value checking is performed! + * + * @author Markus Tak (by cut-and-paste ;-) ) + * @version "$Id: ASN1VisibleString.java,v 1.1 2004/08/09 07:53:21 flautens Exp $" + */ +public class ASN1VisibleString extends ASN1AbstractString { + public ASN1VisibleString() { + super(); + } + + /** + * Creates an instance with the given string value. No constraints can be + * set yet so none are checked. + * + * @param s + * The string value. + */ + public ASN1VisibleString(String s) { + super(s); + } + + public int getTag() { + return ASN1.TAG_VISIBLESTRING; + } +} diff --git a/src/codec/asn1/AbstractEncoder.java b/src/codec/asn1/AbstractEncoder.java new file mode 100644 index 00000000..432b268f --- /dev/null +++ b/src/codec/asn1/AbstractEncoder.java @@ -0,0 +1,262 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @author Volker Roth + * @version "$Id: AbstractEncoder.java,v 1.3 2005/03/22 15:55:36 flautens Exp $" + */ +public abstract class AbstractEncoder extends FilterOutputStream implements + Encoder { + /** + * Creates an encoder that writes its output to the given output stream. + * + * @param out + * The output stream to which the encoded ASN.1 objects are + * written. + */ + public AbstractEncoder(OutputStream out) { + super(out); + } + + /** + * + */ + public void writeType(ASN1Type t) throws ASN1Exception, IOException { + t.encode(this); + } + + /** + * This method encodes identifier and length octets. The given length can be + * negative in which case 0x80 is written to indicate INDEFINITE LENGTH + * encoding. Please note that this is appropriate only for a BER encoding or + * CER encoding (ITU-T Recommenation X.690). Encoders are responsible for + * writing the end of code octets 0x00 0x00 after encoding + * the content octets. + * + * @param tag + * The ASN.1 tag + * @param cls + * The ASN.1 tag class. + * @param prim + * true if the encoding is PRIMITIVE and + * false if it is CONSTRUCTED. + * @param len + * The number of content octets or -1 to indicate INDEFINITE + * LENGTH encoding. + */ + protected void writeHeader(int tag, int cls, boolean prim, int len) + throws IOException { + int b; + int i; + + b = cls & ASN1.CLASS_MASK; + + if (!prim) { + b = b | ASN1.CONSTRUCTED; + } + + if (tag > 30) { + b = b | ASN1.TAG_MASK; + out.write(b); + writeBase128(tag); + } else { + b = b | tag; + out.write(b); + } + if (len == -1) { + out.write(0x80); + } else { + if (len > 127) { + i = (significantBits(len) + 7) / 8; + out.write(i | 0x80); + writeBase256(len); + } else { + out.write(len); + } + } + } + + /** + * This method computes the number of octets needed to encode the identifier + * and length octets of the {@link ASN1Type ASN.1 type} with the given tag + * and contents length. The given length can be negative in which case + * INDEFINITE LENGTH encoding is assumed. + * + * @return The number of octets required for encoding the identifier and + * length octets. + * @param tag + * The ASN.1 tag. + * @param len + * The number of contents octets of the ASN.1 type with the + * given tag and length. + */ + protected int getHeaderLength(int tag, int len) { + int n; + + n = 2; + if (tag > 30) { + n = n + ((significantBits(tag) + 6) / 7); + } + + if (len > 127) { + n = n + ((significantBits(len) + 7) / 8); + } + + return n; + } + + /** + * Writes the given integer to the output in base 128 representation with + * bit 7 of all octets except the last one being set to "1". The + * minimum number of octets necessary is used. + * + * @param n + * The integer to be written to the output. + * @throws IOException + * Thrown by the underlying output stream. + */ + protected void writeBase128(int n) throws IOException { + int i; + int j; + + i = (significantBits(n) + 6) / 7; + j = (i - 1) * 7; + + while (i > 1) { + out.write(((n >>> j) & 0x7f) | 0x80); + j = j - 7; + i--; + } + out.write(n & 0x7f); + } + + /** + * Writes the given integer to the output in base 256 with the minimal + * number of octets. + * + * @param n + * The integer to be written to the output. + * @throws IOException + * Thrown by the underlying output stream. + */ + protected void writeBase256(int n) throws IOException { + int i; + int j; + + i = (significantBits(n) + 7) / 8; + j = (i - 1) * 8; + + while (i > 0) { + out.write((n >>> j) & 0xff); + j = j - 8; + i--; + } + } + + /** + * Counts the number of significant bits in the given integer. There is + * always at least one significant bit. + * + * @param n + * The integer. + * @return The number of significant bits in the given integer. + */ + protected int significantBits(int n) { + int i; + + if (n == 0) { + return 1; + } + + i = 0; + while (n > 255) { + n = n >>> 8; + i += 8; + } + while (n > 0) { + n = n >>> 1; + i++; + } + return i; + } +} diff --git a/src/codec/asn1/ClassInstanceResolver.java b/src/codec/asn1/ClassInstanceResolver.java new file mode 100644 index 00000000..4e26c65d --- /dev/null +++ b/src/codec/asn1/ClassInstanceResolver.java @@ -0,0 +1,149 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * Resolves requests for instances based on a class object. New instances are + * generated by invoking newInstance() + * on the wrapped class + * object. The class must provide a no-arg default constructor. + * + * @author Volker Roth + * @version "$Id: ClassInstanceResolver.java,v 1.2 2000/12/06 17:47:27 vroth Exp $" + */ +public class ClassInstanceResolver extends Object implements Resolver { + /** + * The class that is used as a factory. + */ + private Class factory_; + + /** + * Creates an instance with the given factory class. + * + * @param factory + * The factory class. + * @throws IllegalArgumentException + * if factory has no default constructor, or + * does not implement + * ASN1Type. + */ + public ClassInstanceResolver(Class factory) { + if (factory == null) { + throw new NullPointerException("Need a factory class!"); + } + try { + factory.getConstructor(new Class[0]); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Class " + factory.getName() + + " has no default constructor!"); + } + if (!ASN1Type.class.isAssignableFrom(factory)) { + throw new IllegalArgumentException("Class " + factory.getName() + + " is not an ASN1Type!"); + } + factory_ = factory; + } + + /** + * Returns the class object of the factory class. + * + * @return The class object. + */ + public Class getFactoryClass() { + return factory_; + } + + /** + * Resolves the call by returning a new instance of the the factory class. + * The factory class must have been specified at the time of constructing + * this instance. + * + * @param caller + * The caller. The caller is ignored. + * @throws ResolverException + * if for some reason the call cannot be resolved. + */ + public ASN1Type resolve(ASN1Type caller) throws ResolverException { + try { + return (ASN1Type) factory_.newInstance(); + } catch (Exception e) { + throw new ResolverException("Caught " + e.getClass().getName() + + "(\"" + e.getMessage() + "\")"); + } + } + +} diff --git a/src/codec/asn1/Constraint.java b/src/codec/asn1/Constraint.java new file mode 100644 index 00000000..f00eaeb9 --- /dev/null +++ b/src/codec/asn1/Constraint.java @@ -0,0 +1,93 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * This interface specifies a constraint of some ASN.1 type. Constraints are + * called to check the validity of a type right after initialization and after + * decoding. For instance an OCTET STRING may be defined to be at most 8 octets + * long. This may be implemented by adding a constraint to an + * {@link ASN1OctetString ASN1OctetString} instance that verifies the length of + * the octets string. + * + * @author Volker Roth + * @version "$Id: Constraint.java,v 1.2 2000/12/06 17:47:28 vroth Exp $" + */ +public interface Constraint { + public void constrain(ASN1Type o) throws ConstraintException; + +} diff --git a/src/codec/asn1/ConstraintException.java b/src/codec/asn1/ConstraintException.java new file mode 100644 index 00000000..5833f524 --- /dev/null +++ b/src/codec/asn1/ConstraintException.java @@ -0,0 +1,94 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * Thrown by {@link Constraint Constraint} instances if the validation of some + * ASN.1 type fails. + * + * @author Volker Roth + * @version "$Id: ConstraintException.java,v 1.2 2000/12/06 17:47:28 vroth Exp $" + */ +public class ConstraintException extends ASN1Exception { + public ConstraintException() { + } + + public ConstraintException(String message) { + super(message); + } + +} diff --git a/src/codec/asn1/DERCodeComparator.java b/src/codec/asn1/DERCodeComparator.java new file mode 100644 index 00000000..5235fa85 --- /dev/null +++ b/src/codec/asn1/DERCodeComparator.java @@ -0,0 +1,119 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.util.Comparator; + +/** + * Compares two DER Encodings and orders them according to the strict ordering + * rules applied in DER encoded SET OF types. + * + * @author Volker Roth + * @version "$Id: DERCodeComparator.java,v 1.1 2004/08/09 07:57:53 flautens Exp $" + */ +public class DERCodeComparator extends Object implements Comparator { + public int compare(Object o1, Object o2) { + byte[] a1; + byte[] a2; + int n; + int m; + int x; + int y; + + a1 = (byte[]) o1; + a2 = (byte[]) o2; + m = Math.min(a1.length, a2.length); + + for (n = 0; n < m; n++) { + x = a1[n] & 0xff; + y = a2[n] & 0xff; + + if (x < y) { + return -1; + } else if (x > y) { + return 1; + } + } + if (a1.length == a2.length) { + return 0; + } else if (a1.length < a2.length) { + return -1; + } + return 1; + } + +} \ No newline at end of file diff --git a/src/codec/asn1/DERDecoder.java b/src/codec/asn1/DERDecoder.java new file mode 100644 index 00000000..49cd5f3c --- /dev/null +++ b/src/codec/asn1/DERDecoder.java @@ -0,0 +1,1246 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.util.Iterator; + +/** + * Decodes ASN.1/DER encoded types according to the rules set forth in ITU-T + * Recommendation X.690. + *

+ * + * Decoders can be operated in two modi. The first mode just reads any ASN.1 + * type encountered in a stream and returns the instantiated objects. This mode + * is used if for instance method {@link #readType() readType()} is called. + *

+ * + * The second mode matches the decoded data against an application-specified + * ASN.1 structure. Violations of the structure definition causes an exception + * being thrown. + * + * @author Volker Roth + * @version "$Id: DERDecoder.java,v 1.7 2004/08/09 08:02:10 flautens Exp $" + */ +public class DERDecoder extends FilterInputStream implements Decoder { + protected int tag_; + protected int tagclass_; + protected int length_; + protected boolean indefinite_; + protected boolean primitive_; + protected boolean skip_ = false; + + /** + * The number of octets read from the stream. + */ + protected int pos_ = 0; + + /** + * The marked position in the stream. Marking is not required by this + * decoder anymore. + */ + protected int markpos_ = 0; + + /** + * The maximum allowed length of the input. This is to prevent DoS attacks + * by forging ASN.1/DER structures with insane length octets. + */ + protected int limit_; + + /** + * A buffer for decoding OIDs. At most 32 OID elements are supported. + */ + protected int[] oidbuf_ = new int[32]; + + /** + * A debug switch. + */ + protected boolean debug_ = false; + + /** + * The array holding the suffixes of the classes that implement ASN.1 + * encoding and decoding, indexed by tag number. The array + * {@link #typeclass_ typeclass_} is computed from these names. + */ + static private String[] typename_ = { null, // [0] + "Boolean", // [1] + "Integer", // [2] + "BitString", // [3] + "OctetString", // [4] + "Null", // [5] + "ObjectIdentifier", // [6] + null, // [7] + null, // [8] + null, // [9] + "Enumerated", // [10] + null, // [11] + "UTF8String", // [12] + null, // [13] + null, // [14] + null, // [15] + "Sequence", // [16] + "Set", // [17] + null, // [18] + "PrintableString", // [19] + "T61String", // [20] + null, // [21] + "IA5String", // [22] + "UTCTime", // [23] + "GeneralizedTime", // [24] + null, // [25] + "VisibleString", // [26] + null, // [27] + "UniversalString", // [28] + null, // [29] + "BMPString" // [30] + }; + + /** + * The array of ASN.1 classes implementing the primitive ASN.1 types indexed + * by tag number. A null in the array denotes that no + * implementing class is available for the requested tag number. + */ + static private Class[] typeclass_ = getClasses(); + + /** + * Initializes the static lookup table for mapping ASN1 tags onto + * implementing classes. + * + * @return The class array of implementing classes. + */ + static private Class[] getClasses() { + int n, i; + String s; + Class[] c; + + s = DERDecoder.class.getName(); + i = s.lastIndexOf('.'); + if (i < 0) + s = ""; + else + s = s.substring(0, i) + ".ASN1"; + + n = typename_.length; + c = new Class[n]; + + for (i = 0; i < n; i++) { + if (typename_[i] != null) { + try { + c[i] = Class.forName(s + typename_[i]); + } catch (ClassNotFoundException e) { + c[i] = null; + } + } else + c[i] = null; + } + return c; + } + + /** + * Returns the class that is representing the ASN.1 type with the given tag. + * If no class is known for this tag then an exception is thrown. + * + * @param tag + * The ASN.1 tag of the class to be returned. + * @return The class implementing the ASN.1 type with the given tag. + * @throws ASN1Exception + * if no implementing class is known for the given tag. + * @throws IllegalArgumentException + * if the given tag is negative. + */ + static public Class getClass(int tag) throws ASN1Exception { + Class cls; + + if (tag < 0) + throw new IllegalArgumentException("Tag number is negative!"); + + if (tag < typeclass_.length) { + cls = typeclass_[tag]; + if (cls != null) + return cls; + } + throw new ASN1Exception("Unknown tag! (" + tag + ")"); + } + + /** + * Creates an instance that reads from the given input stream. + * + * @param in + * The input stream to read from. + */ + public DERDecoder(InputStream in) { + super(in); + } + + /** + * Creates an instance that reads from the given input stream, and enforces + * the given maximum number of input octets to the decoder. + * + * @param in + * The input stream to read from. + * @param limit + * The maximum number of octets allowed in the input stream. + * A value of 0 disables checking of upper limits. + */ + public DERDecoder(InputStream in, int limit) { + super(in); + + setInputLimit(limit); + } + + /** + * Sets the maximum number of input octets read from the underlying stream. + * A value of 0 disables limits checking. If a limit is set and the decoder + * detects that the length of a read type wil exceed the limit then an + * exception will be thrown. + *

+ * + * Setting limits prevents DoS attacks on cryptographic services that handle + * ASN.1/DER/BER input. Such code might be tricked into allocating insane + * amounts of buffer memory to decode a structure with forged length octets, + * which might result in out of memory errors. Such code should enforce a + * reasonable upper bound on the size of processed input structures. + * + * @param limit + * The maximum number of octets allowed when readinginput. + */ + public void setInputLimit(int limit) { + if (limit < 0) { + throw new IllegalArgumentException("No negative limits allowed!"); + } + limit_ = limit; + } + + /** + * @return The maximum number of octets the decoded structures are allowed + * to have. + */ + public int getInputLimit() { + return limit_; + } + + public void setDebug(boolean debug) { + debug_ = debug; + } + + public boolean isDebug() { + return debug_; + } + + /** + * Reads the next identifier and length octets from the stream and decodes + * them if {@link #skip_ skip_} is + * false. + *

+ * + * The resulting values are written to {@link #tag_ tag_}, + * {@link #tagclass_ tagclass_}, {@link #primitive_ primitive_}, + * {@link #length_ length_}, and {@link #indefinite_ indefinite_}. + *

+ * + * If the stream is at its end then false is returned and + * true else. If {@link #skip_ skip_} is true + * then nothing is read and true is returned. + *

+ * + * This method does not enforce DER, it can be used to parse BER as well. In + * other words, calling methods have to check for indefinite length + * encodings. + * + * @return true if the next header was read. + */ + protected boolean readNext() throws ASN1Exception, IOException { + int n; + int m; + int j; + + /* + * For debugging purposes. + */ + j = pos_; + + if (skip_) { + if (debug_) { + System.out.println("(" + j + ")\tSkipping."); + } + skip_ = false; + return true; + } + n = read(); + + if (n < 0) { + /* + * Invalidate the current tag. This is required e.g. for detecting + * missing EOC in indefinite length encodings. + */ + tag_ = -1; + return false; + } + if ((n & ASN1.CONSTRUCTED) == 0) { + primitive_ = true; + } else { + primitive_ = false; + } + tagclass_ = n & ASN1.CLASS_MASK; + + if ((n & ASN1.TAG_LONGFORM) == ASN1.TAG_LONGFORM) { + tag_ = readBase128(); + } else { + tag_ = n & ASN1.TAG_MASK; + } + n = read(); + + if (n < 0) { + throw new ASN1Exception("Unexpected EOF, length missing!"); + } + indefinite_ = false; + m = n & ASN1.LENGTH_MASK; + + if ((n & ASN1.LENGTH_LONGFORM) == ASN1.LENGTH_LONGFORM) { + if (m == 0) { + indefinite_ = true; + } else { + m = readBase256(m); + } + } + length_ = m; + + if (length_ < 0) { + throw new ASN1Exception("Negative length: " + length_); + } + /* + * Here, we check if the current position plus the given length exceeds + * the allowed maximum of input. This is to prevent DoS attacks by means + * of forged DER code. Such code could trick the ASN.1 classes into + * allocating insane amounts of memory for ASN.1 structures, hence + * causing out of memory errors. Limits are enforced only if a limit + * greater 0 is set. + */ + if (limit_ > 0) { + m = pos_ + length_ - limit_; + + if (m > 0) { + throw new ASN1Exception("Maximum input limit violated by " + m + + " octets!"); + } + } + if (primitive_ && indefinite_) { + throw new ASN1Exception( + "Encoding can't be PRIMITIVE and INDEFINITE LENGTH!"); + } + // DEBUG + { + if (debug_) + debugHeader(j); + } + return true; + } + + private void debugHeader(int offset) { + StringBuffer sb; + String s; + String t; + + sb = new StringBuffer(); + sb.append("(" + offset + ")\t"); + + switch (tagclass_) { + case ASN1.CLASS_UNIVERSAL: + t = "UNIVERSAL"; + break; + case ASN1.CLASS_PRIVATE: + t = "PRIVATE"; + break; + case ASN1.CLASS_CONTEXT: + t = "CONTEXT SPECIFIC"; + break; + case ASN1.CLASS_APPLICATION: + t = "APPLICATION"; + break; + default: + t = "*INTERNAL ERROR*"; + } + if (tagclass_ == ASN1.CLASS_UNIVERSAL && tag_ < typename_.length) { + s = typename_[tag_]; + if (s == null) { + sb.append("[UNIVERSAL " + tag_ + "] "); + } else { + sb.append(s + " "); + } + } else { + sb.append("[" + t + " " + tag_ + "] "); + } + sb.append((primitive_) ? "PRIMITIVE " : "CONSTRUCTED "); + sb.append("length: "); + if (indefinite_) { + sb.append("indefinite"); + } else { + sb.append(length_); + } + System.out.println(sb.toString()); + } + + /** + * Reads the next header from the stream and matches the given encoding type + * flag and the tag and tag class of the given type against the the current + * header. + *

+ * + * If the given type is tagged IMPLICIT then only then encoding type + * (PRIMITIVE vs. CONSTRUCTED) is verified to the current header. In any + * case will the skipping flag be cleared by this method. This flag can be + * manipulated with method {@link #skipNext skipNext(boolean)}. + * + * Exceptions are thrown in cases of mismatches and if the end of the stream + * is reached. + * + * @param t + * The type to match. + * @param primitive + * A flag saying whether the expected encoding is PRIMITIVE (true) + * or CONSTRUCTED (false). + * @throws EOFException + * if no more types are available because the end of the + * encoding is reached. + * @throws ASN1Exception + * if a type mismatch is detected. + */ + protected void match0(ASN1Type t, boolean primitive) throws ASN1Exception, + IOException { + + if (!t.isExplicit()) { + if (primitive != primitive_) { + throw new ASN1Exception("PRIMTIVE vs. CONSTRUCTED mismatch!"); + } + /* + * Now this is nasty. If the tagging is IMPLICIT and the skipping + * flag is set then this flag has to be cleared since the identifier + * octets must be read implicitely with the data. + */ + skipNext(false); + + return; + } + if (!readNext()) { + throw new EOFException("End of stream reached!"); + } + if (t.isType(tag_, tagclass_)) { + if (primitive != primitive_) { + throw new ASN1Exception("CONSTRUCTED vs. PRIMITIVE mismatch!"); + } + return; + } + throw new ASN1Exception("Type mismatch!"); + } + + /** + * Reads the next header from the stream and matches the tag and tag class + * of the given type against the current header. Exceptions are thrown in + * the case of a mismatch. If the tagging type is not EXPLICIT then this + * method returns silently. In any case will the skipping flag be cleared by + * this method. This flag is manipulated by method + * {@link #skipNext skipNext(boolean)}. + * + * @param t + * The type to match. + * @throws EOFException + * if no more types are available because the end of the + * encoding is reached. + * @throws ASN1Exception + * if a type mismatch is detected. + */ + protected void match1(ASN1Type t) throws ASN1Exception, IOException { + if (!t.isExplicit()) { + /* + * Now this is nasty. If the tagging is IMPLICIT and the skipping + * flag is set then this flag has to be cleared since the identifier + * octets must be read implicitely with the data. + */ + skipNext(false); + + return; + } + if (!readNext()) { + throw new EOFException("End of stream reached!"); + } + if (t.isType(tag_, tagclass_)) { + return; + } + throw new ASN1Exception("Type mismatch!"); + } + + /** + * Reads the next header from the stream and matches it against the given + * tag and tag class. + * + * @param tag + * The tag to match. + * @param tagclass + * The tag class to match. + * @throws ASN1Exception + * if a mismatch is detected. + * @throws EOFException + * if the end of the stream is reached. + */ + protected void match2(int tag, int tagclass) throws IOException, + ASN1Exception { + if (!readNext()) { + throw new EOFException("End of stream reached!"); + } + if (tag != tag_ || tagclass != tagclass_) { + throw new ASN1Exception("Type mismatch!"); + } + return; + } + + /** + * Sets a flag indicating whether reading of the next header should be + * skipped. This is required for decoding types that are tagged IMPLICIT. + * + * @param skip + * true iff the next call to + * {@link #readNext readNext} shall be skipped. + */ + protected void skipNext(boolean skip) { + skip_ = skip; + } + + /** + * Reads the next ASN.1 type in the stream. If the next type is end-of-code + * (EOC) then null is returned. EOC marks the end of + * indefinite length encodings. + * + * @return The ASN.1 type read from the stream or null if the + * type is EOC. + */ + public ASN1Type readType() throws ASN1Exception, IOException { + if (!readNext()) { + throw new EOFException("End of encoding reached!"); + } + if (tag_ == 0 && tagclass_ == 0) { + if (length_ != 0) { + throw new ASN1Exception("EOC with non-zero length!"); + } + return null; + } + if (tagclass_ != ASN1.CLASS_UNIVERSAL) { + ASN1OctetString o; + ASN1TaggedType t; + + /* + * If we encountered a non-UNIVERSAL type with INDEFINITE LENGTH + * encoding then we are in deep trouble. There is no way to + * determine the length of such types in a deterministic way without + * knowledge of the underlying type. We cannot even search for an + * EOC because two consecutive zeroes can appear also in the + * encoding of an underlying INTEGER, OCTET STRING or BIT STRING. + * + * The only thing we can do is to abort with an error message that + * asks the user to try again by decoding with a well-known + * structure instead of using the "fetch what comes next mode" + * operation mode of the decoder. + */ + if (indefinite_) { + throw new ASN1Exception( + "The decoder encountered a non-UNIVERSAL " + + "type with INDEFINITE LENGTH encoding. " + + "There is not sufficient information to " + + "determine the actual length of this " + + "type. Please try again by providing the " + + "appropriate template structure to the " + + "decoder."); + } + /* + * Here I use a nasty trick. The decoded type might be CONSTRUCTED. + * This will trigger an exception if the contents octets are read + * using an OCTET STRING. For this reason I force the encoding type + * to PRIMITIVE. + */ + primitive_ = true; + + /* + * The result of this construction is isomorphic to the use of an + * ASN1Opaque type but processing is slightly faster this way. + */ + o = new ASN1OctetString(); + t = new ASN1TaggedType(tag_, tagclass_, o, false); + + readOctetString(o); + + return t; + } + ASN1Type t; + + try { + t = (ASN1Type) getClass(tag_).newInstance(); + } catch (InstantiationException e) { + throw new ASN1Exception("Internal error, can't instantiate type!"); + } catch (IllegalAccessException e) { + throw new ASN1Exception("Internal error, can't access type!"); + } + /* + * We now decode the UNIVERSAL type we encountered in the stream. If the + * type is an ASN.1 collection then we delegate decoding of the elements + * to method readTypes(ASN1Collection c). Subclasses can override that + * method in order to support indefinite length encodings (BER), and can + * fall back to the implementation in this class in the case of definite + * length encodings (DER). + */ + if (t instanceof ASN1Collection) { + if (primitive_) { + throw new ASN1Exception("Collections cannot be PRIMITIVE!"); + } + readTypes((ASN1Collection) t); + } else { + skipNext(true); + t.decode(this); + } + return t; + } + + /** + * Reads in a sequence of ASN.1 types and stores them in the given + * collection. This method is for use by subclasses only. The + * {@link #length_ length field} of this instance must already be + * initialized with the overall length of the sequence elements to read in. + *

+ * + * Subclasses can override this method in order to handle indefinite length + * encodings as used in BER. In the case of definite length + * encodings,subclasses can fall back to this implementation. + * + * @param c + * The ASN.1 collection in which decoded types are stored. + * @throws ASN1Exception + * if a decoding error occurs. + * @throws IOException + * if guess what... + */ + protected void readTypes(ASN1Collection c) throws ASN1Exception, + IOException { + ASN1Type o; + int end; + + end = pos_ + length_; + + while (end > pos_) { + o = readType(); + + if (o == null) { + throw new ASN1Exception( + "EOC cannot be component of a collection!"); + } + c.add(o); + } + if (end < pos_) { + throw new ASN1Exception("Length short by " + (pos_ - end) + + " octets!"); + } + } + + public void readType(ASN1Type t) throws ASN1Exception, IOException { + t.decode(this); + } + + public void readBoolean(ASN1Boolean t) throws ASN1Exception, IOException { + int b; + + match0(t, true); + b = read(); + + if (b < 0) { + throw new ASN1Exception("Unexpected EOF!"); + } + if (b == 0) { + t.setTrue(false); + } else if (b == 0xff) { + t.setTrue(true); + } else { + throw new ASN1Exception("Bad ASN.1 Boolean encoding!"); + } + } + + public void readInteger(ASN1Integer t) throws ASN1Exception, IOException { + byte[] buf; + + match0(t, true); + + buf = new byte[length_]; + + if (read(buf) < buf.length) { + throw new ASN1Exception("Unexpected EOF!"); + } + t.setBigInteger(new BigInteger(buf)); + } + + public void readBitString(ASN1BitString t) throws ASN1Exception, + IOException { + byte[] buf; + int pad; + + match0(t, true); + + if (length_ < 1) { + throw new ASN1Exception("Length is zero, no initial octet!"); + } + pad = read(); + + if (pad < 0) { + throw new ASN1Exception("Unexpected EOF!"); + } + buf = new byte[length_ - 1]; + + if (buf.length > 0 && read(buf) < buf.length) { + throw new ASN1Exception("Unexpected EOF!"); + } + t.setBits(buf, pad); + } + + public void readOctetString(ASN1OctetString t) throws ASN1Exception, + IOException { + byte[] buf; + + match0(t, true); + + buf = new byte[length_]; + + if (length_ > 0) { + if (read(buf) < buf.length) { + throw new ASN1Exception("Unexpected EOF!"); + } + } + t.setByteArray(buf); + } + + public void readNull(ASN1Null t) throws ASN1Exception, IOException { + match0(t, true); + + if (length_ != 0 || indefinite_) { + throw new ASN1Exception("ASN.1 Null has bad length!"); + } + } + + public void readObjectIdentifier(ASN1ObjectIdentifier t) + throws ASN1Exception, IOException { + int[] oid; + int end; + int n; + int i; + + match0(t, true); + + if (length_ < 1) { + throw new ASN1Exception("OID with not contents octets!"); + } + end = pos_ + length_; + n = read(); + + if (n < 0 || n > 119) { + throw new ASN1Exception("OID contents octet[0] must be [0,119]!"); + } + oidbuf_[0] = n / 40; + oidbuf_[1] = n % 40; + i = 2; + + try { + while (pos_ < end) { + oidbuf_[i++] = readBase128(); + } + if (pos_ != end) { + throw new ASN1Exception("Bad length!"); + } + oid = new int[i]; + + System.arraycopy(oidbuf_, 0, oid, 0, i); + t.setOID(oid); + } catch (ArrayIndexOutOfBoundsException e) { + throw new ASN1Exception("Can't handle more than " + oidbuf_.length + + " OID elements!"); + } + } + + public void readReal(ASN1Type t) throws ASN1Exception { + throw new ASN1Exception("Reals are not yet supported!"); + } + + public void readString(ASN1String t) throws ASN1Exception, IOException { + byte[] buf; + + match0(t, true); + + buf = new byte[length_]; + + if (read(buf) < buf.length) { + throw new ASN1Exception("Unexpected EOF!"); + } + t.setString(t.convert(buf)); + } + + public void readCollection(ASN1Collection t) throws ASN1Exception, + IOException { + Iterator i; + ASN1Type o; + int end; + int n; + + match0(t, false); + + end = pos_ + length_; + i = t.iterator(); + n = 0; + + /* + * This if statement fixes a problem with decoding empty sequences when + * the sequence contains only optional elements. + */ + if (pos_ < end) { + while (i.hasNext()) { + if (!readNext()) { + break; + } + skipNext(true); + o = (ASN1Type) i.next(); + n++; + + if (o.isType(tag_, tagclass_)) { + o.decode(this); + o.setOptional(false); + + if (pos_ == end) { + break; + } + if (pos_ > end) { + throw new ASN1Exception("Length short by " + + (pos_ - end) + " octets!"); + } + } else { + if (!o.isOptional()) { + throw new ASN1Exception("ASN.1 type mismatch!" + + "\nExpected: " + o.getClass().getName() + + "\nIn : " + t.getClass().getName() + + "\nAt index: " + (n - 1) + "\nGot tag : " + + tag_ + " and class: " + tagclass_); + } + } + } + } + while (i.hasNext()) { + o = (ASN1Type) i.next(); + n++; + + if (!o.isOptional()) { + throw new ASN1Exception("ASN.1 type missing!" + "\nExpected: " + + o.getClass().getName() + "\nIn : " + + t.getClass().getName() + "\nAt index: " + (n - 1)); + } + } + if (pos_ < end) { + throw new ASN1Exception("Bad length, " + (end - pos_) + + " contents octets left!"); + } + } + + public void readCollectionOf(ASN1CollectionOf t) throws ASN1Exception, + IOException { + int end; + ASN1Type o; + + match0(t, false); + + t.clear(); + end = pos_ + length_; + + while (pos_ < end) { + try { + o = t.newElement(); + } catch (IllegalStateException e) { + throw new ASN1Exception("Cannot create new element! "); + } + o.decode(this); + } + if (pos_ != end) { + throw new ASN1Exception("Bad length!"); + } + } + + public void readTime(ASN1Time t) throws ASN1Exception, IOException { + readString(t); + } + + /** + * This method also reads in {@link ASN1Opaque ASN1Opaque} types. Opaque + * types take on the tag and tag class of the decoded type and read in the + * contents octets into an OCTET STRING. The opaque type can seamlessly be + * encoded back into the original encoding. No traversal of the inner + * structure of the encoded type is required. + * + * @param t + * The {@link ASN1TaggedType ASN1TaggedType} or + * {@link ASN1Opaque ASN1Opaque} to decode. + */ + public void readTaggedType(ASN1TaggedType t) throws ASN1Exception, + IOException { + ASN1Type o; + + match1(t); + + o = t.getInnerType(); + if (o.isExplicit() && primitive_) { + throw new ASN1Exception("PRIMITIVE vs. CONSTRUCTED mismatch!"); + } + /* + * A nasty trick to make the construction [CLASS TAG] IMPLICIT OCTET + * STRING work for types that are CONSTRUCTED. + */ + if (t instanceof ASN1Opaque) { + if (indefinite_) { + throw new ASN1Exception( + "Cannot decode indefinite length encodings " + + "with ASN1Opaque type!"); + } + primitive_ = true; + } + o.decode(this); + } + + /** + * Reads an ASN.1 CHOICE type. This type is required only on decoding since + * on encoding the type to encode should be clear. The selection of + * alternative types is provided by the given {@link ASN1Choice ASN1Choice} + * instance. The choice must be unambiguous. + *

+ * The CHOICE elements must be tagged EXPLICIT. Otherwise, the + * decoding will abort with an exception. The {@link ASN1Choice ASN1Choice} + * class assures this condition upon adding alternative types to the CHOICE + * type. + * + * @param t + * The collection of choices from which the correct needs to + * be selected. + */ + public void readChoice(ASN1Choice t) throws ASN1Exception, IOException { + ASN1Type o; + + if (!readNext()) + throw new IOException("Unexpected EOF!"); + + skipNext(true); + + o = t.getType(tag_, tagclass_); + if (o == null) + throw new ASN1Exception("Type mismatch!"); + + o.decode(this); + t.setInnerType(o); + } + + /** + * Reads a base 128 number from this input stream. Each consecutive octet + * gives 7 bit of the number with all octets but the last one having the + * most significant bit set to '1'. + *

+ * + * This method is used for reading e.g. the long form of ASN.1 tags. + * However, only tags up to the int range are supported. + * Note: tags longer than that are not detected, but a wrong number + * is being returned. + * + * @return The decoded number. + * @throws ASN1Exception + * iff the end of the data is reached before the number is + * decoded. + * @throws IOException + * iff guess what... + */ + public int readBase128() throws ASN1Exception, IOException { + int n; + int b; + + n = 0; + + while ((b = read()) >= 0) { + n = (n << 7) | (b & 0x7f); + + if ((b & 0x80) == 0) { + break; + } + } + if (b < 0) { + throw new ASN1Exception("Unexpected EOF, base 128 octet missing!"); + } + return n; + } + + /** + * This method reads in an decodes a number in base 256 format. This method + * is used primarily for decoding the ASN.1 length octets. Only lengths up + * to the + * long range are supported. Even that is probably too + * much since we'll hardly have to account for computers with more than 264 + * bytes of memory, right? + *

+ * + * The number is expected to be num bytes long. Certain bad + * encodings are tolerated by this implementation and may lead to errors in + * rare cases. For instance this implementation does not throw an error if + * the number of octets of the encoded number is not minimal (e.g. has + * leading zeroes). Hence, the re-encoding of such a decoded number will + * result in an encoding that is not identical to the original one. + * + * @param num + * The number of octets of the base 256 number to read from + * the input stream. + * @return The decoded number. + * @throws ASN1Exception + * iff the end of the stream is reached before the number + * could be decoded completely. + */ + public int readBase256(int num) throws ASN1Exception, IOException { + int n, b; + + n = 0; + + while (num > 0) { + b = read(); + + if (b < 0) { + throw new ASN1Exception( + "Unexpected EOF, base 256 octet missing!"); + } + n = (n << 8) + b; + num--; + } + return n; + } + + /** + * This method calls the corresponding method of the underlying stream and + * increases the position counter by the number of octets actually read. + */ + public int read() throws IOException { + int b; + + b = in.read(); + + if (b >= 0) { + pos_++; + } + return b; + } + + /** + * This method calls the corresponding method of the underlying stream and + * increases the position counter by the number of octets actually read. + */ + public int read(byte[] b, int off, int len) throws IOException { + int l; + int ls; + + /* + * Since it is not garanteed that the method read(byte[] b, int off, int + * len) from InputStream actually reads as much bytes as specified by + * len-off, it's better to use the following while-loop instead of the + * previous construct: + * + * int l; l = in.read(b,off,len); pos_ += l; return l; + * + * Bugfix from Saied Tazari: on WindowsNT platforms it happened that + * agent communication ended up in unexplainable exceptions, which + * haven't occured since this method has been adjusted. + */ + + ls = 0; + + while (ls < len) { + l = in.read(b, off + ls, len - ls); + + if (l < 0) { + break; + } + ls += l; + } + + pos_ += ls; + return ls; + } + + /** + * This method calls the corresponding method of the underlying stream and + * increases the position counter by the number of octets actually read. + */ + public int read(byte[] b) throws IOException { + int l; + int ls; + + /* + * Since it is not garanteed that the method read(byte[] b) from + * InputStream actually reads as much bytes as specified by b.length, + * it's better to use the following while-loop instead of the previous + * construct: + * + * int l; l = in.read(b); pos_ += l; return l; + * + * Bugfix from Saied Tazari: on WindowsNT platforms it happened that + * agent communication ended up in unexplainable exceptions, which + * haven't occured since this method has been adjusted. + */ + + ls = 0; + + while (ls < b.length) { + l = in.read(b, ls, b.length - ls); + + if (l < 0) { + break; + } + ls += l; + } + + pos_ += ls; + return ls; + } + + /** + * This method calls the corresponding method of the underlying stream and + * increases the position counter by the number of octets actually skipped. + */ + public long skip(long n) throws IOException { + long l; + + l = in.skip(n); + pos_ += (int) l; + return l; + } + + /** + * This method calls the corresponding method of the underlying stream and + * also marks this stream's position, which denotes the overall number of + * octets already read from this stream. + * + * @param readAheadLimit + * The maximum number of bytes that can be read and still + * reset to the marked position. + */ + public void mark(int readAheadLimit) { + in.mark(readAheadLimit); + markpos_ = pos_; + } + + /** + * Resets the unerlying input stream to the last marked position and also + * resets this stream's position to the last marked one. + */ + public void reset() throws IOException { + in.reset(); + pos_ = markpos_; + } + + /** + * Returns true iff the underlying stream supports marking of + * stream positions. This should better be the case, otherwise an exception + * is raised when objects or headers are read from this stream. + * + * @return true if marking is supported by the underlying + * stream. + */ + public boolean markSupported() { + return in.markSupported(); + } + + /** + * Returns the number of bytes that can be read from the underlying stream + * without blocking. + * + * @return The number of bytes available. + */ + public int available() throws IOException { + return in.available(); + } + + /** + * This method closes the underlying stream. + */ + public void close() throws IOException { + in.close(); + in = null; + } + +} diff --git a/src/codec/asn1/DEREncoder.java b/src/codec/asn1/DEREncoder.java new file mode 100644 index 00000000..0960b2b3 --- /dev/null +++ b/src/codec/asn1/DEREncoder.java @@ -0,0 +1,571 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +/** + * If ,right after instantiating the encoder, the strict parameter is set to + * true, the encoder uses the strict encoding rules from X680/X690 for Sets and + * SetOfs. As long as the flag is not set, the encoder behaves as usual. + * + * @author Volker Roth + * @version "$Id: DEREncoder.java,v 1.7 2004/08/09 11:10:08 flautens Exp $" + */ +public class DEREncoder extends AbstractEncoder { + private int[] stack_; + private int sp_; + + /** + * if true, the strict encoding rules for sets and set of are used. + */ + private boolean strict = false; + + /** + * This variable has a bit set for each tag that denotes a CONSTRUCTED type. + */ + private int constructed_ = ((1 << ASN1.TAG_SEQUENCE) | (1 << ASN1.TAG_SET) | (1 << ASN1.TAG_REAL)); + + /** + * Creates an encoder that writes its output to the given output stream. + * + * @param out + * The output stream to which the encoded ASN.1 objects are + * written. + */ + public DEREncoder(OutputStream out) { + super(out); + } + + /** + * returns the value of the strict parameter. + */ + public boolean isStrict() { + return this.strict; + } + + /** + * Sets the value of the strict parameter. + */ + public void setStrict(boolean _strictness) { + this.strict = _strictness; + } + + /** + * Encodes the identifier and length octets. If there are no known lengths + * then this method creates and runs a + * {@link RunLengthEncoder RunLengthEncoder} on the given type in order to + * establish the length of it and of any contained types. This method must + * not be called with OPTIONAL types else errors may occur. It is the + * responsibility of the caller to ascertain this precondition. Only the + * headers of types that are tagged {@link ASN1Type#isExplicit EXPLICIT} are + * encoded. If the given type is tagged IMPLICIT then this method simply + * returns. + * + * @param t + * The type of which the header is encoded. + * @param primitive + * true if the encoding is PRIMITIVE and + * false if it is CONSTRUCTED. + */ + protected void writeHeader(ASN1Type t, boolean primitive) + throws ASN1Exception, IOException { + int length; + + if (!t.isExplicit()) { + return; + } + if (stack_ == null || sp_ == 0) { + RunLengthEncoder enc; + + enc = new RunLengthEncoder(); + enc.writeType(t); + stack_ = enc.getLengthFields(); + sp_ = stack_.length; + + if (sp_ < 1) { + throw new ASN1Exception("Cannot determine length!"); + } + } + length = stack_[--sp_]; + + writeHeader(t.getTag(), t.getTagClass(), primitive, length); + } + + public void writeBoolean(ASN1Boolean t) throws ASN1Exception, IOException { + if (t.isOptional()) + return; + + writeHeader(t, true); + write(t.isTrue() ? 0xff : 0x00); + } + + public void writeInteger(ASN1Integer t) throws ASN1Exception, IOException { + if (t.isOptional()) + return; + + writeHeader(t, true); + write(t.getBigInteger().toByteArray()); + } + + public void help(byte[] b) { + int i; + + for (i = 0; i < b.length; i++) { + System.err.println(" " + (b[i] & 0xff)); + } + } + + /** + * Encodes a bitstring. This method expects that the bitstring instance is + * already compact. In other words, it does not contain trailing zeroes and + * the pad count is in the range of zero to seven. + */ + public void writeBitString(ASN1BitString t) throws ASN1Exception, + IOException { + if (t.isOptional()) { + return; + } + writeHeader(t, true); + write(t.getPadCount()); + + if (!t.isZero()) { + write(t.getBytes()); + } + } + + public void writeOctetString(ASN1OctetString t) throws ASN1Exception, + IOException { + if (t.isOptional()) + return; + + writeHeader(t, true); + write(t.getByteArray()); + } + + public void writeNull(ASN1Null t) throws ASN1Exception, IOException { + if (t.isOptional()) + return; + + writeHeader(t, true); + } + + public void writeObjectIdentifier(ASN1ObjectIdentifier t) + throws ASN1Exception, IOException { + if (t.isOptional()) + return; + + writeHeader(t, true); + + int i; + int[] e; + + e = t.getOID(); + if (e.length < 2) + throw new ASN1Exception("OID must have at least 2 elements!"); + + write(e[0] * 40 + e[1]); + for (i = 2; i < e.length; i++) + writeBase128(e[i]); + } + + public void writeReal(ASN1Type t) { + throw new UnsupportedOperationException("Real is not yet supported!"); + } + + public void writeString(ASN1String t) throws ASN1Exception, IOException { + if (t.isOptional()) + return; + + writeHeader(t, true); + write(t.convert(t.getString())); + } + + public void writeCollection(ASN1Collection t) throws ASN1Exception, + IOException { + if (t.isOptional()) + return; + + Iterator i; + Collection c; + // Collection h; + ArrayList h; + writeHeader(t, false); + + c = t.getCollection(); + + if (isStrict() && t instanceof ASN1SetOf) { + // von VR war writeStrictSetOf(t); + writeStrictSetOf((ASN1SetOf) t); + return; + } + if (isStrict() && t instanceof ASN1Set) { + h = new ArrayList(c.size()); + + h.addAll(c); + Collections.sort(h, new ASN1TagComparator()); + + c = h; + } + try { + for (i = c.iterator(); i.hasNext();) + writeType((ASN1Type) i.next()); + } catch (ClassCastException e) { + throw new ASN1Exception("Non-ASN.1 type in collection!"); + } + } + + /** + * This method fixes the DER SET OF strict encoding issue brought up by + * pea-counter implementations of decoders that insist on sorting the SET OF + * components by encoding -- something no sane implementation requires. + * + * The method of implementation is basically a hack. Don't take this as a + * good programming pattern. Moreover, this implementation becomes + * completely and utterly THREAD UNSAFE. Not that this would mater too much. + */ + protected void writeStrictSetOf(ASN1SetOf t) throws ASN1Exception, + IOException { + ByteArrayOutputStream bos; + OutputStream old; + Collection c; + ArrayList res; + Iterator i; + byte[] buf; + + /* + * Prepare working variables + */ + c = t.getCollection(); + res = new ArrayList(c.size()); + bos = new ByteArrayOutputStream(); + + /* + * Safe the original output stream and replace it with our byte array + * output stream. + */ + old = super.out; + super.out = bos; + + try { + /* + * Go ahead and encode everything into byte arrays. + */ + for (i = c.iterator(); i.hasNext();) { + writeType((ASN1Type) i.next()); + + /* + * If something has been encoded then we safe it for later + * sorting. + */ + if (bos.size() > 0) { + res.add(bos.toByteArray()); + bos.reset(); + } + } + } finally { + /* + * Restore the original output stream. + */ + super.out = old; + } + /* + * Now, we sort the different codes and write them + */ + Collections.sort(res, new DERCodeComparator()); + + for (i = res.iterator(); i.hasNext();) { + buf = (byte[]) i.next(); + + write(buf, 0, buf.length); + } + } + + public void writeTime(ASN1Time t) throws ASN1Exception, IOException { + writeString(t); + } + + public void writeTaggedType(ASN1TaggedType t) throws ASN1Exception, + IOException { + if (t.isOptional()) + return; + + boolean primitive; + ASN1Type o; + int tag; + + o = t.getInnerType(); + + if (!o.isExplicit()) { + if (t instanceof ASN1Opaque) + tag = t.getTag(); + else + tag = o.getTag(); + + primitive = ((constructed_ & (1 << tag)) == 0); + } else + primitive = false; + + writeHeader(t, primitive); + writeType(t.getInnerType()); + } + + public void writeTypeIdentifier(ASN1Type t) { + throw new UnsupportedOperationException( + "TypeIdentifier is not yet supported!"); + } + + public void write(byte[] b) throws IOException { + out.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + /** + * Writes an arbitrary {@link ASN1Type ASN1Type}. The given type is written + * only if it is not declared OPTIONAL. The type is written by calling its + * {@link ASN1Type#encode encode} method with this as the + * argument. The called emthod then should invoke the appropriate encoder + * method of the primitive type to which the given type corresponds. + * + * @param t + * The type to write. + * @throws ASN1Exception + * if the given type cannot be encoded. + * @throws IOException + * if an I/O error occurs. + */ + public void writeType(ASN1Type t) throws ASN1Exception, IOException { + if (!t.isOptional()) + t.encode(this); + } + + /** + * This method encodes identifier and length octets. The given length can be + * negative in which case 0x80 is written to indicate INDEFINITE LENGTH + * encoding. Please note that this is appropriate only for a BER encoding or + * CER encoding (ITU-T Recommenation X.690). Encoders are responsible for + * writing the end of code octets 0x00 0x00 after encoding + * the content octets. + * + * @param tag + * The ASN.1 tag + * @param cls + * The ASN.1 tag class. + * @param prim + * true if the encoding is PRIMITIVE and + * false if it is CONSTRUCTED. + * @param len + * The number of content octets or -1 to indicate INDEFINITE + * LENGTH encoding. + */ + protected void writeHeader(int tag, int cls, boolean prim, int len) + throws IOException { + int b, i; + + b = cls & ASN1.CLASS_MASK; + + if (!prim) + b = b | ASN1.CONSTRUCTED; + + if (tag > 30) { + b = b | ASN1.TAG_MASK; + out.write(b); + writeBase128(tag); + } else { + b = b | tag; + out.write(b); + } + if (len == -1) + out.write(0x80); + else { + if (len > 127) { + i = (significantBits(len) + 7) / 8; + out.write(i | 0x80); + writeBase256(len); + } else + out.write(len); + } + } + + /** + * This method computes the number of octets needed to encode the identifier + * and length octets of the {@link ASN1Type ASN.1 type} with the given tag + * and contents length. The given length can be negative in which case + * INDEFINITE LENGTH encoding is assumed. + * + * @return The number of octets required for encoding the identifier and + * length octets. + * @param tag + * The ASN.1 tag. + * @param len + * The number of contents octets of the ASN.1 type with the + * given tag and length. + */ + protected int getHeaderLength(int tag, int len) { + int n; + + n = 2; + if (tag > 30) + n = n + (significantBits(tag) + 6) / 7; + + if (len > 127) + n = n + (significantBits(len) + 7) / 8; + + return n; + } + + /** + * Writes the given integer to the output in base 128 representation with + * bit 7 of all octets except the last one being set to "1". The + * minimum number of octets necessary is used. + * + * @param n + * The integer to be written to the output. + * @throws IOException + * Thrown by the underlying output stream. + */ + protected void writeBase128(int n) throws IOException { + int i, j; + + i = (significantBits(n) + 6) / 7; + j = (i - 1) * 7; + + while (i > 1) { + out.write(((n >>> j) & 0x7f) | 0x80); + j = j - 7; + i--; + } + out.write(n & 0x7f); + } + + /** + * Writes the given integer to the output in base 256 with the minimal + * number of octets. + * + * @param n + * The integer to be written to the output. + * @throws IOException + * Thrown by the underlying output stream. + */ + protected void writeBase256(int n) throws IOException { + int i, j; + + i = (significantBits(n) + 7) / 8; + j = (i - 1) * 8; + + while (i > 0) { + out.write((n >>> j) & 0xff); + j = j - 8; + i--; + } + } + + /** + * Counts the number of significant bits in the given integer. There is + * always at least one significant bit. + * + * @param n + * The integer. + * @return The number of significant bits in the given integer. + */ + protected int significantBits(int n) { + int i; + + if (n == 0) + return 1; + + i = 0; + while (n > 255) { + n = n >>> 8; + i += 8; + } + while (n > 0) { + n = n >>> 1; + i++; + } + return i; + } +} diff --git a/src/codec/asn1/Decoder.java b/src/codec/asn1/Decoder.java new file mode 100644 index 00000000..c95ed9a3 --- /dev/null +++ b/src/codec/asn1/Decoder.java @@ -0,0 +1,126 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Defines the methods that must be implemented by decoders of ASN.1 types. + * + * + * @author Volker Roth + * @version "$Id: Decoder.java,v 1.2 2000/12/06 17:47:28 vroth Exp $" + */ +public interface Decoder { + + public ASN1Type readType() throws ASN1Exception, IOException; + + public void readType(ASN1Type t) throws ASN1Exception, IOException; + + public void readBoolean(ASN1Boolean t) throws ASN1Exception, IOException; + + public void readInteger(ASN1Integer t) throws ASN1Exception, IOException; + + public void readBitString(ASN1BitString t) throws ASN1Exception, + IOException; + + public void readOctetString(ASN1OctetString t) throws ASN1Exception, + IOException; + + public void readNull(ASN1Null t) throws ASN1Exception, IOException; + + public void readObjectIdentifier(ASN1ObjectIdentifier t) + throws ASN1Exception, IOException; + + public void readReal(ASN1Type t) throws ASN1Exception, IOException; + + public void readString(ASN1String t) throws ASN1Exception, IOException; + + public void readCollection(ASN1Collection t) throws ASN1Exception, + IOException; + + public void readCollectionOf(ASN1CollectionOf t) throws ASN1Exception, + IOException; + + public void readTime(ASN1Time t) throws ASN1Exception, IOException; + + public void readTaggedType(ASN1TaggedType t) throws ASN1Exception, + IOException; + + public void readChoice(ASN1Choice t) throws ASN1Exception, IOException; + +} diff --git a/src/codec/asn1/DefinedByResolver.java b/src/codec/asn1/DefinedByResolver.java new file mode 100644 index 00000000..5279870e --- /dev/null +++ b/src/codec/asn1/DefinedByResolver.java @@ -0,0 +1,154 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * This interface is used by the {@link ASN1OpenType ASN1OpenType} in order to + * resolve the ASN.1 type to decode at runtime. Concrete implementations of this + * interface can be used to model references to type classes as well or to + * compensate for the superseded ASN.1 ANY DEFINED BY type. + *

+ * + * Implementations shall determine and return the correct ASN.1 type to be + * decoded in the defined method. + * + * @author Volker Roth + * @version "$Id: DefinedByResolver.java,v 1.2 2000/12/06 17:47:28 vroth Exp $" + */ +public class DefinedByResolver extends Object implements Resolver { + private OIDRegistry registry_; + private ASN1ObjectIdentifier oid_; + + /** + * Creates an instance that attempts to resolve the given OID against the + * given registry upon calling {@link #resolve resolve}. The OID instance + * used or resolving is the one passed to this constructor. Hence, an OID + * can be added to a compound ASN.1 type and an {@link ASN1OpenType + * ASN1OpenType} can be initialized with this. If the OID is decoded before + * the open type then the open type is resolved against the given registry + * and the decoded OID. In other words the ASN.1 ANY DEFINED BY type can be + * modelled with an ASN1OpenType and an instance of this resolver class. + * + * @param registry + * The registry to resolve the given OID against. + * @param oid + * The oid instance to use when resolving. + */ + public DefinedByResolver(OIDRegistry registry, ASN1ObjectIdentifier oid) { + if (registry == null || oid == null) + throw new NullPointerException("Registry or OID is null!"); + + registry_ = registry; + oid_ = oid; + } + + /** + * Creates an instance that resolves the given OID against the + * {@link OIDRegistry#getGlobalOIDRegistry global OID registry}. + * + * @param oid + * The OID to resolve. + */ + public DefinedByResolver(ASN1ObjectIdentifier oid) { + if (oid == null) + throw new NullPointerException("OID is null!"); + + registry_ = OIDRegistry.getGlobalOIDRegistry(); + oid_ = oid; + } + + /** + * Looks up the private OID in the private registry and returns the resolved + * ASN.1 type. If the OID cannot be resolved against the registry then an + * exception is thrown. + * + * @param caller + * The calling ASN.1 type. + * @throws ResolverException + * if the private OID cannot be mapped onto an ASN.1 type by + * the private registry. + */ + public ASN1Type resolve(ASN1Type caller) throws ResolverException { + ASN1Type t; + + t = registry_.getASN1Type(oid_); + if (t == null) { + throw new ResolverException("Cannot resolve " + oid_); + } + return t; + } +} diff --git a/src/codec/asn1/Encoder.java b/src/codec/asn1/Encoder.java new file mode 100644 index 00000000..f30813f8 --- /dev/null +++ b/src/codec/asn1/Encoder.java @@ -0,0 +1,122 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; + +/** + * Defines the methods that must be implemented by encoders of ASN.1 types. + * + * + * @author Volker Roth + * @version "$Id: Encoder.java,v 1.2 2000/12/06 17:47:29 vroth Exp $" + */ +public interface Encoder { + + public void writeType(ASN1Type t) throws ASN1Exception, IOException; + + public void writeBoolean(ASN1Boolean t) throws ASN1Exception, IOException; + + public void writeInteger(ASN1Integer t) throws ASN1Exception, IOException; + + public void writeBitString(ASN1BitString t) throws ASN1Exception, + IOException; + + public void writeOctetString(ASN1OctetString t) throws ASN1Exception, + IOException; + + public void writeNull(ASN1Null t) throws ASN1Exception, IOException; + + public void writeObjectIdentifier(ASN1ObjectIdentifier t) + throws ASN1Exception, IOException; + + public void writeReal(ASN1Type t) throws ASN1Exception, IOException; + + public void writeString(ASN1String t) throws ASN1Exception, IOException; + + public void writeCollection(ASN1Collection t) throws ASN1Exception, + IOException; + + public void writeTime(ASN1Time t) throws ASN1Exception, IOException; + + public void writeTaggedType(ASN1TaggedType t) throws ASN1Exception, + IOException; + + public void writeTypeIdentifier(ASN1Type t) throws ASN1Exception, + IOException; + +} diff --git a/src/codec/asn1/OIDRegistry.java b/src/codec/asn1/OIDRegistry.java new file mode 100644 index 00000000..d21687b2 --- /dev/null +++ b/src/codec/asn1/OIDRegistry.java @@ -0,0 +1,319 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.security.AccessController; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class maps ASN.1 object identifiers onto ASN.1 types suitable for + * decoding the structure defined by the given OID. It is modelled along the + * lines of the ClassLoader and provides a hierarchy and top-level OID + * registries. + * + * @author Volker Roth + * @version "$Id: OIDRegistry.java,v 1.4 2004/08/09 08:32:22 flautens Exp $" + */ +public class OIDRegistry extends Object { + + /** + * The list of global OID registries. + */ + private static Set registries_ = Collections.synchronizedSet(new HashSet()); + + /** + * The global instance of OID registries. + */ + private static OIDRegistry global_ = new OIDRegistry(); + + /** + * The parent OID registry. + */ + private OIDRegistry parent_ = null; + + /** + * Creates an OID registry. + */ + private OIDRegistry() { + } + + /** + * This method returns the global OIDRegistry instance that may be used for + * querying + * + * @return The global OID registry. + */ + final static public OIDRegistry getGlobalOIDRegistry() { + return global_; + } + + /** + * Adds a registry to the set of globally known ones unless it is already in + * the global set. This method checks the permission + *

+ * + * {@link ASN1Permission ASN1Permission}, " OIDRegistry.add" + *

+ * + * The reference to the parent registry of the given registry is cleared + * before it is added. + * + * @param r + * The registry to add. + * @throws SecurityException + * iff the caller has no right to add registries to the + * global ones. + */ + final public static void addOIDRegistry(OIDRegistry r) { + if (r == null) { + return; + } + AccessController.checkPermission(new ASN1Permission("OIDRegistry.add")); + + r.parent_ = null; + + registries_.add(r); + } + + /** + * Removes the given OID registry from the set of globally known ones. This + * method checks the permission + *

+ * + * {@link ASN1Permission ASN1Permission}, " OIDRegistry.remove" + * + * @param r + * The registry to remove. + * @throws SecurityException + * iff the caller has no right to remove OID registries. + */ + final public static void removeOIDRegistry(OIDRegistry r) { + if (r == null) { + return; + } + AccessController.checkPermission(new ASN1Permission( + "OIDRegistry.remove")); + + registries_.remove(r); + } + + /** + * Creates an OID registry with the given parent. If an OID is not found by + * this registry then the search is delegated to the parent registry. + * + * @param parent + * The parent OID registry. + */ + public OIDRegistry(OIDRegistry parent) { + parent_ = parent; + } + + /** + * Retrieves an ASN.1 type based on the given OID. If no type is found then + * null is returned. This method first calls {@link + * #getLocalASN1Type(ASN1ObjectIdentifier) getLocalASN1Type}. If no ASN.1 + * type is found for the given OID then getASN1Type is called + * for the parent OIDRegistry. + * + * @param oid + * The registered OID of the desired type. + * @return The type or null if no type with the given OID is + * known. + */ + final public ASN1Type getASN1Type(ASN1ObjectIdentifier oid) { + ASN1Type o; + + o = getLocalASN1Type(oid); + + if (o == null && parent_ != null) { + return parent_.getASN1Type(oid); + } + return o; + } + + /** + * Retrieves an ASN.1 type for the given OID or null if no + * such type was found. + *

+ * + * This method should be overridden by subclasses. Subclasses should + * retrieve a pointer to their private synchronized Map with OID to String + * (or Class) mappings, and call the method with the same name but which + * takes an additional Map. + *

+ * + * This implementation searches the global registries for a matching entry. + */ + protected ASN1Type getLocalASN1Type(ASN1ObjectIdentifier oid) { + Iterator i; + ASN1Type o; + OIDRegistry r; + + for (i = registries_.iterator(); i.hasNext();) { + r = (OIDRegistry) i.next(); + o = r.getASN1Type(oid); + + if (o != null) { + return o; + } + } + return null; + } + + /** + * Retrieves an ASN.1 type for the given OID or null if no + * such type was found. Strings in the given Map are replaced + * by the resolved classes. + *

+ * + * This is a convenience method that can be called by subclasses with a Map + * that is specific to the subclass. + * + * @param oid + * The OID that is resolved. + * @param map + * The Map that holds the OID to class (name) + * mapping. The Map values consist either of + * strings (class names) or of the resolved class objects. + */ + protected ASN1Type getLocalASN1Type(ASN1ObjectIdentifier oid, Map map) { + Object o; + Class c; + + if (oid == null || map == null) { + throw new NullPointerException("oid or map"); + } + o = map.get(oid); + + if (o == null) { + return null; + } + try { + if (o instanceof String) { + c = Class.forName((String) o); + + map.put(new ASN1ObjectIdentifier(oid.getOID()), c); + + o = c; + } + c = (Class) o; + + return (ASN1Type) c.newInstance(); + } catch (Exception e) { + e.printStackTrace(System.err); + return null; + } + } + + /** + * This method returns the default OID registry. Subclasses should override + * this method and provide a static instance of an OID registry that + * delegates to the global OID registry if a requested OID could not be + * found locally. This implementation returns the global OID registry by + * default. + * + * @return The default OID registry. + */ + static public OIDRegistry getDefaultRegistry() { + return OIDRegistry.getGlobalOIDRegistry(); + } + + /** + * An OIDRegistry equals another iff both are of the same class. + * + * @return true if both registries are of the same class. + */ + public boolean equals(Object o) { + if (getClass() == o.getClass()) { + return true; + } + return false; + } + + /** + * The hash code of an instance is the hash code of its class. This is + * required to be consistent with the {@link #equals equals()} method. + */ + public int hashCode() { + return getClass().hashCode(); + } + +} diff --git a/src/codec/asn1/Resolver.java b/src/codec/asn1/Resolver.java new file mode 100644 index 00000000..50ccfad8 --- /dev/null +++ b/src/codec/asn1/Resolver.java @@ -0,0 +1,94 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * This interface is used by the {@link ASN1OpenType ASN1OpenType} in order to + * resolve the ASN.1 type to decode at runtime. Concrete implementations of this + * interface can be used to model references to type classes as well or to + * compensate for the superseded ASN.1 ANY DEFINED BY type. + *

+ * + * Implementations shall determine and return the correct ASN.1 type to be + * decoded in the defined method. + * + * @author Volker Roth + * @version "$Id: Resolver.java,v 1.2 2000/12/06 17:47:29 vroth Exp $" + */ +public interface Resolver { + public ASN1Type resolve(ASN1Type caller) throws ResolverException; +} diff --git a/src/codec/asn1/ResolverException.java b/src/codec/asn1/ResolverException.java new file mode 100644 index 00000000..9de8d124 --- /dev/null +++ b/src/codec/asn1/ResolverException.java @@ -0,0 +1,93 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +/** + * Thrown by {@link OpenTypeResolver resolvers} if a problem is detected. + * + * @author Volker Roth + * @version "$Id: ResolverException.java,v 1.2 2000/12/06 17:47:29 vroth Exp $" + */ +public class ResolverException extends ASN1Exception { + public ResolverException() { + } + + public ResolverException(String message) { + super(message); + } + +} diff --git a/src/codec/asn1/RunLengthEncoder.java b/src/codec/asn1/RunLengthEncoder.java new file mode 100644 index 00000000..1dda410d --- /dev/null +++ b/src/codec/asn1/RunLengthEncoder.java @@ -0,0 +1,419 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.asn1; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +/** + * This encoder makes one pass through the given ASN.1 type and computes the + * length of the type encoding according to the DER (ITU-T Recommendation + * X.690). The result is an array of integers with the length of the individual + * non-optional and non-implicit type encodings in the reverse order of the + * order in which the given type is traversed during actual encoding. This array + * is used by the {@link DEREncoder DEREncoder} when encoding a type. + * + * @author Volker Roth + * @version "$Id: RunLengthEncoder.java,v 1.2 2000/12/06 17:47:29 vroth Exp $" + */ +public class RunLengthEncoder extends Object implements Encoder { + /** + * The number of slots by which the internal buffer is incremented if its + * capacity is eexceeded. + */ + public static final int INCREMENT = 256; + + private int[] stack_; + private int tops_; + + private int[] acc_; + private int topa_; + + /** + * Creates an encoder. + */ + public RunLengthEncoder() { + } + + /** + * This method brings in the harvest of the encoding procedure. It returns + * the individual lengths of the DER encodings of the types written to to + * this encoder. The order of length fields is the reverse order of the pre + * order parsing of the written types. + *

+ * + * If this method is called before a type has been encoded then an array of + * zero length is returned. Only non-optional types are counted thus the + * zero length array might be returned also when all encoded types were + * declared optional. + * + * @return The lengths fields. + */ + public int[] getLengthFields() { + if (tops_ == 0) + return new int[0]; + + int[] res; + + res = new int[tops_]; + System.arraycopy(stack_, 0, res, 0, tops_); + return res; + } + + /** + * Encodes the length array of the given type. + */ + public void writeType(ASN1Type o) throws ASN1Exception { + try { + o.encode(this); + } catch (IOException e) { + throw new ASN1Exception("Caught IOException without I/O!"); + } + } + + /** + * This method computes the number of octets needed to encode the identifier + * and length octets of the {@link ASN1Type ASN.1 type} with the given tag + * and contents length. The length must not be negative else an exception is + * thrown. Since this encoder is meant to work in conjunction with a + * {@link DEREncoder DEREncoder} no indefinite length is supported. + * + * @return The number of octets required for encoding the identifier and + * length octets. + * @param tag + * The ASN.1 tag. + * @param len + * The number of contents octets of the ASN.1 type with the + * given tag and length. + * @throws ASN1Exception + * if the given length is negative. + */ + public int getHeaderLength(int tag, int len) throws ASN1Exception { + int n; + + if (len < 0) + throw new ASN1Exception("Length is negative!"); + + n = 2; + if (tag > 30) + n = n + (significantBits(tag) + 6) / 7; + + if (len > 127) + n = n + (significantBits(len) + 7) / 8; + + return n; + } + + /** + * Counts the number of significant bits in the given integer. There is + * always at least one significant bit. + * + * @param n + * The integer. + * @return The number of significant bits in the given integer. + */ + protected int significantBits(int n) { + int i; + + if (n == 0) + return 1; + + i = 0; + while (n > 255) { + n = n >>> 8; + i += 8; + } + while (n > 0) { + n = n >>> 1; + i++; + } + return i; + } + + public void writeBoolean(ASN1Boolean t) throws ASN1Exception { + if (t.isOptional()) + return; + + push(t, 1); + } + + public void writeInteger(ASN1Integer t) throws ASN1Exception { + if (t.isOptional()) + return; + + int n; + + n = t.getBigInteger().bitLength() / 8 + 1; + push(t, n); + } + + public void writeBitString(ASN1BitString t) throws ASN1Exception { + if (t.isOptional()) + return; + + int n; + + if (t.isZero()) + n = 1; + else + n = (t.bitCount() + 7) / 8 + 1; + + push(t, n); + } + + public void writeOctetString(ASN1OctetString t) throws ASN1Exception { + if (t.isOptional()) + return; + + push(t, t.byteCount()); + } + + public void writeNull(ASN1Null t) throws ASN1Exception { + if (t.isOptional()) + return; + + push(t, 0); + } + + public void writeObjectIdentifier(ASN1ObjectIdentifier t) + throws ASN1Exception { + if (t.isOptional()) + return; + + int n; + int i; + int[] e; + + e = t.getOID(); + if (e.length < 2) + throw new ASN1Exception("OID must have at least 2 elements!"); + + for (n = 1, i = 2; i < e.length; i++) + n = n + (significantBits(e[i]) + 6) / 7; + + push(t, n); + } + + public void writeReal(ASN1Type t) { + throw new UnsupportedOperationException("Real is not yet supported!"); + } + + public void writeString(ASN1String t) throws ASN1Exception { + if (t.isOptional()) + return; + + push(t, t.convertedLength(t.getString())); + } + + public void writeCollection(ASN1Collection t) throws ASN1Exception { + if (t.isOptional()) + return; + + int n; + int p; + int i; + ArrayList l; + Collection c; + + c = t.getCollection(); + if (c instanceof ArrayList) + l = (ArrayList) c; + else { + l = new ArrayList(c.size()); + l.addAll(c); + } + try { + for (p = sp(), i = l.size() - 1; i >= 0; i--) + writeType((ASN1Type) l.get(i)); + + n = accumulate(p); + push(t, n); + } catch (ClassCastException e) { + throw new ASN1Exception("Non-ASN.1 type in collection!"); + } + } + + public void writeCollectionOf(ASN1Collection t) throws ASN1Exception { + writeCollection(t); + } + + public void writeTime(ASN1Time t) throws ASN1Exception { + writeString(t); + } + + public void writeTaggedType(ASN1TaggedType t) throws ASN1Exception { + if (t.isOptional()) + return; + + int n; + int p; + + p = sp(); + writeType(t.getInnerType()); + n = accumulate(p); + push(t, n); + } + + public void writeTypeIdentifier(ASN1Type t) { + throw new UnsupportedOperationException( + "TypeIdentifier is not yet supported!"); + } + + /** + * Clears the length array and prepares this encoder for a new run. + */ + protected void reset() { + tops_ = 0; + topa_ = 0; + } + + /** + * Pushes another length integer onto the internal stacks. The value is + * pushed both on the running stack as well as on the accumulator stack. The + * stacks increase dynamically in size in chunks of + * {@link #INCREMENT INCREMENT} integers and never shrink in capacity. + * + * @param t + * The ASN.1 type. + * @param n + * The integer. + */ + protected void push(ASN1Type t, int n) throws ASN1Exception { + if (stack_ == null) { + stack_ = new int[INCREMENT]; + tops_ = 0; + } + if (tops_ == stack_.length) { + int[] stack; + + stack = new int[stack_.length + INCREMENT]; + System.arraycopy(stack_, 0, stack, 0, stack_.length); + stack_ = stack; + } + if (acc_ == null) { + acc_ = new int[INCREMENT]; + topa_ = 0; + } + if (topa_ == acc_.length) { + int[] stack; + + stack = new int[acc_.length + INCREMENT]; + System.arraycopy(acc_, 0, stack, 0, acc_.length); + acc_ = stack; + } + if (t.isExplicit()) { + stack_[tops_++] = n; + acc_[topa_++] = n + getHeaderLength(t.getTag(), n); + } else + acc_[topa_++] = n; + } + + /** + * Returns the accumulator stack pointer. + * + * @return The accumulator stack pointer. + */ + protected int sp() { + return topa_; + } + + /** + * Accumulates all values on the accumulator stack from the given position + * to the top of the stack and returns the result. All accumulated values + * are popped off the stack. + * + * @param pos + * The position to start from. + * @throws IllegalStateException + * if the given position is atop the top of the stack. + */ + protected int accumulate(int pos) { + int n; + int i; + + if (pos > topa_) + throw new IllegalStateException( + "Internal error, bad stack pointer!"); + + for (n = 0, i = pos; i < topa_; i++) + n = n + acc_[i]; + + topa_ = pos; + return n; + } +} diff --git a/src/codec/pkcs8/PrivateKeyInfo.java b/src/codec/pkcs8/PrivateKeyInfo.java new file mode 100644 index 00000000..8a88bd6b --- /dev/null +++ b/src/codec/pkcs8/PrivateKeyInfo.java @@ -0,0 +1,528 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.pkcs8; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import codec.CorruptedCodeException; +import codec.InconsistentStateException; +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1Integer; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1OctetString; +import codec.asn1.ASN1Sequence; +import codec.asn1.ASN1Set; +import codec.asn1.ASN1SetOf; +import codec.asn1.ASN1TaggedType; +import codec.asn1.ASN1Type; +import codec.asn1.DERDecoder; +import codec.asn1.DEREncoder; +import codec.x501.Attribute; +import codec.x509.AlgorithmIdentifier; + +/** + * This class represents a PrivateKeyInfo as defined in PKCS#8. + * The ASN.1 definition of this structure is + *

+ *

+ * + *
+ * PrivateKeyInfo ::= SEQUENCE (
+ *   version Version,  -- 0 for version 1.2 Nov 93
+ *   privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ *   privateKey PrivateKey,
+ *   attributes [0] IMPLICIT Attributes OPTIONAL
+ * }
+ * Version ::= INTEGER
+ * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+ * PrivateKey ::= OCTET STRING
+ * Attributes ::= SET OF Attribute
+ * 
+ * + * The following definitions are taken from the X501 standard: + * + *
+ * Attribute ::= SEQUENCE {
+ *   type AttributeType
+ *   values SET OF AttributeValue
+ *   -- at least one value is required --
+ * }
+ * AttributeType ::= OBJECT IDENTIFIER
+ * AttributeValue ::= ANY
+ * 
+ * + *
+ * + * @author Markus Tak + * @author Volker Roth + * @version "$Id: PrivateKeyInfo.java,v 1.3 2004/08/24 10:01:21 pebinger Exp $" + */ +public class PrivateKeyInfo extends ASN1Sequence { + /** + * The default version. + */ + public static final int VERSION = 0; + + /** + * Version 1.2 November 1993 identifier. + */ + public static final int VERSION_1_2 = 0; + + /** + * Version is the syntax version number, for compatibility with future + * revisions of the . It shall be 0 for that version. + */ + protected ASN1Integer version_; + + /** + * The {@link ASN1ObjectIdentifier OID} of the private key algorithm used in + * this structure. + */ + protected AlgorithmIdentifier algorithm_; + + /** + * Stores the encoded {@link PrivateKey private Key}. The + * interpretation of the contents is defined in the registration of the + * private-key algorithm. For an RSA private key, for example, the contents + * are a DER encoding of a value of type RSAPrivateKey. + */ + transient private ASN1OctetString encodedKey_; + + /** + * Attributes are the extended information that is encrypted along with the + * private-key information. + */ + protected ASN1Set attributes_; + + /** + * This constructor builds the data structure. + */ + public PrivateKeyInfo() { + version_ = new ASN1Integer(VERSION); + add(version_); + + algorithm_ = new AlgorithmIdentifier(); + add(algorithm_); + + encodedKey_ = new ASN1OctetString(); + add(encodedKey_); + + attributes_ = new ASN1SetOf(Attribute.class); + add(new ASN1TaggedType(0, attributes_, false, true)); + } + + /** + * Creates an instance with the given pre encoded raw key. The + * encoded is embedded "as is", the key encoding can be either a + * DER compliant one or a special encoding. + * + * Please note that the byte array returned by the + * getEncoded() + * method of the Key interface must not be passed to this + * constructor because the bytes returned by this method do not contain a + * raw key but a complete PrivateKeyInfo structure (as this one). + * + * @param aid + * The AlgorithmIdentifier with the OID and parameters for + * the raw algorithm that belongs to the given key. + * @param key + * The raw key that shall be wrapped in this instance. + */ + public PrivateKeyInfo(AlgorithmIdentifier aid, byte[] key) { + version_ = new ASN1Integer(VERSION); + add(version_); + + algorithm_ = aid; + add(algorithm_); + + encodedKey_ = new ASN1OctetString(key); + add(encodedKey_); + + attributes_ = new ASN1Set(); + add(new ASN1TaggedType(0, attributes_, false, true)); + } + + /** + * Creates an instance with the given ASN.1 raw key. The given raw + * key is encoded using DER before it is set up in this instance. + *

+ * + * @param aid + * The AlgorithmIdentifier with the OID and parameters for + * the raw algorithm that belongs to the given key. + * @param key + * The raw key that shall be wrapped in this instance. + * @throws InconsistentStateException + * if an exception is thrown while encoding the given key. + * No such exception should ever happen. + */ + public PrivateKeyInfo(AlgorithmIdentifier aid, ASN1Type key) { + ByteArrayOutputStream bos; + DEREncoder enc; + byte[] code; + + version_ = new ASN1Integer(VERSION); + add(version_); + + algorithm_ = aid; + add(algorithm_); + + try { + bos = new ByteArrayOutputStream(); + enc = new DEREncoder(bos); + key.encode(enc); + code = bos.toByteArray(); + enc.close(); + } catch (IOException e) { + throw new InconsistentStateException("Caught IOException!"); + } catch (ASN1Exception e) { + throw new InconsistentStateException("Caught ASN1Exception!"); + } + encodedKey_ = new ASN1OctetString(code); + add(encodedKey_); + + attributes_ = new ASN1Set(); + add(new ASN1TaggedType(0, attributes_, false, true)); + } + + /** + * Creates an instance with the given private key. + * + * @param key + * the actual private key as a java object + * @throws NullPointerException + * if the given key is null. + */ + public PrivateKeyInfo(PrivateKey key) throws InvalidKeyException { + super(2); + setPrivateKey(key); + } + + /** + * Returns the {@link AlgorithmIdentifier AlgorithmIdentifier} of the + * embedded key. + * + * @return The AlgorithmIdentifier. + */ + public AlgorithmIdentifier getAlgorithmIdentifier() { + return algorithm_; + } + + /** + * Returns the private key embedded in this structure. + *

+ * + * This method creates an PKCS8EncodedKeySpec of this instance and feeds it + * into a key factory. In order to locate a suitable key factory, the + * installed providers must define appropriate OID mappings. + * + * @return The private key. + * @throws InconsistentStateException + * if the key spec generated by this method is rejected by + * the key factory that is used to generate the key. + * @throws NoSuchAlgorithmException + * if there is no key factory registered for the algorithm + * of the embedded key or no appropriate OID mapping is + * defined by the installed providers. + */ + public PrivateKey getPrivateKey() throws NoSuchAlgorithmException { + ByteArrayOutputStream bos; + PKCS8EncodedKeySpec spec; + DEREncoder enc; + KeyFactory kf; + String alg; + + try { + bos = new ByteArrayOutputStream(); + enc = new DEREncoder(bos); + encode(enc); + spec = new PKCS8EncodedKeySpec(bos.toByteArray()); + enc.close(); + + alg = algorithm_.getAlgorithmOID().toString(); + kf = KeyFactory.getInstance(alg); + + return kf.generatePrivate(spec); + } catch (ASN1Exception e) { + throw new InconsistentStateException("Internal, encoding error!"); + } catch (IOException e) { + throw new InconsistentStateException( + "Internal, I/O exception caught!"); + } catch (InvalidKeySpecException e) { + throw new InconsistentStateException( + "Encoded key spec rejected by key factory!"); + } + } + + /** + * Initializes this instance with the given private key. + * + * @param key + * The private key from which this instance is initialized. + * @throws InvalidKeyException + * if the given key cannot be decoded properly. + * @throws NullPointerException + * if the given key is null. + */ + public void setPrivateKey(PrivateKey key) throws InvalidKeyException { + if (key == null) + throw new NullPointerException("Key is null!"); + + DERDecoder dec; + + clear(); + + version_ = new ASN1Integer(VERSION); + add(version_); + + algorithm_ = new AlgorithmIdentifier(); + add(algorithm_); + + encodedKey_ = new ASN1OctetString(); + add(encodedKey_); + + attributes_ = new ASN1SetOf(Attribute.class); + add(new ASN1TaggedType(0, attributes_, false, true)); + + try { + dec = new DERDecoder(new ByteArrayInputStream(key.getEncoded())); + + decode(dec); + dec.close(); + } catch (IOException e) { + throw new InvalidKeyException("Caught IOException!"); + } catch (ASN1Exception e) { + throw new InvalidKeyException("Bad encoding!"); + } + } + + /** + * Returns the version number of this instance. + * + * @return The version number. + */ + public int getVersion() { + return version_.getBigInteger().intValue(); + } + + /** + * Sets the version number of this instance. + * + * @param version + * The version number. + */ + public void setVersion(int version) { + version_ = new ASN1Integer(version); + set(0, version_); + } + + /** + * Sets the {@link AlgorithmIdentifier AlgorithmIdentifier} of this + * instance. This algorithm identifier must match the raw key of this + * instance. + *

+ * + * The given instance is set up in this structure. Side effects will occur + * if it is modified subsequently. + * + * @param aid + * The AlgorithmIdentifier. + */ + public void setAlgorithm(AlgorithmIdentifier aid) { + if (aid == null) + throw new NullPointerException("Algorithm identifier is null!"); + + set(1, aid); + algorithm_ = aid; + } + + /** + * Encodes and sets the given ASN.1 key structure as the raw key. + * + * @throws InconsistentStateException + * if an internal error occurs while the key is encoded. + * This should never happen. + */ + protected void setRawKey(ASN1Type key) { + ByteArrayOutputStream bos; + DEREncoder enc; + + try { + bos = new ByteArrayOutputStream(); + enc = new DEREncoder(bos); + key.encode(enc); + encodedKey_ = new ASN1OctetString(bos.toByteArray()); + enc.close(); + set(2, encodedKey_); + } catch (ASN1Exception e) { + throw new InconsistentStateException("Internal, encoding error!"); + } catch (IOException e) { + throw new InconsistentStateException( + "Internal, I/O exception caught!"); + } + } + + /** + * Returns an unmodifiable list view on the attributes. + * + * @return The attributes. + */ + public List getAttributes() { + if (attributes_.isOptional()) { + return null; + } + return Collections.unmodifiableList(attributes_); + } + + /** + * Sets the given attributes. + * + * @param attributes + * The attributes. + */ + public void setAttributes(Collection attributes) { + if (attributes == null) { + throw new NullPointerException("Attributes instance is null!"); + } + attributes_.clear(); + attributes_.addAll(attributes); + attributes_.setOptional(false); + } + + /** + * Returns the raw key material. The key material consists of an encoded + * key structure. ASN.1/DER is often used as the encoding. However, + * this need not always be the case. Elliptic curve cryptosystems use + * specific encodings. + *

+ * + * If the key encoding is ASN.1/DER then the raw key can be retrieved as an + * ASN.1 type by means of the {@link #getDecodedRawKey getDecodedRawKey()} + * method. + *

+ * + * The returned value is a copy. No side effects are caused by modifying it. + * + * @return The raw key bits as a byte array. + */ + public byte[] getRawKey() { + return (byte[]) encodedKey_.getByteArray().clone(); + } + + /** + * Returns an ASN.1 type that represents the decoded raw key. Decoding is + * done by means of ASN.1/DER. Be careful, not all public keys are + * encoded according to DER. Elliptic curve cryptosystems use specific + * encodings. + * + * @return The raw key decoded according to DER. + */ + public ASN1Type getDecodedRawKey() throws CorruptedCodeException { + DERDecoder dec; + ASN1Type raw; + + try { + dec = new DERDecoder(new ByteArrayInputStream(encodedKey_ + .getByteArray())); + + raw = dec.readType(); + dec.close(); + + return raw; + } catch (ASN1Exception e) { + throw new CorruptedCodeException("Cannot decode raw key!"); + } catch (IOException e) { + throw new InconsistentStateException( + "Internal, I/O exception caught!"); + } + } + +} diff --git a/src/codec/util/JCA.java b/src/codec/util/JCA.java new file mode 100644 index 00000000..fd6d0314 --- /dev/null +++ b/src/codec/util/JCA.java @@ -0,0 +1,621 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.util; + +import java.security.Provider; +import java.security.Security; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A number of invariants must hold for the properties defined by the installed + * providers such that this class can work properly: + *

    + *
  • Aliases must be mapped to standard JCA/JCE names whenever possible. All + * Aliases for an engine must map to the same name. + *
  • Two OID mappings with the same OID must map to the same name. + *
  • The slashed form of signature names must be set up as an alias. + *
  • Signature engines that do not have a corresponding cipher engine still + * require a reverse OID mapping of the form Alg.Alias.Cipher.OID.oid = + * name, where name is the cipher name component of a slashed + * form alias for that signature engine. + *
+ * + * @author Volker Roth + * @version "$Id: JCA.java,v 1.5 2004/08/11 14:33:49 flautens Exp $" + */ +public class JCA extends Object { + /** + * The digest/cipher name to signature algorithm name mapping. + */ + private static Map dc2s_ = new HashMap(); + + /** + * The signature algorithm name to digest/cipher name mapping. + */ + private static Map s2dc_ = new HashMap(); + + /** + * The root alias map. Each map entry consists of the lower case engine name + * mapped to another map that holds the aliases for that engine. + */ + protected static Map aliases_ = initAliasLookup(); + + /** + * Let no-one create an instance. + * + */ + private JCA() { + } + + /** + * Reads the properties of the installed providers and builds an optimized + * alias lookup table. All entries of the form + *
    + *
  1. "Alg.Alias."+<engine>+"."+<alias> = + * <value> + *
  2. "Alg.Alias."+<engine>+".OID."+<oid> = + * <value> + *
  3. "Alg.Alias."+<engine>+"."+<oid> = + * <value> + *
+ * are transformed and stored in a hashmap which is used by this class in + * order to do quick lookups of aliases and OID mappings. The stored entries + * are of the form: + *
    + *
  1. <engine>+"."+<alias> = <value> + *
  2. "oid."+<value> = <oid> + *
  3. "oid."+<oid> = <value> + *
+ * In case multiple providers define mappings for the same keys the mapping + * of the first registered provider wins. + */ + static private Map initAliasLookup() { + Enumeration e; + Provider[] provider; + String k; // key + String v; // value + String s; // string + String p; // previous mapping + Map map; + int i; + int j; + + map = new HashMap(); + provider = Security.getProviders(); + + /* + * We start from the last provider and work our way to the first one + * such that aliases of preferred providers overwrite entries of less + * favoured providers. + */ + for (i = provider.length - 1; i >= 0; i--) { + e = provider[i].propertyNames(); + + while (e.hasMoreElements()) { + k = (String) e.nextElement(); + v = provider[i].getProperty(k); + + if (!k.startsWith("Alg.Alias.")) { + continue; + } + /* + * Truncate k to . + */ + k = k.substring(10).toLowerCase(); + j = k.indexOf('.'); + + if (j < 1) { + continue; + } + /* + * Copy to s Truncate k to + */ + s = k.substring(0, j); + k = k.substring(j + 1); + + if (k.length() < 1) { + continue; + } + /* + * If starts with a digit then we assume it is an OID. + * OIDs are uniquely defined, hence we ommit in the oid + * mappings. But we also include the alias mapping for this oid. + */ + if (Character.isDigit(k.charAt(0))) { + p = (String) map.get("oid." + k); + + if (p != null && p.length() >= v.length()) { + continue; + } + map.put("oid." + k, v); + map.put(s + "." + k, v); + } + /* + * If starts with the string "OID." then we found a + * reverse mapping. In that case we swap and the value + * of the mapping, and make an entry of the form "oid."+ = + * + */ + else if (k.startsWith("oid.")) { + k = k.substring(4); + v = v.toLowerCase(); + + map.put("oid." + v, k); + } + /* + * In all other cases we make an entry of the form +"."+ = + * as is defined in the providers. + */ + else { + map.put(s + "." + k, v); + } + } + } + // System.out.println("MAP : "+map); + return map; + } + + /* + * static private void dump(Map map) { Iterator i; Map.Entry entry; + * + * for (i=map.entrySet().iterator(); i.hasNext();) { entry = + * (Map.Entry)i.next(); System.out.println( entry.getKey()+" = + * "+entry.getValue()); } } + */ + + /** + * Returns the JCA standard name for a given OID. The OID must be a string + * of numbers separated by dots, and can be preceded by the prefix + * "OID.". If the OID is not defined in a mapping of some + * registered provider then null is returned. + *

+ * + * OID mappings are unambigous; no engine type is required for the mapping + * and no engine type is returned as part of the result. The returned string + * consists only of the name of the algorithm. + * + * @param oid + * The string with the OID that shall be resolved. + * @return The standard JCA engine name for the given OID or + * null if no such OID is defined. + * @throws NullPointerException + * if the oid is null. + */ + public static String getName(String oid) { + if (oid == null) { + throw new NullPointerException("OID is null!"); + } + if (oid.startsWith("OID.") || oid.startsWith("oid.")) { + oid = oid.substring(4); + } + return (String) aliases_.get("oid." + oid); + } + + /** + * Resolves the given alias to the standard JCA name for the given engine + * type. If no appropriate mapping is defined then null is + * returned. If the given alias is actually an OID string and there is an + * appropriate alias mapping defined for that OID by some provider then the + * corresponding JCA name is returned. + * + * @param engine + * The JCA engine type name. + * @param alias + * The alias to resolve for the given engine type. + * @return The standard JCA name or null if no appropriate + * mapping could be found. + * @throws IllegalArgumentException + * if the alias is an empty string. + * @throws NullPointerException + * if the alias or engine name is null. + */ + public static String resolveAlias(String engine, String alias) { + if (alias == null || engine == null) { + throw new NullPointerException("Engine or alias is null!"); + } + if (alias.length() < 1) { + throw new IllegalArgumentException("Zero-length alias!"); + } + return (String) aliases_.get(engine.toLowerCase() + "." + + alias.toLowerCase()); + } + + /** + * Returns the OID of the given algorithm name. The given name must be the + * JCA standard name of the algorithm and not an alias. Use + * {@link #resolveAlias resolveAlias} to map aliases onto their standard + * names. + * + * @param algorithm + * The JCA standard name of the algorithm for which the OID + * should be returned. + * @return The OID or null if no appropriate mapping could be + * found. + * @throws NullPointerException + * if engine or algorithm is null. + */ + public static String getOID(String algorithm) { + if (algorithm == null) { + throw new NullPointerException("Algorithm is null!"); + } + if (algorithm.length() < 1) { + throw new IllegalArgumentException("Algorithm name is empty!"); + } + if (Character.isDigit(algorithm.charAt(0))) { + return algorithm; + } + return (String) aliases_.get("oid." + algorithm.toLowerCase()); + } + + /** + * Returns the OID of the given algorithm name. The given engine name is + * taken as a hint if the given algorithm name is a non-standard name. In + * that case one shot is given to alias resolving before a second attempt is + * made to map the algorithm to an OID. Alias resolving is done by means of + * the {@link #resolveAlias resolveAlias} method. + * + * @param algorithm + * The JCA standard name of the algorithm for which the OID + * should be returned. + * @param engine + * The engine name that is taken as a hint for alias + * resolving if the algorithm name cannot be resolved in the + * first attempt. + * @return The OID or null if no appropriate mapping could be + * found. + * @throws NullPointerException + * if engine or algorithm is null. + */ + public static String getOID(String algorithm, String engine) { + String oid; + + oid = getOID(algorithm); + + if (oid != null) { + return oid; + } + algorithm = resolveAlias(engine, algorithm); + + if (algorithm == null) { + return null; + } + return getOID(algorithm); + } + + /** + * This method maps a given digest algorithm OID and cipher algorithm OID + * onto the standard name of the combined signature algorithm. For this to + * work the aliases must be well defined such as described below: + *

+ *
Digest Algorithm + *
Alg.Alias.MessageDigest.oid1 = digestAlg + *
Cipher Algorithm + *
Alg.Alias.Cipher.oid2 = cipherAlg + *
Signature Algorithm + *
Alg.Alias.Signature.digestAlg/cipherAlg = + * signatureAlg + *
+ * The oid denotes the sequence of OID numbers separated by dots but + * without a leading "OID.". In some cases, such as the DSA, there + * is no cipher engine corresponding to oid2. In this + * case, oid2 must be mapped to the corresponding name + * by other engine types, such as a KeyFactory. + *

+ * + * All found mappings are cached for future use, as well as the reverse + * mapping, which is much more complicated to synthesise. + * + * @param doid + * The string representation of the digest algorithm OID. The + * OID must have a "OID." prefix. + * @return The standard JCE name of the signature algorithm or + * null if no mapping could be found. + */ + public static String getSignatureName(String doid, String coid) { + String dn; + String cn; + String sn; + String dc; + + dn = getName(doid); + cn = getName(coid); + + if (dn == null || cn == null) { + return null; + } + dc = dn + "/" + cn; + + synchronized (dc2s_) { + sn = (String) dc2s_.get(dc); + + if (sn != null) { + return sn; + } + } + sn = resolveAlias("signature", dc); + + if (sn != null) { + synchronized (dc2s_) { + cn = dc.toLowerCase(); + + if (!dc2s_.containsKey(cn)) { + dc2s_.put(cn, sn); + } + } + synchronized (s2dc_) { + cn = sn.toLowerCase(); + + if (!s2dc_.containsKey(cn)) { + s2dc_.put(cn, dc); + } + } + } + return sn; + } + + /** + * This method maps the standard signature algorithm name to the + * digestAlg/cipherAlg format. This format can be used to + * retrieve the OID of the digest algorithm and cipher algorithm + * respectively. For this to work the aliases must be well defined such as + * described below: + *

+ *
Signature Algorithm + *
Alg.Alias.Signature.d/c = sigAlg where + * d denotes the digest algorithm and c the cipher + * algorithm. sigAlg must be the name under which the algorithm + * engine is published. + *
+ * + * If sigAlg contains a "/" then we assume that the + * given algorithm name is already of the desired form and return + * sigAlg. + * + * @param sigAlg + * The standard signature algorithm name. + * @return The digestAlg/cipherAlg format of the given + * signature algorithm name or + * null if no suitable + * mapping could be found. + */ + public static String getSlashedForm(String sigAlg) { + String v; + + if (sigAlg.indexOf("/") > 0) { + return sigAlg; + } + sigAlg = sigAlg.toLowerCase(); + + synchronized (s2dc_) { + v = (String) s2dc_.get(sigAlg); + + if (v != null) { + return v; + } + } + Iterator i; + String k; + int m; + + for (i = aliases_.keySet().iterator(); i.hasNext();) { + k = (String) i.next(); + + if (!k.startsWith("signature.")) { + continue; + } + v = (String) aliases_.get(k); + + if (!v.equalsIgnoreCase(sigAlg)) { + continue; + } + k = k.substring(10); + m = k.indexOf("/"); + + if (m < 0) { + continue; + } + synchronized (s2dc_) { + if (!s2dc_.containsKey(sigAlg)) { + s2dc_.put(sigAlg, k); + } + } + return k; + } + return null; + } + + /** + * This method maps the given standard signature algorithm name to the + * string representation of the OID associated with the digest algorithm of + * the given signature algorithm. + * + * @param sigAlg + * The standard signature algorithm name. + * @return The string representation of the OID associated with the digest + * alorithm used for sigAlg. + */ + public static String getDigestOID(String sigAlg) { + int n; + String v; + String h; + String r; + + v = getSlashedForm(sigAlg); + + if (v == null) { + return null; + } + n = v.indexOf("/"); + + if (n < 0) { + return null; + } + h = v.substring(0, n); + r = getOID(h); + + if (r != null) { + return r; + } + /* + * We now try to "repair" the bad algorithm name if we find a fitting + * alias instead. + */ + h = resolveAlias("MessageDigest", h); + + if (h == null) { + return null; + } + r = getOID(h); + + if (r != null) { + v = h + "/" + v.substring(n + 1); + + synchronized (s2dc_) { + s2dc_.put(sigAlg, v); + } + } + return r; + } + + /** + * This method maps the given standard signature algorithm name to the + * string representation of the OID associated with the cipher algorithm of + * the given signature algorithm. + *

+ * This conversion is a bit tricky. In cases such as DSA, no corresponding + * Cipher engine exists, since DSA is not designed to be used as a cipher. + * In such cases, some provider needs to set up a bogus alias of the form: + *

+ *
Signature Algorithm + *
Alg.Alias.Cipher.OID.oid = DSA + *
+ * + * The oid denotes the sequence of OID numbers separated by dots but + * without a leading "OID.". + * + * @param sigAlg + * The standard signature algorithm name. + * @return The string representation of the OID associated with the cipher + * alorithm used for sigAlg. + */ + public static String getCipherOID(String sigAlg) { + int n; + String s; + String v; + String r; + + v = getSlashedForm(sigAlg); + + if (v == null) { + return null; + } + n = v.indexOf("/"); + + if (n < 0) { + return null; + } + s = v.substring(n + 1); + r = getOID(s); + + if (r != null) { + return r; + } + /* + * We now try to "repair" the bad algorithm name if we find a fitting + * alias instead. + */ + s = resolveAlias("Signature", s); + + if (s == null) { + return null; + } + r = getOID(s); + + if (r != null) { + v = v.substring(0, n) + "/" + s; + + synchronized (s2dc_) { + s2dc_.put(sigAlg, v); + } + } + return r; + } + +} diff --git a/src/codec/x501/Attribute.java b/src/codec/x501/Attribute.java new file mode 100644 index 00000000..9eba9416 --- /dev/null +++ b/src/codec/x501/Attribute.java @@ -0,0 +1,312 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.x501; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1Null; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1OpenType; +import codec.asn1.ASN1RegisteredType; +import codec.asn1.ASN1Sequence; +import codec.asn1.ASN1Set; +import codec.asn1.ASN1SetOf; +import codec.asn1.ASN1Type; +import codec.asn1.Decoder; +import codec.asn1.DefinedByResolver; +import codec.asn1.OIDRegistry; + +/** + * This class represents an Attribute as defined in X.501 + * standard. The ASN.1 definition of this structure is + *

+ * + *

+ * Attribute ::= SEQUENCE {
+ *   type         AttributeType,
+ *   values       SET OF AttributeValue
+ * }
+ * AttributeType ::= ObjectIdentifier
+ * AttributeValue ::= ANY
+ * 
+ * + * @author Volker Roth + * @version "$Id: Attribute.java,v 1.4 2007/08/30 08:45:05 pebinger Exp $" + */ + +public class Attribute extends ASN1Sequence implements ASN1RegisteredType { + /** + * The Object Identifier specifying the attribute type. + */ + protected ASN1ObjectIdentifier type_; + + /** + * The List of Attribute values. + */ + protected ASN1Set values_; + + /** + * Creates an instance ready for parsing. Any type of ASN.1 structure will + * be accepted as the values of this attribute. An ASN1OpenType + * is used for this. + */ + public Attribute() { + super(2); + + type_ = new ASN1ObjectIdentifier(); + values_ = new ASN1SetOf(ASN1OpenType.class); + + add(type_); + add(values_); + } + + /** + * Creates an instance ready for parsing. The given + * {@link OIDRegistry OIDRegistry} is used to resolve the attribute type. If + * the attribute type cannot be resolved upon decoding then an exception is + * thrown. + * + * @param registry + * The OIDRegistry to use for resolving + * attribute value types, or null + * if the + * global registry shall be used. + */ + public Attribute(OIDRegistry registry) { + super(2); + + if (registry == null) { + registry = OIDRegistry.getGlobalOIDRegistry(); + } + type_ = new ASN1ObjectIdentifier(); + values_ = new ASN1SetOf(new DefinedByResolver(registry, type_)); + + add(type_); + add(values_); + } + + /** + * Creates a new instance that is initialized with the given OID and value. + * Note: the given values are not cloned or copied, they are used + * directly. Hence, the given types must not be modified after hereafter in + * order to avoid side effects. + *

+ * + * The OID must not be null. The + * value can be + * null and is replaced by {@link ASN1Null ASN1Null} in that + * case. + * + * @param oid + * The OID that identifies the given value. + * @param value + * The ASN.1 type. + */ + public Attribute(ASN1ObjectIdentifier oid, ASN1Type value) { + super(2); + + if (oid == null) { + throw new NullPointerException("Need an OID!"); + } + if (value == null) { + value = new ASN1Null(); + } + type_ = oid; + values_ = new ASN1Set(1); + + values_.add(value); + + add(oid); + add(values_); + } + + /** + * The arguments passed to this constructor are set up directly for parsing. + * They are not cloned! The OID of the Attribute is the OID returned by the + * registered type. + * + * @param value + * The registered ASN.1 type. + */ + public Attribute(ASN1RegisteredType value) { + super(2); + + if (value == null) { + throw new NullPointerException("Need a value!"); + } + type_ = value.getOID(); + + if (type_ == null) { + throw new NullPointerException("Value does not provide an OID!"); + } + values_ = new ASN1Set(1); + values_.add(value); + + add(type_); + add(values_); + } + + /** + * This method returns the OID of this Attribute. + * + * @return The OID + */ + public ASN1ObjectIdentifier getOID() { + return type_; + } + + /** + * This method returns an unmodifiable view of the list of values of this + * Attribute. + * + * @return The unmodifiable view of the list of attribute values. + */ + public List valueList() { + return Collections.unmodifiableList(values_); + } + + /** + * returns the number of values in this attribute. + * + * @return The number of values. + */ + public int valueCount() { + return values_.size(); + } + + /** + * Returns the value at the given position where position is between 0 and + * valueCount()-1. + * + * @return The value at the given position. + * @throws ArrayIndexOutOfBoundsException + * if the given position is not within the bounds of the + * list of attribute values. + */ + public ASN1Type valueAt(int index) { + return (ASN1Type) values_.get(index); + } + + /** + * Decodes this instance. If the internal storage object of attributes is a + * ASN1SetOf then that set is transformed into a + * ASN1Set, and any ASN1OpenType instances + * are stripped away. This makes a number of internal objects available for + * garbage collection. + *

+ * + * Consequently, after decoding this instance contains a set with the pure + * attribute values. + * + * @param dec + * The decoder to use. + */ + public void decode(Decoder dec) throws IOException, ASN1Exception { + super.decode(dec); + + if (!(values_ instanceof ASN1SetOf)) { + return; + } + ArrayList list; + ASN1Type o; + Iterator i; + + try { + list = new ArrayList(values_.size()); + + for (i = values_.iterator(); i.hasNext();) { + o = (ASN1Type) i.next(); + + if (o instanceof ASN1OpenType) { + o = ((ASN1OpenType) o).getInnerType(); + } + list.add(o); + } + values_.clear(); + values_.addAll(list); + } catch (ClassCastException e) { + throw new ASN1Exception("Unexpected type in SET OF!"); + } catch (NullPointerException e) { + throw new ASN1Exception("NULL in SET OF!"); + } + } +} diff --git a/src/codec/x509/AlgorithmIdentifier.java b/src/codec/x509/AlgorithmIdentifier.java new file mode 100644 index 00000000..2943241b --- /dev/null +++ b/src/codec/x509/AlgorithmIdentifier.java @@ -0,0 +1,558 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.x509; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import codec.CorruptedCodeException; +import codec.InconsistentStateException; +import codec.asn1.ASN1; +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1Null; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1Opaque; +import codec.asn1.ASN1Sequence; +import codec.asn1.ASN1Type; +import codec.asn1.DEREncoder; +import codec.pkcs8.PrivateKeyInfo; +import codec.util.JCA; + +/** + * This class represents the ASN.1/DER value of the AlgorithmIdentifier defined + * in Annex D to Recommendation X.509. This structure is extensively used for + * instance in the PKCS standards of RSA Inc. The ASN.1 definition of this + * structure is as given below: + *

+ * + *

+ * AlgorithmIdentifier  ::= SEQUENCE{
+ *   algorithm  OBJECT IDENTIFIER,
+ *   parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ * 
+ * + *

+ * For this class to work properly, providers need to define the following + * algorithm aliases for the {@link java.security.AlgorithmParameters + * AlgorithmParameters} implementations they provide: + *

    + *
  1. AlgorithmParameters.MyAlg = class + *
  2. Alg.Alias.AlgorithmParameters.1.2.3.4 = MyAlg + *
  3. Alg.Alias.AlgorithmParameters.OID.1.2.3.4 = MyAlg + *
+ * The first property defined the mapping of the JCE compliant standard name of + * the algorithm to the implementing class. The second provider entry allows + * mapping OID to those algorithm names while the third allows mapping those + * names on corresponding OID. + *

+ * + * The alias definitions are used by this class in order to find an + * AlgorithmParameters implementation for the OID embedded in the X.509 + * AlgorithmIdentifier structure, and to create the OID for a given + * AlgorithmParameters instances. This is done by means of the {@link JCA JCA} + * class, which operates on the engine and alias definitions of the installed + * providers. + *

+ * + * + * + * + * @author Volker Roth + * @version "$Id: AlgorithmIdentifier.java,v 1.3 2004/08/13 11:37:03 pebinger + * Exp $" + * @see JCA + */ +public class AlgorithmIdentifier extends ASN1Sequence { + + /** + * The algorithm parameters of the algorithm specified by this algorithm + * identifier. + */ + protected ASN1Opaque parameters_; + + /** + * The OID of the algorithm. + */ + protected ASN1ObjectIdentifier algorithm_; + + /** + * Creates an {@link AlgorithmIdentifier AlgorithmIdentifier} from the given + * key. The key must be either a public key or a private key. No secret + * (symmetric) keys are accepted. + *

+ * + * The keys encoding must be either a a {@link PrivateKeyInfo + * PrivateKeyInfo} or a {@link SubjectPublicKeyInfo SubjectPublicKeyInfo}. + * + * @param key + * The key from which the AlgorithmIdentifier shall be + * extracted. + * @throws IllegalArgumentException + * if the given key is neither a PublicKey or a PrivateKey. + * @throws CorruptedCodeException + * if an exception was caught while decoding the key's + * encoding. + */ + public static AlgorithmIdentifier createAlgorithmIdentifier(Key key) + throws CorruptedCodeException { + try { + if (key instanceof PublicKey) { + return new SubjectPublicKeyInfo((PublicKey) key) + .getAlgorithmIdentifier(); + } + + if (key instanceof PrivateKey) { + return new PrivateKeyInfo((PrivateKey) key) + .getAlgorithmIdentifier(); + } + + throw new IllegalArgumentException("Key type not supported!"); + + } catch (InvalidKeyException e) { + throw new CorruptedCodeException("Error decoding key!"); + } + } + + /** + * This method builds the tree of ASN.1 objects used for decoding this + * structure. + */ + public AlgorithmIdentifier() { + super(2); + + algorithm_ = new ASN1ObjectIdentifier(); + parameters_ = new ASN1Opaque(); + parameters_.setOptional(true); + add(algorithm_); + add(parameters_); + } + + /** + * Creates an instance initialized to the given algorithm. The algorithm + * must not have parameters since this constructor does not take a parameter + * argument. + * + * @param algorithm + * The JCE standard algorithm name. + * @throws NoSuchAlgorithmException + * if the name cannot be resolved to an OID or the OID has a + * bad syntax. + * @throws NullPointerException + * if algorithm is null. + */ + public AlgorithmIdentifier(String algorithm) + throws NoSuchAlgorithmException { + super(2); + + if (algorithm == null) + throw new NullPointerException("Need an algorithm name!"); + + String oid; + + oid = JCA.getOID(algorithm); + if (oid == null) + throw new NoSuchAlgorithmException("No OID alias for algorithm " + + algorithm); + + try { + algorithm_ = new ASN1ObjectIdentifier(oid); + } catch (IllegalArgumentException e) { + throw new NoSuchAlgorithmException("Bad OID alias for algorithm " + + algorithm); + } + parameters_ = new ASN1Opaque(ASN1.TAG_NULL, ASN1.CLASS_UNIVERSAL, + new byte[0]); + + add(algorithm_); + add(parameters_); + } + + /** + * Creates an instance with the given OID and opaque algorithm parameter + * representation. Both the given OID and the parameter encoding is cloned + * or copied. No side effects occur if these arguments are modified after + * completition of this constructor. + * + * @param oid + * The algorithm object identifier. + * @param b + * The opaque DER encoding of the parameters for the + * algorithm known under the given OID. If no parameters are + * required then null might be passed. In that + * case {@link ASN1Null ASN.1 NULL} is encoded. + * @throws ASN1Exception + * if the opaque representation does not contain a valid DER + * header and contents octets. + */ + public AlgorithmIdentifier(ASN1ObjectIdentifier oid, byte[] b) + throws ASN1Exception { + super(2); + + if (oid == null) + throw new NullPointerException("Need an OID!"); + + algorithm_ = (ASN1ObjectIdentifier) oid.clone(); + + if (b == null) { + /* + * Usually, we'd define the following type as OPTIONAl. However, in + * case no parameters are given a NULL is set instead. + */ + parameters_ = new ASN1Opaque(ASN1.TAG_NULL, ASN1.CLASS_UNIVERSAL, + new byte[0]); + } else + parameters_ = new ASN1Opaque(b); + + add(algorithm_); + add(parameters_); + } + + /** + * Creates an instance that is initialized from the given + * AlgorithmParameters instance. This method attempts to map the algorithm + * name to an ASN.1 OID by calling {@link JCA#getOID(String) JCA#getOID}. + *

+ * + * @param alg + * The name of the algorithm. + * @param params + * The AlgorithmParameters. + * @throws NullPointerException + * if alg is null. + * @throws InvalidAlgorithmParameterException + * if the given parameters have a bad encoding, or the OID + * of the algorithm cannot be determined. + */ + public AlgorithmIdentifier(String alg, AlgorithmParameters params) + throws InvalidAlgorithmParameterException { + super(2); + + String s; + + if (alg == null) + throw new NullPointerException("Algorithm is null!"); + + s = JCA.getOID(alg); + + if (s == null) { + s = "1.3.14.3.2.7"; // DES_CBC + } + + try { + algorithm_ = new ASN1ObjectIdentifier(s); + } catch (IllegalArgumentException e) { + throw new InvalidAlgorithmParameterException( + "Bad OID alias for algorithm " + params.getAlgorithm()); + } + + try { + if (params == null) { + parameters_ = new ASN1Opaque(ASN1.TAG_NULL, + ASN1.CLASS_UNIVERSAL, new byte[0]); + } else { + parameters_ = new ASN1Opaque(params.getEncoded()); + } + } catch (IOException e) { + throw new InvalidAlgorithmParameterException( + "Error during parameter encoding!"); + } catch (ASN1Exception e) { + throw new InvalidAlgorithmParameterException( + "Parameter encoding is not ASN.1/DER!"); + } + + add(algorithm_); + add(parameters_); + } + + /** + * Creates an instance with the given OID and parameters. The parameters are + * encoded according to DER and stored by means of an opaque type. If the + * given parameters are null then an ASN.1 NULL is encoded. + * + * @param oid + * The OID to use. + * @param params + * The ASN.1 type of which the parameters consist. + * @throws InconsistentStateException + * if an internal error occurs; this should never happen. + * @throws ASN1Exception + * if the given parameters cannot be encoded. This should + * rarely happen. + */ + public AlgorithmIdentifier(ASN1ObjectIdentifier oid, ASN1Type params) + throws ASN1Exception { + super(2); + + DEREncoder enc; + ByteArrayOutputStream bos; + + if (oid == null) + throw new NullPointerException("Need an OID!"); + + algorithm_ = (ASN1ObjectIdentifier) oid.clone(); + + try { + if (params == null || (params instanceof ASN1Null)) + parameters_ = new ASN1Opaque(ASN1.TAG_NULL, + ASN1.CLASS_UNIVERSAL, new byte[0]); + else { + bos = new ByteArrayOutputStream(); + enc = new DEREncoder(bos); + params.encode(enc); + + parameters_ = new ASN1Opaque(bos.toByteArray()); + bos.close(); + } + add(algorithm_); + add(parameters_); + } catch (IOException e) { + throw new InconsistentStateException( + "Internal, caught IOException!"); + } + } + + /** + * This method locates a suitable {@link java.security.AlgorithmParameters + * AlgorithmParameters} implementation if it is available from the JCE + * compliant security providers that are installed locally. + *

+ * + * Such providers need to specify the following aliases for this to work: + *

    + *
  • AlgorithmParameters.MyAlg = class + *
  • Alg.Alias.AlgorithmParameters.1.2.3.4 = MyAlg + *
+ * If you ever want to test a provider for compliance with the JCE and + * cleverness, test it against the FhG-IGD PKCS package. If it + * doesn't work then better demand fixes from the provider's vendor. + *

+ * + * This method may be called only if this instance is initialized properly + * either by specifying AlgorithmParameters in a constructor or by parsing a + * valid ASN.1/DER encoding. + * + * @throws NoSuchAlgorithmException + * if no matching AlgorithmParameters engine is found. + * @throws InvalidAlgorithmParameterException + * if the parameters cannot be decoded properly. + * @return The AlgorithmParameters or null if none are + * enclosed in this structure. + */ + public AlgorithmParameters getParameters() throws NoSuchAlgorithmException, + InvalidAlgorithmParameterException { + AlgorithmParameters params; + String s; + + if (parameters_.isOptional()) + return null; + + if (parameters_.getTag() == ASN1.TAG_NULL + && parameters_.getTagClass() == ASN1.CLASS_UNIVERSAL) + return null; + + /* + * Since alias resolution is Provider-local and hardly any Provider does + * it right and complete, we have to resolve aliases on our own. + */ + s = JCA.getName(algorithm_.toString()); + + if (s == null) + throw new NoSuchAlgorithmException("Cannot resolve " + + algorithm_.toString()); + + /* + * If we resolve cipher names then we have to remove the trailing + * padding string, if present. + */ + int n; + + n = s.indexOf("/"); + + if (n > 0) + s = s.substring(0, n); + + params = AlgorithmParameters.getInstance(s); + + try { + params.init(parameters_.getEncoded()); + } catch (IOException e) { + throw new InvalidAlgorithmParameterException( + "Caught IOException(\"" + e.getMessage() + "\")"); + } catch (ASN1Exception e) { + throw new InvalidAlgorithmParameterException( + "Caught ASN1Exception(\"" + e.getMessage() + "\")"); + } + return params; + } + + /** + * This method returns the OID of the algorithm represented by this + * AlgorithmIdentifier. The OID returned is the one used internally. Do not + * modify the returned OID! Otherwise, side effects occur. + * + * @return The algorithm OID. + */ + public ASN1ObjectIdentifier getAlgorithmOID() { + return algorithm_; + } + + /** + * This method returns the JCE standard name of the algorithm specified in + * this AlgorithmIdentifier. However, for this to work a proper alias for + * the algorithm must be defined by some provider. See the general + * documentation of this class for details on that. + *

+ * + * This method calls {@link JCA#getName(String) JCA.getName()} with the + * string representation of this instance's + * {@link #algorithm_ object identifier}. + *

+ * + * If you are {@link #getParameters retrieving the parameters} anyway then + * avoid calling this method and call + * {@link java.security.AlgorithmParameters#getAlgorithm getAlgorithm} on + * the parameter instance instead. + */ + public String getAlgorithmName() { + return JCA.getName(algorithm_.toString()); + } + + /** + * Returns a string representation of this object. + * + * @return The string representation. + */ + public String toString() { + String s; + String t; + + t = "X.509 AlgorithmIdentifier " + algorithm_.toString(); + s = getAlgorithmName(); + + if (s != null) + return t + " (" + s + ")"; + + return t; + } + + /** + * This method returns true if the given object is an + * instance of this class or a subclass thereof and the algorithm OID of the + * given object equals this object's algorithm OID. + * + * @return true if the given object equals this one. + */ + public boolean equals(Object o) { + if (!(o instanceof AlgorithmIdentifier)) + return false; + + return algorithm_.equals(((AlgorithmIdentifier) o).getAlgorithmOID()); + } + + public int hashCode() { + return algorithm_.hashCode(); + } + + /** + * Returns a clone. The clone is a deep copy of this instance except from + * the constraints. Constraints are copied by reference. + * + * @return The clone. + */ + public Object clone() { + AlgorithmIdentifier aid; + + aid = (AlgorithmIdentifier) super.clone(); + aid.clear(); + aid.algorithm_ = (ASN1ObjectIdentifier) algorithm_.clone(); + aid.parameters_ = (ASN1Opaque) parameters_.clone(); + + aid.add(algorithm_); + aid.add(parameters_); + + return aid; + } + +} diff --git a/src/codec/x509/SubjectPublicKeyInfo.java b/src/codec/x509/SubjectPublicKeyInfo.java new file mode 100644 index 00000000..26b357ab --- /dev/null +++ b/src/codec/x509/SubjectPublicKeyInfo.java @@ -0,0 +1,418 @@ +/* ======================================================================== + * + * This file is part of CODEC, which is a Java package for encoding + * and decoding ASN.1 data structures. + * + * Author: Fraunhofer Institute for Computer Graphics Research IGD + * Department A8: Security Technology + * Fraunhoferstr. 5, 64283 Darmstadt, Germany + * + * Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * Hansastr. 27c, 80686 Munich, Germany. + * + * ------------------------------------------------------------------------ + * + * The software package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software package; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA or obtain a copy of the license at + * http://www.fsf.org/licensing/licenses/lgpl.txt. + * + * ------------------------------------------------------------------------ + * + * The CODEC library can solely be used and distributed according to + * the terms and conditions of the GNU Lesser General Public License for + * non-commercial research purposes and shall not be embedded in any + * products or services of any user or of any third party and shall not + * be linked with any products or services of any user or of any third + * party that will be commercially exploited. + * + * The CODEC library has not been tested for the use or application + * for a determined purpose. It is a developing version that can + * possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur + * Foerderung der angewandten Forschung e.V. does not warrant that the + * operation of the CODEC library will be uninterrupted or error-free. + * Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. warrant that the CODEC library will operate and + * interact in an uninterrupted or error-free way together with the + * computer program libraries of third parties which the CODEC library + * accesses and which are distributed together with the CODEC library. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not warrant that the operation of the third parties's computer + * program libraries themselves which the CODEC library accesses will + * be uninterrupted or error-free. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * shall not be liable for any errors or direct, indirect, special, + * incidental or consequential damages, including lost profits resulting + * from the combination of the CODEC library with software of any user + * or of any third party or resulting from the implementation of the + * CODEC library in any products, systems or services of any user or + * of any third party. + * + * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * does not provide any warranty nor any liability that utilization of + * the CODEC library will not interfere with third party intellectual + * property rights or with any other protected third party rights or will + * cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung + * der angewandten Forschung e.V. is currently not aware of any such + * rights. + * + * The CODEC library is supplied without any accompanying services. + * + * ======================================================================== + */ +package codec.x509; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +import codec.CorruptedCodeException; +import codec.InconsistentStateException; +import codec.asn1.ASN1BitString; +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1Sequence; +import codec.asn1.ASN1Type; +import codec.asn1.DERDecoder; +import codec.asn1.DEREncoder; + +/** + * Subject Public Key Info according to RFC2459. Consists of an AlgorithmID and + * the corresponding public key

+ * + *
+ * SubjectPublicKeyInfo  ::=  SEQUENCE  {
+ *   algorithm            AlgorithmIdentifier,
+ *   subjectPublicKey     BIT STRING
+ * }
+ * 
+ * + *
+ * + * Raw public key material embedded in this structure need not be ASN.1/DER + * encoded. Some crypto systems such as elliptic curve systems use specific + * encodings of keys. For this reason, keys can be retrieved by means of two + * alternative ways: + *
    + *
  • Raw encoded keys, returned as byte arrays, and + *
  • Raw decoded keys, returned as ASN.1 types. + *
+ * Only ASN.1/DER is supported as decoding method. Keys encoded in another code + * must be decoded externally. + * + * Creation date: (18.08.99 15:23:09) + * + * @author Markus Tak + * @version "$Id: SubjectPublicKeyInfo.java,v 1.3 2004/08/16 08:53:58 pebinger + * Exp $" + */ +public class SubjectPublicKeyInfo extends ASN1Sequence { + + private AlgorithmIdentifier algorithm_; + + private ASN1BitString encodedKey_; + + /** + * Creates an instance that is ready for decoding. + */ + public SubjectPublicKeyInfo() { + super(2); + + algorithm_ = new AlgorithmIdentifier(); + add(algorithm_); + + encodedKey_ = new ASN1BitString(); + add(encodedKey_); + } + + /** + * Creates an instance with the given {@link AlgorithmIdentifier + * AlgorithmIdentifier} and encoded public key. The public key is wrapped + * into an {@link ASN1BitString ASN1BitString} as required by the + * specification of this structure. Hence, the encoded key is taken "as + * is". + *

+ * + * The given arguments are put into this instance literally. The arguments + * are not copied or cloned. Therefor side effects can occur when the given + * arguments are modified after this constructor was called. + * + * @param aid + * AlgorithmIdentifier of the public key algorithm + * @param key + * The encoded key material. + * @throws NullPointerException + * if either argument is null. + */ + public SubjectPublicKeyInfo(AlgorithmIdentifier aid, byte[] key) { + super(2); + + if (aid == null || key == null) + throw new NullPointerException("Some arg is null!"); + + algorithm_ = aid; + add(algorithm_); + encodedKey_ = new ASN1BitString(key, 0); + add(encodedKey_); + } + + /** + * Creates an instance with the given {@link AlgorithmIdentifier + * AlgorithmIdentifier} and key. The given key must be a raw key + * structure represented by means of an ASN.1 structure. The key structure + * is encoded into a bit string which is put into this instance. + * + * @param aid + * The AlgorithmIdentifier of the public key algorithm. + * @param key + * The public key as a ASN.1 data structure. + */ + public SubjectPublicKeyInfo(AlgorithmIdentifier aid, ASN1Type key) { + super(2); + + algorithm_ = aid; + add(algorithm_); + add(null); + setRawKey(key); + } + + /** + * Creates an instance that is initialized from the given public key. + * + * @param key + * The public key. + * @throws InvalidKeyException + * if the key cannot be decoded properly. + * @throws NullPointerException + * if the given key is null. + */ + public SubjectPublicKeyInfo(PublicKey key) throws InvalidKeyException { + super(2); + setPublicKey(key); + } + + /** + * Returns the public key embedded in this structure. + *

+ * + * This method creates an X509EncodedKeySpec of this instance and feeds it + * into a key factory. In order to locate a suitable key factory, the + * installed providers must define appropriate OID mappings. + * + * @return The public key. + * @throws InconsistentStateException + * if the key spec generated by this method is rejected by + * the key factory that is used to generate the key. + * @throws NoSuchAlgorithmException + * if there is no key factory registered for the algorithm + * of the embedded key or no appropriate OID mapping is + * defined by the installed providers. + */ + public PublicKey getPublicKey() throws NoSuchAlgorithmException { + ByteArrayOutputStream bos; + X509EncodedKeySpec spec; + DEREncoder enc; + KeyFactory kf; + String alg; + + try { + bos = new ByteArrayOutputStream(); + enc = new DEREncoder(bos); + encode(enc); + spec = new X509EncodedKeySpec(bos.toByteArray()); + enc.close(); + + alg = algorithm_.getAlgorithmOID().toString(); + kf = KeyFactory.getInstance(alg); + + return kf.generatePublic(spec); + } catch (ASN1Exception e) { + throw new InconsistentStateException("Internal, encoding error!"); + } catch (IOException e) { + throw new InconsistentStateException( + "Internal, I/O exception caught!"); + } catch (InvalidKeySpecException e) { + throw new InconsistentStateException( + "Encoded key spec rejected by key factory!"); + } + } + + /** + * Initializes this instance with the given public key. + * + * @param key + * The public key from which this instance is initialized. + * @throws InvalidKeyException + * if the given key cannot be decoded properly. + * @throws NullPointerException + * if the given key is null. + */ + public void setPublicKey(PublicKey key) throws InvalidKeyException { + if (key == null) + throw new NullPointerException("Key is null!"); + + DERDecoder dec; + + clear(); + + algorithm_ = new AlgorithmIdentifier(); + add(algorithm_); + encodedKey_ = new ASN1BitString(); + add(encodedKey_); + + try { + dec = new DERDecoder(new ByteArrayInputStream(key.getEncoded())); + + decode(dec); + dec.close(); + } catch (IOException e) { + throw new InvalidKeyException("Caught IOException!"); + } catch (ASN1Exception e) { + throw new InvalidKeyException("Bad encoding!"); + } + } + + /** + * Encodes and sets the given ASN.1 key structure as the raw key. + * + * @throws InconsistentStateException + * if an internal error occurs while the key is encoded. + * This should never happen. + */ + protected void setRawKey(ASN1Type key) { + ByteArrayOutputStream bos; + DEREncoder enc; + + try { + bos = new ByteArrayOutputStream(); + enc = new DEREncoder(bos); + key.encode(enc); + encodedKey_ = new ASN1BitString(bos.toByteArray(), 0); + enc.close(); + set(1, encodedKey_); + } catch (ASN1Exception e) { + throw new InconsistentStateException("Internal, encoding error!"); + } catch (IOException e) { + throw new InconsistentStateException( + "Internal, I/O exception caught!"); + } + } + + /** + * Returns the raw key material. The key material consists of an encoded + * key structure. ASN.1/DER is often used as the encoding. However, + * this need not always be the case. Elliptic curve cryptosystems use + * specific encodings. + *

+ * + * If the key encoding is ASN.1/DER then the raw key can be retrieved as an + * ASN.1 type by means of the {@link #getDecodedRawKey getDecodedRawKey()} + * method. + * + * @return The raw key bytes as a byte array. + */ + public byte[] getRawKey() { + return encodedKey_.getBytes(); + } + + /** + * Returns an ASN.1 type that represents the decoded raw key. Decoding is + * done by means of ASN.1/DER. Be careful, not all public keys are + * encoded according to DER. Elliptic curve cryptosystems use specific + * encodings. + * + * @return The raw key decoded according to DER. + */ + public ASN1Type getDecodedRawKey() throws CorruptedCodeException { + DERDecoder dec; + ASN1Type raw; + + try { + dec = new DERDecoder(new ByteArrayInputStream(encodedKey_ + .getBytes())); + + raw = dec.readType(); + dec.close(); + + return raw; + } catch (ASN1Exception e) { + throw new CorruptedCodeException("Cannot decode raw key!"); + } catch (IOException e) { + throw new InconsistentStateException( + "Internal, I/O exception caught!"); + } + } + + /** + * Returns the {@link AlgorithmIdentifier AlgorithmIdentifier} of the public + * key. + * + * @return The key algorithm's AlgorithmIdentifier. + */ + public AlgorithmIdentifier getAlgorithmIdentifier() { + return algorithm_; + } + + /** + * sets the AlgorithmIdentifier for this public key + * + * @param aid + * AlgorithmIdentifier of this public key + */ + public void setAlgorithmIdentifier(AlgorithmIdentifier aid) { + if (aid == null) + throw new NullPointerException("Need an algorithm identifier!"); + + set(0, aid); + algorithm_ = aid; + } + + /** + * @deprecated This method is no longer used. + */ + public ASN1Type getKeyStruct() throws CorruptedCodeException { + return getKeyStruct(true); + } + + /** + * @param derDecode + * true if the raw key shall be decoded. + * @deprecated This method is no longer used. + */ + public ASN1Type getKeyStruct(boolean derDecode) + throws CorruptedCodeException { + if (derDecode) + return getDecodedRawKey(); + + return encodedKey_; + } + + /** + * @param key + * The key structure. + * @deprecated This method is no longer used. + */ + public void setKeyStruct(ASN1Type key) { + setRawKey(key); + } + +} diff --git a/src/de/flexiprovider/api/AsymmetricBlockCipher.java b/src/de/flexiprovider/api/AsymmetricBlockCipher.java new file mode 100644 index 00000000..eb546e71 --- /dev/null +++ b/src/de/flexiprovider/api/AsymmetricBlockCipher.java @@ -0,0 +1,555 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.api; + +import java.io.ByteArrayOutputStream; + +import de.flexiprovider.api.exceptions.BadPaddingException; +import de.flexiprovider.api.exceptions.IllegalBlockSizeException; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.InvalidParameterException; +import de.flexiprovider.api.exceptions.ShortBufferException; +import de.flexiprovider.api.keys.Key; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * The AsymmetricBlockCipher class extends CipherSpi. An instance of this class + * will be created by the Cipher.getInstance(String)-mechanism of the Cipher + * class.
+ * NOTE: Some Ciphers are using Padding. OneAndZeroesPadding is used as default + * padding. However padding can still be specified, but mode is not supported: + * Example: Cipher.getInstance("SomeCipher/NONE/SomePadding"); If you try to + * intantiate the cipher with something else than "NONE" as mode, + * NoSuchAlgorithmException is thrown. + * + * @author Thomas Wahrenbruch + * @author Hristo Indzhov + */ +public abstract class AsymmetricBlockCipher extends Cipher { + + /** + * ParameterSpec used with this cipher + */ + protected AlgorithmParameterSpec paramSpec; + + /** + * Internal buffer + */ + protected ByteArrayOutputStream buf; + + /** + * The maximum number of bytes the cipher can decrypt. + */ + protected int maxPlainTextSize; + + /** + * The maximum number of bytes the cipher can encrypt. + */ + protected int cipherTextSize; + + /** + * The AsymmetricBlockCipher() constructor + */ + public AsymmetricBlockCipher() { + buf = new ByteArrayOutputStream(); + } + + /** + * Return the block size (in bytes). Note: although the ciphers extending + * this class are not block ciphers, the method was adopted to return the + * maximal plaintext and ciphertext sizes for non hybrid ciphers. If the + * cipher is hybrid, it returns 0. + * + * @return if the cipher is not a hybrid one the max plain/cipher text size + * is returned, otherwise 0 is returned + */ + public final int getBlockSize() { + return opMode == ENCRYPT_MODE ? maxPlainTextSize : cipherTextSize; + } + + /** + * @return null since no initialization vector is used. + */ + public final byte[] getIV() { + return null; + } + + /** + * Return the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length inLen (in bytes). This call takes into + * account any unprocessed (buffered) data from a previous update call, and + * padding. The actual output length of the next update() or doFinal() call + * may be smaller than the length returned by this method. + *

+ * If the input length plus the length of the buffered data exceeds the + * maximum length, 0 is returned. + * + * @param inLen + * the length of the input + * @return the length of the ciphertext or 0 if the input is too + * long. + */ + public final int getOutputSize(int inLen) { + + int totalLen = inLen + buf.size(); + + int maxLen = getBlockSize(); + + if (totalLen > maxLen) { + // the length of the input exceeds the maximal supported length + return 0; + } + + return maxLen; + } + + /** + *

+ * Returns the parameters used with this cipher. + *

+ * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + *

+ * + * @return the parameters used with this cipher, or null if this cipher does + * not use any parameters. + */ + public final AlgorithmParameterSpec getParameters() { + return paramSpec; + } + + /** + * Initializes the cipher for encryption by forwarding it to + * initEncrypt(Key, FlexiSecureRandom). + * + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + * + * @param key + * the encryption or decryption key. + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + */ + public final void initEncrypt(Key key) throws InvalidKeyException { + try { + initEncrypt(key, null, Registry.getSecureRandom()); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize this cipher for encryption by forwarding it to + * initEncrypt(Key, FlexiSecureRandom, AlgorithmParameterSpec). + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + * + * @param key + * the encryption or decryption key. + * @param random + * the source of randomness. + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + */ + public final void initEncrypt(Key key, SecureRandom random) + throws InvalidKeyException { + + try { + initEncrypt(key, null, random); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initializes the cipher for encryption by forwarding it to + * initEncrypt(Key, FlexiSecureRandom, AlgorithmParameterSpec). + * + * @param key + * the encryption or decryption key. + * @param params + * the algorithm parameters. + * + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException + * if the given algortihm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and params + * is null. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + initEncrypt(key, params, Registry.getSecureRandom()); + } + + /** + * This method initializes the AsymmetricBlockCipher with a certain key for + * data encryption. + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + *

+ * + * @param key + * the key which has to be used to encrypt data. + * @param secureRandom + * the source of randomness. + * @param params + * the algorithm parameters. + * + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException + * if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and params + * is null. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom secureRandom) throws InvalidKeyException, + InvalidAlgorithmParameterException { + opMode = ENCRYPT_MODE; + initCipherEncrypt(key, params, secureRandom); + } + + /** + * Initialize the cipher for decryption by forwarding it to + * {@link #initDecrypt(Key, AlgorithmParameterSpec)}. + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + * + * @param key + * the encryption or decryption key. + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + */ + public final void initDecrypt(Key key) throws InvalidKeyException { + try { + initDecrypt(key, null); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * This method initializes the AsymmetricBlockCipher with a certain key for + * data decryption. + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + *

+ * + * @param key + * the key which has to be used to decrypt data. + * @param params + * the algorithm parameters. + * + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException + * if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and params + * is null. + */ + public final void initDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + opMode = DECRYPT_MODE; + initCipherDecrypt(key, params); + } + + /** + * Continue a multiple-part encryption or decryption operation. This method + * just writes the input into an internal buffer. + * + * @param input + * byte array containing the next part of the input + * @param inOff + * index in the array where the input starts + * @param inLen + * length of the input + * @return a new buffer with the result (always empty) + */ + public final byte[] update(byte[] input, int inOff, int inLen) { + if (inLen != 0) { + buf.write(input, inOff, inLen); + } + return new byte[0]; + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the output buffer + * @param outOff + * the offset where the result is stored + * @return the length of the output (always 0) + */ + public final int update(byte[] input, int inOff, int inLen, byte[] output, + int outOff) { + update(input, inOff, inLen); + return 0; + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @return a new buffer with the result + * @throws IllegalBlockSizeException + * if the plaintext or ciphertext size is too large. + * @throws BadPaddingException + * if the ciphertext is invalid. + */ + public final byte[] doFinal(byte[] input, int inOff, int inLen) + throws IllegalBlockSizeException, BadPaddingException { + + checkLength(inLen); + update(input, inOff, inLen); + byte[] mBytes = buf.toByteArray(); + buf.reset(); + + switch (opMode) { + case ENCRYPT_MODE: + return messageEncrypt(mBytes); + + case DECRYPT_MODE: + return messageDecrypt(mBytes); + + default: + return null; + + } + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the buffer for the result + * @param outOff + * the offset where the result is stored + * @return the output length + * @throws ShortBufferException + * if the output buffer is too small to hold the result. + * @throws IllegalBlockSizeException + * if the plaintext or ciphertext size is too large. + * @throws BadPaddingException + * if the ciphertext is invalid. + */ + public final int doFinal(byte[] input, int inOff, int inLen, byte[] output, + int outOff) throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + + if (output.length < getOutputSize(inLen)) { + throw new ShortBufferException("Output buffer too short."); + } + + byte[] out = doFinal(input, inOff, inLen); + System.arraycopy(out, 0, output, outOff, out.length); + return out.length; + } + + /** + * Since asymmetric block ciphers do not support modes, this method does + * nothing. + * + * @param modeName + * the cipher mode (unused) + */ + protected final void setMode(String modeName) { + // empty + } + + /** + * Since asymmetric block ciphers do not support padding, this method does + * nothing. + * + * @param paddingName + * the name of the padding scheme (not used) + */ + protected final void setPadding(String paddingName) { + // empty + } + + /** + * Check if the message length plus the length of the input length can be + * en/decrypted. This method uses the specific values + * {@link #maxPlainTextSize} and {@link #cipherTextSize} which are set by + * the implementations. If the input length plus the length of the internal + * buffer is greater than {@link #maxPlainTextSize} for encryption or not + * equal to {@link #cipherTextSize} for decryption, an + * {@link IllegalBlockSizeException} will be thrown. + * + * @param inLen + * length of the input to check + * @throws IllegalBlockSizeException + * if the input length is invalid. + */ + protected void checkLength(int inLen) throws IllegalBlockSizeException { + + int inLength = inLen + buf.size(); + + if (opMode == ENCRYPT_MODE) { + if (inLength > maxPlainTextSize) { + throw new IllegalBlockSizeException( + "The length of the plaintext (" + inLength + + " bytes) is not supported by " + + "the cipher (max. " + maxPlainTextSize + + " bytes)."); + } + } else if (opMode == DECRYPT_MODE) { + if (inLength != cipherTextSize) { + throw new IllegalBlockSizeException( + "Illegal ciphertext length (expected " + cipherTextSize + + " bytes, was " + inLength + " bytes)."); + } + } + + } + + /** + * Initialize the AsymmetricBlockCipher with a certain key for data + * encryption. + * + * @param key + * the key which has to be used to encrypt data + * @param params + * the algorithm parameters + * @param sr + * the source of randomness + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherEncrypt(Key key, + AlgorithmParameterSpec params, SecureRandom sr) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Initialize the AsymmetricBlockCipher with a certain key for data + * encryption. + * + * @param key + * the key which has to be used to decrypt data + * @param params + * the algorithm parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherDecrypt(Key key, + AlgorithmParameterSpec params) throws InvalidKeyException, + InvalidAlgorithmParameterException; + + /** + * Encrypt the message stored in input. The method should also perform an + * additional length check. + * + * @param input + * the message to be encrypted (usually the message length is + * less than or equal to maxPlainTextSize) + * @return the encrypted message (it has length equal to maxCipherTextSize_) + * @throws IllegalBlockSizeException + * if the input is inappropriate for this cipher. + * @throws BadPaddingException + * if the input format is invalid. + */ + protected abstract byte[] messageEncrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException; + + /** + * Decrypt the ciphertext stored in input. The method should also perform an + * additional length check. + * + * @param input + * the ciphertext to be decrypted (the ciphertext length is + * less than or equal to maxCipherTextSize) + * @return the decrypted message + * @throws IllegalBlockSizeException + * if the input is inappropriate for this cipher. + * @throws BadPaddingException + * if the input format is invalid. + */ + protected abstract byte[] messageDecrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException; + +} diff --git a/src/de/flexiprovider/api/AsymmetricHybridCipher.java b/src/de/flexiprovider/api/AsymmetricHybridCipher.java new file mode 100644 index 00000000..165cefd7 --- /dev/null +++ b/src/de/flexiprovider/api/AsymmetricHybridCipher.java @@ -0,0 +1,431 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + */ +package de.flexiprovider.api; + +import de.flexiprovider.api.exceptions.BadPaddingException; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.InvalidParameterException; +import de.flexiprovider.api.exceptions.ShortBufferException; +import de.flexiprovider.api.keys.Key; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * The AsymmetricHybridCipher class extends CipherSpi. An instance of this class + * will be created by the Cipher.getInstance(String)-mechanism of the Cipher + * class.
+ * NOTE: Some Ciphers are using Padding. OneAndZeroesPadding is used as default + * padding. However padding can still be specified, but mode is not supported: + * Example: Cipher.getInstance("SomeCipher/NONE/SomePadding"); If you try to + * intantiate the cipher with something else than "NONE" as mode, + * NoSuchAlgorithmException is thrown. + * + * @author Thomas Wahrenbruch + * @author Hristo Indzhov + */ +public abstract class AsymmetricHybridCipher extends Cipher { + + /** + * ParameterSpec used with this cipher + */ + protected AlgorithmParameterSpec paramSpec; + + /** + * Since asymmetric hybrid ciphers do not support modes, this method does + * nothing. + * + * @param modeName + * the cipher mode (unused) + */ + protected final void setMode(String modeName) { + // empty + } + + /** + * Since asymmetric hybrid ciphers do not support padding, this method does + * nothing. + * + * @param paddingName + * the name of the padding scheme (not used) + */ + protected final void setPadding(String paddingName) { + // empty + } + + /** + * @return null since no initialization vector is used. + */ + public final byte[] getIV() { + return null; + } + + /** + * @return 0 since the implementing algorithms are not block ciphers + */ + public final int getBlockSize() { + return 0; + } + + /** + * Return the parameters used with this cipher. + *

+ * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * + * @return the parameters used with this cipher, or null if this + * cipher does not use any parameters. + */ + public final AlgorithmParameterSpec getParameters() { + return paramSpec; + } + + /** + * Return the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length inLen (in bytes). This call takes into + * account any unprocessed (buffered) data from a previous update call, and + * padding. The actual output length of the next update() or doFinal() call + * may be smaller than the length returned by this method. + * + * @param inLen + * the length of the input + * @return the length of the output of the next update() or + * doFinal() call + */ + public final int getOutputSize(int inLen) { + return opMode == ENCRYPT_MODE ? encryptOutputSize(inLen) + : decryptOutputSize(inLen); + } + + /** + * Initialize the cipher for encryption by forwarding it to + * {@link #initEncrypt(Key, AlgorithmParameterSpec, SecureRandom)}. + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using {@link #getParameters()}. + * + * @param key + * the encryption key + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidParameterException + * if this cipher needs algorithm parameters for + * initialization and cannot generate parameters itself. + */ + public final void initEncrypt(Key key) throws InvalidKeyException { + try { + initEncrypt(key, null, Registry.getSecureRandom()); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize this cipher for encryption by forwarding it to + * {@link #initEncrypt(Key, AlgorithmParameterSpec, SecureRandom)}. + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using {@link #getParameters()}. + * + * @param key + * the encryption key + * @param random + * the source of randomness + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidParameterException + * if this cipher needs algorithm parameters for + * initialization and cannot generate parameters itself. + */ + public final void initEncrypt(Key key, SecureRandom random) + throws InvalidKeyException { + try { + initEncrypt(key, null, random); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize the cipher for encryption by forwarding it to initEncrypt(Key, + * FlexiSecureRandom, AlgorithmParameterSpec). + * + * @param key + * the encryption key + * @param params + * the algorithm parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException + * if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is initialized with + * null parameters and cannot generate parameters + * itself. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + initEncrypt(key, params, Registry.getSecureRandom()); + } + + /** + * Initialize the cipher with a certain key for data encryption. + *

+ * If this cipher requires any random bytes (e.g., for parameter + * generation), it will get them from random. + *

+ * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key + * the encryption key + * @param random + * the source of randomness + * @param params + * the algorithm parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException + * if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is initialized with + * null parameters and cannot generate parameters + * itself. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + opMode = ENCRYPT_MODE; + initCipherEncrypt(key, params, random); + } + + /** + * Initialize the cipher for decryption by forwarding it to initDecrypt(Key, + * FlexiSecureRandom). + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using {@link #getParameters()}. + * + * @param key + * the decryption key + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + */ + public final void initDecrypt(Key key) throws InvalidKeyException { + try { + initDecrypt(key, null); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize the cipher with a certain key for data decryption. + *

+ * If this cipher requires any random bytes (e.g., for parameter + * generation), it will get them from random. + *

+ * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + * + * @param key + * the decryption key + * @param params + * the algorithm parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException + * if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is initialized with + * null parameters and cannot generate parameters + * itself. + */ + public final void initDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + opMode = DECRYPT_MODE; + initCipherDecrypt(key, params); + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @return a new buffer with the result (maybe an empty byte array) + */ + public abstract byte[] update(byte[] input, int inOff, int inLen); + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the output buffer + * @param outOff + * the offset where the result is stored + * @return the length of the output + * @throws ShortBufferException + * if the output buffer is too small to hold the result. + */ + public final int update(byte[] input, int inOff, int inLen, byte[] output, + int outOff) throws ShortBufferException { + if (output.length < getOutputSize(inLen)) { + throw new ShortBufferException("output"); + } + byte[] out = update(input, inOff, inLen); + System.arraycopy(out, 0, output, outOff, out.length); + return out.length; + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @return a new buffer with the result + * @throws BadPaddingException + * if the ciphertext is invalid. + */ + public abstract byte[] doFinal(byte[] input, int inOff, int inLen) + throws BadPaddingException; + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the buffer for the result + * @param outOff + * the offset where the result is stored + * @return the output length + * @throws ShortBufferException + * if the output buffer is too small to hold the result. + * @throws BadPaddingException + * if the ciphertext is invalid. + */ + public final int doFinal(byte[] input, int inOff, int inLen, byte[] output, + int outOff) throws ShortBufferException, BadPaddingException { + + if (output.length < getOutputSize(inLen)) { + throw new ShortBufferException("Output buffer too short."); + } + byte[] out = doFinal(input, inOff, inLen); + System.arraycopy(out, 0, output, outOff, out.length); + return out.length; + } + + /** + * Compute the output size of an update() or doFinal() operation of a hybrid + * asymmetric cipher in encryption mode when given input of the specified + * length. + * + * @param inLen + * the length of the input + * @return the output size + */ + protected abstract int encryptOutputSize(int inLen); + + /** + * Compute the output size of an update() or doFinal() operation of a hybrid + * asymmetric cipher in decryption mode when given input of the specified + * length. + * + * @param inLen + * the length of the input + * @return the output size + */ + protected abstract int decryptOutputSize(int inLen); + + /** + * Initialize the AsymmetricHybridCipher with a certain key for data + * encryption. + * + * @param key + * the key which has to be used to encrypt data + * @param params + * the algorithm parameters + * @param sr + * the source of randomness + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherEncrypt(Key key, + AlgorithmParameterSpec params, SecureRandom sr) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Initialize the AsymmetricHybridCipher with a certain key for data + * encryption. + * + * @param key + * the key which has to be used to decrypt data + * @param params + * the algorithm parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherDecrypt(Key key, + AlgorithmParameterSpec params) throws InvalidKeyException, + InvalidAlgorithmParameterException; + +} diff --git a/src/de/flexiprovider/api/BlockCipher.java b/src/de/flexiprovider/api/BlockCipher.java new file mode 100644 index 00000000..28ffd533 --- /dev/null +++ b/src/de/flexiprovider/api/BlockCipher.java @@ -0,0 +1,897 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ +package de.flexiprovider.api; + +import java.lang.reflect.Method; + +import de.flexiprovider.api.exceptions.BadPaddingException; +import de.flexiprovider.api.exceptions.IllegalBlockSizeException; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.InvalidParameterException; +import de.flexiprovider.api.exceptions.NoSuchModeException; +import de.flexiprovider.api.exceptions.NoSuchPaddingException; +import de.flexiprovider.api.exceptions.ShortBufferException; +import de.flexiprovider.api.keys.Key; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.common.mode.ModeParameterSpec; +import de.flexiprovider.common.util.JavaSecureRandomWrapper; + +/** + * The BlockCipher class extends CipherSpi and handles the creation of + * PaddingScheme, BlockCipher and Mode. An instance of this class will be + * created by the Cipher.getInstance(String)-mechanism of the Cipher class. + *

+ * BlockCipher ensures that a Mode and a PaddingsScheme are created, even if the + * user has set no preferences. For this to work, BlockCipher requires version + * 1.3 of the mode class that can handle a getInstance()-call without arguments + * to return a default Mode or Padding. + *

+ * BlockCipher will acknowledge only one call of engineSetMode() and + * engineSetPadding() to ensure that these settings aren't changed while + * encrypting. + * + * @author Christoph Sesterhenn, Christoph Ender + * @author Ralf-Philipp Weinmann + * @author Martin Döring, Johannes Müller + */ +public abstract class BlockCipher extends Cipher { + + /** + * The reference to the mode class. + */ + private Mode mode; + + /** + * The reference to the padding scheme. + */ + private PaddingScheme paddingScheme; + + /** + * AlgorithmParameterSpec + */ + private AlgorithmParameterSpec paramSpec; + + /** + * This buffer holds the outsize left by an update operation + */ + private byte[] buffer = null; + + /** + * the block size of the mode + */ + private int modeBlockSize; + + /** + * Used to check if an initialization method has been called. + */ + private boolean initialized = false; + + /** + * the source of randomness, if necessary + */ + protected SecureRandom random; + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * Initialize this cipher object with proper key and algorithm parameters, + * and some random seed. Before a cipher object is ready for data + * processing, it has to be initialized according to the desired + * cryptographic operation, which is specified by the opmode parameter + * (either ENCRYPT_MODE or DECCRYPT_MODE). e.g. + * cipher_obj.init(Cipher.ENCRYPT_MODE, key, alg_params, random_seed); The + * Cipher init will call the proper CipherSpi engineInit method. Note: If + * the Mode needs an initialization vector, a try to retrieve it from the + * AlgorithmParametersSpec is made. + * + * @param opmode + * the operation mode for which this cipher is used + * (ENCRYPT_MODE or DECRYPT_MODE) + * @param key + * the key + * @param paramSpec + * the algorithm parameters + * @param javaRand + * the random seed + * @throws java.security.InvalidKeyException + * if the key is inappropriate for initializing this block + * cipher. + * @throws java.security.InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + protected final void engineInit(int opmode, java.security.Key key, + java.security.spec.AlgorithmParameterSpec paramSpec, + java.security.SecureRandom javaRand) + throws java.security.InvalidKeyException, + java.security.InvalidAlgorithmParameterException { + + random = new JavaSecureRandomWrapper(javaRand); + initModeAndPadding(); + opMode = opmode; + + buffer = new byte[0]; + ModeParameterSpec modeParams; + AlgorithmParameterSpec cipherParams; + + if (paramSpec == null) { + modeParams = null; + cipherParams = null; + } else if (paramSpec instanceof javax.crypto.spec.IvParameterSpec) { + modeParams = new ModeParameterSpec( + (javax.crypto.spec.IvParameterSpec) paramSpec); + cipherParams = null; + + } else if (paramSpec instanceof ModeParameterSpec) { + modeParams = (ModeParameterSpec) paramSpec; + cipherParams = null; + + } else { + if (!(paramSpec instanceof AlgorithmParameterSpec)) { + throw new java.security.InvalidAlgorithmParameterException( + "unsupported type"); + } + cipherParams = (AlgorithmParameterSpec) paramSpec; + + byte[] iv; + Method getIV; + try { + getIV = cipherParams.getClass().getMethod("getIV", null); + iv = (byte[]) getIV.invoke(cipherParams, null); + } catch (Exception ex) { + // if no getIV() method is found, iv remains null + iv = null; + } + + if (iv == null) { + modeParams = null; + } else { + modeParams = new ModeParameterSpec(iv); + } + } + + if (!(key instanceof SecretKey)) { + throw new java.security.InvalidKeyException("unsupported type"); + } + + if (opmode == ENCRYPT_MODE) { + mode.initEncrypt((SecretKey) key, modeParams, cipherParams); + } else if (opmode == DECRYPT_MODE) { + mode.initDecrypt((SecretKey) key, modeParams, cipherParams); + } + modeBlockSize = mode.blockSize; + paddingScheme.setBlockSize(modeBlockSize); + + initialized = true; + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Set the mode of this cipher. The mode can only be set once. + * + * @param modeName + * the cipher mode + * @throws NoSuchModeException + * if neither the mode with the given name nor the default + * mode can be found + */ + public final void setMode(String modeName) throws NoSuchModeException { + if (mode != null) { + return; + } + + if ((modeName == null) || (modeName == "")) { + mode = Registry.getMode(); + } else { + mode = Registry.getMode(modeName); + } + mode.setBlockCipher(this); + } + + /** + * Set the padding scheme of this cipher. The padding scheme can only be set + * once. + * + * @param paddingName + * the padding scheme + * @throws NoSuchPaddingException + * if the requested padding scheme cannot be found. + */ + public final void setPadding(String paddingName) + throws NoSuchPaddingException { + if (paddingScheme != null) { + return; + } + + if (paddingName == null || paddingName.equals("")) { + paddingScheme = Registry.getPaddingScheme(); + } else { + paddingScheme = Registry.getPaddingScheme(paddingName); + } + } + + /** + * Return the initialization vector. This is useful in the context of + * password-based encryption or decryption, where the IV is derived from a + * user-provided passphrase. + * + * @return the initialization vector in a new buffer, or null if + * the underlying algorithm does not use an IV, or if the IV has not + * yet been set. + */ + public final byte[] getIV() { + return initialized ? mode.iv : null; + } + + /** + * Return the blocksize the algorithm uses. This method will usually be + * called by the mode. + * + * @return the blocksize of the cipher + */ + protected abstract int getCipherBlockSize(); + + /** + * @return the block size of the used mode, or -1 if the cipher has not been + * initialized. + */ + public final int getBlockSize() { + return initialized ? modeBlockSize : -1; + } + + /** + * Return the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length inputLen (in bytes). + *

+ * This call takes into account any unprocessed (buffered) data from a + * previous update call, and padding. + *

+ * The actual output length of the next update or doFinal call may be + * smaller than the length returned by this method. + * + * @param inLen + * the input length (in bytes) + * @return the required output buffer size (in bytes) + */ + public final int getOutputSize(int inLen) { + if (!initialized) { + return -1; + } + final int newInLen = inLen + (buffer == null ? 0 : buffer.length); + return newInLen + paddingScheme.padLength(newInLen); + } + + /** + * Return the parameters used with this cipher. + *

+ * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * + * @return the parameters used with this cipher, or null if this cipher does + * not use any parameters. + */ + public final AlgorithmParameterSpec getParameters() { + return initialized ? paramSpec : null; + } + + /** + * Initialize this cipher with a key and a source of randomness for + * encryption. + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + * + * @param key + * the encryption key + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidParameterException + * if this block cipher requires parameters for + * initialization and cannot generate parameters itself. + */ + public final void initEncrypt(Key key) throws InvalidKeyException, + InvalidParameterException { + initEncrypt(key, Registry.getSecureRandom()); + } + + /** + * Initialize this cipher with a key and a source of randomness for + * encryption. + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random and they will be stored in the class variable rndBytes. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + * + * @param key + * the encryption key + * @param random + * the source of randomness + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidParameterException + * if this block cipher requires parameters for + * initialization and cannot generate parameters itself. + */ + public final void initEncrypt(Key key, SecureRandom random) + throws InvalidKeyException, InvalidParameterException { + + try { + initEncrypt(key, (ModeParameterSpec) null, + (AlgorithmParameterSpec) null, random); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for encryption. + *

+ * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and raise an + * InvalidAlgorithmParameterException if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key + * the encryption key + * @param cipherParams + * the cipher parameters + * @param random + * the source of randomness + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec cipherParams, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + initEncrypt(key, null, cipherParams, random); + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for encryption. + *

+ * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and raise an + * InvalidAlgorithmParameterException if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key + * the encryption key + * @param modeParams + * the mode parameters + * @param cipherParams + * the cipher parameters + * @param random + * the source of randomness + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + public final void initEncrypt(Key key, ModeParameterSpec modeParams, + AlgorithmParameterSpec cipherParams, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + this.random = random != null ? random : Registry.getSecureRandom(); + initModeAndPadding(); + opMode = ENCRYPT_MODE; + + buffer = new byte[0]; + paramSpec = cipherParams; + + if (!(key instanceof SecretKey)) { + throw new InvalidKeyException("unsupported type"); + } + + mode.initEncrypt((SecretKey) key, modeParams, cipherParams); + modeBlockSize = mode.blockSize; + paddingScheme.setBlockSize(modeBlockSize); + + initialized = true; + } + + /** + * Initialize this cipher with a key and a source of randomness for + * decryption. + *

+ * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + * + * @param key + * the encryption key + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws InvalidParameterException + * if the parameters are + */ + public final void initDecrypt(Key key) throws InvalidKeyException, + InvalidParameterException { + try { + initDecrypt(key, (ModeParameterSpec) null, + (AlgorithmParameterSpec) null); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for decryption. + *

+ * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and throw an + * {@link InvalidAlgorithmParameterException} if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key + * the encryption key + * @param cipherParams + * the cipher parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + public final void initDecrypt(Key key, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + initDecrypt(key, null, cipherParams); + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for decryption. + *

+ * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and throw an + * {@link InvalidAlgorithmParameterException} if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key + * the encryption key + * @param modeParams + * the mode parameters + * @param cipherParams + * the cipher parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + public final void initDecrypt(Key key, ModeParameterSpec modeParams, + AlgorithmParameterSpec cipherParams) throws InvalidKeyException, + InvalidAlgorithmParameterException { + + initModeAndPadding(); + opMode = DECRYPT_MODE; + + buffer = new byte[0]; + paramSpec = cipherParams; + + if (!(key instanceof SecretKey)) { + throw new InvalidKeyException("unsupported type"); + } + + mode.initDecrypt((SecretKey) key, modeParams, cipherParams); + modeBlockSize = mode.blockSize; + paddingScheme.setBlockSize(modeBlockSize); + + initialized = true; + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @return a new buffer with the result (maybe an empty byte array) + */ + public final byte[] update(byte[] input, int inOff, int inLen) { + + // if the cipher is not initialized or input is empty, return an empty + // byte array + if (!initialized || input == null || inLen <= 0) { + return new byte[0]; + } + + // concatenate buffer and input (only if buffer is not empty) + int bufLen = buffer.length; + int newInLen, newInOff; + byte[] newInput; + if (bufLen == 0) { + newInOff = inOff; + newInLen = inLen; + newInput = input; + } else { + newInLen = bufLen + inLen; + newInOff = 0; + newInput = new byte[newInLen]; + System.arraycopy(buffer, 0, newInput, 0, bufLen); + System.arraycopy(input, inOff, newInput, bufLen, inLen); + } + + // compute number of blocks to process and remaining bytes + int numBlocks = newInLen / modeBlockSize; + int numBytes = numBlocks * modeBlockSize; + int remaining = newInLen - numBytes; + if (opMode == DECRYPT_MODE && remaining == 0) { + remaining = modeBlockSize; + numBlocks--; + numBytes -= modeBlockSize; + } + + byte[] output = new byte[numBytes]; + int outOff = 0; + + // process whole blocks + for (int block = 0; block < numBlocks; block++) { + if (opMode == ENCRYPT_MODE) { + mode.nextChunkEncrypt(newInput, newInOff, output, outOff); + } else if (opMode == DECRYPT_MODE) { + mode.nextChunkDecrypt(newInput, newInOff, output, outOff); + } + newInOff += modeBlockSize; + outOff += modeBlockSize; + } + + // copy unprocessed bytes to buffer + buffer = new byte[remaining]; + System.arraycopy(newInput, newInOff, buffer, 0, remaining); + + return output; + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the output buffer + * @param outOff + * the offset where the result is stored + * @return the length of the output + * @throws ShortBufferException + * if the output buffer is too small to hold the result. + */ + public final int update(byte[] input, int inOff, int inLen, byte[] output, + int outOff) throws ShortBufferException { + + // if the cipher is not initialized or input is empty return 0 + if (!initialized || input == null || inLen <= 0) { + return 0; + } + + // compute number of bytes to process + int newInLen = buffer.length + inLen; + int remaining = newInLen % modeBlockSize; + if (opMode == DECRYPT_MODE && remaining == 0) { + remaining = modeBlockSize; + } + int numBytes = newInLen - remaining; + + if (output.length - outOff < numBytes) { + throw new ShortBufferException("output"); + } + + byte[] update = update(input, inOff, inLen); + System.arraycopy(update, 0, output, outOff, update.length); + + return update.length; + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @return a new buffer with the result + * @throws IllegalBlockSizeException + * if the total input length is not a multiple of the block + * size (for encryption when no padding is used or for + * decryption). + * @throws BadPaddingException + * if unpadding fails. + */ + public final byte[] doFinal(byte[] input, int inOff, int inLen) + throws IllegalBlockSizeException, BadPaddingException { + + byte[] output = new byte[0]; + + if (input == null && buffer == null) { + return output; + } + + byte[] update = update(input, inOff, inLen); + int updLen = update.length; + int bufLen = buffer.length; + + if (opMode == ENCRYPT_MODE) { + int padLen = paddingScheme.padLength(bufLen); + if (padLen == 0) { + return update; + } + output = new byte[updLen + bufLen + padLen]; + System.arraycopy(update, 0, output, 0, updLen); + System.arraycopy(buffer, 0, output, updLen, bufLen); + paddingScheme.pad(output, updLen, bufLen); + mode.nextChunkEncrypt(output, updLen, output, updLen); + } else if (opMode == DECRYPT_MODE) { + if (bufLen != modeBlockSize) { + throw new IllegalBlockSizeException( + "ciphertext length is not a multiple of block size"); + } + mode.nextChunkDecrypt(buffer, 0, buffer, 0); + int padOffset = paddingScheme.unpad(buffer, 0, modeBlockSize); + output = new byte[updLen + padOffset]; + System.arraycopy(update, 0, output, 0, updLen); + System.arraycopy(buffer, 0, output, updLen, padOffset); + } + + buffer = null; + mode.reset(); + + return output; + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the buffer for the result + * @param outOff + * the offset where the result is stored + * @return the output length + * @throws ShortBufferException + * if the output buffer is too small to hold the result. + * @throws IllegalBlockSizeException + * if the total input length is not a multiple of the block + * size (for encryption when no padding is used or for + * decryption). + * @throws BadPaddingException + * if unpadding fails. + */ + public final int doFinal(byte[] input, int inOff, int inLen, byte[] output, + int outOff) throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + + byte[] doFinal = doFinal(input, inOff, inLen); + int outLen = doFinal.length; + if (outLen == 0) { + return 0; + } + if (output.length - outOff < outLen) { + throw new ShortBufferException("output"); + } + System.arraycopy(doFinal, 0, output, outOff, outLen); + return outLen; + } + + /** + * Initialize the block cipher with a secret key and parameters for data + * encryption. + * + * @param key + * the secret key + * @param params + * the parameters + * @throws InvalidKeyException + * if the given key is inappropriate for this cipher. + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for this + * cipher. + */ + protected abstract void initCipherEncrypt(SecretKey key, + AlgorithmParameterSpec params) throws InvalidKeyException, + InvalidAlgorithmParameterException; + + /** + * Initialize the block cipher with a secret key and parameters for data + * decryption. + * + * @param key + * the secret key + * @param params + * the parameters + * @throws InvalidKeyException + * if the given key is inappropriate for this cipher. + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for this + * cipher. + */ + protected abstract void initCipherDecrypt(SecretKey key, + AlgorithmParameterSpec params) throws InvalidKeyException, + InvalidAlgorithmParameterException; + + /** + * Encrypt a single block of data. It has to be assured that the array + * input contains a whole block starting at inOff and + * that out is large enough to hold an encrypted block starting + * at outOff. + * + * @param input + * array of bytes which contains the plaintext to be + * encrypted + * @param inOff + * index in array in, where the plaintext block starts + * @param output + * array of bytes which will contain the ciphertext startig + * at outOffset + * @param outOff + * index in array out, where the ciphertext block will start + */ + protected abstract void singleBlockEncrypt(byte[] input, int inOff, + byte[] output, int outOff); + + /** + * Decrypt a single block of data. It has to be assured that the array + * input contains a whole block starting at inOff and + * that output is large enough to hold an decrypted block + * starting at outOff. + * + * @param input + * array of bytes which contains the ciphertext to be + * decrypted + * @param inOff + * index in array in, where the ciphertext block starts + * @param output + * array of bytes which will contain the plaintext starting + * at outOffset + * @param outOff + * index in array out, where the plaintext block will start + */ + protected abstract void singleBlockDecrypt(byte[] input, int inOff, + byte[] output, int outOff); + + /** + * Check if mode and padding are set. If not, instantiate the default ones. + */ + private void initModeAndPadding() { + if (mode == null) { + mode = Registry.getMode(); + mode.setBlockCipher(this); + } + + if (paddingScheme == null) { + paddingScheme = Registry.getPaddingScheme(); + } + } + +} diff --git a/src/de/flexiprovider/api/Cipher.java b/src/de/flexiprovider/api/Cipher.java new file mode 100644 index 00000000..f3cb3523 --- /dev/null +++ b/src/de/flexiprovider/api/Cipher.java @@ -0,0 +1,729 @@ +package de.flexiprovider.api; + +import java.security.spec.InvalidParameterSpecException; + +import de.flexiprovider.api.exceptions.BadPaddingException; +import de.flexiprovider.api.exceptions.IllegalBlockSizeException; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.InvalidParameterException; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.NoSuchModeException; +import de.flexiprovider.api.exceptions.NoSuchPaddingException; +import de.flexiprovider.api.exceptions.ShortBufferException; +import de.flexiprovider.api.keys.Key; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.api.parameters.AlgorithmParameters; +import de.flexiprovider.common.util.JavaSecureRandomWrapper; + +public abstract class Cipher extends javax.crypto.CipherSpi { + + /** + * Constant specifying encrypt mode. + */ + public static final int ENCRYPT_MODE = javax.crypto.Cipher.ENCRYPT_MODE; + + /** + * Constant specifying decrypt mode. + */ + public static final int DECRYPT_MODE = javax.crypto.Cipher.DECRYPT_MODE; + + /** + * The operation mode for this cipher ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}). + */ + protected int opMode; + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * Initialize this cipher object with a proper key and some random seed. + * Before a cipher object is ready for data processing, it has to be + * initialized according to the desired cryptographic operation, which is + * specified by the opMode parameter. + *

+ * If this cipher (including its underlying mode or padding scheme) requires + * any random bytes, it will obtain them from random. + *

+ * Note: If the mode needs an initialization vector, a blank array is used + * in this case. + * + * @param opMode + * the operation mode ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}) + * @param key + * the key + * @param random + * the random seed + * @throws java.security.InvalidKeyException + * if the key is inappropriate for initializing this cipher. + */ + protected final void engineInit(int opMode, java.security.Key key, + java.security.SecureRandom random) + throws java.security.InvalidKeyException { + + try { + engineInit(opMode, key, + (java.security.spec.AlgorithmParameterSpec) null, random); + } catch (java.security.InvalidAlgorithmParameterException e) { + throw new InvalidParameterException(e.getMessage()); + } + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness. The cipher is initialized for encryption or + * decryption, depending on the value of opMode. + *

+ * If this cipher (including its underlying mode or padding scheme) requires + * any random bytes, it will obtain them from random. Note that + * when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + *

+ * Note: If the mode needs an initialization vector, a try to retrieve it + * from the AlgorithmParametersSpec is made. + * + * @param opMode + * the operation mode ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}) + * @param key + * the key + * @param algParams + * the algorithm parameters + * @param random + * the random seed + * @throws java.security.InvalidKeyException + * if the key is inappropriate for initializing this block + * cipher. + * @throws java.security.InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + protected final void engineInit(int opMode, java.security.Key key, + java.security.AlgorithmParameters algParams, + java.security.SecureRandom random) + throws java.security.InvalidKeyException, + java.security.InvalidAlgorithmParameterException { + + // if algParams are not specified, initialize without them + if (algParams == null) { + engineInit(opMode, key, random); + return; + } + + java.security.spec.AlgorithmParameterSpec paramSpec = null; + try { + paramSpec = algParams.getParameterSpec(Registry + .getAlgParamSpecClass(algParams.getAlgorithm())); + } catch (NoSuchAlgorithmException e) { + throw new InvalidAlgorithmParameterException( + "Unknown algorithm parameters."); + } catch (InvalidParameterSpecException e) { + throw new RuntimeException( + "Internal error: invalid parameters type."); + } + + engineInit(opMode, key, paramSpec, random); + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness. The cipher is initialized for one of the following + * four operations: encryption, decryption, key wrapping or key unwrapping, + * depending on the value of opMode. If this cipher (including its + * underlying feedback or padding scheme) requires any random bytes (e.g., + * for parameter generation), it will get them from random. Note that when a + * Cipher object is initialized, it loses all previously-acquired state. In + * other words, initializing a Cipher is equivalent to creating a new + * instance of that Cipher and initializing it. + * + * @param opMode + * the operation mode ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}) + * @param key + * the encryption key + * @param params + * the algorithm parameters + * @param javaRand + * the source of randomness + * @throws java.security.InvalidKeyException + * if the given key is inappropriate for initializing this + * cipher + * @throws java.security.InvalidAlgorithmParameterException + * if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and the + * parameters are null. + */ + protected void engineInit(int opMode, java.security.Key key, + java.security.spec.AlgorithmParameterSpec params, + java.security.SecureRandom javaRand) + throws java.security.InvalidKeyException, + java.security.InvalidAlgorithmParameterException { + + if ((params != null) && !(params instanceof AlgorithmParameterSpec)) { + throw new java.security.InvalidAlgorithmParameterException(); + } + + if ((key == null) || !(key instanceof Key)) { + throw new java.security.InvalidKeyException(); + } + + this.opMode = opMode; + + if (opMode == ENCRYPT_MODE) { + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + initEncrypt((Key) key, (AlgorithmParameterSpec) params, flexiRand); + + } else if (opMode == DECRYPT_MODE) { + initDecrypt((Key) key, (AlgorithmParameterSpec) params); + + } + } + + /** + * Return the result of the last step of a multi-step en-/decryption + * operation or the result of a single-step en-/decryption operation by + * processing the given input data and any remaining buffered data. The data + * to be processed is given in an input byte array. Beginning at + * inputOffset, only the first inputLen bytes are en-/decrypted, including + * any buffered bytes of a previous update operation. If necessary, padding + * is performed. The result is returned as a output byte array. + * + * @param input + * the byte array holding the data to be processed + * @param inOff + * the offset indicating the start position within the input + * byte array + * @param inLen + * the number of bytes to be processed + * @return the byte array containing the en-/decrypted data + * @throws javax.crypto.IllegalBlockSizeException + * if the ciphertext length is not a multiple of the + * blocklength. + * @throws javax.crypto.BadPaddingException + * if unpadding is not possible. + */ + protected final byte[] engineDoFinal(byte[] input, int inOff, int inLen) + throws javax.crypto.IllegalBlockSizeException, + javax.crypto.BadPaddingException { + return doFinal(input, inOff, inLen); + } + + /** + * Perform the last step of a multi-step en-/decryption operation or a + * single-step en-/decryption operation by processing the given input data + * and any remaining buffered data. The data to be processed is given in an + * input byte array. Beginning at inputOffset, only the first inputLen bytes + * are en-/decrypted, including any buffered bytes of a previous update + * operation. If necessary, padding is performed. The result is stored in + * the given output byte array, beginning at outputOffset. The number of + * bytes stored in this byte array are returned. + * + * @param input + * the byte array holding the data to be processed + * @param inOff + * the offset indicating the start position within the input + * byte array + * @param inLen + * the number of bytes to be processed + * @param output + * the byte array for holding the result + * @param outOff + * the offset indicating the start position within the output + * byte array to which the en/decrypted data is written + * @return the number of bytes stored in the output byte array + * @throws javax.crypto.ShortBufferException + * if the output buffer is too short to hold the output. + * @throws javax.crypto.IllegalBlockSizeException + * if the ciphertext length is not a multiple of the + * blocklength. + * @throws javax.crypto.BadPaddingException + * if unpadding is not possible. + */ + protected final int engineDoFinal(byte[] input, int inOff, int inLen, + byte[] output, int outOff) + throws javax.crypto.ShortBufferException, + javax.crypto.IllegalBlockSizeException, + javax.crypto.BadPaddingException { + return doFinal(input, inOff, inLen, output, outOff); + } + + /** + * @return the block size (in bytes), or 0 if the underlying algorithm is + * not a block cipher + */ + protected final int engineGetBlockSize() { + return getBlockSize(); + } + + /** + * Return the key size of the given key object in bits. + * + * @param key + * the key object + * @return the key size in bits of the given key object + * @throws java.security.InvalidKeyException + * if key is invalid. + */ + protected final int engineGetKeySize(java.security.Key key) + throws java.security.InvalidKeyException { + if (!(key instanceof Key)) { + throw new java.security.InvalidKeyException("Unsupported key."); + } + return getKeySize((Key) key); + } + + /** + * Return the initialization vector. This is useful in the context of + * password-based encryption or decryption, where the IV is derived from a + * user-provided passphrase. + * + * @return the initialization vector in a new buffer, or null if + * the underlying algorithm does not use an IV, or if the IV has not + * yet been set. + */ + protected final byte[] engineGetIV() { + return getIV(); + } + + /** + * Return the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length inputLen (in bytes). + *

+ * This call takes into account any unprocessed (buffered) data from a + * previous update call, and padding. + *

+ * The actual output length of the next update or doFinal call may be + * smaller than the length returned by this method. + * + * @param inLen + * the input length (in bytes) + * @return the required output buffer size (in bytes) + */ + protected final int engineGetOutputSize(int inLen) { + return getOutputSize(inLen); + } + + /** + * Returns the parameters used with this cipher. + *

+ * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * + * @return the parameters used with this cipher, or null if this cipher does + * not use any parameters. + */ + protected final java.security.AlgorithmParameters engineGetParameters() { + + final class JavaAlgorithmParameters extends + java.security.AlgorithmParameters { + JavaAlgorithmParameters(AlgorithmParameters params, String algName) { + super(params, null, algName); + } + } + + String algName = getName(); + AlgorithmParameters params; + try { + params = Registry.getAlgParams(algName); + } catch (NoSuchAlgorithmException e) { + return null; + } + + JavaAlgorithmParameters algParams = new JavaAlgorithmParameters(params, + algName); + + AlgorithmParameterSpec algParamSpec = getParameters(); + if (algParamSpec == null) { + return null; + } + + try { + algParams.init(algParamSpec); + } catch (java.security.spec.InvalidParameterSpecException ipse) { + throw new RuntimeException("InvalidParameterSpecException: " + + ipse.getMessage()); + } + + return algParams; + } + + /** + * Set the mode of this cipher. + * + * @param modeName + * the cipher mode + * @throws java.security.NoSuchAlgorithmException + * if neither the mode with the given name nor the default + * mode can be found + */ + protected final void engineSetMode(String modeName) + throws java.security.NoSuchAlgorithmException { + setMode(modeName); + } + + /** + * Set the padding scheme of this cipher. + * + * @param paddingName + * the padding scheme + * @throws javax.crypto.NoSuchPaddingException + * if the requested padding scheme cannot be found. + */ + protected final void engineSetPadding(String paddingName) + throws javax.crypto.NoSuchPaddingException { + setPadding(paddingName); + } + + /** + * Return the result of the next step of a multi-step en-/decryption + * operation. The data to be processed is given in an input byte array. + * Beginning at inputOffset, only the first inputLen bytes are + * en-/decrypted. The result is returned as a byte array. + * + * @param input + * the byte array holding the data to be processed + * @param inOff + * the offset indicating the start position within the input + * byte array + * @param inLen + * the number of bytes to be processed + * @return the byte array containing the en-/decrypted data + */ + protected final byte[] engineUpdate(byte[] input, int inOff, int inLen) { + return update(input, inOff, inLen); + } + + /** + * Perform the next step of a multi-step en-/decryption operation. The data + * to be processed is given in an input byte array. Beginning at + * inputOffset, only the first inputLen bytes are en-/decrypted. The result + * is stored in the given output byte array, beginning at outputOffset. The + * number of bytes stored in this output byte array are returned. + * + * @param input + * the byte array holding the data to be processed + * @param inOff + * the offset indicating the start position within the input + * byte array + * @param inLen + * the number of bytes to be processed + * @param output + * the byte array for holding the result + * @param outOff + * the offset indicating the start position within the output + * byte array to which the en-/decrypted data is written + * @return the number of bytes that are stored in the output byte array + * @throws javax.crypto.ShortBufferException + * if the output buffer is too short to hold the output. + */ + protected final int engineUpdate(final byte[] input, final int inOff, + final int inLen, byte[] output, final int outOff) + throws javax.crypto.ShortBufferException { + return update(input, inOff, inLen, output, outOff); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for encryption. + *

+ * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and raise an + * InvalidAlgorithmParameterException if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key + * the encryption key + * @param cipherParams + * the cipher parameters + * @param random + * the source of randomness + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + public abstract void initEncrypt(Key key, + AlgorithmParameterSpec cipherParams, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for decryption. + *

+ * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and throw an + * {@link InvalidAlgorithmParameterException} if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + *

+ * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + *

+ * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key + * the encryption key + * @param cipherParams + * the cipher parameters + * @throws InvalidKeyException + * if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing this + * block cipher. + */ + public abstract void initDecrypt(Key key, + AlgorithmParameterSpec cipherParams) throws InvalidKeyException, + InvalidAlgorithmParameterException; + + /** + * @return the name of this cipher + */ + public abstract String getName(); + + /** + * @return the block size (in bytes), or 0 if the underlying algorithm is + * not a block cipher + */ + public abstract int getBlockSize(); + + /** + * Returns the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length inputLen (in bytes). + *

+ * This call takes into account any unprocessed (buffered) data from a + * previous update call, and padding. + *

+ * The actual output length of the next update or doFinal call may be + * smaller than the length returned by this method. + * + * @param inputLen + * the input length (in bytes) + * @return the required output buffer size (in bytes) + */ + public abstract int getOutputSize(int inputLen); + + /** + * Return the key size of the given key object in bits. + * + * @param key + * the key object + * @return the key size in bits of the given key object + * @throws InvalidKeyException + * if key is invalid. + */ + public abstract int getKeySize(Key key) throws InvalidKeyException; + + /** + * Returns the parameters used with this cipher. + *

+ * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * + * @return the parameters used with this cipher, or null if this cipher does + * not use any parameters. + */ + public abstract AlgorithmParameterSpec getParameters(); + + /** + * Return the initialization vector. This is useful in the context of + * password-based encryption or decryption, where the IV is derived from a + * user-provided passphrase. + * + * @return the initialization vector in a new buffer, or null if + * the underlying algorithm does not use an IV, or if the IV has not + * yet been set. + */ + public abstract byte[] getIV(); + + /** + * Set the mode of this cipher. + * + * @param mode + * the cipher mode + * @throws NoSuchModeException + * if the requested mode cannot be found. + */ + protected abstract void setMode(String mode) throws NoSuchModeException; + + /** + * Set the padding mechanism of this cipher. + * + * @param padding + * the padding mechanism + * @throws NoSuchPaddingException + * if the requested padding scheme cannot be found. + */ + protected abstract void setPadding(String padding) + throws NoSuchPaddingException; + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @return a new buffer with the result (maybe an empty byte array) + */ + public final byte[] update(byte[] input) { + return update(input, 0, input.length); + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @return a new buffer with the result (maybe an empty byte array) + */ + public abstract byte[] update(byte[] input, int inOff, int inLen); + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the output buffer + * @param outOff + * the offset where the result is stored + * @return the length of the output + * @throws ShortBufferException + * if the output buffer is too small to hold the result. + */ + public abstract int update(byte[] input, int inOff, int inLen, + byte[] output, int outOff) throws ShortBufferException; + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @return a new buffer with the result + * @throws IllegalBlockSizeException + * if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException + * if this cipher is a block cipher and unpadding fails. + */ + public final byte[] doFinal() throws IllegalBlockSizeException, + BadPaddingException { + return doFinal(null, 0, 0); + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @return a new buffer with the result + * @throws IllegalBlockSizeException + * if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException + * if this cipher is a block cipher and unpadding fails. + */ + public final byte[] doFinal(byte[] input) throws IllegalBlockSizeException, + BadPaddingException { + return doFinal(input, 0, input.length); + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @return a new buffer with the result + * @throws IllegalBlockSizeException + * if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException + * if this cipher is a block cipher and unpadding fails. + */ + public abstract byte[] doFinal(byte[] input, int inOff, int inLen) + throws IllegalBlockSizeException, BadPaddingException; + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input + * the input buffer + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + * @param output + * the buffer for the result + * @param outOff + * the offset where the result is stored + * @return the output length + * @throws ShortBufferException + * if the output buffer is too small to hold the result. + * @throws IllegalBlockSizeException + * if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException + * if this cipher is a block cipher and unpadding fails. + */ + public abstract int doFinal(byte[] input, int inOff, int inLen, + byte[] output, int outOff) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException; + +} diff --git a/src/de/flexiprovider/api/KeyAgreement.java b/src/de/flexiprovider/api/KeyAgreement.java new file mode 100644 index 00000000..510f2115 --- /dev/null +++ b/src/de/flexiprovider/api/KeyAgreement.java @@ -0,0 +1,259 @@ +package de.flexiprovider.api; + +import javax.crypto.KeyAgreementSpi; + +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.ShortBufferException; +import de.flexiprovider.api.keys.Key; +import de.flexiprovider.api.keys.PrivateKey; +import de.flexiprovider.api.keys.PublicKey; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.common.util.JavaSecureRandomWrapper; + +public abstract class KeyAgreement extends KeyAgreementSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * JCA adapter for FlexiAPI method init(): initialize this + * KeyAgreementSpi with a key and a source of randomness. + * + * @param key + * the secret key of the party initializing the key agreement + * @param javaRand + * the source of randomness + * @throws java.security.InvalidKeyException + * if the key is invalid. + * @throws RuntimeException + * if parameters are required for initialization. + */ + protected final void engineInit(java.security.Key key, + java.security.SecureRandom javaRand) + throws java.security.InvalidKeyException { + + if (!(key instanceof PrivateKey)) { + throw new java.security.InvalidKeyException(); + } + + try { + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + init((PrivateKey) key, (AlgorithmParameterSpec) null, flexiRand); + } catch (InvalidAlgorithmParameterException e) { + throw new RuntimeException("algorithm parameters required"); + } + } + + /** + * JCA adapter for FlexiAPI method init(): initialize this + * KeyAgreementSpi with a key, algorithm parameters, and a source + * of randomness. + * + * @param key + * the secret key of the party initializing the key agreement + * @param params + * the algorithm parameters + * @param javaRand + * the source of randomness + * @throws java.security.InvalidKeyException + * if the key is invalid. + * @throws java.security.InvalidAlgorithmParameterException + * if the parameters are invalid or null and parameters are + * needed for initialization. + */ + protected final void engineInit(java.security.Key key, + java.security.spec.AlgorithmParameterSpec params, + java.security.SecureRandom javaRand) + throws java.security.InvalidKeyException, + java.security.InvalidAlgorithmParameterException { + + if (!(key instanceof PrivateKey)) { + throw new java.security.InvalidKeyException(); + } + + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + init((PrivateKey) key, (AlgorithmParameterSpec) params, flexiRand); + } + + /** + * JCA adapter for FlexiAPI method {@link #generateSecret()} : generate a + * shared secret and return it as a byte array. + * + * @return the shared secret as byte array. + * @throws IllegalStateException + * if the object is not in doPhase. + */ + protected final byte[] engineGenerateSecret() throws IllegalStateException { + return generateSecret(); + } + + /** + * JCA adapter for FlexiAPI method {@link #generateSecret(byte[], int)}: + * generate a shared secret and place it into the buffer + * sharedSecret, beginning at offset. + * + * @param sharedSecret + * the buffer to hold the shared secret + * @param offset + * the offset in sharedSecret where the shared + * secret will be stored + * @return the number of bytes written in sharedSecret + * @throws IllegalStateException + * if the key agreement scheme has not been initialized + * properly. + * @throws javax.crypto.ShortBufferException + * if sharedSecret is too small to to hold the + * shared secret + */ + protected final int engineGenerateSecret(byte[] sharedSecret, int offset) + throws IllegalStateException, javax.crypto.ShortBufferException { + + return generateSecret(sharedSecret, offset); + } + + /** + * JCA adapter for FlexiAPI method {@link #generateSecret(String)}: + * generate a shared secret via the algorithm specified in + * algorithm. + * + * @param algorithm + * the desired algorithm for the generation of the secret + * @return the shared secret + * @throws IllegalStateException + * if the key agreement scheme has not been initialized + * properly. + * @throws java.security.NoSuchAlgorithmException + * if algorithm is invalid. + */ + protected final javax.crypto.SecretKey engineGenerateSecret(String algorithm) + throws IllegalStateException, + java.security.NoSuchAlgorithmException { + + return generateSecret(algorithm); + } + + /** + * JCA adapter for FlexiAPI method {@link #doPhase(PublicKey, boolean)}: + * execute the next phase of this key agreement with the given key that was + * received from one of the other parties involved in this key agreement. + * + * @param key + * the public key of the other party + * @param lastPhase + * true if this is the last phase of the key + * agreement. After the last phase only + * generateSecret may be called. + * @return the shared secret + * @throws IllegalStateException + * if the key agreement scheme has not been initialized + * properly. + * @throws java.security.InvalidKeyException + * if the key is invalid. + */ + protected final java.security.Key engineDoPhase(java.security.Key key, + boolean lastPhase) throws java.security.InvalidKeyException, + IllegalStateException { + + if (!(key instanceof PublicKey)) { + throw new java.security.InvalidKeyException(); + } + + return doPhase((PublicKey) key, lastPhase); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Initialize this key agreement with a private key, algorithm parameters, + * and a source of randomness. + * + * @param privKey + * the private key of the party initializing the key + * agreement + * @param params + * the algorithm parameters + * @param random + * the source of randomness + * @throws InvalidKeyException + * if the key is invalid. + * @throws InvalidAlgorithmParameterException + * if the parameters are invalid. + */ + public abstract void init(PrivateKey privKey, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Generate a shared secret and return it as a byte array. + * + * @return the shared secret as byte array. + * @throws IllegalStateException + * if the key agreement scheme has not been initialized + * properly. + */ + public abstract byte[] generateSecret() throws IllegalStateException; + + /** + * Generate a shared secret and place it into the buffer + * sharedSecret, beginning at offset. + * + * @param sharedSecret + * the buffer to hold the shared secret + * @param offset + * the offset in sharedSecret where the shared + * secret will be stored + * @return the number of bytes written in sharedSecret + * @throws IllegalStateException + * if the key agreement scheme has not been initialized + * properly. + * @throws ShortBufferException + * if sharedSecret is too small to to hold the + * shared secret + */ + public abstract int generateSecret(byte[] sharedSecret, int offset) + throws IllegalStateException, ShortBufferException; + + /** + * Generate a shared secret via the algorithm specified in + * algorithm. + * + * @param algorithm + * the desired algorithm for the generation of the secret + * @return the shared secret + * @throws IllegalStateException + * if the key agreement scheme has not been initialized + * properly. + * @throws NoSuchAlgorithmException + * if algorithm is invalid. + */ + public abstract SecretKey generateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException; + + /** + * Execute the next phase of this key agreement with the given public key + * that was received from one of the other parties involved in this key + * agreement. + * + * @param pubKey + * the public key of the other party + * @param lastPhase + * true if this is the last phase of the key + * agreement. After the last phase only + * generateSecret may be called. + * @return the shared secret + * @throws IllegalStateException + * if the key agreement scheme has not been initialized + * properly. + * @throws InvalidKeyException + * if the key is invalid. + */ + public abstract Key doPhase(PublicKey pubKey, boolean lastPhase) + throws InvalidKeyException, IllegalStateException; + +} diff --git a/src/de/flexiprovider/api/KeyDerivation.java b/src/de/flexiprovider/api/KeyDerivation.java new file mode 100644 index 00000000..4d2c5390 --- /dev/null +++ b/src/de/flexiprovider/api/KeyDerivation.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.api; + +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * This class defines a key derivation function. All the abstract + * methods in this class must be implemented by each cryptographic service + * provider who wishes to supply the implementation of a particular Key + * Derivation algorithm. A key derivation function is used to generate a longer + * or shorter secret key, with a second secret shared by both parties. The + * derived secret key may be used by other schemes which use different key + * length, to the normal secret keys. + * + * @author Jochen Hechler + * @author Marcus Stögbauer + * @author Martin Döring + */ +public abstract class KeyDerivation { + + /** + * Initialize this KDF with a secret and parameters. + * + * @param secret + * the secret from which to derive the key + * @param params + * the parameters + * @throws InvalidKeyException + * if the secret is invalid. + * @throws InvalidAlgorithmParameterException + * if the parameters are invalid. + */ + public abstract void init(byte[] secret, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Start the derivation process and return the derived key. If supported by + * the concrete implementation, the derived key will be of the specified + * length. + * + * @param keySize + * the desired length of the derived key + * @return the derived key with the specified length, or null if + * the key size is < 0. + */ + public abstract byte[] deriveKey(int keySize); + +} diff --git a/src/de/flexiprovider/api/Mac.java b/src/de/flexiprovider/api/Mac.java new file mode 100644 index 00000000..e50cd2ec --- /dev/null +++ b/src/de/flexiprovider/api/Mac.java @@ -0,0 +1,211 @@ +package de.flexiprovider.api; + +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.ShortBufferException; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * This class implements a "message authentication code" (MAC), a method to + * ensure the integrity of data transmitted between two parties who share a + * common secret key. + * + *

+ * The best way to describe a MAC is as a keyed one-way hash function, + * which looks like: + * + *

+ *

+ * D = MAC(K, M)

+ * + *

+ * where K is the key, M is the message, and D + * is the resulting digest. One party will usually send the concatenation + * M || D to the other party, who will then verify D by + * computing D' in a similar fashion. If D == D', then + * the message is assumed to be authentic. + * + * @author Martin Döring, Johannes Müller + */ +public abstract class Mac extends javax.crypto.MacSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + protected final int engineGetMacLength() { + return getMacLength(); + } + + protected void engineInit(java.security.Key key, + java.security.spec.AlgorithmParameterSpec params) + throws java.security.InvalidKeyException, + java.security.InvalidAlgorithmParameterException { + + if (!(key instanceof SecretKey)) { + throw new java.security.InvalidKeyException(); + } + if ((params != null) && !(params instanceof AlgorithmParameterSpec)) { + throw new java.security.InvalidAlgorithmParameterException(); + } + init((SecretKey) key, (AlgorithmParameterSpec) params); + } + + protected final void engineUpdate(byte input) { + update(input); + } + + protected final void engineUpdate(byte[] input, int offset, int len) { + update(input, offset, len); + } + + protected final byte[] engineDoFinal() { + return doFinal(); + } + + protected final void engineReset() { + reset(); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Return the MAC length. This method is guaranteed to return a sane value + * only after the MAC has been initialized. + * + * @return the MAC length + */ + public abstract int getMacLength(); + + /** + * Initialize this MAC with a key and no parameters. + * + * @param key + * The key to initialize this instance with. + * @throws InvalidKeyException + * If the key is unacceptable. + */ + public final void init(SecretKey key) throws InvalidKeyException { + try { + init(key, null); + } catch (InvalidAlgorithmParameterException iape) { + throw new IllegalArgumentException("This MAC needs parameters"); + } + } + + /** + * Initialize this MAC with a key and parameters. + * + * @param key + * The key to initialize this instance with. + * @param params + * The algorithm-specific parameters. + * @throws InvalidAlgorithmParameterException + * If the algorithm parameters are unacceptable. + * @throws InvalidKeyException + * If the key is unacceptable. + */ + public abstract void init(SecretKey key, AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException, InvalidKeyException; + + /** + * Update the computation with a single byte. + * + * @param input + * The next byte. + */ + public abstract void update(byte input); + + /** + * Update the computation with a byte array. + * + * @param input + * The next bytes. + */ + public final void update(byte[] input) { + update(input, 0, input.length); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Update the computation with a portion of a byte array. + * + * @param input + * The next bytes. + * @param offset + * The index in input to start. + * @param length + * The number of bytes to update. + */ + public abstract void update(byte[] input, int offset, int length); + + /** + * Finishes the computation of a MAC and returns the digest. + * + *

+ * After this method succeeds, it may be used again as just after a call to + * init, and can compute another MAC using the same key and + * parameters. + * + * @return The message authentication code. + */ + public abstract byte[] doFinal(); + + /** + * Finishes the computation of a MAC with a final byte array (or computes a + * MAC over those bytes only) and returns the digest. + * + *

+ * After this method succeeds, it may be used again as just after a call to + * init, and can compute another MAC using the same key and + * parameters. + * + * @param input + * The bytes to add. + * @return The message authentication code. + */ + public final byte[] doFinal(byte[] input) { + update(input); + return doFinal(); + } + + /** + * Finishes the computation of a MAC and places the result into the given + * array. + *

+ * After this method succeeds, it may be used again as just after a call to + * init, and can compute another MAC using the same key and + * parameters. + * + * @param output + * The destination for the result. + * @param outOffset + * The index in the output array to start. + * @return the number of bytes stored in output. + * @throws ShortBufferException + * If output is not large enough to hold the + * result. + */ + public final int doFinal(byte[] output, int outOffset) + throws ShortBufferException { + if (output.length - outOffset < getMacLength()) { + throw new ShortBufferException(); + } + byte[] mac = doFinal(); + System.arraycopy(mac, 0, output, outOffset, mac.length); + return mac.length; + } + + /** + * Reset this instance. A call to this method returns this instance back to + * the state it was in just after it was initialized. + */ + public abstract void reset(); + +} diff --git a/src/de/flexiprovider/api/MessageDigest.java b/src/de/flexiprovider/api/MessageDigest.java new file mode 100644 index 00000000..b0437fcd --- /dev/null +++ b/src/de/flexiprovider/api/MessageDigest.java @@ -0,0 +1,143 @@ +package de.flexiprovider.api; + +import de.flexiprovider.api.exceptions.DigestException; + +public abstract class MessageDigest extends java.security.MessageDigestSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + protected final int engineGetDigestLength() { + return getDigestLength(); + } + + protected final void engineUpdate(byte input) { + update(input); + } + + protected final void engineUpdate(byte[] input, int offset, int len) { + update(input, offset, len); + } + + protected final byte[] engineDigest() { + return digest(); + } + + protected final void engineReset() { + reset(); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * @return the digest length in bytes + */ + public abstract int getDigestLength(); + + /** + * Update the digest using the specified byte. + * + * @param input + * the byte to use for the update + */ + public abstract void update(byte input); + + /** + * Update the digest using the specified array of bytes, starting at the + * specified offset. + * + * @param input + * the array of bytes to use for the update + */ + public final void update(byte[] input) { + if (input == null) { + return; + } + update(input, 0, input.length); + } + + /** + * Update the digest using the specified array of bytes, starting at the + * specified offset. + * + * @param input + * the array of bytes to use for the update + * @param offset + * the offset to start from in the array of bytes + * @param len + * the number of bytes to use, starting at offset + */ + public abstract void update(byte[] input, int offset, int len); + + /** + * Complete the hash computation by performing final operations such as + * padding. Once {@link #digest()} has been called, the engine should be + * reset (see {@link #reset()}). Resetting is the responsibility of the + * engine implementor. + * + * @return the array of bytes for the resulting hash value. + */ + public abstract byte[] digest(); + + /** + * Update the digest and complete the hash computation by performing final + * operations such as padding. Once {@link #digest(byte[])} has been called, + * the engine should be reset (see {@link #reset()}). Resetting is the + * responsibility of the engine implementor. + * + * @param input + * the array of bytes to use for the update + * @return the array of bytes for the resulting hash value + */ + public final byte[] digest(byte[] input) { + update(input); + return digest(); + } + + /** + * Complete the hash computation by performing final operations such as + * padding. Once {@link #digest(byte[], int, int)} has been called, the + * engine should be reset (see {@link #reset()}). Resetting is the + * responsibility of the engine implementor. + * + * @param buf + * the output buffer in which to store the digest + * @param offset + * offset to start from in the output buffer + * @param len + * number of bytes within buf allotted for the digest. Both + * this default implementation and the SUN provider do not + * return partial digests. The presence of this parameter is + * solely for consistency in our API's. If the value of this + * parameter is less than the actual digest length, the + * method will throw a DigestException. This parameter is + * ignored if its value is greater than or equal to the + * actual digest length. + * @return the length of the digest stored in the output buffer. + * @throws DigestException + * if an error occurs. + */ + public final int digest(byte[] buf, int offset, int len) + throws DigestException { + + byte[] digest = digest(); + if (len < digest.length) { + throw new DigestException("partial digests not returned"); + } + if (buf.length - offset < digest.length) { + throw new DigestException("insufficient space in the output " + + "buffer to store the digest"); + } + System.arraycopy(digest, 0, buf, offset, digest.length); + return digest.length; + } + + /** + * Reset the digest for further use. + */ + public abstract void reset(); + +} diff --git a/src/de/flexiprovider/api/Mode.java b/src/de/flexiprovider/api/Mode.java new file mode 100644 index 00000000..9557fa2f --- /dev/null +++ b/src/de/flexiprovider/api/Mode.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.api; + +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.common.mode.ModeParameterSpec; + +/** + * The abstract class Mode is the base for all modes implemented in the package + * cdc. It defines the set of methods which have to be implemented to cooperate + * with the BasicCipher class. Each class that implements such a mode has to + * exist in the package "de.flexiprovider.common.mode" and the classname must be + * the same as the name used in the call of Cipher.getInstance(). + * e.g. if you call Cipher.getInstance("SAFER+/CBC/NoPadding"), the + * code will look for the class de.flexiprovider.common.mode.CBC + * + * @author Marcus Lippert + * @author Ralf-Philipp Weinmann + */ +public abstract class Mode { + + /** + * Reference to the underlying block cipher + */ + private BlockCipher blockCipher; + + /** + * The initialization vector + */ + protected byte[] iv; + + /** + * The block size of the mode + */ + protected int blockSize; + + /** + * Set the {@link BlockCipher} to use with this mode. + * + * @param blockCipher + * the block cipher + */ + final void setBlockCipher(BlockCipher blockCipher) { + this.blockCipher = blockCipher; + } + + /*--------------------------------------------------- + * Mode specific abstract methods + ---------------------------------------------------*/ + + /** + * Initialize the Mode object for encryption and compute the mode block + * size. This value usually depends on the block size of the used block + * cipher. It is supposed that all block ciphers return a sane value via + * {@link BlockCipher#getCipherBlockSize()} after initialization. + * + * @param key + * the key used for encryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected abstract void initEncrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Initialize the Mode object for decryption and compute the mode block + * size. This value usually depends on the block size of the used block + * cipher. It is supposed that all block ciphers return a sane value via + * {@link BlockCipher#getCipherBlockSize()} after initialization. + * + * @param key + * the key used for decryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected abstract void initDecrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Encrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected abstract void nextChunkEncrypt(final byte[] input, + final int inOff, byte[] output, final int outOff); + + /** + * Decrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected abstract void nextChunkDecrypt(final byte[] input, + final int inOff, byte[] output, final int outOff); + + /** + * reset() is called after doFinal() in order to prepare the mode for the + * next operation. + */ + protected abstract void reset(); + + /*--------------------------------------------------- + * Adapter classes to BlockCipher + ---------------------------------------------------*/ + + /** + * Initialize the block cipher for encryption. + * + * @param key + * the secret key to use for encryption + * @param cipherParams + * the parameters + * @throws InvalidKeyException + * if the given key is inappropriate for this cipher. + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for this + * cipher. + */ + protected final void initCipherEncrypt(SecretKey key, + AlgorithmParameterSpec cipherParams) throws InvalidKeyException, + InvalidAlgorithmParameterException { + blockCipher.initCipherEncrypt(key, cipherParams); + } + + /** + * Initialize the block cipher for decryption. + * + * @param key + * the secret key to use for decryption + * @param cipherParams + * the parameters + * @throws InvalidKeyException + * if the given key is inappropriate for this cipher. + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for this + * cipher. + */ + protected final void initCipherDecrypt(SecretKey key, + AlgorithmParameterSpec cipherParams) throws InvalidKeyException, + InvalidAlgorithmParameterException { + blockCipher.initCipherDecrypt(key, cipherParams); + } + + /** + * Return the block size of the underlying cipher. It is supposed that all + * block ciphers return a sane value via + * {@link BlockCipher#getCipherBlockSize()} after initialization. + * + * @return the block size of the underlying cipher + */ + protected final int getCipherBlockSize() { + return blockCipher.getCipherBlockSize(); + } + + /** + * Encrypt a single block with the cipher engine. + * + * @param input + * array of bytes which contains the plaintext to be + * encrypted + * @param inOff + * index in array in, where the plaintext block starts + * @param output + * array of bytes which will contain the ciphertext startig + * at outOffset + * @param outOff + * index in array out, where the ciphertext block will start + */ + protected final void singleBlockEncrypt(byte[] input, int inOff, + byte[] output, int outOff) { + blockCipher.singleBlockEncrypt(input, inOff, output, outOff); + } + + /** + * Decrypt a single block with the cipher engine. + * + * @param input + * array of bytes which contains the ciphertext to be + * decrypted + * @param inOff + * index in array in, where the ciphertext block starts + * @param output + * array of bytes which will contain the plaintext starting + * at outOffset + * @param outOff + * index in array out, where the plaintext block will start + */ + protected final void singleBlockDecrypt(byte[] input, int inOff, + byte[] output, int outOff) { + blockCipher.singleBlockDecrypt(input, inOff, output, outOff); + } + +} diff --git a/src/de/flexiprovider/api/PaddingScheme.java b/src/de/flexiprovider/api/PaddingScheme.java new file mode 100644 index 00000000..2e62ee0c --- /dev/null +++ b/src/de/flexiprovider/api/PaddingScheme.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.api; + +import de.flexiprovider.api.exceptions.BadPaddingException; + +/** + * To encrypt a plaintext with a block cipher the input is divided into blocks + * whose length depends on the encryption algorithm used. To ensure that the + * block size will divide the length of the input, the last block is padded to + * the needed length. Vice versa, when decrypting a ciphertext, the padding has + * to be removed. + *

+ * To make a new padding scheme available, one has to write a subclass of + * de.flexiprovider.common.padding.PaddingScheme in order to allow a + * blockcipher to use it. This subclass has to exist in the package + * de.flexiprovider.common.padding and the class name must be the + * same as the name used in the call of Cipher.getInstance(). E.g., if you call + * Cipher.getInstance("SAFER+/CBC/NoPadding"), then there must exist a class + * de.flexiprovider.common.padding.NoPadding. + * + * @author Christoph Ender + * @author Christoph Sesterhenn + * @author Marcus Lippert + * @author Martin Strese + */ +public abstract class PaddingScheme { + + /** + * Block size used for padding + */ + protected int blockSize = -1; + + /** + * Tell the padding scheme the block size to which input will be padded. + * + * @param blockSize + * length of one block + */ + final void setBlockSize(int blockSize) { + if (blockSize > 0) { + this.blockSize = blockSize; + } + } + + /** + * Return the number of bytes which will be appended to the the plaintext + * during padding. + * + * @param inLen + * the length of the plaintext to be padded + * @return the number of padding bytes (may be 0) + */ + protected abstract int padLength(int inLen); + + /** + * Pad the input to make its length divisible by the the block length. The + * padding is written to the same buffer which is used for input. The caller + * has to ensure that the input array is large enough to hold the padding + * bytes. + * + * @param input + * byte array containing the plaintext to be padded + * @param inOff + * index where the plaintext starts + * @param inLen + * length of the plaintext + * @throws BadPaddingException + * if the input buffer is too small to hold the padding + * bytes. + */ + protected abstract void pad(byte[] input, int inOff, int inLen) + throws BadPaddingException; + + /** + * Given the plaintext that includes the padding bytes, unpad the plaintext + * and return the index indicating where the padding bytes start. + * + * @param input + * byte array containing the padded plaintext + * @param inOff + * index where the plaintext starts + * @param inLen + * size of the plaintext + * @return index in the array where the padding bytes start + * @throws BadPaddingException + * if unpadding fails. + */ + protected abstract int unpad(byte[] input, int inOff, int inLen) + throws BadPaddingException; + +} diff --git a/src/de/flexiprovider/api/Registry.java b/src/de/flexiprovider/api/Registry.java new file mode 100644 index 00000000..b2bab5b3 --- /dev/null +++ b/src/de/flexiprovider/api/Registry.java @@ -0,0 +1,804 @@ +package de.flexiprovider.api; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.NoSuchModeException; +import de.flexiprovider.api.exceptions.NoSuchPaddingException; +import de.flexiprovider.api.exceptions.RegistrationException; +import de.flexiprovider.api.keys.KeyFactory; +import de.flexiprovider.api.keys.KeyPairGenerator; +import de.flexiprovider.api.keys.SecretKeyFactory; +import de.flexiprovider.api.keys.SecretKeyGenerator; +import de.flexiprovider.api.parameters.AlgorithmParameterGenerator; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.api.parameters.AlgorithmParameters; +import de.flexiprovider.common.mode.CBC; +import de.flexiprovider.common.mode.CFB; +import de.flexiprovider.common.mode.CFBParameterSpec; +import de.flexiprovider.common.mode.CTR; +import de.flexiprovider.common.mode.ECB; +import de.flexiprovider.common.mode.ModeParamGenParameterSpec; +import de.flexiprovider.common.mode.ModeParameterGenerator; +import de.flexiprovider.common.mode.ModeParameterSpec; +import de.flexiprovider.common.mode.ModeParameters; +import de.flexiprovider.common.mode.OFB; +import de.flexiprovider.common.mode.OFBParameterSpec; +import de.flexiprovider.common.padding.NoPadding; +import de.flexiprovider.common.padding.OneAndZeroesPadding; +import de.flexiprovider.common.padding.PKCS5Padding; +import de.flexiprovider.common.util.DefaultPRNG; + +/** + * This class is responsible for the registration and instantiation of all + * cryptographic algorithms of the FlexiProvider. It provides methods for adding + * registrations of algorithms and methods for instantiating registered + * algorithms. + * + * @author Johannes Müller + * @author Martin Döring + */ +public abstract class Registry { + + /* algorithm type constants */ + + /** + * Constant for asymmetric block ciphers + */ + public static final int ASYMMETRIC_BLOCK_CIPHER = 0; + + /** + * Constant for asymmetric hybrid ciphers + */ + public static final int ASYMMETRIC_HYBRID_CIPHER = 1; + + /** + * Constant for symmetric block ciphers + */ + public static final int BLOCK_CIPHER = 2; + + /** + * Constant for modes of operation + */ + public static final int MODE = 3; + + /** + * Constant for padding schemes + */ + public static final int PADDING_SCHEME = 4; + + /** + * Constant for generic ciphers + */ + public static final int CIPHER = 5; + + /** + * Constant for message authentication codes (MACs) + */ + public static final int MAC = 6; + + /** + * Constant for message digests (hash functions) + */ + public static final int MESSAGE_DIGEST = 7; + + /** + * Constant for PRNGs + */ + public static final int SECURE_RANDOM = 8; + + /** + * Constant for digital signatures + */ + public static final int SIGNATURE = 9; + + /** + * Constant for algorithm parameter specifications + */ + public static final int ALG_PARAM_SPEC = 10; + + /** + * Constant for algorithm parameters (used to encode and decode parameter + * specifications) + */ + public static final int ALG_PARAMS = 11; + + /** + * Constant for algorithm parameter generators + */ + public static final int ALG_PARAM_GENERATOR = 12; + + /** + * Constant for secret key generators + */ + public static final int SECRET_KEY_GENERATOR = 13; + + /** + * Constant for key pair generators + */ + public static final int KEY_PAIR_GENERATOR = 14; + + /** + * Constant for secret key factories + */ + public static final int SECRET_KEY_FACTORY = 15; + + /** + * Constant for key factories + */ + public static final int KEY_FACTORY = 16; + + /** + * Constant for key derivations + */ + public static final int KEY_DERIVATION = 17; + + /** + * Constant for key agreements + */ + public static final int KEY_AGREEMENT = 18; + + /* hash tables for the different algorithm types */ + + private static final Hashtable asymBlockCiphers = new Hashtable(); + private static final Hashtable asymHybridCiphers = new Hashtable(); + private static final Hashtable blockCiphers = new Hashtable(); + private static final Hashtable modes = new Hashtable(); + private static final Hashtable paddingSchemes = new Hashtable(); + private static final Hashtable ciphers = new Hashtable(); + private static final Hashtable macs = new Hashtable(); + private static final Hashtable messageDigests = new Hashtable(); + private static final Hashtable secureRandoms = new Hashtable(); + private static final Hashtable signatures = new Hashtable(); + private static final Hashtable algParamSpecs = new Hashtable(); + private static final Hashtable algParams = new Hashtable(); + private static final Hashtable algParamGenerators = new Hashtable(); + private static final Hashtable secretKeyGenerators = new Hashtable(); + private static final Hashtable keyPairGenerators = new Hashtable(); + private static final Hashtable secretKeyFactories = new Hashtable(); + private static final Hashtable keyFactories = new Hashtable(); + private static final Hashtable keyDerivations = new Hashtable(); + private static final Hashtable keyAgreements = new Hashtable(); + + // array holding all hash tables (indexed by algorithm type) + private static final Hashtable[] hashtables = { asymBlockCiphers, + asymHybridCiphers, blockCiphers, modes, paddingSchemes, ciphers, + macs, messageDigests, secureRandoms, signatures, algParamSpecs, + algParams, algParamGenerators, secretKeyGenerators, + keyPairGenerators, secretKeyFactories, keyFactories, + keyDerivations, keyAgreements }; + + // array holding all algorithm types (used for registration type checking) + private static final Class[] algClasses = { AsymmetricBlockCipher.class, + AsymmetricHybridCipher.class, BlockCipher.class, Mode.class, + PaddingScheme.class, Cipher.class, Mac.class, MessageDigest.class, + SecureRandom.class, Signature.class, AlgorithmParameterSpec.class, + AlgorithmParameters.class, AlgorithmParameterGenerator.class, + SecretKeyGenerator.class, KeyPairGenerator.class, + SecretKeyFactory.class, KeyFactory.class, KeyDerivation.class, + KeyAgreement.class }; + + // hash table for standard algorithm parameters + private static final Hashtable standardAlgParams = new Hashtable(); + + static { + add(ALG_PARAM_SPEC, CFBParameterSpec.class, "CFB"); + add(ALG_PARAM_SPEC, OFBParameterSpec.class, "OFB"); + add(ALG_PARAM_SPEC, ModeParameterSpec.class, new String[] { "Mode", + "IV" }); + add(ALG_PARAMS, ModeParameters.class, new String[] { "Mode", "IV" }); + add(ALG_PARAM_SPEC, ModeParamGenParameterSpec.class, new String[] { + "ModeParamGen", "IVParamGen" }); + add(ALG_PARAM_GENERATOR, ModeParameterGenerator.class, new String[] { + "Mode", "IV" }); + + add(MODE, ECB.class, "ECB"); + add(MODE, CBC.class, "CBC"); + add(MODE, OFB.class, "OFB"); + add(MODE, CFB.class, "CFB"); + add(MODE, CTR.class, "CTR"); + + add(PADDING_SCHEME, NoPadding.class, "NoPadding"); + add(PADDING_SCHEME, OneAndZeroesPadding.class, "OneAndZeroesPadding"); + add(PADDING_SCHEME, PKCS5Padding.class, "PKCS5Padding"); + } + + /** + * Register an algorithm of the given type under the given name. + * + * @param type + * the algorithm type + * @param algClass + * the class implementing the algorithm + * @param algName + * the name for the algorithm + * @throws RegistrationException + * if the expected and actual algorithm types do not match or an + * algorithm is already registered under the given name. + */ + public static final void add(int type, Class algClass, String algName) { + add(type, algClass, new String[] { algName }); + } + + /** + * Register an algorithm of the given type under the given names. + * + * @param type + * the algorithm type + * @param algClass + * the class implementing the algorithm + * @param algNames + * the names for the algorithm + * @throws RegistrationException + * if the expected and actual algorithm types do not match or an + * algorithm is already registered under one of the given names. + */ + public static final void add(int type, Class algClass, String[] algNames) { + Hashtable table = getHashtable(type); + // trivial cases + if ((table == null) || (algClass == null) || (algNames == null) + || (algNames.length == 0)) { + return; + } + + // type checking + Class expClass = algClasses[type]; + if (!expClass.isAssignableFrom(algClass)) { + throw new RegistrationException( + "expected and actual algorithm types do not match"); + } + + // register first name + table.put(algNames[0], algClass); + + // register additional names (aliases) + for (int i = 1; i < algNames.length; i++) { + table.put(algNames[i], algNames[0]); + } + } + + /** + * Return all algorithms of the given type. + * + * @param type + * the algorithm type + * @return an {@link Enumeration} of all algorithms contained in the hash + * table + */ + public static final Enumeration getAlgorithms(int type) { + Hashtable table = getHashtable(type); + if (table == null) { + return null; + } + return table.keys(); + } + + /** + * Return all names of the given algorithm and type. + * + * @param type + * the algorithm type + * @param name + * (one of the) names of the algorithm + * @return a {@link Vector} containing all names of the algorithm + */ + public static final Vector getNames(int type, String name) { + Hashtable table = getHashtable(type); + if (table == null) { + return null; + } + Enumeration algorithms = getAlgorithms(type); + Object target = resolveAlias(table, name); + Vector result = new Vector(); + while (algorithms.hasMoreElements()) { + String key = (String) algorithms.nextElement(); + if (resolveAlias(table, key).equals(target)) { + result.addElement(key); + } + } + return result; + } + + /** + * Return an instance of the specified asymmetric block cipher. + * + * @param algName + * the name of the asymmetric block cipher + * @return a new {@link AsymmetricBlockCipher} object implementing the + * chosen algorithm + * @throws NoSuchAlgorithmException + * if the asymmetric block cipher cannot be found. + */ + public static final AsymmetricBlockCipher getAsymmetricBlockCipher( + String algName) throws NoSuchAlgorithmException { + return (AsymmetricBlockCipher) getInstance(asymBlockCiphers, algName); + } + + /** + * Return an instance of the specified asymmetric hybrid cipher. + * + * @param algName + * the name of the asymmetric hybrid cipher + * @return a new {@link AsymmetricHybridCipher} object implementing the + * chosen algorithm + * @throws NoSuchAlgorithmException + * if the asymmetric hybrid cipher cannot be found. + */ + public static final AsymmetricHybridCipher getAsymmetricHybridCipher( + String algName) throws NoSuchAlgorithmException { + return (AsymmetricHybridCipher) getInstance(asymHybridCiphers, algName); + } + + /** + * Try to find an algorithm with the specified name inside the corresponding + * hashtable and return an instance of the algorithm. + * + * @param transformation + * the transformation (either of the form 'algorithm' or + * 'algorithm/mode/padding') + * @return block cipher object + * @throws NoSuchAlgorithmException + * if the block cipher or mode cannot be found. + * @throws NoSuchPaddingException + * if the padding scheme cannot be found. + */ + public static final BlockCipher getBlockCipher(String transformation) + throws NoSuchAlgorithmException, NoSuchPaddingException { + + String algName, modeName = null, paddingName = null; + int endIndex = transformation.indexOf('/'); + if (endIndex < 0) { + // transformation is of the form 'algorithm' + algName = transformation; + } else { + // transformation is of the form 'algorithm/mode/padding' + + // get 'algorithm' + algName = transformation.substring(0, endIndex); + + // get 'mode/padding' + String modePadding = transformation.substring(endIndex + 1); + endIndex = modePadding.indexOf("/"); + if (endIndex == -1) { + // if no padding is specified + throw new NoSuchAlgorithmException( + "Badly formed transformation: only 'algorithm' " + + "or 'algorithm/mode/padding' allowed."); + } + + // get 'mode' + modeName = modePadding.substring(0, endIndex); + + // get 'padding' + paddingName = modePadding.substring(endIndex + 1); + + // if even more information is provided, transformation is invalid + if (paddingName.indexOf("/") != -1) { + throw new NoSuchAlgorithmException( + "Badly formed transformation: only 'algorithm' " + + "or 'algorithm/mode/padding' allowed."); + } + } + + BlockCipher result = (BlockCipher) getInstance(blockCiphers, algName); + if(modeName != null) + result.setMode(modeName); + if(paddingName != null) + result.setPadding(paddingName); + + return result; + } + + /** + * @return an instance of the default mode (CBC) + */ + protected static final Mode getMode() { + return new CBC(); + } + + /** + * Return an instance of the specified mode. + * + * @param modeName + * the name of the mode + * @return a new {@link Mode} object implementing the chosen algorithm + * @throws NoSuchModeException + * if the mode cannot be found. + */ + protected static final Mode getMode(String modeName) + throws NoSuchModeException { + try { + return (Mode) getInstance(modes, modeName); + } catch (NoSuchAlgorithmException e) { + throw new NoSuchModeException(e.getMessage()); + } + } + + /** + * @return an instance of the default padding scheme (PKCS5Padding) + */ + protected static final PaddingScheme getPaddingScheme() { + return new PKCS5Padding(); + } + + /** + * Return an instance of the specified padding scheme. + * + * @param paddingName + * the name of the padding scheme + * @return a new {@link PaddingScheme} object implementing the chosen + * algorithm + * @throws NoSuchPaddingException + * if the padding scheme cannot be found. + */ + protected static final PaddingScheme getPaddingScheme(String paddingName) + throws NoSuchPaddingException { + try { + return (PaddingScheme) getInstance(paddingSchemes, paddingName); + } catch (NoSuchAlgorithmException e) { + throw new NoSuchPaddingException(e.getMessage()); + } + } + + /** + * Return an instance of the specified cipher. + * + * @param algName + * the name of the cipher + * @return a new {@link Cipher} object implementing the chosen algorithm + * @throws NoSuchAlgorithmException + * if the cipher cannot be found. + */ + public static final Cipher getCipher(String algName) + throws NoSuchAlgorithmException { + return (Cipher) getInstance(ciphers, algName); + } + + /** + * Return an instance of the specified message authentication code (MAC). + * + * @param algName + * the name of the MAC + * @return a new {@link Mac} object implementing the chosen algorithm + * @throws NoSuchAlgorithmException + * if the MAC cannot be found. + */ + public static final Mac getMAC(String algName) + throws NoSuchAlgorithmException { + return (Mac) getInstance(macs, algName); + } + + /** + * Return an instance of the specified message digest. + * + * @param algName + * the name of the message digest + * @return a new {@link MessageDigest} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the message digest cannot be found. + */ + public static final MessageDigest getMessageDigest(String algName) + throws NoSuchAlgorithmException { + return (MessageDigest) getInstance(messageDigests, algName); + } + + /** + * Return an instance of the specified source of randomness. + * + * @param algName + * the name of the source of randomness + * @return a new {@link SecureRandom} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the source of randomness cannot be found. + */ + public static final SecureRandom getSecureRandom(String algName) + throws NoSuchAlgorithmException { + return (SecureRandom) getInstance(secureRandoms, algName); + } + + /** + * @return the default secure random + * @throws RuntimeException + * if the default secure random cannot be instantiated. + */ + public static final SecureRandom getSecureRandom() { + return new DefaultPRNG(); + } + + /** + * Return an instance of the specified signature. + * + * @param algName + * the name of the signature + * @return a new {@link Signature} object implementing the chosen algorithm + * @throws NoSuchAlgorithmException + * if the signature cannot be found. + */ + public static final Signature getSignature(String algName) + throws NoSuchAlgorithmException { + return (Signature) getInstance(signatures, algName); + } + + /** + * Return the algorithm parameter specification class corresponding to the + * given algorithm name. + * + * @param algName + * the algorithm name + * @return the corresponding algorithm parameter specification class + * @throws NoSuchAlgorithmException + * if the parameters class cannot be found. + */ + public static final Class getAlgParamSpecClass(String algName) + throws NoSuchAlgorithmException { + Class algorithmClass = (Class) resolveAlias(algParamSpecs, algName); + if (algorithmClass == null) { + throw new NoSuchAlgorithmException(algName); + } + return algorithmClass; + } + + /** + * Return an instance of the algorithm parameter specification class + * corresponding to the given algorithm name. + * + * @param paramName + * the name of the standard algorithm parameters + * @return the standard algorithm parameters + * @throws InvalidAlgorithmParameterException + * if the parameters cannot be found. + */ + public static final AlgorithmParameterSpec getAlgParamSpec(String paramName) + throws InvalidAlgorithmParameterException { + try { + return (AlgorithmParameterSpec) getInstance(algParamSpecs, + paramName); + } catch (NoSuchAlgorithmException e) { + throw new InvalidAlgorithmParameterException( + "Unknown parameters: '" + paramName + "'."); + } + } + + /** + * Register a list of (names of) standardized algorithm parameters for the + * given algorithm. Additionally, each parameter set has to be registered + * separately using the {@link #add(int, Class, String)} or + * {@link #add(int, Class, String[])} method with the + * {@link #ALG_PARAM_SPEC} type. + * + * @param algName + * the name of the algorithm + * @param paramNames + * the names of the standardized algorithm parameters suitable + * for the specified algorithm + */ + public static final void addStandardAlgParams(String algName, + String[] paramNames) { + addStandardAlgParams(new String[] { algName }, paramNames); + } + + /** + * Register a list of standardized algorithm parameters for the given list + * of algorithms. Additionally, each parameter set has to be registered + * separately using the {@link #add(int, Class, String)} or + * {@link #add(int, Class, String[])} method with the + * {@link #ALG_PARAM_SPEC} type. + * + * @param algNames + * the names of the algorithms + * @param paramNames + * the names of the standardized algorithm parameters suitable + * for the specified algorithm + */ + public static final void addStandardAlgParams(String[] algNames, + String[] paramNames) { + + if ((algNames == null) || (paramNames == null)) { + return; + } + + // build vector containing the parameter set names + Vector params = new Vector(paramNames.length); + for (int i = 0; i < paramNames.length; i++) { + params.addElement(paramNames[i]); + } + + // register first name + standardAlgParams.put(algNames[0], params); + + // register additional names (aliases) + for (int i = 1; i < algNames.length; i++) { + standardAlgParams.put(algNames[i], algNames[0]); + } + } + + /** + * Return the set of standardized algorithm parameters registered for the + * given algorithm, or null if no parameters are registered for the + * given algorithm. + * + * @param algName + * the algorithm name + * @return the {@link Vector} of standardized algorithms parameters for the + * specified algorithm, or null if no parameters are + * registered for the algorithm + */ + public static final Vector getStandardAlgParams(String algName) { + return (Vector) resolveAlias(standardAlgParams, algName); + } + + /** + * Return an instance of the specified algorithm parameters. + * + * @param algName + * the name of the algorithm parameters + * @return a new {@link AlgorithmParameters} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the algorithm parameters cannot be found. + */ + public static final AlgorithmParameters getAlgParams(String algName) + throws NoSuchAlgorithmException { + return (AlgorithmParameters) getInstance(algParams, algName); + } + + /** + * Return an instance of the specified algorithm parameter generator. + * + * @param algName + * the name of the algorithm parameter generator + * @return a new {@link AlgorithmParameterGenerator} object implementing the + * chosen algorithm + * @throws NoSuchAlgorithmException + * if the algorithm parameter generator cannot be found. + */ + public static final AlgorithmParameterGenerator getAlgParamGenerator( + String algName) throws NoSuchAlgorithmException { + return (AlgorithmParameterGenerator) getInstance(algParamGenerators, + algName); + } + + /** + * Return an instance of the specified secret key generator. + * + * @param algName + * the name of the secret key generator + * @return a new {@link SecretKeyGenerator} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the secret key generator cannot be found. + */ + public static final SecretKeyGenerator getSecretKeyGenerator(String algName) + throws NoSuchAlgorithmException { + return (SecretKeyGenerator) getInstance(secretKeyGenerators, algName); + } + + /** + * Return an instance of the specified key pair generator. + * + * @param algName + * the name of the key pair generator + * @return a new {@link KeyPairGenerator} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the key pair generator cannot be found. + */ + public static final KeyPairGenerator getKeyPairGenerator(String algName) + throws NoSuchAlgorithmException { + return (KeyPairGenerator) getInstance(keyPairGenerators, algName); + } + + /** + * Return an instance of the specified secret key factory. + * + * @param algName + * the name of the secret key factory + * @return a new {@link SecretKeyFactory} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the secret key factory cannot be found. + */ + public static final SecretKeyFactory getSecretKeyFactory(String algName) + throws NoSuchAlgorithmException { + return (SecretKeyFactory) getInstance(secretKeyFactories, algName); + } + + /** + * Return an instance of the specified key factory. + * + * @param algName + * the name of the key factory + * @return a new {@link KeyFactory} object implementing the chosen algorithm + * @throws NoSuchAlgorithmException + * if the key factory cannot be found. + */ + public static final KeyFactory getKeyFactory(String algName) + throws NoSuchAlgorithmException { + return (KeyFactory) getInstance(keyFactories, algName); + } + + /** + * Return an instance of the specified key derivation function. + * + * @param algName + * the name of the key derivation function + * @return a new {@link KeyDerivation} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the key derivation cannot be found. + */ + public static final KeyDerivation getKeyDerivation(String algName) + throws NoSuchAlgorithmException { + return (KeyDerivation) getInstance(keyDerivations, algName); + } + + /** + * Return an instance of the specified key agreement scheme. + * + * @param algName + * the name of the key agreement scheme + * @return a new {@link KeyAgreement} object implementing the chosen + * algorithm + * @throws NoSuchAlgorithmException + * if the key agreement scheme cannot be found. + */ + public static final KeyAgreement getKeyAgreement(String algName) + throws NoSuchAlgorithmException { + return (KeyAgreement) getInstance(keyAgreements, algName); + } + + private static Hashtable getHashtable(int type) { + if (type > hashtables.length) { + return null; + } + return hashtables[type]; + } + + private static Object resolveAlias(Hashtable table, String name) { + Object value = name; + do { + String algName = (String) value; + value = table.get(algName); + } while (value != null && (value instanceof String)); + return value; + } + + /** + * Try to find an algorithm with the specified name inside the corresponding + * hashtable and return an instance of the algorithm. + * + * @param table + * hashtable containing the algorithm + * @param name + * the algorithm name + * @return a new object implementing the chosen algorithm, or null + * if the algorithm name is null + * @throws NoSuchAlgorithmException + * if the algorithm cannot be found. + */ + private static Object getInstance(Hashtable table, String name) + throws NoSuchAlgorithmException { + if (name == null) { + return null; + } + Class algClass = (Class) resolveAlias(table, name); + if (algClass == null) { + throw new NoSuchAlgorithmException(name); + } + try { + return algClass.newInstance(); + } catch (InstantiationException e) { + throw new RegistrationException("Instantiation exception: " + + e.getMessage()); + } catch (IllegalAccessException e) { + throw new RegistrationException("Illegal access exception: " + + e.getMessage()); + } + } + +} diff --git a/src/de/flexiprovider/api/SecureRandom.java b/src/de/flexiprovider/api/SecureRandom.java new file mode 100644 index 00000000..00d7ca57 --- /dev/null +++ b/src/de/flexiprovider/api/SecureRandom.java @@ -0,0 +1,59 @@ +package de.flexiprovider.api; + +import de.flexiprovider.common.math.FlexiBigInt; +import de.flexiprovider.common.math.IntegerFunctions; +import de.flexiprovider.common.util.BigEndianConversions; +import de.flexiprovider.common.util.LittleEndianConversions; + +public abstract class SecureRandom extends java.security.SecureRandomSpi { + + protected final byte[] engineGenerateSeed(int numBytes) { + return generateSeed(numBytes); + } + + protected final void engineNextBytes(byte[] bytes) { + nextBytes(bytes); + } + + protected final void engineSetSeed(byte[] seed) { + setSeed(seed); + } + + public abstract byte[] generateSeed(int numBytes); + + public abstract void nextBytes(byte[] bytes); + + public abstract void setSeed(byte[] seed); + + public final int nextInt() { + byte[] intBytes = new byte[4]; + nextBytes(intBytes); + return BigEndianConversions.OS2IP(intBytes); + } + + public final int nextInt(int upperBound) { + int result; + int octL = IntegerFunctions.ceilLog256(upperBound); + do { + byte[] intBytes = new byte[octL]; + nextBytes(intBytes); + result = BigEndianConversions.OS2IP(intBytes, 0, octL); + } while (result < 0 || result >= upperBound); + return result; + } + + public final long nextLong(long upperBound){ + int octL = IntegerFunctions.ceilLog256(upperBound); + int bitLength=IntegerFunctions.floorLog(FlexiBigInt.valueOf(upperBound))+1; + int difference=octL*8-bitLength; + + long result; + do { + byte[] intBytes = new byte[octL]; + nextBytes(intBytes); + result = LittleEndianConversions.toLong(intBytes); + result>>>=difference; + } while (result < 0 || result >= upperBound); + return result; + } +} diff --git a/src/de/flexiprovider/api/Signature.java b/src/de/flexiprovider/api/Signature.java new file mode 100644 index 00000000..aa0c0f0d --- /dev/null +++ b/src/de/flexiprovider/api/Signature.java @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ +package de.flexiprovider.api; + +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.SignatureException; +import de.flexiprovider.api.keys.PrivateKey; +import de.flexiprovider.api.keys.PublicKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.common.util.JavaSecureRandomWrapper; + +/** + * This class defines the Service Provider Interface (SPI) for + * the Signature class, which is used to provide the functionality of + * a digital signature algorithm. Digital signatures are used for authentication + * and integrity assurance of digital data. . + *

+ * All the abstract methods in this class must be implemented by each + * cryptographic service provider who wishes to supply the implementation of a + * particular signature algorithm. + * + * @author Martin Döring, Johannes Müller + */ +public abstract class Signature extends java.security.SignatureSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * @deprecated + */ + protected final Object engineGetParameter(String param) + throws java.security.InvalidParameterException { + // method is deprecated + return null; + } + + /** + * @deprecated + */ + protected final void engineSetParameter(String param, Object value) + throws java.security.InvalidParameterException { + // method is deprecated + } + + protected final void engineInitSign(java.security.PrivateKey privateKey) + throws java.security.InvalidKeyException { + if ((privateKey == null) || !(privateKey instanceof PrivateKey)) { + throw new java.security.InvalidKeyException(); + } + initSign((PrivateKey) privateKey); + } + + protected final void engineInitSign(java.security.PrivateKey privateKey, + java.security.SecureRandom javaRand) + throws java.security.InvalidKeyException { + if ((privateKey == null) || !(privateKey instanceof PrivateKey)) { + throw new java.security.InvalidKeyException(); + } + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + initSign((PrivateKey) privateKey, flexiRand); + } + + protected final void engineInitVerify(java.security.PublicKey publicKey) + throws java.security.InvalidKeyException { + if ((publicKey == null) || !(publicKey instanceof PublicKey)) { + throw new java.security.InvalidKeyException(); + } + initVerify((PublicKey) publicKey); + } + + protected void engineSetParameter( + java.security.spec.AlgorithmParameterSpec params) + throws java.security.InvalidAlgorithmParameterException { + if (params != null && !(params instanceof AlgorithmParameterSpec)) { + throw new java.security.InvalidAlgorithmParameterException(); + } + setParameters((AlgorithmParameterSpec) params); + } + + protected final void engineUpdate(byte b) + throws java.security.SignatureException { + update(b); + } + + protected final void engineUpdate(byte[] b, int off, int len) + throws java.security.SignatureException { + update(b, off, len); + } + + protected final byte[] engineSign() throws java.security.SignatureException { + return sign(); + } + + protected final boolean engineVerify(byte[] sigBytes) + throws java.security.SignatureException { + return verify(sigBytes); + } + + protected final boolean engineVerify(byte[] sigBytes, int offset, int length) + throws java.security.SignatureException { + return verify(sigBytes, offset, length); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Initialize the signature with the specified private key for signing + * operations. + * + * @param privKey + * the private key of the identity whose signature will be + * generated. + * @throws InvalidKeyException + * if the key is invalid for initializing the signature. + */ + public final void initSign(PrivateKey privKey) throws InvalidKeyException { + initSign(privKey, Registry.getSecureRandom()); + } + + /** + * Initialize the signature with the specified private key and source of + * randomness for signing operations. + * + * @param privKey + * the private key of the identity whose signature will be + * generated. + * @param random + * the source of randomness + * @throws InvalidKeyException + * if the key is invalid for initializing the signature. + */ + public abstract void initSign(PrivateKey privKey, SecureRandom random) + throws InvalidKeyException; + + /** + * Initialize the signature with the specified public key for verification + * operations. + * + * @param pubKey + * the public key of the identity whose signature is going to + * be verified + * @throws InvalidKeyException + * if the key is invalid for initializing the signature. + */ + public abstract void initVerify(PublicKey pubKey) + throws InvalidKeyException; + + /** + * Initialize the signature with the specified parameters. + * + * @param params + * the parameters + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for this + * signature. + */ + public abstract void setParameters(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException; + + /** + * Update the data to be signed or verified using the specified byte. + * + * @param input + * the data byte + * @throws SignatureException + * if the engine is not initialized properly. + */ + public abstract void update(byte input) throws SignatureException; + + /** + * Update the data to be signed or verified using the specified byte array. + * + * @param input + * the data byte array + * @throws SignatureException + * if the engine is not initialized properly. + */ + public final void update(byte[] input) throws SignatureException { + update(input, 0, input.length); + } + + /** + * Update the data to be signed or verified, using the specified byte array + * of the specified length, starting at the specified offset. + * + * @param input + * the data byte array + * @param inOff + * the offset to start from in the array of bytes + * @param inLen + * the number of bytes to use, starting at inOff + * @throws SignatureException + * if the engine is not initialized properly + */ + public abstract void update(byte[] input, int inOff, int inLen) + throws SignatureException; + + /** + * Return the signature of all the data updated so far. + * + * @return the signature + * @throws SignatureException + * if the engine is not initialized properly. + */ + public abstract byte[] sign() throws SignatureException; + + /** + * Update the data to be signed and return the signature of all the data + * updated so far. + * + * @param input + * the data byte array + * @return the signature + * @throws SignatureException + * if the engine is not initialized properly. + */ + public final byte[] sign(byte[] input) throws SignatureException { + update(input); + return sign(); + } + + /** + * Verify the passed-in signature of the specified message. + * + * @param signature + * the signature + * @return true if the signature is valid, false + * otherwise. + * @throws SignatureException + * if the engine is not initialized properly or the + * passed-in signature is improperly encoded or of the wrong + * type. + */ + public abstract boolean verify(byte[] signature) throws SignatureException; + + /** + * Update the data to be verified and verify the passed-in signature. + * + * @param input + * the data byte array + * @param signature + * the signature + * @return true if the signature is valid, false + * otherwise. + * @throws SignatureException + * if the engine is not initialized properly or the + * passed-in signature is improperly encoded or of the wrong + * type. + */ + public final boolean verify(byte[] input, byte[] signature) + throws SignatureException { + update(input); + return verify(signature); + } + + /** + * Verify the passed-in signature. + * + * @param signature + * the signature + * @param sigOff + * the offset where the signature starts + * @param sigLen + * the length of the signature + * @return true if the signature is valid, false + * otherwise. + * @throws SignatureException + * if the engine is not initialized properly or the + * passed-in signature is improperly encoded or of the wrong + * type. + */ + public final boolean verify(byte[] signature, int sigOff, int sigLen) + throws SignatureException { + byte[] sig = new byte[sigLen]; + System.arraycopy(signature, sigOff, sig, 0, sigLen); + return verify(sig); + } + + /** + * Update the data to be verified and verify the passed-in signature. + * + * @param input + * the data byte array + * @param signature + * the signature + * @param sigOff + * the offset where the signature starts + * @param sigLen + * the length of the signature + * @return true if the signature is valid, false + * otherwise. + * @throws SignatureException + * if the engine is not initialized properly or the + * passed-in signature is improperly encoded or of the wrong + * type. + */ + public final boolean verify(byte[] input, byte[] signature, int sigOff, + int sigLen) throws SignatureException { + update(input); + return verify(signature, sigOff, sigLen); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/BadPaddingException.java b/src/de/flexiprovider/api/exceptions/BadPaddingException.java new file mode 100644 index 00000000..44339cef --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/BadPaddingException.java @@ -0,0 +1,13 @@ +package de.flexiprovider.api.exceptions; + +public class BadPaddingException extends javax.crypto.BadPaddingException { + + public BadPaddingException() { + super(); + } + + public BadPaddingException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/DigestException.java b/src/de/flexiprovider/api/exceptions/DigestException.java new file mode 100644 index 00000000..7b10a064 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/DigestException.java @@ -0,0 +1,9 @@ +package de.flexiprovider.api.exceptions; + +public class DigestException extends java.security.DigestException { + + public DigestException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/IllegalBlockSizeException.java b/src/de/flexiprovider/api/exceptions/IllegalBlockSizeException.java new file mode 100644 index 00000000..6c767337 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/IllegalBlockSizeException.java @@ -0,0 +1,10 @@ +package de.flexiprovider.api.exceptions; + +public class IllegalBlockSizeException extends + javax.crypto.IllegalBlockSizeException { + + public IllegalBlockSizeException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/InvalidAlgorithmParameterException.java b/src/de/flexiprovider/api/exceptions/InvalidAlgorithmParameterException.java new file mode 100644 index 00000000..a1bba86f --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/InvalidAlgorithmParameterException.java @@ -0,0 +1,14 @@ +package de.flexiprovider.api.exceptions; + +public class InvalidAlgorithmParameterException extends + java.security.InvalidAlgorithmParameterException { + + public InvalidAlgorithmParameterException() { + super(); + } + + public InvalidAlgorithmParameterException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/InvalidKeyException.java b/src/de/flexiprovider/api/exceptions/InvalidKeyException.java new file mode 100644 index 00000000..61156471 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/InvalidKeyException.java @@ -0,0 +1,13 @@ +package de.flexiprovider.api.exceptions; + +public class InvalidKeyException extends java.security.InvalidKeyException { + + public InvalidKeyException() { + super(); + } + + public InvalidKeyException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/InvalidKeySpecException.java b/src/de/flexiprovider/api/exceptions/InvalidKeySpecException.java new file mode 100644 index 00000000..382a4f0b --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/InvalidKeySpecException.java @@ -0,0 +1,14 @@ +package de.flexiprovider.api.exceptions; + +public class InvalidKeySpecException extends + java.security.spec.InvalidKeySpecException { + + public InvalidKeySpecException() { + super(); + } + + public InvalidKeySpecException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/InvalidParameterException.java b/src/de/flexiprovider/api/exceptions/InvalidParameterException.java new file mode 100644 index 00000000..545e963a --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/InvalidParameterException.java @@ -0,0 +1,10 @@ +package de.flexiprovider.api.exceptions; + +public class InvalidParameterException extends + java.security.InvalidParameterException { + + public InvalidParameterException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/InvalidParameterSpecException.java b/src/de/flexiprovider/api/exceptions/InvalidParameterSpecException.java new file mode 100644 index 00000000..98903658 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/InvalidParameterSpecException.java @@ -0,0 +1,10 @@ +package de.flexiprovider.api.exceptions; + +public class InvalidParameterSpecException extends + java.security.spec.InvalidParameterSpecException { + + public InvalidParameterSpecException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/NoSuchAlgorithmException.java b/src/de/flexiprovider/api/exceptions/NoSuchAlgorithmException.java new file mode 100644 index 00000000..36402b05 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/NoSuchAlgorithmException.java @@ -0,0 +1,10 @@ +package de.flexiprovider.api.exceptions; + +public class NoSuchAlgorithmException extends + java.security.NoSuchAlgorithmException { + + public NoSuchAlgorithmException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/NoSuchModeException.java b/src/de/flexiprovider/api/exceptions/NoSuchModeException.java new file mode 100644 index 00000000..9fbfa4f8 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/NoSuchModeException.java @@ -0,0 +1,20 @@ +package de.flexiprovider.api.exceptions; + +/** + * Exception used to indicate that a mode of operation cannot be found. + * + * @author Martin Döring + */ +public class NoSuchModeException extends NoSuchAlgorithmException { + + /** + * Constructor. + * + * @param msg + * the error message + */ + public NoSuchModeException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/NoSuchPaddingException.java b/src/de/flexiprovider/api/exceptions/NoSuchPaddingException.java new file mode 100644 index 00000000..334f803e --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/NoSuchPaddingException.java @@ -0,0 +1,9 @@ +package de.flexiprovider.api.exceptions; + +public class NoSuchPaddingException extends javax.crypto.NoSuchPaddingException { + + public NoSuchPaddingException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/RegistrationException.java b/src/de/flexiprovider/api/exceptions/RegistrationException.java new file mode 100644 index 00000000..8f5d0048 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/RegistrationException.java @@ -0,0 +1,29 @@ +package de.flexiprovider.api.exceptions; + +/** + * Exception used to indicate registration errors (used by the + * {@link de.flexiprovider.api.Registry Registry} class). Since this exception + * is thrown during static initialization, it extends {@link RuntimeException}. + * + * @author Martin Döring + */ +public class RegistrationException extends RuntimeException { + + /** + * Default constructor. + */ + public RegistrationException() { + super(); + } + + /** + * Constructor. + * + * @param s + * the error message + */ + public RegistrationException(String s) { + super(s); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/ShortBufferException.java b/src/de/flexiprovider/api/exceptions/ShortBufferException.java new file mode 100644 index 00000000..6a0e9dd1 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/ShortBufferException.java @@ -0,0 +1,13 @@ +package de.flexiprovider.api.exceptions; + +public class ShortBufferException extends javax.crypto.ShortBufferException { + + public ShortBufferException() { + super(); + } + + public ShortBufferException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/exceptions/SignatureException.java b/src/de/flexiprovider/api/exceptions/SignatureException.java new file mode 100644 index 00000000..3c36c204 --- /dev/null +++ b/src/de/flexiprovider/api/exceptions/SignatureException.java @@ -0,0 +1,9 @@ +package de.flexiprovider.api.exceptions; + +public class SignatureException extends java.security.SignatureException { + + public SignatureException(String msg) { + super(msg); + } + +} diff --git a/src/de/flexiprovider/api/keys/Key.java b/src/de/flexiprovider/api/keys/Key.java new file mode 100644 index 00000000..f734a5ea --- /dev/null +++ b/src/de/flexiprovider/api/keys/Key.java @@ -0,0 +1,7 @@ +package de.flexiprovider.api.keys; + +public interface Key extends java.security.Key { + + // empty + +} diff --git a/src/de/flexiprovider/api/keys/KeyFactory.java b/src/de/flexiprovider/api/keys/KeyFactory.java new file mode 100644 index 00000000..3e422241 --- /dev/null +++ b/src/de/flexiprovider/api/keys/KeyFactory.java @@ -0,0 +1,189 @@ +package de.flexiprovider.api.keys; + +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.InvalidKeySpecException; +import de.flexiprovider.pki.PKCS8EncodedKeySpec; +import de.flexiprovider.pki.X509EncodedKeySpec; + +public abstract class KeyFactory extends java.security.KeyFactorySpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * JCA adapter for FlexiAPI method generatePublic(): generates a public key + * object from the provided key specification (key material). + * + * @param keySpec + * the specification (key material) of the public key + * @return the public key + * @throws java.security.spec.InvalidKeySpecException + * if the given key specification is inappropriate for this + * key factory to produce a public key. + */ + protected java.security.PublicKey engineGeneratePublic( + java.security.spec.KeySpec keySpec) + throws java.security.spec.InvalidKeySpecException { + + if (keySpec != null && !(keySpec instanceof KeySpec)) { + if (keySpec instanceof java.security.spec.X509EncodedKeySpec) { + KeySpec encKeySpec = new X509EncodedKeySpec( + (java.security.spec.X509EncodedKeySpec) keySpec); + return generatePublic(encKeySpec); + } + + throw new java.security.spec.InvalidKeySpecException(); + } + + return generatePublic((KeySpec) keySpec); + } + + /** + * JCA adapter for FlexiAPI method generatePrivate(): generate a private key + * object from the provided key specification (key material). + * + * @param keySpec + * the specification (key material) of the private key + * @return the private key + * @throws java.security.spec.InvalidKeySpecException + * if the given key specification is inappropriate for this + * key factory to produce a private key. + */ + protected java.security.PrivateKey engineGeneratePrivate( + java.security.spec.KeySpec keySpec) + throws java.security.spec.InvalidKeySpecException { + + if (keySpec != null && !(keySpec instanceof KeySpec)) { + if (keySpec instanceof java.security.spec.PKCS8EncodedKeySpec) { + KeySpec encKeySpec = new PKCS8EncodedKeySpec( + (java.security.spec.PKCS8EncodedKeySpec) keySpec); + return generatePrivate(encKeySpec); + } + + throw new java.security.spec.InvalidKeySpecException(); + } + + return generatePrivate((KeySpec) keySpec); + } + + /** + * JCA adapter for FlexiAPI method getKeySpec(): return a specification (key + * material) of the given key object. keySpec identifies the + * specification class in which the key material should be returned. It + * could, for example, be DSAPublicKeySpec.class, to indicate + * that the key material should be returned in an instance of the + * DSAPublicKeySpec class. + * + * @param key + * the key + * @param keySpec + * the specification class in which the key material should + * be returned + * @return the underlying key specification (key material) in an instance of + * the requested specification class + * @throws java.security.spec.InvalidKeySpecException + * if the requested key specification is inappropriate for + * the given key, or the given key cannot be dealt with + * (e.g., the given key has an unrecognized format). + */ + protected final java.security.spec.KeySpec engineGetKeySpec( + java.security.Key key, Class keySpec) + throws java.security.spec.InvalidKeySpecException { + + if (!(key instanceof Key)) { + throw new java.security.spec.InvalidKeySpecException(); + } + + return getKeySpec((Key) key, keySpec); + } + + /** + * JCA adapter for FlexiAPI method translateKey(): translate a key object, + * whose provider may be unknown or potentially untrusted, into a + * corresponding key object of this key factory. + * + * @param key + * the key whose provider is unknown or untrusted + * @return the translated key + * @throws java.security.InvalidKeyException + * if the given key cannot be processed by this key factory. + */ + protected final java.security.Key engineTranslateKey(java.security.Key key) + throws java.security.InvalidKeyException { + + if (!(key instanceof Key)) { + throw new java.security.InvalidKeyException(); + } + + return translateKey((Key) key); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Generate a public key object from the provided key specification (key + * material). + * + * @param keySpec + * the specification (key material) of the public key + * @return the public key + * @throws InvalidKeySpecException + * if the given key specification is inappropriate for this + * key factory to produce a public key. + */ + public abstract PublicKey generatePublic(KeySpec keySpec) + throws InvalidKeySpecException; + + /** + * Generate a private key object from the provided key specification (key + * material). + * + * @param keySpec + * the specification (key material) of the private key + * @return the private key + * @throws InvalidKeySpecException + * if the given key specification is inappropriate for this + * key factory to produce a private key. + */ + public abstract PrivateKey generatePrivate(KeySpec keySpec) + throws InvalidKeySpecException; + + /** + * Return a specification (key material) of the given key object. + * keySpec identifies the specification class in which the key + * material should be returned. It could, for example, be + * DSAPublicKeySpec.class, to indicate that the key material + * should be returned in an instance of the DSAPublicKeySpec + * class. + * + * @param key + * the key + * @param keySpec + * the specification class in which the key material should + * be returned + * @return the underlying key specification (key material) in an instance of + * the requested specification class + * @throws InvalidKeySpecException + * if the requested key specification is inappropriate for + * the given key, or the given key cannot be dealt with + * (e.g., the given key has an unrecognized format). + */ + public abstract KeySpec getKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException; + + /** + * Translate a key object, whose provider may be unknown or potentially + * untrusted, into a corresponding key object of this key factory. + * + * @param key + * the key whose provider is unknown or untrusted + * @return the translated key + * @throws InvalidKeyException + * if the given key cannot be processed by this key factory. + */ + public abstract Key translateKey(Key key) throws InvalidKeyException; + +} diff --git a/src/de/flexiprovider/api/keys/KeyPair.java b/src/de/flexiprovider/api/keys/KeyPair.java new file mode 100644 index 00000000..b0ec2718 --- /dev/null +++ b/src/de/flexiprovider/api/keys/KeyPair.java @@ -0,0 +1,47 @@ +package de.flexiprovider.api.keys; + +/** + * This class is a simple holder for a key pair (a public key and a private + * key). It does not enforce any security, and, when initialized, should be + * treated like a private key. + */ +public final class KeyPair { + + java.security.KeyPair pair; + + /** + * Construct a key pair from the given public key and private key. + *

+ * Note that this constructor only stores references to the public and + * private key components in the generated key pair. This is safe, because + * Key objects are immutable. + * + * @param publicKey + * the public key. + * + * @param privateKey + * the private key. + */ + public KeyPair(PublicKey publicKey, PrivateKey privateKey) { + pair = new java.security.KeyPair(publicKey, privateKey); + } + + /** + * Return a reference to the public key component of this key pair. + * + * @return a reference to the public key. + */ + public PublicKey getPublic() { + return (PublicKey) pair.getPublic(); + } + + /** + * Return a reference to the private key component of this key pair. + * + * @return a reference to the private key. + */ + public PrivateKey getPrivate() { + return (PrivateKey) pair.getPrivate(); + } + +} diff --git a/src/de/flexiprovider/api/keys/KeyPairGenerator.java b/src/de/flexiprovider/api/keys/KeyPairGenerator.java new file mode 100644 index 00000000..beb50f0c --- /dev/null +++ b/src/de/flexiprovider/api/keys/KeyPairGenerator.java @@ -0,0 +1,132 @@ +package de.flexiprovider.api.keys; + +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidParameterException; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.common.util.JavaSecureRandomWrapper; + +public abstract class KeyPairGenerator extends + java.security.KeyPairGeneratorSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * JCA adapter for FlexiAPI method + * {@link #initialize(AlgorithmParameterSpec, SecureRandom)}: initialize + * the key pair generator using the specified parameter set and source of + * randomness. + * + * @param params + * the parameter set used to generate the keys + * @param javaRand + * the source of randomness for this generator + * @throws java.security.InvalidAlgorithmParameterException + * if the given parameters are inappropriate for this key + * pair generator. + */ + public void initialize(java.security.spec.AlgorithmParameterSpec params, + java.security.SecureRandom javaRand) + throws java.security.InvalidAlgorithmParameterException { + + if (params != null && !(params instanceof AlgorithmParameterSpec)) { + throw new java.security.InvalidAlgorithmParameterException(); + } + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + initialize((AlgorithmParameterSpec) params, flexiRand); + } + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * Initialize the key pair generator for a certain key size using a default + * parameter set and the SecureRandom implementation of the + * highest-priority installed provider as the source of randomness. (If none + * of the installed providers supply an implementation of + * SecureRandom, a system-provided source of randomness is + * used.) + * + * @param keysize + * the keysize. This is an algorithm-specific metric, such as + * modulus length, specified in number of bits. + * @param javaRand + * the source of randomness for this generator + * @throws InvalidParameterException + * if the keysize is not supported by this + * KeyPairGenerator object. + */ + public final void initialize(int keysize, + java.security.SecureRandom javaRand) + throws InvalidParameterException { + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + initialize(keysize, flexiRand); + } + + /** + * JCA adapter to FlexiAPI method {@link #genKeyPair()}: generate a key + * pair. Unless an initialization method is called using a KeyPairGenerator + * interface, algorithm-specific defaults will be used. This will generate a + * new key pair every time it is called. + * + * @return a newly generated KeyPair + */ + public final java.security.KeyPair generateKeyPair() { + return genKeyPair().pair; + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Initialize the key pair generator using the specified parameter set and + * the SecureRandom implementation of the highest-priority + * installed provider as the source of randomness. (If none of the installed + * providers supply an implementation of SecureRandom, a + * system-provided source of randomness is used.). + * + * @param params + * the parameter set used to generate the keys + * @param random + * a source of randomness + * @throws InvalidAlgorithmParameterException + * if the given parameters are inappropriate for this key + * pair generator. + */ + public abstract void initialize(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException; + + /** + * Initialize the key pair generator for a certain keysize using a default + * parameter set and the SecureRandom implementation of the + * highest-priority installed provider as the source of randomness. (If none + * of the installed providers supply an implementation of + * SecureRandom, a system-provided source of randomness is + * used.) + * + * @param keysize + * the keysize. This is an algorithm-specific metric, such as + * modulus length, specified in number of bits. + * @param random + * the source of randomness for this generator + * @throws InvalidParameterException + * if the keysize is not supported by this + * KeyPairGenerator object. + */ + public abstract void initialize(int keysize, SecureRandom random) + throws InvalidParameterException; + + /** + * Generate a key pair. Unless an initialization method is called using a + * KeyPairGenerator interface, algorithm-specific defaults will be used. + * This will generate a new key pair every time it is called. + * + * @return a newly generated {@link KeyPair} + */ + public abstract KeyPair genKeyPair(); + +} diff --git a/src/de/flexiprovider/api/keys/KeySpec.java b/src/de/flexiprovider/api/keys/KeySpec.java new file mode 100644 index 00000000..1114a021 --- /dev/null +++ b/src/de/flexiprovider/api/keys/KeySpec.java @@ -0,0 +1,11 @@ +package de.flexiprovider.api.keys; + +/** + * A (transparent) specification of the key material that constitutes a + * cryptographic key. + */ +public interface KeySpec extends java.security.spec.KeySpec { + + // empty + +} diff --git a/src/de/flexiprovider/api/keys/PrivateKey.java b/src/de/flexiprovider/api/keys/PrivateKey.java new file mode 100644 index 00000000..18435069 --- /dev/null +++ b/src/de/flexiprovider/api/keys/PrivateKey.java @@ -0,0 +1,53 @@ +package de.flexiprovider.api.keys; + +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1Type; +import codec.pkcs8.PrivateKeyInfo; +import de.flexiprovider.common.util.ASN1Tools; +import de.flexiprovider.pki.AlgorithmIdentifier; + +public abstract class PrivateKey implements Key, java.security.PrivateKey { + + /** + * Return the encoding format, PKCS #8. + * + * @return "PKCS#8" + */ + public final String getFormat() { + return "PKCS#8"; + } + + /** + * Return the key in its primary encoding format, PKCS #8. + * + * @return the PKCS #8 encoded key. + */ + public final byte[] getEncoded() { + AlgorithmIdentifier aid; + try { + aid = new AlgorithmIdentifier(getOID(), getAlgParams()); + } catch (ASN1Exception asn1e) { + throw new RuntimeException("ASN1Exception: " + asn1e.getMessage()); + } + PrivateKeyInfo spki = new PrivateKeyInfo(aid, getKeyData()); + return ASN1Tools.derEncode(spki); + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected abstract ASN1ObjectIdentifier getOID(); + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected abstract ASN1Type getAlgParams(); + + /** + * @return the keyData to encode in the SubjectPublicKeyInfo structure + */ + protected abstract byte[] getKeyData(); + +} diff --git a/src/de/flexiprovider/api/keys/PublicKey.java b/src/de/flexiprovider/api/keys/PublicKey.java new file mode 100644 index 00000000..49d3a983 --- /dev/null +++ b/src/de/flexiprovider/api/keys/PublicKey.java @@ -0,0 +1,53 @@ +package de.flexiprovider.api.keys; + +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1Type; +import codec.x509.SubjectPublicKeyInfo; +import de.flexiprovider.common.util.ASN1Tools; +import de.flexiprovider.pki.AlgorithmIdentifier; + +public abstract class PublicKey implements Key, java.security.PublicKey { + + /** + * Return the encoding format, X.509. + * + * @return "X.509" + */ + public final String getFormat() { + return "X.509"; + } + + /** + * Return the key in its primary encoding format, X.509. + * + * @return the X.509 encoded key. + */ + public final byte[] getEncoded() { + AlgorithmIdentifier aid; + try { + aid = new AlgorithmIdentifier(getOID(), getAlgParams()); + } catch (ASN1Exception asn1e) { + throw new RuntimeException("ASN1Exception: " + asn1e.getMessage()); + } + SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo(aid, getKeyData()); + return ASN1Tools.derEncode(spki); + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected abstract ASN1ObjectIdentifier getOID(); + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected abstract ASN1Type getAlgParams(); + + /** + * @return the keyData to encode in the SubjectPublicKeyInfo structure + */ + protected abstract byte[] getKeyData(); + +} diff --git a/src/de/flexiprovider/api/keys/SecretKey.java b/src/de/flexiprovider/api/keys/SecretKey.java new file mode 100644 index 00000000..598a40ea --- /dev/null +++ b/src/de/flexiprovider/api/keys/SecretKey.java @@ -0,0 +1,7 @@ +package de.flexiprovider.api.keys; + +public interface SecretKey extends Key, javax.crypto.SecretKey { + + // empty + +} diff --git a/src/de/flexiprovider/api/keys/SecretKeyFactory.java b/src/de/flexiprovider/api/keys/SecretKeyFactory.java new file mode 100644 index 00000000..ee1b1fc3 --- /dev/null +++ b/src/de/flexiprovider/api/keys/SecretKeyFactory.java @@ -0,0 +1,69 @@ +package de.flexiprovider.api.keys; + +import javax.crypto.SecretKeyFactorySpi; + +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.InvalidKeySpecException; + +public abstract class SecretKeyFactory extends SecretKeyFactorySpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + protected javax.crypto.SecretKey engineGenerateSecret( + java.security.spec.KeySpec keySpec) + throws java.security.spec.InvalidKeySpecException { + + if (keySpec == null) { + throw new java.security.spec.InvalidKeySpecException(); + } + + if (!(keySpec instanceof KeySpec)) { + if (keySpec instanceof javax.crypto.spec.SecretKeySpec) { + javax.crypto.spec.SecretKeySpec javaSpec = (javax.crypto.spec.SecretKeySpec) keySpec; + KeySpec secretKeySpec = new SecretKeySpec( + javaSpec.getEncoded(), javaSpec.getAlgorithm()); + return generateSecret(secretKeySpec); + } + + throw new java.security.spec.InvalidKeySpecException(); + } + + return generateSecret((KeySpec) keySpec); + } + + protected java.security.spec.KeySpec engineGetKeySpec( + javax.crypto.SecretKey key, Class keySpec) + throws java.security.spec.InvalidKeySpecException { + + if ((key == null) || (keySpec == null) || !(key instanceof SecretKey)) { + throw new java.security.spec.InvalidKeySpecException(); + } + return getKeySpec((SecretKey) key, keySpec); + } + + protected javax.crypto.SecretKey engineTranslateKey( + javax.crypto.SecretKey key) + throws java.security.InvalidKeyException { + + if ((key == null) || !(key instanceof SecretKey)) { + throw new java.security.InvalidKeyException(); + } + return translateKey((SecretKey) key); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + public abstract SecretKey generateSecret(KeySpec keySpec) + throws InvalidKeySpecException; + + public abstract KeySpec getKeySpec(SecretKey key, Class keySpec) + throws InvalidKeySpecException; + + public abstract SecretKey translateKey(SecretKey key) + throws InvalidKeyException; + +} diff --git a/src/de/flexiprovider/api/keys/SecretKeyGenerator.java b/src/de/flexiprovider/api/keys/SecretKeyGenerator.java new file mode 100644 index 00000000..cb6419f5 --- /dev/null +++ b/src/de/flexiprovider/api/keys/SecretKeyGenerator.java @@ -0,0 +1,64 @@ +package de.flexiprovider.api.keys; + +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.common.util.JavaSecureRandomWrapper; + +public abstract class SecretKeyGenerator extends javax.crypto.KeyGeneratorSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + protected final javax.crypto.SecretKey engineGenerateKey() { + return generateKey(); + } + + protected final void engineInit(java.security.SecureRandom javaRand) { + init(new JavaSecureRandomWrapper(javaRand)); + } + + protected final void engineInit(int keysize, + java.security.SecureRandom javaRand) { + init(keysize, new JavaSecureRandomWrapper(javaRand)); + } + + protected void engineInit(java.security.spec.AlgorithmParameterSpec params, + java.security.SecureRandom javaRand) + throws java.security.InvalidAlgorithmParameterException { + if (params != null && !(params instanceof AlgorithmParameterSpec)) { + throw new java.security.InvalidAlgorithmParameterException(); + } + init((AlgorithmParameterSpec) params, new JavaSecureRandomWrapper( + javaRand)); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + public abstract SecretKey generateKey(); + + public final void init() { + init(Registry.getSecureRandom()); + } + + public abstract void init(SecureRandom random); + + public final void init(int keySize) { + init(keySize, Registry.getSecureRandom()); + } + + public abstract void init(int keySize, SecureRandom random); + + public final void init(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + init(params, Registry.getSecureRandom()); + } + + public abstract void init(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException; + +} diff --git a/src/de/flexiprovider/api/keys/SecretKeySpec.java b/src/de/flexiprovider/api/keys/SecretKeySpec.java new file mode 100644 index 00000000..aca9d8af --- /dev/null +++ b/src/de/flexiprovider/api/keys/SecretKeySpec.java @@ -0,0 +1,14 @@ +package de.flexiprovider.api.keys; + +public class SecretKeySpec extends javax.crypto.spec.SecretKeySpec implements + KeySpec { + + public SecretKeySpec(byte[] key, String algorithm) { + super(key, algorithm); + } + + public SecretKeySpec(byte[] key, int offset, int len, String algorithm) { + super(key, offset, len, algorithm); + } + +} diff --git a/src/de/flexiprovider/api/parameters/AlgorithmParameterGenerator.java b/src/de/flexiprovider/api/parameters/AlgorithmParameterGenerator.java new file mode 100644 index 00000000..9370e97b --- /dev/null +++ b/src/de/flexiprovider/api/parameters/AlgorithmParameterGenerator.java @@ -0,0 +1,133 @@ +package de.flexiprovider.api.parameters; + +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.common.mode.ModeParameterSpec; +import de.flexiprovider.common.util.JavaSecureRandomWrapper; + +public abstract class AlgorithmParameterGenerator extends + java.security.AlgorithmParameterGeneratorSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * JCA adapter for FlexiAPI method {@link #generateParameters()}: generate + * parameters. + * + * @return the generated parameters + */ + protected final java.security.AlgorithmParameters engineGenerateParameters() { + + /** + * JCA adapter class, used to translate from {@link AlgorithmParameters} + * to {@link java.security.AlgorithmParameters}. + */ + final class JCAAlgorithmParameters extends + java.security.AlgorithmParameters { + private JCAAlgorithmParameters(AlgorithmParameters params) { + super(params, null, null); + } + } + + JCAAlgorithmParameters algParams = new JCAAlgorithmParameters( + getAlgorithmParameters()); + + try { + algParams.init(generateParameters()); + } catch (java.security.spec.InvalidParameterSpecException ipse) { + throw new RuntimeException("InvalidParameterSpecException: " + + ipse.getMessage()); + } + + return algParams; + } + + /** + * JCA adapter for FlexiAPI methods init(): initialize the parameter + * generator with a key size and a source of randomness. + * + * @param keySize + * the key size + * @param javaRand + * the source of randomness + */ + protected final void engineInit(int keySize, + java.security.SecureRandom javaRand) { + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + init(keySize, flexiRand); + } + + /** + * JCA adapter for FlexiAPI methods init(): initialize the parameter + * generator with a parameter specification and a source of randomness. + * + * @param genParamSpec + * the parameter specification + * @param javaRand + * the source of randomness + * @throws java.security.InvalidAlgorithmParameterException + * if the given parameters are invalid. + */ + protected final void engineInit( + java.security.spec.AlgorithmParameterSpec genParamSpec, + java.security.SecureRandom javaRand) + throws java.security.InvalidAlgorithmParameterException { + SecureRandom flexiRand = new JavaSecureRandomWrapper(javaRand); + ModeParameterSpec paramSpec; + if (genParamSpec instanceof javax.crypto.spec.IvParameterSpec) { + paramSpec = new ModeParameterSpec( + (javax.crypto.spec.IvParameterSpec) genParamSpec); + init(paramSpec, flexiRand); + } else { + if (!(genParamSpec instanceof AlgorithmParameterSpec)) { + throw new java.security.InvalidAlgorithmParameterException(); + } + init((AlgorithmParameterSpec) genParamSpec, flexiRand); + } + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * @return an instance of the {@link AlgorithmParameters} class + * corresponding to the generated parameters + */ + protected abstract AlgorithmParameters getAlgorithmParameters(); + + /** + * Initialize the parameter generator with a key size and a source of + * randomness. + * + * @param keySize + * the key size + * @param random + * the source of randomness + */ + public abstract void init(int keySize, SecureRandom random); + + /** + * Initialize the parameter generator with a parameter specification and a + * source of randomness. + * + * @param genParamSpec + * the parameter specification + * @param random + * the source of randomness + * @throws InvalidAlgorithmParameterException + * if the given parameters are invalid. + */ + public abstract void init(AlgorithmParameterSpec genParamSpec, + SecureRandom random) throws InvalidAlgorithmParameterException; + + /** + * Generate parameters. + * + * @return the generated parameters + */ + public abstract AlgorithmParameterSpec generateParameters(); + +} diff --git a/src/de/flexiprovider/api/parameters/AlgorithmParameterSpec.java b/src/de/flexiprovider/api/parameters/AlgorithmParameterSpec.java new file mode 100644 index 00000000..b0c5d9ac --- /dev/null +++ b/src/de/flexiprovider/api/parameters/AlgorithmParameterSpec.java @@ -0,0 +1,15 @@ +package de.flexiprovider.api.parameters; + +/** + * A (transparent) specification of cryptographic parameters. + *

+ * This interface contains no methods or constants. Its only purpose is to group + * (and provide type safety for) all parameter specifications. All parameter + * specifications must implement this interface. + */ +public interface AlgorithmParameterSpec extends + java.security.spec.AlgorithmParameterSpec { + + // empty + +} diff --git a/src/de/flexiprovider/api/parameters/AlgorithmParameters.java b/src/de/flexiprovider/api/parameters/AlgorithmParameters.java new file mode 100644 index 00000000..85e374f3 --- /dev/null +++ b/src/de/flexiprovider/api/parameters/AlgorithmParameters.java @@ -0,0 +1,221 @@ +package de.flexiprovider.api.parameters; + +import java.io.IOException; + +import de.flexiprovider.api.exceptions.InvalidParameterSpecException; + +/** + * This class defines the interface used to manage algorithm parameters. + * + * @see de.flexiprovider.api.parameters.AlgorithmParameterSpec + */ +public abstract class AlgorithmParameters extends + java.security.AlgorithmParametersSpi { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * JCA adapter for FlexiAPI method {@link #init(AlgorithmParameterSpec)}: + * initialize this parameters object using the parameters specified in + * paramSpec. + * + * @param params + * the parameter specification + * @throws java.security.spec.InvalidParameterSpecException + * if paramSpec is inappropriate for + * initialization. + */ + protected void engineInit(java.security.spec.AlgorithmParameterSpec params) + throws java.security.spec.InvalidParameterSpecException { + + if ((params == null) || !(params instanceof AlgorithmParameterSpec)) { + throw new java.security.spec.InvalidParameterSpecException(); + } + init((AlgorithmParameterSpec) params); + } + + /** + * JCA adapter for FlexiAPI method {@link #init(byte[])}: import the + * specified parameters and decode them according to the primary decoding + * format for parameters. The primary decoding format for parameters is + * ASN.1, if an ASN.1 specification for this type of parameters exists. + * + * @param params + * the encoded parameters + * @throws IOException + * on decoding errors. + */ + protected final void engineInit(byte[] params) throws IOException { + init(params); + } + + /** + * JCA adapter for FlexiAPI method {@link #init(byte[], String)}: import + * the specified parameters and decode them according to the specified + * decoding format. If format is null, the primary decoding + * format for parameters is used. The primary decoding format is ASN.1, if + * an ASN.1 specification for these parameters exists. + * + * @param params + * the encoded parameters + * @param format + * the decoding format + * @throws IOException + * on decoding errors. + */ + protected final void engineInit(byte[] params, String format) + throws IOException { + init(params, format); + } + + /** + * JCA adapter for FlexiAPI method {@link #getEncoded()}: return the + * parameters in their primary encoding format. The primary encoding format + * for parameters is ASN.1, if an ASN.1 specification for this type of + * parameters exists. + * + * @return the parameters encoded in their primary encoding format + * @throws IOException + * on encoding errors. + */ + protected final byte[] engineGetEncoded() throws IOException { + return getEncoded(); + } + + /** + * JCA adapter for FlexiAPI method {@link #getEncoded(String)}: return the + * parameters in the specified encoding format. If format is + * null, the primary decoding format is used. The primary encoding format + * for parameters is ASN.1, if an ASN.1 specification for this type of + * parameters exists. + * + * @param format + * the encoding format + * @return the parameters encoded in the specified encoding format + * @throws IOException + * on encoding errors. + */ + protected final byte[] engineGetEncoded(String format) throws IOException { + return getEncoded(format); + } + + /** + * JCA adapter for FlexiAPI method {@link #getParameterSpec(Class)}: return + * a (transparent) specification of this parameters object. + * paramSpec identifies the specification class in which the + * parameters should be returned. It could, for example, be + * {@link java.security.spec.DSAParameterSpec}.class , to + * indicate that the parameters should be returned in an instance of the + * {@link java.security.spec.DSAParameterSpec} class. + * + * @param paramSpec + * the the specification class in which the parameters should + * be returned + * @return the parameter specification + * @throws java.security.spec.InvalidParameterSpecException + * if the requested parameter specification is inappropriate + * for this parameter object. + */ + protected java.security.spec.AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws java.security.spec.InvalidParameterSpecException { + if (!(AlgorithmParameterSpec.class.isAssignableFrom(paramSpec))) { + throw new java.security.spec.InvalidParameterSpecException( + "Unsupported parameter specification."); + } + return getParameterSpec(paramSpec); + } + + /** + * JCA adapter for FlexiAPI method {@link #toString()}. + * + * @return a human readable form of this parameters object + */ + protected final String engineToString() { + return toString(); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Initialize the parameters with the given parameter specification. + * + * @param paramSpec + * the parameter specification + * @throws InvalidParameterSpecException + * if the parameter specification is null or of + * an unsupported type. + */ + public abstract void init(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException; + + /** + * Import the given encoded parameters and decode them according to the + * primary encoding format (ASN.1). + * + * @param encParams + * the encoded parameters + * @throws IOException + * on decoding errors. + */ + public abstract void init(byte[] encParams) throws IOException; + + /** + * Import the given encoded parameters and decode them according to the + * specified encoding format. + * + * @param encParams + * the encoded parameters + * @param format + * the encoding format + * @throws IOException + * on decoding errors or if the encoding format is + * null or not supported. + */ + public abstract void init(byte[] encParams, String format) + throws IOException; + + /** + * Encode the parameters according to the primary encoding format (ASN.1). + * + * @return the encoded parameters + * @throws IOException + * on encoding errors. + */ + public abstract byte[] getEncoded() throws IOException; + + /** + * Encode the parameters according to the specified encoding format. + * + * @param format + * the encoding format + * @return the encoded parameters + * @throws IOException + * on encoding errors or if the encoding format is + * null or not supported. + */ + public abstract byte[] getEncoded(String format) throws IOException; + + /** + * Return a transparent specification of the parameters. + * + * @param paramSpec + * the desired parameter specification type + * @return the parameter specification + * @throws InvalidParameterSpecException + * if the parameter specification type is null or + * or not supported + */ + public abstract AlgorithmParameterSpec getParameterSpec(Class paramSpec) + throws InvalidParameterSpecException; + + /** + * @return a human readable form of the parameters + */ + public abstract String toString(); + +} diff --git a/src/de/flexiprovider/common/exceptions/ECException.java b/src/de/flexiprovider/common/exceptions/ECException.java new file mode 100644 index 00000000..de19812c --- /dev/null +++ b/src/de/flexiprovider/common/exceptions/ECException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.exceptions; + +/** + * This exception is the parentclass of all exceptions, that relate to the ec - + * arithmetic. + * + * @author Birgit Henhapl + * @see de.flexiprovider.common.math.ellipticcurves.Point + * @see de.flexiprovider.common.math.ellipticcurves.PointGFP + */ +public class ECException extends RuntimeException { + + private static final String diagnostic = "An ec-specific exception was thrown"; + + /** + * Default constructor. Calls super-constructor with the message "An + * ec-specific exception was thrown". + */ + public ECException() { + super(diagnostic); + } + + /** + * Constructor with the message "An ec-specific exception was thrown: + * cause" + * + * @param cause + * String specifying cause of exception + */ + public ECException(String cause) { + super(diagnostic + ": " + cause); + } + +} diff --git a/src/de/flexiprovider/common/exceptions/NoQuadraticResidueException.java b/src/de/flexiprovider/common/exceptions/NoQuadraticResidueException.java new file mode 100644 index 00000000..d4029b51 --- /dev/null +++ b/src/de/flexiprovider/common/exceptions/NoQuadraticResidueException.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.exceptions; + +import de.flexiprovider.common.math.FlexiBigInt; + +/** + * This exception is thrown, if the square root modulo a prime of a nonquadratic + * residue is to be calculated. + * + * @author Birgit Henhapl + * @see de.flexiprovider.common.math.ellipticcurves.Point + * @see de.flexiprovider.common.math.ellipticcurves.PointGFP + */ +public class NoQuadraticResidueException extends ECException { + + /** + * Default constructor. Calls super-constructor with no message. + */ + public NoQuadraticResidueException() { + super(); + } + + /** + * Constructs NoQuadraticResidueException with the message:
+ * NoQuadraticResidueException:
+ * a = a is not a quadratic residue mod p = p. + * + * @param a + * the value to calculate the square root from + * @param p + * characteristic of the underlying field + */ + public NoQuadraticResidueException(FlexiBigInt a, FlexiBigInt p) { + super("NoQuadraticResidueException:\na = " + a + " is not" + + "a quadratic residue mod p = " + p + "."); + } + +} diff --git a/src/de/flexiprovider/common/math/FlexiBigInt.java b/src/de/flexiprovider/common/math/FlexiBigInt.java new file mode 100644 index 00000000..aa6ddfd4 --- /dev/null +++ b/src/de/flexiprovider/common/math/FlexiBigInt.java @@ -0,0 +1,224 @@ +package de.flexiprovider.common.math; + +import java.math.BigInteger; + +import de.flexiprovider.api.SecureRandom; + +public final class FlexiBigInt { + + private class JavaSecureRandom extends java.security.SecureRandom { + JavaSecureRandom(SecureRandom flexiRand) { + super(flexiRand, null); + } + } + + public BigInteger bigInt; + + public FlexiBigInt(byte[] val) { + bigInt = new BigInteger(val); + } + + public FlexiBigInt(String val) { + bigInt = new BigInteger(val); + } + + public FlexiBigInt(int signum, byte[] magnitude) { + bigInt = new BigInteger(signum, magnitude); + } + + public FlexiBigInt(String val, int radix) { + bigInt = new BigInteger(val, radix); + } + + public FlexiBigInt(int numBits, SecureRandom flexiRand) { + JavaSecureRandom javaRand = new JavaSecureRandom(flexiRand); + bigInt = new BigInteger(numBits, javaRand); + } + + public FlexiBigInt(int bitLength, int certainty, SecureRandom flexiRand) { + JavaSecureRandom javaRand = new JavaSecureRandom(flexiRand); + bigInt = new BigInteger(bitLength, certainty, javaRand); + } + + public FlexiBigInt(BigInteger bigInt) { + this.bigInt = bigInt; + } + + public static FlexiBigInt valueOf(long val) { + return new FlexiBigInt(BigInteger.valueOf(val)); + } + + public static final FlexiBigInt ZERO = new FlexiBigInt(BigInteger.ZERO); + + public static final FlexiBigInt ONE = valueOf(1); + + public FlexiBigInt add(FlexiBigInt addend) { + return new FlexiBigInt(bigInt.add(addend.bigInt)); + } + + public FlexiBigInt subtract(FlexiBigInt minuend) { + return new FlexiBigInt(bigInt.subtract(minuend.bigInt)); + } + + public FlexiBigInt multiply(FlexiBigInt factor) { + return new FlexiBigInt(bigInt.multiply(factor.bigInt)); + } + + public FlexiBigInt divide(FlexiBigInt divisor) { + return new FlexiBigInt(bigInt.divide(divisor.bigInt)); + } + + public FlexiBigInt[] divideAndRemainder(FlexiBigInt divisor) { + BigInteger[] dar = bigInt.divideAndRemainder(divisor.bigInt); + return new FlexiBigInt[] { new FlexiBigInt(dar[0]), + new FlexiBigInt(dar[1]) }; + } + + public FlexiBigInt remainder(FlexiBigInt divisor) { + return new FlexiBigInt(bigInt.remainder(divisor.bigInt)); + } + + public FlexiBigInt pow(int exponent) { + return new FlexiBigInt(bigInt.pow(exponent)); + } + + public FlexiBigInt gcd(FlexiBigInt val) { + return new FlexiBigInt(bigInt.gcd(val.bigInt)); + } + + public FlexiBigInt abs() { + return new FlexiBigInt(bigInt.abs()); + } + + public FlexiBigInt negate() { + return new FlexiBigInt(bigInt.negate()); + } + + public int signum() { + return bigInt.signum(); + } + + public FlexiBigInt mod(FlexiBigInt modulus) { + return new FlexiBigInt(bigInt.mod(modulus.bigInt)); + } + + public FlexiBigInt modPow(FlexiBigInt exponent, FlexiBigInt modulus) { + return new FlexiBigInt(bigInt.modPow(exponent.bigInt, modulus.bigInt)); + } + + public FlexiBigInt modInverse(FlexiBigInt modulus) { + return new FlexiBigInt(bigInt.modInverse(modulus.bigInt)); + } + + public FlexiBigInt shiftLeft(int n) { + return new FlexiBigInt(bigInt.shiftLeft(n)); + } + + public FlexiBigInt shiftRight(int n) { + return new FlexiBigInt(bigInt.shiftRight(n)); + } + + public FlexiBigInt and(FlexiBigInt val) { + return new FlexiBigInt(bigInt.and(val.bigInt)); + } + + public FlexiBigInt or(FlexiBigInt val) { + return new FlexiBigInt(bigInt.or(val.bigInt)); + } + + public FlexiBigInt xor(FlexiBigInt val) { + return new FlexiBigInt(bigInt.xor(val.bigInt)); + } + + public FlexiBigInt not() { + return new FlexiBigInt(bigInt.not()); + } + + public FlexiBigInt andNot(FlexiBigInt val) { + return new FlexiBigInt(bigInt.andNot(val.bigInt)); + } + + public boolean testBit(int n) { + return bigInt.testBit(n); + } + + public FlexiBigInt setBit(int n) { + return new FlexiBigInt(bigInt.setBit(n)); + } + + public FlexiBigInt clearBit(int n) { + return new FlexiBigInt(bigInt.clearBit(n)); + } + + public FlexiBigInt flipBit(int n) { + return new FlexiBigInt(bigInt.flipBit(n)); + } + + public int getLowestSetBit() { + return bigInt.getLowestSetBit(); + } + + public int bitLength() { + return bigInt.bitLength(); + } + + public int bitCount() { + return bigInt.bitCount(); + } + + public boolean isProbablePrime(int certainty) { + return bigInt.isProbablePrime(certainty); + } + + public int compareTo(FlexiBigInt other) { + return bigInt.compareTo(other.bigInt); + } + + public FlexiBigInt min(FlexiBigInt other) { + return new FlexiBigInt(bigInt.min(other.bigInt)); + } + + public FlexiBigInt max(FlexiBigInt other) { + return new FlexiBigInt(bigInt.max(other.bigInt)); + } + + public boolean equals(Object other) { + if (!(other instanceof FlexiBigInt)) { + return false; + } + return bigInt.equals(((FlexiBigInt) other).bigInt); + } + + public int hashCode() { + return bigInt.hashCode(); + } + + public String toString(int radix) { + return bigInt.toString(radix); + } + + public String toString() { + return bigInt.toString(); + } + + public byte[] toByteArray() { + return bigInt.toByteArray(); + } + + public int intValue() { + return bigInt.intValue(); + } + + public long longValue() { + return bigInt.longValue(); + } + + public float floatValue() { + return bigInt.floatValue(); + } + + public double doubleValue() { + return bigInt.doubleValue(); + } + +} diff --git a/src/de/flexiprovider/common/math/IntegerFunctions.java b/src/de/flexiprovider/common/math/IntegerFunctions.java new file mode 100644 index 00000000..bec008f7 --- /dev/null +++ b/src/de/flexiprovider/common/math/IntegerFunctions.java @@ -0,0 +1,1327 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ +package de.flexiprovider.common.math; + +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.common.exceptions.NoQuadraticResidueException; + +/** + * Class of number-theory related functions for use with integers represented as + * int's or FlexiBigInt objects. + * + * @author Ralf-P. Weinmann + * @author Martin Döring + */ +public final class IntegerFunctions { + + private static final FlexiBigInt ZERO = FlexiBigInt.ZERO; + + private static final FlexiBigInt ONE = FlexiBigInt.ONE; + + private static final FlexiBigInt TWO = FlexiBigInt.valueOf(2); + + private static final FlexiBigInt FOUR = FlexiBigInt.valueOf(4); + + private static final int[] SMALL_PRIMES = { 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41 }; + + private static final long SMALL_PRIME_PRODUCT = 3L * 5 * 7 * 11 * 13 * 17 + * 19 * 23 * 29 * 31 * 37 * 41; + + private static SecureRandom sr = null; + + // the jacobi function uses this lookup table + private static final int[] jacobiTable = { 0, 1, 0, -1, 0, -1, 0, 1 }; + + private IntegerFunctions() { + // empty + } + + /** + * Computes the value of the Jacobi symbol (A|B). The following properties + * hold for the Jacobi symbol which makes it a very efficient way to + * evaluate the Legendre symbol + *

+ * (A|B) = 0 IF gcd(A,B) > 1
+ * (-1|B) = 1 IF n = 1 (mod 1)
+ * (-1|B) = -1 IF n = 3 (mod 4)
+ * (A|B) (C|B) = (AC|B)
+ * (A|B) (A|C) = (A|CB)
+ * (A|B) = (C|B) IF A = C (mod B)
+ * (2|B) = 1 IF N = 1 OR 7 (mod 8)
+ * (2|B) = 1 IF N = 3 OR 5 (mod 8) + *

+ * + * @param A + * integer value + * @param B + * integer value + * @return value of the jacobi symbol (A|B) + */ + public static int jacobi(FlexiBigInt A, FlexiBigInt B) { + FlexiBigInt a, b, v; + long k = 1; + + k = 1; + + // test trivial cases + if (B.equals(ZERO)) { + a = A.abs(); + return a.equals(ONE) ? 1 : 0; + } + + if (!A.testBit(0) && !B.testBit(0)) { + return 0; + } + + a = A; + b = B; + + if (b.signum() == -1) { // b < 0 + b = b.negate(); // b = -b + if (a.signum() == -1) { + k = -1; + } + } + + v = ZERO; + while (!b.testBit(0)) { + v = v.add(ONE); // v = v + 1 + b = b.divide(TWO); // b = b/2 + } + + if (v.testBit(0)) { + k = k * jacobiTable[a.intValue() & 7]; + } + + if (a.signum() < 0) { // a < 0 + if (b.testBit(1)) { + k = -k; // k = -k + } + a = a.negate(); // a = -a + } + + // main loop + while (a.signum() != 0) { + v = ZERO; + while (!a.testBit(0)) { // a is even + v = v.add(ONE); + a = a.divide(TWO); + } + if (v.testBit(0)) { + k = k * jacobiTable[b.intValue() & 7]; + } + + if (a.compareTo(b) < 0) { // a < b + // swap and correct intermediate result + FlexiBigInt x = a; + a = b; + b = x; + if (a.testBit(1) && b.testBit(1)) { + k = -k; + } + } + a = a.subtract(b); + } + + return b.equals(ONE) ? (int) k : 0; + } + + /** + * Computes the square root of a FlexiBigInt modulo a prime employing the + * Shanks-Tonelli algorithm. + * + * @param a + * value out of which we extract the square root + * @param p + * prime modulus that determines the underlying field + * @return a number b such that b2 = a (mod p) if + * a is a quadratic residue modulo p. + * @throws NoQuadraticResidueException + * if a is a quadratic non-residue modulo p + */ + public static FlexiBigInt ressol(FlexiBigInt a, FlexiBigInt p) + throws NoQuadraticResidueException { + + FlexiBigInt v = null; + + if (a.compareTo(ZERO) < 0) { + a = a.add(p); + } + + if (a.equals(ZERO)) { + return ZERO; + } + + if (p.equals(TWO)) { + return a; + } + + // p = 3 mod 4 + if (p.testBit(0) && p.testBit(1)) { + if (jacobi(a, p) == 1) { // a quadr. residue mod p + v = p.add(ONE); // v = p+1 + v = v.shiftRight(2); // v = v/4 + return a.modPow(v, p); // return a^v mod p + // return --> a^((p+1)/4) mod p + } + throw new NoQuadraticResidueException(a, p); + } + + long t = 0; + + // initialization + // compute k and s, where p = 2^s (2k+1) +1 + + FlexiBigInt k = p.subtract(ONE); // k = p-1 + long s = 0; + while (!k.testBit(0)) { // while k is even + s++; // s = s+1 + k = k.shiftRight(1); // k = k/2 + } + + k = k.subtract(ONE); // k = k - 1 + k = k.shiftRight(1); // k = k/2 + + // initial values + FlexiBigInt r = a.modPow(k, p); // r = a^k mod p + + FlexiBigInt n = r.multiply(r).remainder(p); // n = r^2 % p + n = n.multiply(a).remainder(p); // n = n * a % p + r = r.multiply(a).remainder(p); // r = r * a %p + + if (n.equals(ONE)) { + return r; + } + + // non-quadratic residue + FlexiBigInt z = TWO; // z = 2 + while (jacobi(z, p) == 1) { + // while z quadratic residue + z = z.add(ONE); // z = z + 1 + } + + v = k; + v = v.multiply(TWO); // v = 2k + v = v.add(ONE); // v = 2k + 1 + FlexiBigInt c = z.modPow(v, p); // c = z^v mod p + + // iteration + while (n.compareTo(ONE) == 1) { // n > 1 + k = n; // k = n + t = s; // t = s + s = 0; + + while (!k.equals(ONE)) { // k != 1 + k = k.multiply(k).mod(p); // k = k^2 % p + s++; // s = s + 1 + } + + t -= s; // t = t - s + if (t == 0) { + throw new NoQuadraticResidueException(a, p); + } + + v = ONE; + for (long i = 0; i < t - 1; i++) { + v = v.shiftLeft(1); // v = 1 * 2^(t - 1) + } + c = c.modPow(v, p); // c = c^v mod p + r = r.multiply(c).remainder(p); // r = r * c % p + c = c.multiply(c).remainder(p); // c = c^2 % p + n = n.multiply(c).mod(p); // n = n * c % p + } + return r; + } + + /** + * Computes the greatest common divisor of the two specified integers + * + * @param u + * - first integer + * @param v + * - second integer + * @return gcd(a, b) + */ + public static int gcd(int u, int v) { + return FlexiBigInt.valueOf(u).gcd(FlexiBigInt.valueOf(v)).intValue(); + } + + /** + * Extended euclidian algorithm (computes gcd and representation). + * + * @param a + * the first integer + * @param b + * the second integer + * @return (g,u,v), where g = gcd(abs(a),abs(b)) = ua + vb + */ + public static int[] extGCD(int a, int b) { + FlexiBigInt ba = FlexiBigInt.valueOf(a); + FlexiBigInt bb = FlexiBigInt.valueOf(b); + FlexiBigInt[] bresult = extgcd(ba, bb); + int[] result = new int[3]; + result[0] = bresult[0].intValue(); + result[1] = bresult[1].intValue(); + result[2] = bresult[2].intValue(); + return result; + } + + public static FlexiBigInt divideAndRound(FlexiBigInt a, FlexiBigInt b) { + if (a.signum() < 0) { + return divideAndRound(a.negate(), b).negate(); + } + if (b.signum() < 0) { + return divideAndRound(a, b.negate()).negate(); + } + return a.shiftLeft(1).add(b).divide(b.shiftLeft(1)); + } + + public static FlexiBigInt[] divideAndRound(FlexiBigInt[] a, FlexiBigInt b) { + FlexiBigInt[] out = new FlexiBigInt[a.length]; + for (int i = 0; i < a.length; i++) { + out[i] = divideAndRound(a[i], b); + } + return out; + } + + /** + * Compute the smallest integer that is greater than or equal to the + * logarithm to the base 2 of the given FlexiBigInt. + * + * @param a + * the integer + * @return ceil[log(a)] + */ + public static int ceilLog(FlexiBigInt a) { + int result = 0; + FlexiBigInt p = FlexiBigInt.ONE; + while (p.compareTo(a) < 0) { + result++; + p = p.shiftLeft(1); + } + return result; + } + + /** + * Compute the smallest integer that is greater than or equal to the + * logarithm to the base 2 of the given integer. + * + * @param a + * the integer + * @return ceil[log(a)] + */ + public static int ceilLog(int a) { + int log = 0; + int i = 1; + while (i < a) { + i <<= 1; + log++; + } + return log; + } + + /** + * Compute ceil(log_256 n), the number of bytes needed to encode + * the integer n. + * + * @param n + * the integer + * @return the number of bytes needed to encode n + */ + public static int ceilLog256(int n) { + if (n == 0) { + return 1; + } + int m; + if (n < 0) { + m = -n; + } else { + m = n; + } + + int d = 0; + while (m > 0) { + d++; + m >>>= 8; + } + return d; + } + + /** + * Compute ceil(log_256 n), the number of bytes needed to encode + * the long integer n. + * + * @param n + * the long integer + * @return the number of bytes needed to encode n + */ + public static int ceilLog256(long n) { + if (n == 0) { + return 1; + } + long m; + if (n < 0) { + m = -n; + } else { + m = n; + } + + int d = 0; + while (m > 0) { + d++; + m >>>= 8; + } + return d; + } + + /** + * Compute the integer part of the logarithm to the base 2 of the given + * integer. + * + * @param a + * the integer + * @return floor[log(a)] + */ + public static int floorLog(FlexiBigInt a) { + int result = -1; + FlexiBigInt p = FlexiBigInt.ONE; + while (p.compareTo(a) <= 0) { + result++; + p = p.shiftLeft(1); + } + return result; + } + + /** + * Compute the integer part of the logarithm to the base 2 of the given + * integer. + * + * @param a + * the integer + * @return floor[log(a)] + */ + public static int floorLog(int a) { + int h = 0; + if (a <= 0) { + return -1; + } + int p = a >>> 1; + while (p > 0) { + h++; + p >>>= 1; + } + + return h; + } + + /** + * Compute the largest h with 2^h | a if a!=0. + * + * @param a + * an integer + * @return the largest h with 2^h | a if a!=0, + * 0 otherwise + */ + public static int maxPower(int a) { + int h = 0; + if (a != 0) { + int p = 1; + while ((a & p) == 0) { + h++; + p <<= 1; + } + } + + return h; + } + + /** + * @param a + * an integer + * @return the number of ones in the binary representation of an integer + * a + */ + public static int bitCount(int a) { + int h = 0; + while (a != 0) { + h += a & 1; + a >>>= 1; + } + + return h; + } + + /** + * determines the order of g modulo p, p prime and 1 < g < p. This algorithm + * is only efficient for small p (see X9.62-1998, p. 68). + * + * @param g + * an integer with 1 < g < p + * @param p + * a prime + * @return the order k of g (that is k is the smallest integer with + * gk = 1 mod p + */ + public static int order(int g, int p) { + int b, j; + + b = g % p; // Reduce g mod p first. + j = 1; + + // Check whether g == 0 mod p (avoiding endless loop). + if (b == 0) { + throw new IllegalArgumentException(g + " is not an element of Z/(" + + p + "Z)^*; it is not meaningful to compute its order."); + } + + // Compute the order of g mod p: + while (b != 1) { + b *= g; + b %= p; + if (b < 0) { + b += p; + } + j++; + } + + return j; + } + + /** + * Reduces an integer into a given interval + * + * @param n + * - the integer + * @param begin + * - left bound of the interval + * @param end + * - right bound of the interval + * @return n reduced into [begin,end] + * + */ + public static FlexiBigInt reduceInto(FlexiBigInt n, FlexiBigInt begin, + FlexiBigInt end) { + return n.subtract(begin).mod(end.subtract(begin)).add(begin); + } + + /** + * Compute ae. + * + * @param a + * the base + * @param e + * the exponent + * @return ae + */ + public static int pow(int a, int e) { + int result = 1; + while (e > 0) { + if ((e & 1) == 1) { + result *= a; + } + a *= a; + e >>>= 1; + } + return result; + } + + /** + * Compute ae. + * + * @param a + * the base + * @param e + * the exponent + * @return ae + */ + public static long pow(long a, int e) { + long result = 1; + while (e > 0) { + if ((e & 1) == 1) { + result *= a; + } + a *= a; + e >>>= 1; + } + return result; + } + + /** + * Compute ae mod n. + * + * @param a + * the base + * @param e + * the exponent + * @param n + * the modulus + * @return ae mod n + */ + public static int modPow(int a, int e, int n) { + if (n <= 0 || (n * n) > Integer.MAX_VALUE || e < 0) { + return 0; + } + int result = 1; + a = (a % n + n) % n; + while (e > 0) { + if ((e & 1) == 1) { + result = (result * a) % n; + } + a = (a * a) % n; + e >>>= 1; + } + return result; + } + + /** + * Extended euclidian algorithm (computes gcd and representation). + * + * @param a + * - the first integer + * @param b + * - the second integer + * @return (d,u,v), where d = gcd(a,b) = ua + vb + */ + public static FlexiBigInt[] extgcd(FlexiBigInt a, FlexiBigInt b) { + FlexiBigInt u = FlexiBigInt.ONE; + FlexiBigInt v = FlexiBigInt.ZERO; + FlexiBigInt d = a; + if (b.signum() != 0) { + FlexiBigInt v1 = FlexiBigInt.ZERO; + FlexiBigInt v3 = b; + while (v3.signum() != 0) { + FlexiBigInt[] tmp = d.divideAndRemainder(v3); + FlexiBigInt q = tmp[0]; + FlexiBigInt t3 = tmp[1]; + FlexiBigInt t1 = u.subtract(q.multiply(v1)); + u = v1; + d = v3; + v1 = t1; + v3 = t3; + } + v = d.subtract(a.multiply(u)).divide(b); + } + return new FlexiBigInt[] { d, u, v }; + } + + /** + * Computation of the least common multiple of a set of FlexiBigInts. + * + * @param numbers + * - the set of numbers + * @return the lcm(numbers) + */ + public static FlexiBigInt leastCommonMultiple(FlexiBigInt[] numbers) { + int n = numbers.length; + FlexiBigInt result = numbers[0]; + for (int i = 1; i < n; i++) { + FlexiBigInt gcd = result.gcd(numbers[i]); + result = result.multiply(numbers[i]).divide(gcd); + } + return result; + } + + /** + * Returns a long integer whose value is (a mod m). This method + * differs from % in that it always returns a non-negative + * integer. + * + * @param a + * value on which the modulo operation has to be performed. + * @param m + * the modulus. + * @return a mod m + */ + public static long mod(long a, long m) { + long result = a % m; + if (result < 0) { + result += m; + } + return result; + } + + /** + * Computes the modular inverse of an integer a + * + * @param a + * - the integer to invert + * @param mod + * - the modulus + * @return a-1 mod n + */ + public static int modInverse(int a, int mod) { + return FlexiBigInt.valueOf(a).modInverse(FlexiBigInt.valueOf(mod)) + .intValue(); + } + + /** + * Computes the modular inverse of an integer a + * + * @param a + * - the integer to invert + * @param mod + * - the modulus + * @return a-1 mod n + */ + public static long modInverse(long a, long mod) { + return FlexiBigInt.valueOf(a).modInverse(FlexiBigInt.valueOf(mod)) + .longValue(); + } + + /** + * Tests whether an integer a is power of another integer + * p. + * + * @param a + * - the first integer + * @param p + * - the second integer + * @return n if a = p^n or -1 otherwise + */ + public static int isPower(int a, int p) { + if (a <= 0) { + return -1; + } + int n = 0; + int d = a; + while (d > 1) { + if (d % p != 0) { + return -1; + } + d /= p; + n++; + } + return n; + } + + /** + * Find and return the least non-trivial divisor of an integer a. + * + * @param a + * - the integer + * @return divisor p >1 or 1 if a = -1,0,1 + */ + public static int leastDiv(int a) { + if (a < 0) { + a = -a; + } + if (a == 0) { + return 1; + } + if ((a & 1) == 0) { + return 2; + } + int p = 3; + while (p <= (a / p)) { + if ((a % p) == 0) { + return p; + } + p += 2; + } + + return a; + } + + /** + * Miller-Rabin-Test, determines wether the given integer is probably prime + * or composite. This method returns true if the given integer is + * prime with probability 1 - 2-20. + * + * @param n + * the integer to test for primality + * @return true if the given integer is prime with probability + * 2-100, false otherwise + */ + public static boolean isPrime(int n) { + if (n < 2) { + return false; + } + if (n == 2) { + return true; + } + if ((n & 1) == 0) { + return false; + } + if (n < 42) { + for (int i = 0; i < SMALL_PRIMES.length; i++) { + if (n == SMALL_PRIMES[i]) { + return true; + } + } + } + + if ((n % 3 == 0) || (n % 5 == 0) || (n % 7 == 0) || (n % 11 == 0) + || (n % 13 == 0) || (n % 17 == 0) || (n % 19 == 0) + || (n % 23 == 0) || (n % 29 == 0) || (n % 31 == 0) + || (n % 37 == 0) || (n % 41 == 0)) { + return false; + } + + return FlexiBigInt.valueOf(n).isProbablePrime(20); + } + + /** + * Short trial-division test to find out whether a number is not prime. This + * test is usually used before a Miller-Rabin primality test. + * + * @param candidate + * the number to test + * @return true if the number has no factor of the tested primes, + * false if the number is definitely composite + */ + public static boolean passesSmallPrimeTest(FlexiBigInt candidate) { + final int[] smallPrime = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, + 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, + 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, + 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, + 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, + 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, + 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, + 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, + 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, + 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, + 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, + 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, + 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, + 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, + 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, + 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, + 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, + 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, + 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, + 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, + 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499 }; + + for (int i = 0; i < smallPrime.length; i++) { + if (candidate.mod(FlexiBigInt.valueOf(smallPrime[i])).equals( + FlexiBigInt.ZERO)) { + return false; + } + } + return true; + } + + /** + * Returns the largest prime smaller than the given integer + * + * @param n + * - upper bound + * @return the largest prime smaller than n, or 1 if + * n <= 2 + */ + public static int nextSmallerPrime(int n) { + if (n <= 2) { + return 1; + } + + if (n == 3) { + return 2; + } + + if ((n & 1) == 0) { + n--; + } else { + n -= 2; + } + + while (n > 3 & !isPrime(n)) { + n -= 2; + } + return n; + } + + /** + * Compute the next probable prime greater than n with the + * specified certainty. + * + * @param n + * a integer number + * @param certainty + * the certainty that the generated number is prime + * @return the next prime greater than n + */ + public static FlexiBigInt nextProbablePrime(FlexiBigInt n, int certainty) { + + if (n.signum() < 0 || n.signum() == 0 || n.equals(ONE)) { + return TWO; + } + + FlexiBigInt result = n.add(ONE); + + // Ensure an odd number + if (!result.testBit(0)) { + result = result.add(ONE); + } + + while (true) { + // Do cheap "pre-test" if applicable + if (result.bitLength() > 6) { + long r = result.remainder( + FlexiBigInt.valueOf(SMALL_PRIME_PRODUCT)).longValue(); + if ((r % 3 == 0) || (r % 5 == 0) || (r % 7 == 0) + || (r % 11 == 0) || (r % 13 == 0) || (r % 17 == 0) + || (r % 19 == 0) || (r % 23 == 0) || (r % 29 == 0) + || (r % 31 == 0) || (r % 37 == 0) || (r % 41 == 0)) { + result = result.add(TWO); + continue; // Candidate is composite; try another + } + } + + // All candidates of bitLength 2 and 3 are prime by this point + if (result.bitLength() < 4) { + return result; + } + + // The expensive test + if (result.isProbablePrime(certainty)) { + return result; + } + + result = result.add(TWO); + } + } + + /** + * Compute the next probable prime greater than n with the default + * certainty (20). + * + * @param n + * a integer number + * @return the next prime greater than n + */ + public static FlexiBigInt nextProbablePrime(FlexiBigInt n) { + return nextProbablePrime(n, 20); + } + + /** + * Computes the next prime greater than n. + * + * @param n + * a integer number + * @return the next prime greater than n + */ + public static FlexiBigInt nextPrime(long n) { + long i; + boolean found = false; + long result = 0; + + if (n <= 1) { + return FlexiBigInt.valueOf(2); + } + if (n == 2) { + return FlexiBigInt.valueOf(3); + } + + for (i = n + 1 + (n & 1); (i <= n << 1) && !found; i += 2) { + for (long j = 3; (j <= i >> 1) && !found; j += 2) { + if (i % j == 0) { + found = true; + } + } + if (found) { + found = false; + } else { + result = i; + found = true; + } + } + return FlexiBigInt.valueOf(result); + } + + /** + * Computes the binomial coefficient (n|t) ("n over t"). Formula:
+ *

    + *
  • if n !=0 and t != 0 then (n|t) = Mult(i=1, t): (n-(i-1))/i
  • + *
  • if t = 0 then (n|t) = 1
  • + *
  • if n = 0 and t > 0 then (n|t) = 0
  • + *
+ * + * @param n + * - the "upper" integer + * @param t + * - the "lower" integer + * @return the binomialcoefficient "n over t" as FlexiBigInt + */ + public static FlexiBigInt binomial(int n, int t) { + + FlexiBigInt result = FlexiBigInt.ONE; + + if (n == 0) { + if (t == 0) { + return result; + } + return FlexiBigInt.ZERO; + } + + // the property (n|t) = (n|n-t) be used to reduce numbers of operations + if (t > (n >>> 1)) { + t = n - t; + } + + for (int i = 1; i <= t; i++) { + result = (result.multiply(FlexiBigInt.valueOf(n - (i - 1)))) + .divide(FlexiBigInt.valueOf(i)); + } + + return result; + } + + public static FlexiBigInt randomize(FlexiBigInt upperBound) { + if (sr == null) { + sr = Registry.getSecureRandom(); + } + return randomize(upperBound, sr); + } + + public static FlexiBigInt randomize(FlexiBigInt upperBound, + SecureRandom prng) { + int blen = upperBound.bitLength(); + FlexiBigInt randomNum = FlexiBigInt.valueOf(0); + + if (prng == null) { + prng = sr != null ? sr : Registry.getSecureRandom(); + } + + for (int i = 0; i < 20; i++) { + randomNum = new FlexiBigInt(blen, prng); + if (randomNum.compareTo(upperBound) < 0) { + return randomNum; + } + } + return randomNum.mod(upperBound); + } + + /** + * Extract the truncated square root of a FlexiBigInt. + * + * @param a + * - value out of which we extract the square root + * @return the truncated square root of a + */ + public static FlexiBigInt squareRoot(FlexiBigInt a) { + int bl; + FlexiBigInt result, remainder, b; + + if (a.compareTo(ZERO) < 0) { + throw new ArithmeticException( + "cannot extract root of negative number" + a + "."); + } + + bl = a.bitLength(); + result = ZERO; + remainder = ZERO; + + // if the bit length is odd then extra step + if ((bl & 1) != 0) { + result = result.add(ONE); + bl--; + } + + while (bl > 0) { + remainder = remainder.multiply(FOUR); + remainder = remainder.add(FlexiBigInt.valueOf((a.testBit(--bl) ? 2 + : 0) + + (a.testBit(--bl) ? 1 : 0))); + b = result.multiply(FOUR).add(ONE); + result = result.multiply(TWO); + if (remainder.compareTo(b) != -1) { + result = result.add(ONE); + remainder = remainder.subtract(b); + } + } + + return result; + } + + /** + * Takes an approximation of the root from an integer base, using newton's + * algorithm + * + * @param base + * the base to take the root from + * @param root + * the root, for example 2 for a square root + */ + public static float intRoot(int base, int root) { + float gNew = base / root; + float gOld = 0; + int counter = 0; + while (Math.abs(gOld - gNew) > 0.0001) { + float gPow = floatPow(gNew, root); + while (Float.isInfinite(gPow)) { + gNew = (gNew + gOld) / 2; + gPow = floatPow(gNew, root); + } + counter += 1; + gOld = gNew; + gNew = gOld - (gPow - base) / (root * floatPow(gOld, root - 1)); + } + return gNew; + } + + /** + * Calculation of a logarithmus of a float param + * + * @param param + * @return + */ + public static float floatLog(float param) { + double arg = (param - 1) / (param + 1); + double arg2 = arg; + int counter = 1; + float result = (float) arg; + + while (arg2 > 0.001) { + counter += 2; + arg2 *= arg * arg; + result += (1. / counter) * arg2; + } + return 2 * result; + } + + /** + * int power of a base float, only use for small ints + * + * @param f + * @param i + * @return + */ + public static float floatPow(float f, int i) { + float g = 1; + for (; i > 0; i--) { + g *= f; + } + return g; + } + + /** + * calculate the logarithm to the base 2. + * + * @param x + * any double value + * @return log_2(x) + * + * @deprecated use MathFunctions.log(double) instead + */ + public static double log(double x) { + if (x > 0 && x < 1) { + double d = 1 / x; + double result = -log(d); + return result; + } + + int tmp = 0; + double tmp2 = 1; + double d = x; + + while (d > 2) { + d = d / 2; + tmp += 1; + tmp2 *= 2; + } + double rem = x / tmp2; + rem = logBKM(rem); + return tmp + rem; + } + + /** + * calculate the logarithm to the base 2. + * + * @param x + * any long value >=1 + * @return log_2(x) + * + * @deprecated use MathFunctions.log(long) instead + */ + public static double log(long x) { + int tmp = floorLog(FlexiBigInt.valueOf(x)); + long tmp2 = 1 << tmp; + double rem = (double) x / (double) tmp2; + rem = logBKM(rem); + return tmp + rem; + } + + /** + * BKM Algorithm to calculate logarithms to the base 2. + * + * @param arg + * a double value with 1<= arg<= 4.768462058 + * + * @return log_2(arg) + * + * @deprecated use MathFunctions.logBKM(double) instead + */ + private static double logBKM(double arg) { + double ae[] = // A_e[k] = log_2 (1 + 0.5^k) + { + 1.0000000000000000000000000000000000000000000000000000000000000000000000000000, + 0.5849625007211561814537389439478165087598144076924810604557526545410982276485, + 0.3219280948873623478703194294893901758648313930245806120547563958159347765589, + 0.1699250014423123629074778878956330175196288153849621209115053090821964552970, + 0.0874628412503394082540660108104043540112672823448206881266090643866965081686, + 0.0443941193584534376531019906736094674630459333742491317685543002674288465967, + 0.0223678130284545082671320837460849094932677948156179815932199216587899627785, + 0.0112272554232541203378805844158839407281095943600297940811823651462712311786, + 0.0056245491938781069198591026740666017211096815383520359072957784732489771013, + 0.0028150156070540381547362547502839489729507927389771959487826944878598909400, + 0.0014081943928083889066101665016890524233311715793462235597709051792834906001, + 0.0007042690112466432585379340422201964456668872087249334581924550139514213168, + 0.0003521774803010272377989609925281744988670304302127133979341729842842377649, + 0.0001760994864425060348637509459678580940163670081839283659942864068257522373, + 0.0000880524301221769086378699983597183301490534085738474534831071719854721939, + 0.0000440268868273167176441087067175806394819146645511899503059774914593663365, + 0.0000220136113603404964890728830697555571275493801909791504158295359319433723, + 0.0000110068476674814423006223021573490183469930819844945565597452748333526464, + 0.0000055034343306486037230640321058826431606183125807276574241540303833251704, + 0.0000027517197895612831123023958331509538486493412831626219340570294203116559, + 0.0000013758605508411382010566802834037147561973553922354232704569052932922954, + 0.0000006879304394358496786728937442939160483304056131990916985043387874690617, + 0.0000003439652607217645360118314743718005315334062644619363447395987584138324, + 0.0000001719826406118446361936972479533123619972434705828085978955697643547921, + 0.0000000859913228686632156462565208266682841603921494181830811515318381744650, + 0.0000000429956620750168703982940244684787907148132725669106053076409624949917, + 0.0000000214978311976797556164155504126645192380395989504741781512309853438587, + 0.0000000107489156388827085092095702361647949603617203979413516082280717515504, + 0.0000000053744578294520620044408178949217773318785601260677517784797554422804, + 0.0000000026872289172287079490026152352638891824761667284401180026908031182361, + 0.0000000013436144592400232123622589569799954658536700992739887706412976115422, + 0.0000000006718072297764289157920422846078078155859484240808550018085324187007, + 0.0000000003359036149273187853169587152657145221968468364663464125722491530858, + 0.0000000001679518074734354745159899223037458278711244127245990591908996412262, + 0.0000000000839759037391617577226571237484864917411614198675604731728132152582, + 0.0000000000419879518701918839775296677020135040214077417929807824842667285938, + 0.0000000000209939759352486932678195559552767641474249812845414125580747434389, + 0.0000000000104969879676625344536740142096218372850561859495065136990936290929, + 0.0000000000052484939838408141817781356260462777942148580518406975851213868092, + 0.0000000000026242469919227938296243586262369156865545638305682553644113887909, + 0.0000000000013121234959619935994960031017850191710121890821178731821983105443, + 0.0000000000006560617479811459709189576337295395590603644549624717910616347038, + 0.0000000000003280308739906102782522178545328259781415615142931952662153623493, + 0.0000000000001640154369953144623242936888032768768777422997704541618141646683, + 0.0000000000000820077184976595619616930350508356401599552034612281802599177300, + 0.0000000000000410038592488303636807330652208397742314215159774270270147020117, + 0.0000000000000205019296244153275153381695384157073687186580546938331088730952, + 0.0000000000000102509648122077001764119940017243502120046885379813510430378661, + 0.0000000000000051254824061038591928917243090559919209628584150482483994782302, + 0.0000000000000025627412030519318726172939815845367496027046030028595094737777, + 0.0000000000000012813706015259665053515049475574143952543145124550608158430592, + 0.0000000000000006406853007629833949364669629701200556369782295210193569318434, + 0.0000000000000003203426503814917330334121037829290364330169106716787999052925, + 0.0000000000000001601713251907458754080007074659337446341494733882570243497196, + 0.0000000000000000800856625953729399268240176265844257044861248416330071223615, + 0.0000000000000000400428312976864705191179247866966320469710511619971334577509, + 0.0000000000000000200214156488432353984854413866994246781519154793320684126179, + 0.0000000000000000100107078244216177339743404416874899847406043033792202127070, + 0.0000000000000000050053539122108088756700751579281894640362199287591340285355, + 0.0000000000000000025026769561054044400057638132352058574658089256646014899499, + 0.0000000000000000012513384780527022205455634651853807110362316427807660551208, + 0.0000000000000000006256692390263511104084521222346348012116229213309001913762, + 0.0000000000000000003128346195131755552381436585278035120438976487697544916191, + 0.0000000000000000001564173097565877776275512286165232838833090480508502328437, + 0.0000000000000000000782086548782938888158954641464170239072244145219054734086, + 0.0000000000000000000391043274391469444084776945327473574450334092075712154016, + 0.0000000000000000000195521637195734722043713378812583900953755962557525252782, + 0.0000000000000000000097760818597867361022187915943503728909029699365320287407, + 0.0000000000000000000048880409298933680511176764606054809062553340323879609794, + 0.0000000000000000000024440204649466840255609083961603140683286362962192177597, + 0.0000000000000000000012220102324733420127809717395445504379645613448652614939, + 0.0000000000000000000006110051162366710063906152551383735699323415812152114058, + 0.0000000000000000000003055025581183355031953399739107113727036860315024588989, + 0.0000000000000000000001527512790591677515976780735407368332862218276873443537, + 0.0000000000000000000000763756395295838757988410584167137033767056170417508383, + 0.0000000000000000000000381878197647919378994210346199431733717514843471513618, + 0.0000000000000000000000190939098823959689497106436628681671067254111334889005, + 0.0000000000000000000000095469549411979844748553534196582286585751228071408728, + 0.0000000000000000000000047734774705989922374276846068851506055906657137209047, + 0.0000000000000000000000023867387352994961187138442777065843718711089344045782, + 0.0000000000000000000000011933693676497480593569226324192944532044984865894525, + 0.0000000000000000000000005966846838248740296784614396011477934194852481410926, + 0.0000000000000000000000002983423419124370148392307506484490384140516252814304, + 0.0000000000000000000000001491711709562185074196153830361933046331030629430117, + 0.0000000000000000000000000745855854781092537098076934460888486730708440475045, + 0.0000000000000000000000000372927927390546268549038472050424734256652501673274, + 0.0000000000000000000000000186463963695273134274519237230207489851150821191330, + 0.0000000000000000000000000093231981847636567137259618916352525606281553180093, + 0.0000000000000000000000000046615990923818283568629809533488457973317312233323, + 0.0000000000000000000000000023307995461909141784314904785572277779202790023236, + 0.0000000000000000000000000011653997730954570892157452397493151087737428485431, + 0.0000000000000000000000000005826998865477285446078726199923328593402722606924, + 0.0000000000000000000000000002913499432738642723039363100255852559084863397344, + 0.0000000000000000000000000001456749716369321361519681550201473345138307215067, + 0.0000000000000000000000000000728374858184660680759840775119123438968122488047, + 0.0000000000000000000000000000364187429092330340379920387564158411083803465567, + 0.0000000000000000000000000000182093714546165170189960193783228378441837282509, + 0.0000000000000000000000000000091046857273082585094980096891901482445902524441, + 0.0000000000000000000000000000045523428636541292547490048446022564529197237262, + 0.0000000000000000000000000000022761714318270646273745024223029238091160103901 }; + int n = 53; + double x = 1; + double y = 0; + double z; + double s = 1; + int k; + + for (k = 0; k < n; k++) { + z = x + x * s; + if (z <= arg) { + x = z; + y += ae[k]; + } + s *= 0.5; + } + return y; + } + + public static boolean isIncreasing(int[] a) { + for (int i = 1; i < a.length; i++) { + if (a[i - 1] >= a[i]) { + System.out.println("a[" + (i - 1) + "] = " + a[i - 1] + " >= " + + a[i] + " = a[" + i + "]"); + return false; + } + } + return true; + } + + public static byte[] integerToOctets(FlexiBigInt val) { + byte[] valBytes = val.abs().toByteArray(); + + // check whether the array includes a sign bit + if ((val.bitLength() & 7) != 0) { + return valBytes; + } + // get rid of the sign bit (first byte) + byte[] tmp = new byte[val.bitLength() >> 3]; + System.arraycopy(valBytes, 1, tmp, 0, tmp.length); + return tmp; + } + + public static FlexiBigInt octetsToInteger(byte[] data, int offset, + int length) { + byte[] val = new byte[length + 1]; + + val[0] = 0; + System.arraycopy(data, offset, val, 1, length); + return new FlexiBigInt(val); + } + + public static FlexiBigInt octetsToInteger(byte[] data) { + return octetsToInteger(data, 0, data.length); + } + + public static void main(String[] args) { + System.out.println("test"); + // System.out.println(intRoot(37, 5)); + // System.out.println(floatPow((float)2.5, 4)); + System.out.println(floatLog(10)); + System.out.println("test2"); + } +} diff --git a/src/de/flexiprovider/common/math/MathFunctions.java b/src/de/flexiprovider/common/math/MathFunctions.java new file mode 100644 index 00000000..20fc902a --- /dev/null +++ b/src/de/flexiprovider/common/math/MathFunctions.java @@ -0,0 +1,278 @@ +package de.flexiprovider.common.math; + +/** + * Class of number-theory related functions providing J2ME conform + * implementations of functions contained in the java.lang.Math package but not + * provided for J2ME. + * + * + * @author Johannes Braun + * + */ +public final class MathFunctions { + + private MathFunctions(){ + //empty + } + + /** + * Compute eexponent + * J2ME conform implementation of the "Math.exp" function. + * @param exponent + * the exponent + * @return eexponent + */ + public static double exp(double exponent) { + double d = exponent; + boolean negative = false; + double result = 1; + int iterations = 20; + + if (d < 0) { + negative = true; + d *= -1; + } + + if (d >= 1) { + int temp = (int) d; + double a=Math.E; + while (temp > 0) { + if ((temp & 1) == 1) { + result *=a; + } + a *= a; + temp >>>= 1; + } + if ((d - Math.floor(d)) == 0) { + if (negative) { + return 1 / result; + } else { + return result; + } + } + } + + double temp = d - Math.floor(d); + double den = 1; + double num = 1; + double result2 = 1; + + for (int i = 1; i < iterations; i++) { + den *= i; + num *= temp; + result2 += num / den; + } + + if (negative) { + return 1 / (result * result2); + } else { + return result * result2; + } + + } + + /** + * calculate the logarithm to the base 2. + * + * @param x + * any double value + * @return log_2(x) + */ + public static double log(double x) { + if(x<=0) + throw new ArithmeticException("log(x) only defined for positive values"); + if(x>0&&x<1){ + double d= 1/x; + double result=-log(d); + return result; + } + + int tmp=0; + double tmp2=1; + double d=x; + + while(d>2){ + d=d/2; + tmp+=1; + tmp2*=2; + } + double rem = x / tmp2; + rem = logBKM(rem); + return (double) tmp + rem; + } + + /** + * calculate the logarithm to the base 2. + * + * @param x + * any long value >=1 + * @return log_2(x) + */ + public static double log(long x) { + if(x<=0) + throw new ArithmeticException("log(x) only defined for positive values"); + + int tmp = IntegerFunctions.floorLog(FlexiBigInt.valueOf(x)); + long tmp2 = 1l << tmp; + double rem = (double) x / (double) tmp2; + rem = logBKM(rem); + return (double) tmp + rem; + } + + /** + * BKM Algorithm to calculate logarithms to the base 2. + * + * @param arg + * a double value with 1<= arg<= 4.768462058 + * + * @return log_2(arg) + */ + private static double logBKM(double arg) { + if(arg<=0) + throw new ArithmeticException("logBKM(x) only defined for positive values"); + double ae[] = // A_e[k] = log_2 (1 + 0.5^k) + { + 1.0000000000000000000000000000000000000000000000000000000000000000000000000000, + 0.5849625007211561814537389439478165087598144076924810604557526545410982276485, + 0.3219280948873623478703194294893901758648313930245806120547563958159347765589, + 0.1699250014423123629074778878956330175196288153849621209115053090821964552970, + 0.0874628412503394082540660108104043540112672823448206881266090643866965081686, + 0.0443941193584534376531019906736094674630459333742491317685543002674288465967, + 0.0223678130284545082671320837460849094932677948156179815932199216587899627785, + 0.0112272554232541203378805844158839407281095943600297940811823651462712311786, + 0.0056245491938781069198591026740666017211096815383520359072957784732489771013, + 0.0028150156070540381547362547502839489729507927389771959487826944878598909400, + 0.0014081943928083889066101665016890524233311715793462235597709051792834906001, + 0.0007042690112466432585379340422201964456668872087249334581924550139514213168, + 0.0003521774803010272377989609925281744988670304302127133979341729842842377649, + 0.0001760994864425060348637509459678580940163670081839283659942864068257522373, + 0.0000880524301221769086378699983597183301490534085738474534831071719854721939, + 0.0000440268868273167176441087067175806394819146645511899503059774914593663365, + 0.0000220136113603404964890728830697555571275493801909791504158295359319433723, + 0.0000110068476674814423006223021573490183469930819844945565597452748333526464, + 0.0000055034343306486037230640321058826431606183125807276574241540303833251704, + 0.0000027517197895612831123023958331509538486493412831626219340570294203116559, + 0.0000013758605508411382010566802834037147561973553922354232704569052932922954, + 0.0000006879304394358496786728937442939160483304056131990916985043387874690617, + 0.0000003439652607217645360118314743718005315334062644619363447395987584138324, + 0.0000001719826406118446361936972479533123619972434705828085978955697643547921, + 0.0000000859913228686632156462565208266682841603921494181830811515318381744650, + 0.0000000429956620750168703982940244684787907148132725669106053076409624949917, + 0.0000000214978311976797556164155504126645192380395989504741781512309853438587, + 0.0000000107489156388827085092095702361647949603617203979413516082280717515504, + 0.0000000053744578294520620044408178949217773318785601260677517784797554422804, + 0.0000000026872289172287079490026152352638891824761667284401180026908031182361, + 0.0000000013436144592400232123622589569799954658536700992739887706412976115422, + 0.0000000006718072297764289157920422846078078155859484240808550018085324187007, + 0.0000000003359036149273187853169587152657145221968468364663464125722491530858, + 0.0000000001679518074734354745159899223037458278711244127245990591908996412262, + 0.0000000000839759037391617577226571237484864917411614198675604731728132152582, + 0.0000000000419879518701918839775296677020135040214077417929807824842667285938, + 0.0000000000209939759352486932678195559552767641474249812845414125580747434389, + 0.0000000000104969879676625344536740142096218372850561859495065136990936290929, + 0.0000000000052484939838408141817781356260462777942148580518406975851213868092, + 0.0000000000026242469919227938296243586262369156865545638305682553644113887909, + 0.0000000000013121234959619935994960031017850191710121890821178731821983105443, + 0.0000000000006560617479811459709189576337295395590603644549624717910616347038, + 0.0000000000003280308739906102782522178545328259781415615142931952662153623493, + 0.0000000000001640154369953144623242936888032768768777422997704541618141646683, + 0.0000000000000820077184976595619616930350508356401599552034612281802599177300, + 0.0000000000000410038592488303636807330652208397742314215159774270270147020117, + 0.0000000000000205019296244153275153381695384157073687186580546938331088730952, + 0.0000000000000102509648122077001764119940017243502120046885379813510430378661, + 0.0000000000000051254824061038591928917243090559919209628584150482483994782302, + 0.0000000000000025627412030519318726172939815845367496027046030028595094737777, + 0.0000000000000012813706015259665053515049475574143952543145124550608158430592, + 0.0000000000000006406853007629833949364669629701200556369782295210193569318434, + 0.0000000000000003203426503814917330334121037829290364330169106716787999052925, + 0.0000000000000001601713251907458754080007074659337446341494733882570243497196, + 0.0000000000000000800856625953729399268240176265844257044861248416330071223615, + 0.0000000000000000400428312976864705191179247866966320469710511619971334577509, + 0.0000000000000000200214156488432353984854413866994246781519154793320684126179, + 0.0000000000000000100107078244216177339743404416874899847406043033792202127070, + 0.0000000000000000050053539122108088756700751579281894640362199287591340285355, + 0.0000000000000000025026769561054044400057638132352058574658089256646014899499, + 0.0000000000000000012513384780527022205455634651853807110362316427807660551208, + 0.0000000000000000006256692390263511104084521222346348012116229213309001913762, + 0.0000000000000000003128346195131755552381436585278035120438976487697544916191, + 0.0000000000000000001564173097565877776275512286165232838833090480508502328437, + 0.0000000000000000000782086548782938888158954641464170239072244145219054734086, + 0.0000000000000000000391043274391469444084776945327473574450334092075712154016, + 0.0000000000000000000195521637195734722043713378812583900953755962557525252782, + 0.0000000000000000000097760818597867361022187915943503728909029699365320287407, + 0.0000000000000000000048880409298933680511176764606054809062553340323879609794, + 0.0000000000000000000024440204649466840255609083961603140683286362962192177597, + 0.0000000000000000000012220102324733420127809717395445504379645613448652614939, + 0.0000000000000000000006110051162366710063906152551383735699323415812152114058, + 0.0000000000000000000003055025581183355031953399739107113727036860315024588989, + 0.0000000000000000000001527512790591677515976780735407368332862218276873443537, + 0.0000000000000000000000763756395295838757988410584167137033767056170417508383, + 0.0000000000000000000000381878197647919378994210346199431733717514843471513618, + 0.0000000000000000000000190939098823959689497106436628681671067254111334889005, + 0.0000000000000000000000095469549411979844748553534196582286585751228071408728, + 0.0000000000000000000000047734774705989922374276846068851506055906657137209047, + 0.0000000000000000000000023867387352994961187138442777065843718711089344045782, + 0.0000000000000000000000011933693676497480593569226324192944532044984865894525, + 0.0000000000000000000000005966846838248740296784614396011477934194852481410926, + 0.0000000000000000000000002983423419124370148392307506484490384140516252814304, + 0.0000000000000000000000001491711709562185074196153830361933046331030629430117, + 0.0000000000000000000000000745855854781092537098076934460888486730708440475045, + 0.0000000000000000000000000372927927390546268549038472050424734256652501673274, + 0.0000000000000000000000000186463963695273134274519237230207489851150821191330, + 0.0000000000000000000000000093231981847636567137259618916352525606281553180093, + 0.0000000000000000000000000046615990923818283568629809533488457973317312233323, + 0.0000000000000000000000000023307995461909141784314904785572277779202790023236, + 0.0000000000000000000000000011653997730954570892157452397493151087737428485431, + 0.0000000000000000000000000005826998865477285446078726199923328593402722606924, + 0.0000000000000000000000000002913499432738642723039363100255852559084863397344, + 0.0000000000000000000000000001456749716369321361519681550201473345138307215067, + 0.0000000000000000000000000000728374858184660680759840775119123438968122488047, + 0.0000000000000000000000000000364187429092330340379920387564158411083803465567, + 0.0000000000000000000000000000182093714546165170189960193783228378441837282509, + 0.0000000000000000000000000000091046857273082585094980096891901482445902524441, + 0.0000000000000000000000000000045523428636541292547490048446022564529197237262, + 0.0000000000000000000000000000022761714318270646273745024223029238091160103901}; + int n= 53; + double x = 1; + double y = 0; + double z; + double s = 1; + int k; + + for (k = 0; k < n; k++) { + z = x + x * s; + if (z <= arg) { + x = z; + y += ae[k]; + } + s *= 0.5; + } + return y; + } + + /** + * round a double value to its nearest long integer value + * @param d + * the double to be rounded + * @return the nearest long integer + */ + public static long round(double d){ + return (long)((d >= 0) ? Math.floor(d+0.5) : Math.ceil(d-0.5)); + } + + /** + * round a double value to a specified number of decimal places + * @param d + * the double to be rounded + * @param digits + * the number of remaining decimal places after rounding + * @return the rounded double + */ + public static double round(double d, int digits) { + int m = IntegerFunctions.pow(10, digits); + double d2 = d * m; + double res =(d >= 0) ? Math.floor(d2+0.5) : Math.ceil(d2-0.5); + return res / m; + } + +} diff --git a/src/de/flexiprovider/common/math/polynomials/GFP32Polynomial.java b/src/de/flexiprovider/common/math/polynomials/GFP32Polynomial.java new file mode 100644 index 00000000..9073fdd9 --- /dev/null +++ b/src/de/flexiprovider/common/math/polynomials/GFP32Polynomial.java @@ -0,0 +1,483 @@ +package de.flexiprovider.common.math.polynomials; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Vector; + +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1Integer; +import codec.asn1.ASN1Sequence; +import codec.asn1.ASN1SequenceOf; +import codec.asn1.DERDecoder; +import codec.asn1.DEREncoder; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.common.util.ASN1Tools; +import de.flexiprovider.common.util.IntUtils; + +/** + * An Element of this class represents a Polynomial within a GFP Ring Structure. + *

+ * The Structure is defined by the modulo Function and the large "prime" p. This + * Class has methods for multiplying, adding and reducing Polynomials. + *

+ * This Class has been developed mainly for the LMOTS Signature scheme, so the + * available methods may not be a complete Implementation of the methods that + * may be required or expected on Ring Arithmetic and some of the implemented + * Methods are intended only for use of the LMOTS Signature scheme. Such as the + * ability to create random Polynomials within this Ring Structure with a custom + * modulo limit or Multiplication of a Polynomial with a Vector of Polynomials. + */ +public class GFP32Polynomial { + + private int[] f; + private int[] poly; + private int degree; + private int p; + + private SecureRandom generator; + + /** + * Constructor for decoding a previously encoded {@link GFP32Polynomial} + * using the getEncoded() method + * + * @param encoded + * the byte array containing the encoded {@link GFP32Polynomial} + * @throws IOException + * @throws ASN1Exception + */ + public GFP32Polynomial(byte[] encoded) throws ASN1Exception, IOException { + ByteArrayInputStream in = new ByteArrayInputStream(encoded); + DERDecoder decoder = new DERDecoder(in); + ASN1Sequence gfpSequence = new ASN1Sequence(3); + gfpSequence.add(new ASN1SequenceOf(ASN1Integer.class)); + gfpSequence.add(new ASN1Integer()); + gfpSequence.add(new ASN1SequenceOf(ASN1Integer.class)); + gfpSequence.decode(decoder); + in.close(); + + ASN1SequenceOf asn1F = (ASN1SequenceOf) gfpSequence.get(0); + ASN1Integer asn1P = (ASN1Integer) gfpSequence.get(1); + ASN1SequenceOf asn1Poly = (ASN1SequenceOf) gfpSequence.get(2); + + int[] poly = new int[asn1Poly.size()]; + for (int i = poly.length - 1; i >= 0; i--) { + poly[i] = ASN1Tools.getFlexiBigInt((ASN1Integer) asn1Poly.get(i)) + .intValue(); + } + this.poly = poly; + int[] f = new int[asn1F.size()]; + degree = f.length - 1; + for (int i = degree; i >= 0; i--) { + f[i] = ASN1Tools.getFlexiBigInt((ASN1Integer) asn1F.get(i)) + .intValue(); + } + this.f = f; + p = ASN1Tools.getFlexiBigInt(asn1P).intValue(); + + generator = Registry.getSecureRandom(); + } + + /** + * Standard Constructor for generating a new GFPPolynomial + * + * @param f + * the modulo Polynomial of the Ring + * @param p + * the modulo "prime" of the Ring + * @param poly + * the Polynomial an int array, most significant entry is right + */ + public GFP32Polynomial(int[] f, int p, int[] poly) { + this.f = f; + degree = f.length - 1; + this.p = p; + this.poly = reduce(poly); + generator = Registry.getSecureRandom(); + } + + /** + * Special Constructor without a Polynomial parameter but with a Secure + * Random generator. This Constructor can only be used for methods which do + * not require a Polynomial to be present, such as generatePoly() + * + * @param f + * the modulo Polynomial of the Ring + * @param p + * the modulo "prime" of the Ring + * @param gen + * a predefined secure Random Number Generator + */ + public GFP32Polynomial(int[] f, int p, SecureRandom gen) { + this.f = f; + degree = f.length - 1; + this.p = p; + generator = Registry.getSecureRandom(); + } + + /** + * adds the given Polynomial to this Polynomial and returns the result + * + * @param gfp + * the Polynomial to be added + * @return the Addition of the two Polynomials + */ + public GFP32Polynomial add(GFP32Polynomial gfp) { + if (!paramEqual(gfp)) { + return null; + } + int[] b = gfp.getPoly(); + int[] a = poly; + if (a.length < b.length) { + a = b; + b = poly; + } + + int[] result = new int[a.length]; + + for (int i = a.length - 1; i >= 0; i--) { + result[i] = a[i]; + if (i < b.length) { + result[i] = (result[i] + b[i]) % p; + } + } + + return new GFP32Polynomial(f, p, reduce(result)); + } + + /** + * Adds the supplied Polynomial to this Polynomial and sets this Polynomial + * as the result + * + * @param gfp + * the Polynomial to be added + */ + public void addToThis(GFP32Polynomial gfp) { + poly = add(gfp).getPoly(); + } + + private int[] compress(int[] a) { + return compress(a, p); + } + + private int[] compress(int[] a, int p) { + for (int i = a.length; i > 0; i--) { + if (a[i - 1] > (p / 2)) { + a[i - 1] -= p; + } + } + return a; + } + + /** + * Subtracts p from every entry in this polynomial with a value greater p/2. + *

+ * This Function is used for calculating the correct Norm of a Polynomial, + * since the Norm uses the absolute Value for determining the Maximum + * + * @return the compressed polynomial + */ + public int[] compressThis() { + return compress(poly); + } + + public boolean equals(Object other) { + if ((other == null) || !(other instanceof GFP32Polynomial)) { + return false; + } + + GFP32Polynomial otherPol = (GFP32Polynomial) other; + return ((p == otherPol.p) && IntUtils.equals(f, otherPol.f) && IntUtils + .equals(compressThis(), otherPol.compressThis())); + } + + /** + * also compares SecureRandom, but since that class has no default equals + * operator, this method is useless (for now) + * + * @param gfp + * the {@link GFP32Polynomial} to compare to. + * @return + */ + private boolean fullEquals(GFP32Polynomial gfp) { + return ((p == gfp.getP()) && IntUtils.equals(f, gfp.getF()) + && IntUtils.equals(poly, gfp.getPoly()) && generator.equals(gfp + .getRandomizer())); + } + + /** + * Generates a random Polynomial, complying to the specification of this + * Ring + * + * @return the randomly generated Polynomial + */ + public GFP32Polynomial generatePoly() { + return generatePoly(0); + } + + /** + * Generates a random Polynomial with the specified limit, denoting the + * maximum Value of entries in this Polynomial. + * + * @param limit + * the limit to be used for modulo in the generated Polynomial. + * Only used if the supplied number is lower than p, otherwise p + * is used. + * @return the randomly generated Polynomial + */ + public GFP32Polynomial generatePoly(int limit) { + return generatePoly(limit, false); + } + + public GFP32Polynomial generatePoly(int limit, boolean negative) { + if ((limit == 0) || (limit > p)) { + limit = p; + } + int[] resPoly = new int[degree]; + for (int i = degree; i > 0; i--) { + if (negative) { + resPoly[i - 1] = generator.nextInt((limit * 2) - 1); + } else { + resPoly[i - 1] = generator.nextInt(limit); + } + } + if (negative) { + return new GFP32Polynomial(f, p, compress(resPoly, (limit * 2) - 1)); + } + return new GFP32Polynomial(f, p, reduceZeros(resPoly)); + } + + public byte[] getEncoded() throws ASN1Exception, IOException { + // TODO use ASN.1 + ASN1Sequence gfpSequence = new ASN1Sequence(3); + ASN1Integer asn1P = new ASN1Integer(p); + ASN1SequenceOf asn1Poly = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf asn1F = new ASN1SequenceOf(ASN1Integer.class); + for (int i = 0; i < poly.length; i++) { + asn1Poly.add(new ASN1Integer(poly[i])); + } + for (int i = 0; i < f.length; i++) { + asn1F.add(new ASN1Integer(f[i])); + } + gfpSequence.add(asn1F); + gfpSequence.add(asn1P); + gfpSequence.add(asn1Poly); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + gfpSequence.encode(new DEREncoder(baos)); + byte[] res = baos.toByteArray(); + baos.flush(); + baos.close(); + + return res; + } + + public int[] getF() { + return f; + } + + public int getP() { + return p; + } + + /** + * + * @return The (reduced) Polynomial as an int array + */ + public int[] getPoly() { + return poly; + } + + public SecureRandom getRandomizer() { + return generator; + } + + private int[] mod(int[] poly) { + for (int i = poly.length; i > 0; i--) { + if (poly[i - 1] < 0) { + poly[i - 1] = (poly[i - 1] % p) + p; + } else { + poly[i - 1] = poly[i - 1] % p; + } + } + return poly; + } + + /** + * multiplies the given Polynomial to this Polynomial and returns the Result + * + * @param gfp + * the Polynomial to be multiplied + * @return the Product of the two Polynomials + */ + public GFP32Polynomial multiply(GFP32Polynomial gfp) { + int[] a = poly; + int[] z = gfp.getPoly(); + int degree = a.length + z.length - 1; + int[] result = new int[degree]; + // Arrays.fill(result, 0); + + a = compress(a); + z = compress(z); + + for (int i = a.length - 1; i >= 0; i--) { + for (int j = z.length - 1; j >= 0; j--) { + long l = a[i]; + long k = z[j]; + k = (l * k) % p; + result[i + j] = ((int) k + result[i + j]) % p; + } + } + + return new GFP32Polynomial(f, p, reduce(result)); + } + + /** + * multiplies this Polynomial with a Vector of Polynomials and returns the + * Result + *

+ * Multiplication with a Vector is designed as follows: + *

+ * Vector � = (a1, a2, ... , am), Polynomial p � * p = (a1*p, a2*p, ... + * , am * p) + * + * @param k + * the Vector of Polynomials to be multiplied + * @return the Vector of the Product + */ + public Vector multiply(Vector k) { + Vector result = new Vector(); + result.setSize(k.size()); + + for (int i = k.size() - 1; i >= 0; i--) { + GFP32Polynomial next = (GFP32Polynomial) k.elementAt(i); + result.setElementAt(multiply(next), i); + + } + + return result; + } + + /** + * Multiplies the given Polynomial to this Polynomial and sets this + * Polynomial to the Result + * + * @param gfp + * the Polynomial to be multiplied + */ + public void multiplyToThis(GFP32Polynomial gfp) { + poly = multiply(gfp).getPoly(); + } + + public boolean paramEqual(GFP32Polynomial gfp) { + return ((p == gfp.p) && IntUtils.equals(f, gfp.f)); + } + + public void print() { + System.out.println("printing GFP"); + System.out.println("p: " + getP()); + System.out.println("f: " + printPoly(getF())); + System.out.println("poly: " + printPoly(mod(getPoly()))); + } + + private String printPoly(int[] arr) { + String result = "{"; + for (int i = arr.length - 1; i > 0; i--) { + result += arr[i] + ", "; + } + result += arr[0] + "}"; + return result; + } + + /** + * This Methods reduces a Polynomial in int array representation, with the + * most significant value rightmost. This means that the supplied Polynomial + * will be calculated modulo the Ring Polynomial f and the remainder is + * returned. + * + * @param z + * The supplied Polynomial to be reduced + * @return the remainder of the reduced Polynomial + */ + private int[] reduce(int[] z) { + z = reduceZeros(mod(z)); + if (z.length < f.length) { + return z; + } + int exp = z.length - f.length; + long v = ((-z[z.length - 1]) + p) % p; + int zSize = z.length - 1; + int[] newZ = new int[zSize]; + for (int i = zSize; i > 0; i--) { + if (i <= exp) { + newZ[i - 1] = z[i - 1]; + } else { + long l = f[i - exp - 1]; + long k = (l * v) % p; + newZ[i - 1] = ((int) k + z[i - 1]) % p; + } + } + return reduce(newZ); + } + + private int[] reduceZeros(int[] z) { + int zSize = z.length; + for (int i = z.length; i > 0; i--) { + if (z[i - 1] == 0) { + zSize--; + } else { + break; + } + } + + if (zSize == z.length) { + return z; + } + int[] newZ = new int[zSize]; + System.arraycopy(z, 0, newZ, 0, zSize); + return newZ; + } + + public GFP32Polynomial subtract(GFP32Polynomial gfp) { + if (!paramEqual(gfp)) { + return null; + } + int[] b = gfp.getPoly(); + int[] a = poly; + + int[] result = new int[Math.max(a.length, b.length)]; + + for (int i = result.length - 1; i >= 0; i--) { + if (i < b.length && i < a.length) { + result[i] = (a[i] - b[i]) % p; + } else if (i < b.length) { + result[i] = -b[i]; + } else { + result[i] = a[i]; + } + } + + return new GFP32Polynomial(f, p, reduce(result)); + } + + public void subtractFromThis(GFP32Polynomial gfp) { + poly = subtract(gfp).getPoly(); + } + + // public static void main(String[] args) throws ASN1Exception, IOException + // { + // GFPPolynomial gfp = new GFPPolynomial(new int[]{5, 0, 0, 0, 0, 0, 0, 0, + // 1}, 19, new int[]{2, 5, 7, 12, 239, 124, 12312, 123, 23, 5325, 6322, + // 12322}); + // byte[] testArr = gfp.getEncoded(); + // + // System.out.println(new FlexiBigInt(testArr)); + // GFPPolynomial gfp2 = new GFPPolynomial(testArr); + // gfp.print(); + // gfp2.print(); + // if (gfp.equals(gfp2)) { + // System.out.println("YAHOOOOO"); + // } + // } +} diff --git a/src/de/flexiprovider/common/mode/CBC.java b/src/de/flexiprovider/common/mode/CBC.java new file mode 100644 index 00000000..5d54aed4 --- /dev/null +++ b/src/de/flexiprovider/common/mode/CBC.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.Mode; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * Cipher Block Chaining (CBC) mode for symmetric block ciphers. For further + * information, see "Handbook of Applied Cryptography", Note 7.13. + * + * @author Ralf-P. Weinmann + */ +public class CBC extends Mode { + + /** + * just a help buffer + */ + private byte[] buf; + + /** + * Temporary buffer used for chaining two blocks (by an xor operation). + */ + private byte[] chainingBlock; + + /** + * Initialize the Mode object for encryption. + * + * @param key + * the key used for encryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initEncrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherEncrypt(key, cipherParams); + initCommon(modeParams); + } + + /** + * Initialize the Mode object for decryption. + * + * @param key + * the key used for decryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initDecrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherDecrypt(key, cipherParams); + initCommon(modeParams); + } + + /** + * CBC common initialization. + */ + private void initCommon(ModeParameterSpec modeParams) { + blockSize = getCipherBlockSize(); + + iv = new byte[blockSize]; + if (modeParams != null) { + // obtain IV from mode parameters + byte[] iv = modeParams.getIV(); + + if (iv.length < blockSize) { + // if IV is too short, fill with zeroes + System.arraycopy(iv, 0, this.iv, 0, iv.length); + } else if (iv.length > blockSize) { + // if IV is too long, use only first bytes + System.arraycopy(iv, 0, this.iv, 0, blockSize); + } else { + // else, use the IV + this.iv = iv; + } + } + + buf = new byte[blockSize]; + chainingBlock = new byte[blockSize]; + reset(); + } + + /** + * Encrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkEncrypt(byte[] input, int inOff, + byte[] output, int outOff) { + + for (int i = blockSize - 1; i >= 0; i--) { + chainingBlock[i] ^= input[inOff + i]; + } + + singleBlockEncrypt(chainingBlock, 0, output, outOff); + System.arraycopy(output, outOff, chainingBlock, 0, blockSize); + } + + /** + * Decrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkDecrypt(byte[] input, int inOff, + byte[] output, int outOff) { + + singleBlockDecrypt(input, inOff, buf, 0); + for (int i = blockSize - 1; i >= 0; i--) { + output[outOff + i] = (byte) (chainingBlock[i] ^ buf[i]); + } + + System.arraycopy(input, inOff, chainingBlock, 0, blockSize); + } + + /** + * Reset chaining block to initialization vector. + */ + protected final void reset() { + System.arraycopy(iv, 0, chainingBlock, 0, iv.length); + } + +} diff --git a/src/de/flexiprovider/common/mode/CFB.java b/src/de/flexiprovider/common/mode/CFB.java new file mode 100644 index 00000000..90987867 --- /dev/null +++ b/src/de/flexiprovider/common/mode/CFB.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.Mode; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * Cipher feedback mode for symmetric block ciphers. + *

+ * This class currently only supports 1-byte feedback which should be sufficient + * for most applications. + * + * For further information, see "Handbook of Applied Cryptography", Note 7.17. + * + * WARNING: CFB feedback should be in BYTES. The default value + * is 1 Byte(8 Bits) + * + * @author Ralf-P. Weinmann + */ +public class CFB extends Mode { + + /** + * a help buffer + */ + private byte[] buf; + + /** + * The shift register needed by the mode. + */ + private byte[] feedbackBlock; + + /** + * Initialize the Mode object for encryption. + * + * @param key + * the key used for encryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initEncrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherEncrypt(key, cipherParams); + int cipherBlockSize = getCipherBlockSize(); + + iv = new byte[cipherBlockSize]; + if (modeParams != null) { + // obtain IV from mode parameters + byte[] iv = modeParams.getIV(); + + if (iv.length < cipherBlockSize) { + // if IV is too short, fill with zeroes + System.arraycopy(iv, 0, this.iv, 0, iv.length); + } else if (iv.length > cipherBlockSize) { + // if IV is too long, use only first bytes + System.arraycopy(iv, 0, this.iv, 0, cipherBlockSize); + } else { + // else, use the IV + this.iv = iv; + } + } + + if (modeParams instanceof CFBParameterSpec) { + // get block size + blockSize = ((OFBParameterSpec) modeParams).getBlockSize(); + // check block size + if (blockSize > cipherBlockSize) { + blockSize = cipherBlockSize; + } + } else { + // default: set block size to cipher block size + blockSize = cipherBlockSize; + } + + buf = new byte[cipherBlockSize]; + feedbackBlock = new byte[cipherBlockSize]; + reset(); + } + + /** + * Initialize the Mode object for decryption. + * + * @param key + * the key used for decryption + * @param modeParams + * additional mode parameters + * @param paramSpec + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initDecrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec paramSpec) + throws InvalidKeyException, InvalidAlgorithmParameterException { + initEncrypt(key, modeParams, paramSpec); + } + + /** + * Encrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkEncrypt(final byte[] input, final int inOff, + byte[] output, final int outOff) { + nextChunk(input, inOff, output, outOff); + // fill feedback block with ciphertext + System.arraycopy(output, outOff, feedbackBlock, 0, blockSize); + } + + /** + * Decrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkDecrypt(byte[] input, int inOff, + byte[] output, int outOff) { + nextChunk(input, inOff, output, outOff); + // fill feedback block with ciphertext + System.arraycopy(input, inOff, feedbackBlock, 0, blockSize); + } + + private void nextChunk(byte[] input, int inOff, byte[] output, int outOff) { + + // encrypt feedback block + singleBlockEncrypt(feedbackBlock, 0, buf, 0); + + // compute ciphertext block + for (int i = 0; i < blockSize; i++) { + output[outOff + i] = (byte) (buf[i] ^ input[inOff + i]); + } + + // shift feedback block + System.arraycopy(feedbackBlock, 0, feedbackBlock, blockSize, + feedbackBlock.length - blockSize); + } + + /** + * Reset shift block to initialization vector. + */ + protected final void reset() { + System.arraycopy(iv, 0, feedbackBlock, 0, iv.length); + } + +} diff --git a/src/de/flexiprovider/common/mode/CFBParameterSpec.java b/src/de/flexiprovider/common/mode/CFBParameterSpec.java new file mode 100644 index 00000000..5a21f43d --- /dev/null +++ b/src/de/flexiprovider/common/mode/CFBParameterSpec.java @@ -0,0 +1,33 @@ +package de.flexiprovider.common.mode; + +/** + * This class is the parameter specification of the Cipher Feedback Mode + * + * @author Johannes Müller + */ +public class CFBParameterSpec extends ModeParameterSpec { + + // the block size + private int blockSize; + + /** + * Constructor. Set the passed initialization vector and block size. + * + * @param iv + * the initialization vector + * @param blockSize + * the block size + */ + public CFBParameterSpec(byte[] iv, int blockSize) { + super(iv); + this.blockSize = blockSize; + } + + /** + * @return the block size + */ + public final int getBlockSize() { + return blockSize; + } + +} diff --git a/src/de/flexiprovider/common/mode/CTR.java b/src/de/flexiprovider/common/mode/CTR.java new file mode 100644 index 00000000..1156437b --- /dev/null +++ b/src/de/flexiprovider/common/mode/CTR.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.Mode; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * Counter (CTR) mode for symmetric block ciphers. + * + * Please see the following document for more information: H. Lipmaa, P. + * Rogaway, D. Wagner: Comments to NIST concerning AES Modes of Operations: + * CTR-Mode Encryption + * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/ctr/ctr-spec.pdf + * + * @author Ralf-P. Weinmann + * @author Martin Döring + */ +public class CTR extends Mode { + + // the counter value + private byte[] counter; + + // the feedback block + private byte[] feedbackBlock; + + /** + * Initialize the Mode object for encryption. + * + * @param key + * the key used for encryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the underlying + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initEncrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherEncrypt(key, cipherParams); + initCommon(modeParams); + } + + /** + * Initialize the Mode object for decryption. + * + * @param key + * the key used for decryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the underlying + * block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initDecrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherDecrypt(key, cipherParams); + initCommon(modeParams); + } + + /** + * CTR common initialization. + */ + private void initCommon(ModeParameterSpec modeParams) { + blockSize = getCipherBlockSize(); + + iv = new byte[blockSize]; + if (modeParams != null) { + // obtain IV from mode parameters + byte[] iv = modeParams.getIV(); + + if (iv.length < blockSize) { + // if IV is too short, fill with zeroes + System.arraycopy(iv, 0, this.iv, 0, iv.length); + } else if (iv.length > blockSize) { + // if IV is too long, use only first bytes + System.arraycopy(iv, 0, this.iv, 0, blockSize); + } else { + // else, use the IV + this.iv = iv; + } + } + + feedbackBlock = new byte[blockSize]; + counter = new byte[blockSize]; + reset(); + } + + /** + * Encrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkEncrypt(final byte[] input, final int inOff, + byte[] output, final int outOff) { + + singleBlockEncrypt(counter, 0, feedbackBlock, 0); + + int inCarry = 1; + for (int i = blockSize - 1; i >= 0; i--) { + output[outOff + i] = (byte) (feedbackBlock[i] ^ input[inOff + i]); + // increase counter value + int x = (counter[i] & 0xff) + inCarry; + counter[i] = (byte) x; + inCarry = (x > 255) ? 1 : 0; + } + } + + /** + * Decrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkDecrypt(final byte[] input, final int inOff, + byte[] output, final int outOff) { + nextChunkEncrypt(input, inOff, output, outOff); + } + + /** + * Reset counter value to initialization vector. + */ + protected final void reset() { + System.arraycopy(iv, 0, counter, 0, blockSize); + } + +} diff --git a/src/de/flexiprovider/common/mode/ECB.java b/src/de/flexiprovider/common/mode/ECB.java new file mode 100644 index 00000000..52150b80 --- /dev/null +++ b/src/de/flexiprovider/common/mode/ECB.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.Mode; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * This class implements the Electronic Codebook Mode (ECB) for symmetric block + * ciphers. + *

+ * The ECB mode directly applies the forward cipher function onto each block of + * plaintext. Under a given key, blocks of plaintext that are equal are mapped + * onto the same block of ciphertext. Decryption is accomplished by applying the + * inverse cipher function onto blocks of ciphertext. + * + * @author Ralf-P. Weinmann + */ +public class ECB extends Mode { + + /** + * Initialize the Mode object for encryption. + * + * @param key + * the key used for encryption + * @param modeParams + * additional mode parameters (not used) + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initEncrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherEncrypt(key, cipherParams); + blockSize = getCipherBlockSize(); + } + + /** + * Initialize the Mode object for decryption. + * + * @param key + * the key used for decryption + * @param modeParams + * additional mode parameters (not used) + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initDecrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherDecrypt(key, cipherParams); + blockSize = getCipherBlockSize(); + } + + /** + * Encrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkEncrypt(final byte[] input, final int inOff, + byte[] output, final int outOff) { + singleBlockEncrypt(input, inOff, output, outOff); + } + + /** + * Decrypt the next data block. Any special features of the Mode should be + * implemented here. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkDecrypt(final byte[] input, final int inOff, + byte[] output, final int outOff) { + singleBlockDecrypt(input, inOff, output, outOff); + } + + /** + * ECB does not perform any operations on reset. + */ + protected final void reset() { + // empty + } + +} diff --git a/src/de/flexiprovider/common/mode/ModeParamGenParameterSpec.java b/src/de/flexiprovider/common/mode/ModeParamGenParameterSpec.java new file mode 100644 index 00000000..15f98220 --- /dev/null +++ b/src/de/flexiprovider/common/mode/ModeParamGenParameterSpec.java @@ -0,0 +1,48 @@ +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * This class specifies parameters used for initializing the + * {@link ModeParameterGenerator}. The parameters consist of the byte length of + * the initialization vector. + * + * @author Martin Döring + */ +public class ModeParamGenParameterSpec implements AlgorithmParameterSpec { + + /** + * The default length of the IV (8 bytes) + */ + public static final int DEFAULT_LENGTH = 8; + + private int ivLength; + + /** + * Construct the default mode parameter generation parameters. Set the + * length of the IV to {@link #DEFAULT_LENGTH}. The default length is + * chosen (somewhat arbitrarily) as 8 bytes. + */ + public ModeParamGenParameterSpec() { + this(DEFAULT_LENGTH); + } + + /** + * Construct new parameters from the desired length of the initialization + * vector (IV) in bytes. + * + * @param ivLength + * the length of the IV in bytes + */ + public ModeParamGenParameterSpec(int ivLength) { + this.ivLength = ivLength; + } + + /** + * @return the length of the IV in bytes + */ + public int getIVLength() { + return ivLength; + } + +} diff --git a/src/de/flexiprovider/common/mode/ModeParameterGenerator.java b/src/de/flexiprovider/common/mode/ModeParameterGenerator.java new file mode 100644 index 00000000..cd0e2265 --- /dev/null +++ b/src/de/flexiprovider/common/mode/ModeParameterGenerator.java @@ -0,0 +1,114 @@ +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.parameters.AlgorithmParameterGenerator; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.api.parameters.AlgorithmParameters; + +/** + * This class is used to generate initialization vectors (IVs) used by the modes + * CBC, CFB, OFB, and CTR. + * + * @author Martin Döring + */ +public class ModeParameterGenerator extends AlgorithmParameterGenerator { + + // the length of the IV + private int ivLength; + + // the source of randomness + private SecureRandom random; + + // flag indicating whether the parameter generator has been initialized + private boolean initialized; + + /** + * @return an instance of the {@link AlgorithmParameters} class + * corresponding to the generated parameters + */ + protected AlgorithmParameters getAlgorithmParameters() { + return new ModeParameters(); + } + + /** + * Initialize the parameter generator with parameters and a source of + * randomness. If the parameters are null, the + * {@link ModeParamGenParameterSpec#ModeParamGenParameterSpec() default parameters} + * are used. + * + * @param genParams + * the parameters + * @param random + * the source of randomness + * @throws InvalidAlgorithmParameterException + * if the parameters are not an instance of + * {@link ModeParamGenParameterSpec}. + */ + public void init(AlgorithmParameterSpec genParams, SecureRandom random) + throws InvalidAlgorithmParameterException { + + ModeParamGenParameterSpec modeGenParams; + if (genParams == null) { + modeGenParams = new ModeParamGenParameterSpec(); + } else if (genParams instanceof ModeParamGenParameterSpec) { + modeGenParams = (ModeParamGenParameterSpec) genParams; + } else { + throw new InvalidAlgorithmParameterException("unsupported type"); + } + + ivLength = modeGenParams.getIVLength(); + this.random = random != null ? random : Registry.getSecureRandom(); + + initialized = true; + } + + /** + * Initialize the parameter generator with the desired length of the IV in + * bytes and the source of randomness used to generate the IV. + * + * @param ivLength + * the length of the IV in bytes + * @param random + * the source of randomness + */ + public void init(int ivLength, SecureRandom random) { + ModeParamGenParameterSpec genParams = new ModeParamGenParameterSpec( + ivLength); + try { + init(genParams, random); + } catch (InvalidAlgorithmParameterException e) { + // the parameters are correct and must be accepted + throw new RuntimeException("internal error"); + } + } + + private void initDefault() { + ModeParamGenParameterSpec defaultGenParams = new ModeParamGenParameterSpec(); + try { + init(defaultGenParams, random); + } catch (InvalidAlgorithmParameterException e) { + // the parameters are correct and must be accepted + throw new RuntimeException("internal error"); + } + } + + /** + * Generate a new IV using the length and source of randomness specified + * during initialization. + * + * @return the generated IV encapsulated in an instance of + * {@link ModeParameterSpec} + */ + public AlgorithmParameterSpec generateParameters() { + if (!initialized) { + initDefault(); + } + + byte[] iv = new byte[ivLength]; + random.nextBytes(iv); + return new ModeParameterSpec(iv); + } + +} diff --git a/src/de/flexiprovider/common/mode/ModeParameterSpec.java b/src/de/flexiprovider/common/mode/ModeParameterSpec.java new file mode 100644 index 00000000..7fcec4f0 --- /dev/null +++ b/src/de/flexiprovider/common/mode/ModeParameterSpec.java @@ -0,0 +1,56 @@ +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * This interface groups (and provides type safety for) all mode parameter + * specifications. All mode parameter specifications must implement this + * interface. + */ +public class ModeParameterSpec extends javax.crypto.spec.IvParameterSpec + implements AlgorithmParameterSpec { + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * Converts a JCA IvParameterSpec in a Flexi IvParameterSpec. + * + * @param params + * the JCA IvParameterSpec. + */ + public ModeParameterSpec(javax.crypto.spec.IvParameterSpec params) { + super(params.getIV()); + } + + // **************************************************** + // FlexiAPI methods + // **************************************************** + + /** + * Constructor. Set the initialization vector (IV). The IV may be + * null. + * + * @param iv + * the IV + */ + public ModeParameterSpec(byte[] iv) { + super(iv); + } + + /** + * Constructor. Set the initialization vector (IV). The IV must not be null. + * + * @param iv + * the byte array containing the IV + * @param offset + * the offset where the IV starts + * @param length + * the length of the IV + */ + public ModeParameterSpec(byte[] iv, int offset, int length) { + super(iv, offset, length); + } + +} diff --git a/src/de/flexiprovider/common/mode/ModeParameters.java b/src/de/flexiprovider/common/mode/ModeParameters.java new file mode 100644 index 00000000..d44d24de --- /dev/null +++ b/src/de/flexiprovider/common/mode/ModeParameters.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ +package de.flexiprovider.common.mode; + +import java.io.IOException; + +import javax.crypto.spec.IvParameterSpec; + +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1OctetString; +import de.flexiprovider.api.exceptions.InvalidParameterSpecException; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.api.parameters.AlgorithmParameters; +import de.flexiprovider.common.util.ASN1Tools; +import de.flexiprovider.common.util.ByteUtils; + +/** + * This class is used as an opaque representation of initialization vectors used + * as mode parameters. ASN.1/DER encoding and decoding are supported. Parameters + * are encoded as + * + *

+ *   ModeParameters = OCTET STRING (SIZE 8) IV
+ * 
+ * + * @author Norbert Trummel + * @author Sylvain Franke + */ +public class ModeParameters extends AlgorithmParameters { + + private byte[] iv; + + /** + * JCA adapter for FlexiAPI method {@link #init(AlgorithmParameterSpec)}: + * initialize this parameters object using the parameters specified in + * paramSpec. This method overrides the corresponding method of + * {@link AlgorithmParameters} in order to provide support for + * {@link IvParameterSpec}. + * + * @param params + * the parameter specification + * @throws java.security.spec.InvalidParameterSpecException + * if paramSpec is inappropriate for + * initialization. + */ + protected void engineInit(java.security.spec.AlgorithmParameterSpec params) + throws java.security.spec.InvalidParameterSpecException { + + if (params == null) { + throw new java.security.spec.InvalidParameterSpecException(); + } + if (!(params instanceof AlgorithmParameterSpec)) { + if (params instanceof IvParameterSpec) { + iv = ((IvParameterSpec) params).getIV(); + return; + } + throw new java.security.spec.InvalidParameterSpecException(); + } + + init((AlgorithmParameterSpec) params); + } + + /** + * JCA adapter for FlexiAPI method {@link #getParameterSpec(Class)}: return + * a (transparent) specification of this parameters object. This method + * overrides the corresponding method of {@link AlgorithmParameters} in + * order to provide support for {@link IvParameterSpec}. + * + * @param paramSpec + * the the specification class in which the parameters should + * be returned + * @return the parameter specification + * @throws java.security.spec.InvalidParameterSpecException + * if the requested parameter specification is inappropriate + * for this parameter object. + */ + protected java.security.spec.AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws java.security.spec.InvalidParameterSpecException { + + if (!(AlgorithmParameterSpec.class.isAssignableFrom(paramSpec))) { + if (paramSpec == IvParameterSpec.class) { + return getParameterSpec(ModeParameterSpec.class); + } + throw new java.security.spec.InvalidParameterSpecException( + "Unsupported parameter specification."); + } + + return getParameterSpec(paramSpec); + } + + /** + * Initialize this parameters object using the parameters specified in + * paramSpec. + * + * @param paramSpec + * the parameter specification + * @throws InvalidParameterSpecException + * if the given parameter specification is inappropriate for + * the initialization of this parameter object. + */ + public final void init(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException { + + if (paramSpec == null) { + throw new InvalidParameterSpecException("Null parameters."); + } + + if (!(paramSpec instanceof ModeParameterSpec)) { + throw new InvalidParameterSpecException( + "Unsupported parameter specification."); + } + iv = ((ModeParameterSpec) paramSpec).getIV(); + } + + /** + * Import the specified parameters and decodes them according to the primary + * decoding format for parameters. The primary decoding format for + * parameters is ASN.1. + * + * @param encParams + * the encoded parameters + * @throws IOException + * on decoding errors + */ + public final void init(byte[] encParams) throws IOException { + ASN1OctetString asn1IV = new ASN1OctetString(); + try { + ASN1Tools.derDecode(encParams, asn1IV); + } catch (ASN1Exception e) { + throw new IOException("Illegal encoding."); + } + iv = asn1IV.getByteArray(); + } + + /** + * Import the specified parameters and decodes them according to the + * specified decoding format. Only "ASN.1" is supported at the moment. + * + * @param params + * the encoded parameters. + * @param format + * the name of the decoding format. + * @throws IOException + * if format is not equal to "ASN.1". + */ + public final void init(byte[] params, String format) throws IOException { + if (!(format == "ASN.1")) { + throw new IOException("Unsupported encoding format."); + } + init(params); + } + + /** + * @return the ASN.1 encoded parameters + */ + public final byte[] getEncoded() { + return ASN1Tools.derEncode(new ASN1OctetString(iv)); + } + + /** + * Return the parameters encoded in the specified format. Only "ASN.1" is + * supported at the moment. + * + * @param format + * the encoding format + * @return the encoded parameters + * @throws IOException + * if format is not equal to "ASN.1". + */ + public final byte[] getEncoded(String format) throws IOException { + if (!(format == "ASN.1")) { + throw new IOException("Unsupported encoding format."); + } + return getEncoded(); + } + + /** + * Return a (transparent) specification of this parameters object. + * paramSpec identifies the specification class in which the + * parameters should be returned. Only {@link ModeParameterSpec} is + * supported. + * + * @param paramSpec + * the specification class in which the parameters should be + * returned + * @return the parameter specification + * @throws InvalidParameterSpecException + * if the requested parameter specification is inappropriate + * for this parameters object. + */ + public final AlgorithmParameterSpec getParameterSpec(Class paramSpec) + throws InvalidParameterSpecException { + if (!(paramSpec.isAssignableFrom(ModeParameterSpec.class))) { + throw new InvalidParameterSpecException( + "Unsupported parameter specification."); + } + return new ModeParameterSpec(iv); + } + + /** + * @return a formatted string describing the parameters + */ + public final String toString() { + return "IV: " + ByteUtils.toHexString(iv); + } + +} diff --git a/src/de/flexiprovider/common/mode/OFB.java b/src/de/flexiprovider/common/mode/OFB.java new file mode 100644 index 00000000..ff48d5a0 --- /dev/null +++ b/src/de/flexiprovider/common/mode/OFB.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.mode; + +import de.flexiprovider.api.Mode; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.keys.SecretKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * Output feedback mode for symmetric block ciphers as per ISO/IEC 10116 (full + * feedback) + *

+ * We do not support the FIPS PUB 81 version of OFB, since the expected cycle + * length dramatically decreases if the feedback is smaller than the block size + * of the cipher. + *

+ * TODO WARNING! OFB currently supports just 1-byte feedback + *

+ * For further information, see "Handbook of Applied Cryptography", Note 7.24. + * + * @author Ralf-P. Weinmann + */ +public class OFB extends Mode { + + // the output buffer + private byte[] buf; + + // the feedback block + private byte[] feedbackBlock; + + /** + * Initialize the Mode object for encryption. + * + * @param key + * the key used for encryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initEncrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + initCipherEncrypt(key, cipherParams); + int cipherBlockSize = getCipherBlockSize(); + + iv = new byte[cipherBlockSize]; + if (modeParams != null) { + // obtain IV from mode parameters + byte[] iv = modeParams.getIV(); + + if (iv.length < cipherBlockSize) { + // if IV is too short, fill with zeroes + System.arraycopy(iv, 0, this.iv, 0, iv.length); + } else if (iv.length > cipherBlockSize) { + // if IV is too long, use only first bytes + System.arraycopy(iv, 0, this.iv, 0, cipherBlockSize); + } else { + // else, use the IV + this.iv = iv; + } + } + + if (modeParams instanceof OFBParameterSpec) { + // get block size + blockSize = ((OFBParameterSpec) modeParams).getBlockSize(); + // check block size + if (blockSize > cipherBlockSize) { + blockSize = cipherBlockSize; + } + } else { + // default: set block size to cipher block size + blockSize = cipherBlockSize; + } + + feedbackBlock = new byte[cipherBlockSize]; + buf = new byte[cipherBlockSize]; + reset(); + } + + /** + * Initialize the Mode object for decryption. + * + * @param key + * the key used for decryption + * @param modeParams + * additional mode parameters + * @param cipherParams + * additional algorithm parameters + * @throws InvalidKeyException + * if the key is inappropriate for initializing the + * underlying block cipher. + * @throws InvalidAlgorithmParameterException + * if the parameters are inappropriate for initializing the + * underlying block cipher. + */ + protected final void initDecrypt(SecretKey key, + ModeParameterSpec modeParams, AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, InvalidAlgorithmParameterException { + initEncrypt(key, modeParams, cipherParams); + } + + /** + * Encrypt the next data block. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkEncrypt(final byte[] input, final int inOff, + byte[] output, final int outOff) { + + singleBlockEncrypt(buf, 0, feedbackBlock, 0); + + byte[] swap = buf; + buf = feedbackBlock; + feedbackBlock = swap; + + for (int i = 0; i < blockSize; i++) { + output[outOff + i] = (byte) (feedbackBlock[i] ^ input[inOff + i]); + } + } + + /** + * Decrypt the next data block. + * + * @param input + * input data buffer + * @param inOff + * input data offset + * @param output + * output data buffer + * @param outOff + * output data offset + */ + protected final void nextChunkDecrypt(final byte[] input, final int inOff, + byte[] output, final int outOff) { + nextChunkEncrypt(input, inOff, output, outOff); + } + + /** + * Reset feedback block to encrypted initialization vector. + */ + protected final void reset() { + singleBlockEncrypt(iv, 0, feedbackBlock, 0); + } + +} diff --git a/src/de/flexiprovider/common/mode/OFBParameterSpec.java b/src/de/flexiprovider/common/mode/OFBParameterSpec.java new file mode 100644 index 00000000..8de89ae5 --- /dev/null +++ b/src/de/flexiprovider/common/mode/OFBParameterSpec.java @@ -0,0 +1,33 @@ +package de.flexiprovider.common.mode; + +/** + * This class is the parameter specification of the Cipher Feedback Mode + * + * @author Johannes Müller + */ +public class OFBParameterSpec extends ModeParameterSpec { + + // the block size + private int blockSize; + + /** + * Constructor. Set the passed initialization vector and block size. + * + * @param iv + * the initialization vector + * @param blockSize + * the block size + */ + public OFBParameterSpec(byte[] iv, int blockSize) { + super(iv); + this.blockSize = blockSize; + } + + /** + * @return the block size + */ + public final int getBlockSize() { + return blockSize; + } + +} diff --git a/src/de/flexiprovider/common/padding/NoPadding.java b/src/de/flexiprovider/common/padding/NoPadding.java new file mode 100644 index 00000000..9dcfb71d --- /dev/null +++ b/src/de/flexiprovider/common/padding/NoPadding.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.padding; + +import de.flexiprovider.api.PaddingScheme; +import de.flexiprovider.api.exceptions.BadPaddingException; + +/** + * NoPadding leaves the input unchanged in case of a suitable blocksize. + * Otherwise, it will throw a BadPaddingException. + * + * @author Andre Maric + * @author Witold Wegner + */ +public class NoPadding extends PaddingScheme { + + protected int padLength(int inLen) { + return 0; + } + + protected void pad(byte[] input, int inOff, int inLen) + throws BadPaddingException { + if (blockSize < 1 || (inLen % blockSize != 0)) { + throw new BadPaddingException("invalid input length"); + } + } + + protected int unpad(byte[] input, int inOff, int inLen) { + return inOff + inLen; + } + +} diff --git a/src/de/flexiprovider/common/padding/OneAndZeroesPadding.java b/src/de/flexiprovider/common/padding/OneAndZeroesPadding.java new file mode 100644 index 00000000..8a582099 --- /dev/null +++ b/src/de/flexiprovider/common/padding/OneAndZeroesPadding.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1998-2008 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ +package de.flexiprovider.common.padding; + +import de.flexiprovider.api.PaddingScheme; +import de.flexiprovider.api.exceptions.BadPaddingException; + +/** + * OneAndZeroesPadding pads the plaintext by appending a single byte 1 and + * several 0 bytes. + * + * @author Christoph Sesterhenn + * @author Christoph Ender + * @author Martin Döring + */ +public class OneAndZeroesPadding extends PaddingScheme { + + protected int padLength(int inLen) { + return blockSize - (inLen % blockSize); + } + + protected void pad(byte[] input, int inOff, int inLen) { + int padLength = padLength(inLen); + + input[inOff + inLen] = (byte) 0x80; + for (int i = 1; i < padLength; i++) { + input[inOff + inLen + i] = 0; + } + } + + protected int unpad(byte[] input, int inOff, int inLen) + throws BadPaddingException { + while (inLen >= 0 && input[inOff + inLen - 1] != (byte) 0x80) { + if (input[inOff + inLen - 1] != 0) { + return -1; + } + inLen--; + } + + // compute start index of padding bytes + int padOffset = inOff + inLen - 1; + + // check correctness + if (padOffset == -1) { + throw new BadPaddingException("unpadding failed"); + } + + // return start index of padding bytes + return padOffset; + } + +} diff --git a/src/de/flexiprovider/common/padding/PKCS5Padding.java b/src/de/flexiprovider/common/padding/PKCS5Padding.java new file mode 100644 index 00000000..929fa5c7 --- /dev/null +++ b/src/de/flexiprovider/common/padding/PKCS5Padding.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.padding; + +import de.flexiprovider.api.PaddingScheme; +import de.flexiprovider.api.exceptions.BadPaddingException; + +/** + * PKCS5Padding pads the plaintext by the method described in the PKCS #5 and + * PKCS #7 standards. + * + * @author Ulrich Dudszus + * @author Tom Kollmar + * @author Martin Döring + */ +public class PKCS5Padding extends PaddingScheme { + + protected int padLength(int inLen) { + return blockSize - (inLen % blockSize); + } + + protected void pad(byte[] input, int inOff, int inLen) { + // compute the pad length + int padLength = padLength(inLen); + + // pad the input + int index = inOff + inLen; + for (int i = 0; i < padLength; i++) { + input[index++] = (byte) padLength; + } + } + + protected int unpad(byte[] input, int inOff, int inLen) + throws BadPaddingException { + // the pad length is stored in last byte of the input + int last = inOff + inLen - 1; + byte padLength = input[last--]; + + // check correctness + if (padLength < 0 || padLength > inLen) { + throw new BadPaddingException("unpadding failed"); + } + for (int i = 1; i < padLength; i++) { + if (input[last--] != padLength) { + throw new BadPaddingException("unpadding failed"); + } + } + + // return start index of padding bytes + return ++last; + } + +} diff --git a/src/de/flexiprovider/common/util/ASN1Tools.java b/src/de/flexiprovider/common/util/ASN1Tools.java new file mode 100644 index 00000000..cba7a856 --- /dev/null +++ b/src/de/flexiprovider/common/util/ASN1Tools.java @@ -0,0 +1,95 @@ +package de.flexiprovider.common.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1Integer; +import codec.asn1.ASN1Type; +import codec.asn1.DERDecoder; +import codec.asn1.DEREncoder; +import de.flexiprovider.common.math.FlexiBigInt; + +/** + * ASN.1 utility class. Used to translate between {@link FlexiBigInt} and + * {@link java.math.BigInteger} types. Provides DER encoding and decoding + * methods. + * + * @author Martin Döring + */ +public final class ASN1Tools { + + /** + * Default constructor (private). + */ + private ASN1Tools() { + // empty + } + + /** + * Create a new {@link ASN1Integer} from the given {@link FlexiBigInt} + * value. + * + * @param value + * the {@link FlexiBigInt} value + * @return a new {@link ASN1Integer} holding the {@link FlexiBigInt} value + */ + public static ASN1Integer createInteger(FlexiBigInt value) { + return new ASN1Integer(value.bigInt); + } + + /** + * Get the {@link FlexiBigInt} value from the given {@link ASN1Integer}. + * + * @param value + * the {@link ASN1Integer} + * @return the {@link FlexiBigInt} value stored in the {@link ASN1Integer} + */ + public static FlexiBigInt getFlexiBigInt(ASN1Integer value) { + return new FlexiBigInt(value.getBigInteger()); + } + + /** + * DER encode the given ASN.1 structure. + * + * @param type + * the ASN.1 structure + * @return the DER encoded ASN.1 structure + */ + public static byte[] derEncode(ASN1Type type) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DEREncoder encoder = new DEREncoder(baos); + type.encode(encoder); + byte[] result = baos.toByteArray(); + encoder.close(); + return result; + } catch (ASN1Exception e) { + throw new RuntimeException("ASN1Exception: " + e.getMessage()); + } catch (IOException e) { + throw new RuntimeException("IOException: " + e.getMessage()); + } + } + + /** + * Decode the given DER encoded ASN.1 structure. + * + * @param encoding + * the encoded ASN.1 structure + * @param type + * the type holding the decoding + * @throws IOException + * on decoding errors. + * @throws ASN1Exception + * on decoding errors. + */ + public static void derDecode(byte[] encoding, ASN1Type type) + throws ASN1Exception, IOException { + ByteArrayInputStream bais = new ByteArrayInputStream(encoding); + DERDecoder decoder = new DERDecoder(bais); + type.decode(decoder); + decoder.close(); + } + +} diff --git a/src/de/flexiprovider/common/util/BigEndianConversions.java b/src/de/flexiprovider/common/util/BigEndianConversions.java new file mode 100644 index 00000000..cbf9e672 --- /dev/null +++ b/src/de/flexiprovider/common/util/BigEndianConversions.java @@ -0,0 +1,304 @@ +package de.flexiprovider.common.util; + +import de.flexiprovider.common.math.IntegerFunctions; + +/** + * This is a utility class containing data type conversions using big-endian + * byte order. + * + * @see LittleEndianConversions + */ +public final class BigEndianConversions { + + /** + * Default constructor (private). + */ + private BigEndianConversions() { + // empty + } + + /** + * Convert an integer to an octet string of length 4 according to IEEE 1363, + * Section 5.5.3. + * + * @param x + * the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(int x) { + byte[] result = new byte[4]; + result[0] = (byte) (x >>> 24); + result[1] = (byte) (x >>> 16); + result[2] = (byte) (x >>> 8); + result[3] = (byte) x; + return result; + } + + /** + * Convert an integer to an octet string according to IEEE 1363, Section + * 5.5.3. Length checking is performed. + * + * @param x + * the integer to convert + * @param oLen + * the desired length of the octet string + * @return an octet string of length oLen representing the + * integer x, or null if the integer is + * negative + * @throws ArithmeticException + * if x can't be encoded into oLen + * octets. + */ + public static byte[] I2OSP(int x, int oLen) throws ArithmeticException { + if (x < 0) { + return null; + } + int octL = IntegerFunctions.ceilLog256(x); + if (octL > oLen) { + throw new ArithmeticException( + "Cannot encode given integer into specified number of octets."); + } + byte[] result = new byte[oLen]; + for (int i = oLen - 1; i >= oLen - octL; i--) { + result[i] = (byte) (x >>> (8 * (oLen - 1 - i))); + } + return result; + } + + /** + * Convert an integer to an octet string of length 4 according to IEEE 1363, + * Section 5.5.3. + * + * @param input + * the integer to convert + * @param output + * byte array holding the output + * @param outOff + * offset in output array where the result is stored + */ + public static void I2OSP(int input, byte[] output, int outOff) { + output[outOff++] = (byte) (input >>> 24); + output[outOff++] = (byte) (input >>> 16); + output[outOff++] = (byte) (input >>> 8); + output[outOff] = (byte) input; + } + + /** + * Convert an integer to an octet string of length 8 according to IEEE 1363, + * Section 5.5.3. + * + * @param input + * the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(long input) { + byte[] output = new byte[8]; + output[0] = (byte) (input >>> 56); + output[1] = (byte) (input >>> 48); + output[2] = (byte) (input >>> 40); + output[3] = (byte) (input >>> 32); + output[4] = (byte) (input >>> 24); + output[5] = (byte) (input >>> 16); + output[6] = (byte) (input >>> 8); + output[7] = (byte) input; + return output; + } + + /** + * Convert an integer to an octet string of length 8 according to IEEE 1363, + * Section 5.5.3. + * + * @param input + * the integer to convert + * @param output + * byte array holding the output + * @param outOff + * offset in output array where the result is stored + */ + public static void I2OSP(long input, byte[] output, int outOff) { + output[outOff++] = (byte) (input >>> 56); + output[outOff++] = (byte) (input >>> 48); + output[outOff++] = (byte) (input >>> 40); + output[outOff++] = (byte) (input >>> 32); + output[outOff++] = (byte) (input >>> 24); + output[outOff++] = (byte) (input >>> 16); + output[outOff++] = (byte) (input >>> 8); + output[outOff] = (byte) input; + } + + /** + * Convert an integer to an octet string of the specified length according + * to IEEE 1363, Section 5.5.3. No length checking is performed (i.e., if + * the integer cannot be encoded into length octets, it is + * truncated). + * + * @param input + * the integer to convert + * @param output + * byte array holding the output + * @param outOff + * offset in output array where the result is stored + * @param length + * the length of the encoding + */ + public static void I2OSP(int input, byte[] output, int outOff, int length) { + for (int i = length - 1; i >= 0; i--) { + output[outOff + i] = (byte) (input >>> (8 * (length - 1 - i))); + } + } + + /** + * Convert an octet string to an integer according to IEEE 1363, Section + * 5.5.3. + * + * @param input + * the byte array holding the octet string + * @return an integer representing the octet string input, or + * 0 if the represented integer is negative or too large + * or the byte array is empty + * @throws ArithmeticException + * if the length of the given octet string is larger than 4. + */ + public static int OS2IP(byte[] input) { + if (input.length > 4) { + throw new ArithmeticException("invalid input length"); + } + if (input.length == 0) { + return 0; + } + int result = 0; + for (int j = 0; j < input.length; j++) { + result |= (input[j] & 0xff) << (8 * (input.length - 1 - j)); + } + return result; + } + + /** + * Convert a byte array of length 4 beginning at offset into an + * integer. + * + * @param input + * the byte array + * @param inOff + * the offset into the byte array + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff) { + int result = (input[inOff++] & 0xff) << 24; + result |= (input[inOff++] & 0xff) << 16; + result |= (input[inOff++] & 0xff) << 8; + result |= input[inOff] & 0xff; + return result; + } + + /** + * Convert an octet string to an integer according to IEEE 1363, Section + * 5.5.3. + * + * @param input + * the byte array holding the octet string + * @param inOff + * the offset in the input byte array where the octet string + * starts + * @param inLen + * the length of the encoded integer + * @return an integer representing the octet string bytes, or + * 0 if the represented integer is negative or too large + * or the byte array is empty + */ + public static int OS2IP(byte[] input, int inOff, int inLen) { + if ((input.length == 0) || input.length < inOff + inLen - 1) { + return 0; + } + int result = 0; + for (int j = 0; j < inLen; j++) { + result |= (input[inOff + j] & 0xff) << (8 * (inLen - j - 1)); + } + return result; + } + + /** + * Convert a byte array of length 8 beginning at inOff into a + * long integer. + * + * @param input + * the byte array + * @param inOff + * the offset into the byte array + * @return the resulting long integer + */ + public static long OS2LIP(byte[] input, int inOff) { + long result = ((long) input[inOff++] & 0xff) << 56; + result |= ((long) input[inOff++] & 0xff) << 48; + result |= ((long) input[inOff++] & 0xff) << 40; + result |= ((long) input[inOff++] & 0xff) << 32; + result |= ((long) input[inOff++] & 0xff) << 24; + result |= (input[inOff++] & 0xff) << 16; + result |= (input[inOff++] & 0xff) << 8; + result |= input[inOff] & 0xff; + return result; + } + + /** + * Convert an int array into a byte array. + * + * @param input + * the int array + * @return the converted array + */ + public static byte[] toByteArray(final int[] input) { + byte[] result = new byte[input.length << 2]; + for (int i = 0; i < input.length; i++) { + I2OSP(input[i], result, i << 2); + } + return result; + } + + /** + * Convert an int array into a byte array of the specified length. No length + * checking is performed (i.e., if the last integer cannot be encoded into + * length % 4 octets, it is truncated). + * + * @param input + * the int array + * @param length + * the length of the converted array + * @return the converted array + */ + public static byte[] toByteArray(final int[] input, int length) { + final int intLen = input.length; + byte[] result = new byte[length]; + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) { + I2OSP(input[i], result, index); + } + I2OSP(input[intLen - 1], result, index, length - index); + return result; + } + + /** + * Convert a byte array into an int array. + * + * @param input + * the byte array + * @return the converted array + */ + public static int[] toIntArray(byte[] input) { + final int intLen = (input.length + 3) / 4; + final int lastLen = input.length & 0x03; + int[] result = new int[intLen]; + + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) { + result[i] = OS2IP(input, index); + } + if (lastLen != 0) { + result[intLen - 1] = OS2IP(input, index, lastLen); + } else { + result[intLen - 1] = OS2IP(input, index); + } + + return result; + } + +} diff --git a/src/de/flexiprovider/common/util/ByteUtils.java b/src/de/flexiprovider/common/util/ByteUtils.java new file mode 100644 index 00000000..0e6677a0 --- /dev/null +++ b/src/de/flexiprovider/common/util/ByteUtils.java @@ -0,0 +1,461 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.common.util; + +/** + * This class is a utility class for manipulating byte arrays. + */ +public final class ByteUtils { + + private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /** + * Default constructor (private) + */ + private ByteUtils() { + // empty + } + + /** + * Compare two byte arrays (perform null checks beforehand). + * + * @param left + * the first byte array + * @param right + * the second byte array + * @return the result of the comparison + */ + public static boolean equals(byte[] left, byte[] right) { + if (left == null) { + return right == null; + } + if (right == null) { + return false; + } + + if (left.length != right.length) { + return false; + } + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) { + result &= left[i] == right[i]; + } + return result; + } + + /** + * Compare two two-dimensional byte arrays. No null checks are performed. + * + * @param left + * the first byte array + * @param right + * the second byte array + * @return the result of the comparison + */ + public static boolean equals(byte[][] left, byte[][] right) { + if (left.length != right.length) { + return false; + } + + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) { + result &= ByteUtils.equals(left[i], right[i]); + } + + return result; + } + + /** + * Compare two three-dimensional byte arrays. No null checks are performed. + * + * @param left + * the first byte array + * @param right + * the second byte array + * @return the result of the comparison + */ + public static boolean equals(byte[][][] left, byte[][][] right) { + if (left.length != right.length) { + return false; + } + + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) { + if (left[i].length != right[i].length) { + return false; + } + for (int j = left[i].length - 1; j >= 0; j--) { + result &= ByteUtils.equals(left[i][j], right[i][j]); + } + } + + return result; + } + + /** + * Computes a hashcode based on the contents of a one-dimensional byte array + * rather than its identity. + * + * @param array + * the array to compute the hashcode of + * @return the hashcode + */ + public static int deepHashCode(byte[] array) { + int result = 1; + for (int i = 0; i < array.length; i++) { + result = 31 * result + array[i]; + } + return result; + } + + /** + * Computes a hashcode based on the contents of a two-dimensional byte array + * rather than its identity. + * + * @param array + * the array to compute the hashcode of + * @return the hashcode + */ + public static int deepHashCode(byte[][] array) { + int result = 1; + for (int i = 0; i < array.length; i++) { + result = 31 * result + deepHashCode(array[i]); + } + return result; + } + + /** + * Computes a hashcode based on the contents of a three-dimensional byte + * array rather than its identity. + * + * @param array + * the array to compute the hashcode of + * @return the hashcode + */ + public static int deepHashCode(byte[][][] array) { + int result = 1; + for (int i = 0; i < array.length; i++) { + result = 31 * result + deepHashCode(array[i]); + } + return result; + } + + + /** + * Return a clone of the given byte array (performs null check beforehand). + * + * @param array + * the array to clone + * @return the clone of the given array, or null if the array is + * null + */ + public static byte[] clone(byte[] array) { + if (array == null) { + return null; + } + byte[] result = new byte[array.length]; + System.arraycopy(array, 0, result, 0, array.length); + return result; + } + + /** + * Convert a string containing hexadecimal characters to a byte-array. + * + * @param s + * a hex string + * @return a byte array with the corresponding value + */ + public static byte[] fromHexString(String s) { + char[] rawChars = s.toUpperCase().toCharArray(); + + int hexChars = 0; + for (int i = 0; i < rawChars.length; i++) { + if ((rawChars[i] >= '0' && rawChars[i] <= '9') + || (rawChars[i] >= 'A' && rawChars[i] <= 'F')) { + hexChars++; + } + } + + byte[] byteString = new byte[(hexChars + 1) >> 1]; + + int pos = hexChars & 1; + + for (int i = 0; i < rawChars.length; i++) { + if (rawChars[i] >= '0' && rawChars[i] <= '9') { + byteString[pos >> 1] <<= 4; + byteString[pos >> 1] |= rawChars[i] - '0'; + } else if (rawChars[i] >= 'A' && rawChars[i] <= 'F') { + byteString[pos >> 1] <<= 4; + byteString[pos >> 1] |= rawChars[i] - 'A' + 10; + } else { + continue; + } + pos++; + } + + return byteString; + } + + /** + * Convert a byte array to the corresponding hexstring. + * + * @param input + * the byte array to be converted + * @return the corresponding hexstring + */ + public static String toHexString(byte[] input) { + String result = ""; + for (int i = 0; i < input.length; i++) { + result += HEX_CHARS[(input[i] >>> 4) & 0x0f]; + result += HEX_CHARS[(input[i]) & 0x0f]; + } + return result; + } + + /** + * Convert a byte array to the corresponding hex string. + * + * @param input + * the byte array to be converted + * @param prefix + * the prefix to put at the beginning of the hex string + * @param seperator + * a separator string + * @return the corresponding hex string + */ + public static String toHexString(byte[] input, String prefix, + String seperator) { + String result = new String(prefix); + for (int i = 0; i < input.length; i++) { + result += HEX_CHARS[(input[i] >>> 4) & 0x0f]; + result += HEX_CHARS[(input[i]) & 0x0f]; + if (i < input.length - 1) { + result += seperator; + } + } + return result; + } + + /** + * Convert a byte array to the corresponding bit string. + * + * @param input + * the byte array to be converted + * @return the corresponding bit string + */ + public static String toBinaryString(byte[] input) { + String result = ""; + int i; + for (i = 0; i < input.length; i++) { + int e = input[i]; + for (int ii = 0; ii < 8; ii++) { + int b = (e >>> ii) & 1; + result += b; + } + if (i != input.length - 1) { + result += " "; + } + } + return result; + } + + /** + * Convert a binary String into a byte array. The last byte of the array is + * filled with zeros if necessary. + * + * @param s + * the binary String to be converted + * @return the byte array + * + */ + public static byte[] fromBinaryString(String s) { + int length = (int) Math.ceil((double) s.length() / 8.0); + byte[] b = new byte[length]; + + for (int j = 0; j < length; j++) { + String sub; + if (j * 8 + 8 < s.length()) + sub = s.substring(j * 8, j * 8 + 8); + else + sub = s.substring(j * 8); + + byte tmp = 0; + for (int i = 0; i < sub.length(); i++) { + if (sub.charAt(i) == '1') { + tmp += 1 << i; + } + } + b[j] = tmp; + } + return b; + } + + /** + * Compute the bitwise XOR of two arrays of bytes. The arrays have to be of + * same length. No length checking is performed. + * + * @param x1 + * the first array + * @param x2 + * the second array + * @return x1 XOR x2 + */ + public static byte[] xor(byte[] x1, byte[] x2) { + byte[] out = new byte[x1.length]; + + for (int i = x1.length - 1; i >= 0; i--) { + out[i] = (byte) (x1[i] ^ x2[i]); + } + return out; + } + + /** + * Concatenate two byte arrays. No null checks are performed. + * + * @param x1 + * the first array + * @param x2 + * the second array + * @return (x2||x1) (little-endian order, i.e. x1 is at lower memory + * addresses) + */ + public static byte[] concatenate(byte[] x1, byte[] x2) { + byte[] result = new byte[x1.length + x2.length]; + + System.arraycopy(x1, 0, result, 0, x1.length); + System.arraycopy(x2, 0, result, x1.length, x2.length); + + return result; + } + + /** + * Convert a 2-dimensional byte array into a 1-dimensional byte array by + * concatenating all entries. + * + * @param array + * a 2-dimensional byte array + * @return the concatenated input array + */ + public static byte[] concatenate(byte[][] array) { + int rowLength = array[0].length; + byte[] result = new byte[array.length * rowLength]; + int index = 0; + for (int i = 0; i < array.length; i++) { + System.arraycopy(array[i], 0, result, index, rowLength); + index += rowLength; + } + return result; + } + + /** + * Split a byte array input into two arrays at index, + * i.e. the first array will have the lower index bytes, the + * second one the higher input.length - index bytes. + * + * @param input + * the byte array to be split + * @param index + * the index where the byte array is split + * @return the splitted input array as an array of two byte arrays + * @throws ArrayIndexOutOfBoundsException + * if index is out of bounds + */ + public static byte[][] split(byte[] input, int index) + throws ArrayIndexOutOfBoundsException { + if (index > input.length) { + throw new ArrayIndexOutOfBoundsException(); + } + byte[][] result = new byte[2][]; + result[0] = new byte[index]; + result[1] = new byte[input.length - index]; + System.arraycopy(input, 0, result[0], 0, index); + System.arraycopy(input, index, result[1], 0, input.length - index); + return result; + } + + /** + * Split a byte array input into n arrays of length + * length. + * + * @param input + * the byte array to be split + * @param n + * the number of resulting arrays + * @param length + * the length of the resulting arrays + * + * @return the splitted input array as an array of multiple byte arrays + * @throws ArrayIndexOutOfBoundsException + * if n*length is out of bounds + */ + public static byte[][] split(byte[] input, int n, int length) + throws ArrayIndexOutOfBoundsException { + if (n*length > input.length) { + throw new ArrayIndexOutOfBoundsException(); + } + byte[][] result = new byte[n][length]; + + for(int i=0;iinput, ranging from start + * (inclusively) to end (exclusively) + */ + public static byte[] subArray(byte[] input, int start, int end) { + byte[] result = new byte[end - start]; + System.arraycopy(input, start, result, 0, end - start); + return result; + } + + /** + * Generate a subarray of a given byte array. + * + * @param input + * the input byte array + * @param start + * the start index + * @return a subarray of input, ranging from start to + * the end of the array + */ + public static byte[] subArray(byte[] input, int start) { + return subArray(input, start, input.length); + } + + /** + * Rewrite a byte array as a char array + * + * @param input - + * the byte array + * @return char array + */ + public static char[] toCharArray(byte[] input) { + char[] result = new char[input.length]; + for (int i = 0; i < input.length; i++) { + result[i] = (char) input[i]; + } + return result; + } + +} diff --git a/src/de/flexiprovider/common/util/DefaultPRNG.java b/src/de/flexiprovider/common/util/DefaultPRNG.java new file mode 100644 index 00000000..8d1ded6f --- /dev/null +++ b/src/de/flexiprovider/common/util/DefaultPRNG.java @@ -0,0 +1,30 @@ +package de.flexiprovider.common.util; + +import de.flexiprovider.api.SecureRandom; + +/** + * FlexiAPI wrapper for the default PRNG obtained via JCA. + * + * @author Martin Döring + */ +public class DefaultPRNG extends SecureRandom { + + java.security.SecureRandom javaRand; + + public DefaultPRNG() { + javaRand = new java.security.SecureRandom(); + } + + public byte[] generateSeed(int numBytes) { + return javaRand.generateSeed(numBytes); + } + + public void nextBytes(byte[] bytes) { + javaRand.nextBytes(bytes); + } + + public void setSeed(byte[] seed) { + javaRand.setSeed(seed); + } + +} diff --git a/src/de/flexiprovider/common/util/FlexiBigIntUtils.java b/src/de/flexiprovider/common/util/FlexiBigIntUtils.java new file mode 100644 index 00000000..c9c3a807 --- /dev/null +++ b/src/de/flexiprovider/common/util/FlexiBigIntUtils.java @@ -0,0 +1,126 @@ +package de.flexiprovider.common.util; + +import de.flexiprovider.common.math.FlexiBigInt; + +public final class FlexiBigIntUtils { + + /** + * Default constructor (private). + */ + private FlexiBigIntUtils() { + // empty + } + + /** + * Checks if two FlexiBigInt arrays contain the same entries + * + * @param a + * first FlexiBigInt array + * @param b + * second FlexiBigInt array + * @return true or false + */ + public static boolean equals(FlexiBigInt[] a, FlexiBigInt[] b) { + int flag = 0; + + if (a.length != b.length) { + return false; + } + for (int i = 0; i < a.length; i++) { + // avoid branches here! + // problem: compareTo on FlexiBigInts is not + // guaranteed constant-time! + flag |= a[i].compareTo(b[i]); + } + return flag == 0; + } + + /** + * Fill the given FlexiBigInt array with the given value. + * + * @param array + * the array + * @param value + * the value + */ + public static void fill(FlexiBigInt[] array, FlexiBigInt value) { + for (int i = array.length - 1; i >= 0; i--) { + array[i] = value; + } + } + + /** + * Generates a subarray of a given FlexiBigInt array. + * + * @param input - + * the input FlexiBigInt array + * @param start - + * the start index + * @param end - + * the end index + * @return a subarray of input, ranging from start to + * end + */ + public static FlexiBigInt[] subArray(FlexiBigInt[] input, int start, int end) { + FlexiBigInt[] result = new FlexiBigInt[end - start]; + System.arraycopy(input, start, result, 0, end - start); + return result; + } + + /** + * Converts a FlexiBigInt array into an integer array + * + * @param input - + * the FlexiBigInt array + * @return the integer array + */ + public static int[] toIntArray(FlexiBigInt[] input) { + int[] result = new int[input.length]; + for (int i = 0; i < input.length; i++) { + result[i] = input[i].intValue(); + } + return result; + } + + /** + * Converts a FlexiBigInt array into an integer array, reducing all + * FlexiBigInts mod q. + * + * @param q - + * the modulus + * @param input - + * the FlexiBigInt array + * @return the integer array + */ + public static int[] toIntArrayModQ(int q, FlexiBigInt[] input) { + FlexiBigInt bq = FlexiBigInt.valueOf(q); + int[] result = new int[input.length]; + for (int i = 0; i < input.length; i++) { + result[i] = input[i].mod(bq).intValue(); + } + return result; + } + + /** + * Return the value of big as a byte array. Although FlexiBigInt + * has such a method, it uses an extra bit to indicate the sign of the + * number. For elliptic curve cryptography, the numbers usually are + * positive. Thus, this helper method returns a byte array of minimal + * length, ignoring the sign of the number. + * + * @param value + * the FlexiBigInt value to be converted to a byte + * array + * @return the value big as byte array + */ + public static byte[] toMinimalByteArray(FlexiBigInt value) { + byte[] valBytes = value.toByteArray(); + if ((valBytes.length == 1) || (value.bitLength() & 0x07) != 0) { + return valBytes; + } + byte[] result = new byte[value.bitLength() >> 3]; + System.arraycopy(valBytes, 1, result, 0, result.length); + return result; + } + +} diff --git a/src/de/flexiprovider/common/util/IntUtils.java b/src/de/flexiprovider/common/util/IntUtils.java new file mode 100644 index 00000000..ace87104 --- /dev/null +++ b/src/de/flexiprovider/common/util/IntUtils.java @@ -0,0 +1,208 @@ +package de.flexiprovider.common.util; + +import de.flexiprovider.common.math.FlexiBigInt; + +public final class IntUtils { + + /** + * Default constructor (private). + */ + private IntUtils() { + // empty + } + + /** + * Compare two int arrays. No null checks are performed. + * + * @param left + * the first int array + * @param right + * the second int array + * @return the result of the comparison + */ + public static boolean equals(int[] left, int[] right) { + if (left.length != right.length) { + return false; + } + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) { + result &= left[i] == right[i]; + } + return result; + } + + /** + * Return a clone of the given int array. No null checks are performed. + * + * @param array + * the array to clone + * @return the clone of the given array + */ + public static int[] clone(int[] array) { + int[] result = new int[array.length]; + System.arraycopy(array, 0, result, 0, array.length); + return result; + } + + /** + * Return a clone of the given long array. No null checks are performed. + * + * @param array + * the array to clone + * @return the clone of the given array + */ + public static long[] clone(long[] array) { + long[] result = new long[array.length]; + System.arraycopy(array, 0, result, 0, array.length); + return result; + } + + /** + * Fill the given int array with the given value. + * + * @param array + * the array + * @param value + * the value + */ + public static void fill(int[] array, int value) { + for (int i = array.length - 1; i >= 0; i--) { + array[i] = value; + } + } + + /** + * Sorts this array of integers according to the Quicksort algorithm. After + * calling this method this array is sorted in ascending order with the + * smallest integer taking position 0 in the array. + *

+ * + * This implementation is based on the quicksort algorithm as described in + * Data Structures In Java by Thomas A. Standish, Chapter 10, + * ISBN 0-201-30564-X. + * + * @param source + * the array of integers that needs to be sorted. + * + */ + public static void quicksort(int[] source) { + quicksort(source, 0, source.length - 1); + } + + /** + * Sort a subarray of a source array. The subarray is specified by its start + * and end index. + * + * @param source + * the int array to be sorted + * @param left + * the start index of the subarray + * @param right + * the end index of the subarray + */ + public static void quicksort(int[] source, int left, int right) { + if (right > left) { + int index = partition(source, left, right, right); + quicksort(source, left, index - 1); + quicksort(source, index + 1, right); + } + } + + /** + * Split a subarray of a source array into two partitions. The left + * partition contains elements that have value less than or equal to the + * pivot element, the right partition contains the elements that have larger + * value. + * + * @param source + * the int array whose subarray will be splitted + * @param left + * the start position of the subarray + * @param right + * the end position of the subarray + * @param pivotIndex + * the index of the pivot element inside the array + * @return the new index of the pivot element inside the array + */ + private static int partition(int[] source, int left, int right, + int pivotIndex) { + + int pivot = source[pivotIndex]; + source[pivotIndex] = source[right]; + source[right] = pivot; + + int index = left; + + for (int i = left; i < right; i++) { + if (source[i] <= pivot) { + int tmp = source[index]; + source[index] = source[i]; + source[i] = tmp; + index++; + } + } + + int tmp = source[index]; + source[index] = source[right]; + source[right] = tmp; + + return index; + } + + /** + * Generates a subarray of a given int array. + * + * @param input - + * the input int array + * @param start - + * the start index + * @param end - + * the end index + * @return a subarray of input, ranging from start to + * end + */ + public static int[] subArray(final int[] input, final int start, + final int end) { + int[] result = new int[end - start]; + System.arraycopy(input, start, result, 0, end - start); + return result; + } + + /** + * Convert an int array to a {@link FlexiBigInt} array. + * + * @param input + * the int array + * @return the {@link FlexiBigInt} array + */ + public static FlexiBigInt[] toFlexiBigIntArray(int[] input) { + FlexiBigInt[] result = new FlexiBigInt[input.length]; + for (int i = 0; i < input.length; i++) { + result[i] = FlexiBigInt.valueOf(input[i]); + } + return result; + } + + /** + * @param input + * an int array + * @return a human readable form of the given int array + */ + public static String toString(int[] input) { + String result = ""; + for (int i = 0; i < input.length; i++) { + result += input[i] + " "; + } + return result; + } + + /** + * @param input + * an int arary + * @return the int array as hex string + */ + public static String toHexString(int[] input) { + return ByteUtils.toHexString(BigEndianConversions.toByteArray(input)); + } + +} diff --git a/src/de/flexiprovider/common/util/JavaSecureRandomWrapper.java b/src/de/flexiprovider/common/util/JavaSecureRandomWrapper.java new file mode 100644 index 00000000..35f1f13c --- /dev/null +++ b/src/de/flexiprovider/common/util/JavaSecureRandomWrapper.java @@ -0,0 +1,25 @@ +package de.flexiprovider.common.util; + +import de.flexiprovider.api.SecureRandom; + +public class JavaSecureRandomWrapper extends SecureRandom { + + private java.security.SecureRandom javaRand; + + public JavaSecureRandomWrapper(java.security.SecureRandom javaRand) { + this.javaRand = javaRand; + } + + public byte[] generateSeed(int numBytes) { + return javaRand.generateSeed(numBytes); + } + + public void nextBytes(byte[] bytes) { + javaRand.nextBytes(bytes); + } + + public void setSeed(byte[] seed) { + javaRand.setSeed(seed); + } + +} diff --git a/src/de/flexiprovider/common/util/LittleEndianConversions.java b/src/de/flexiprovider/common/util/LittleEndianConversions.java new file mode 100644 index 00000000..b676ab32 --- /dev/null +++ b/src/de/flexiprovider/common/util/LittleEndianConversions.java @@ -0,0 +1,327 @@ +package de.flexiprovider.common.util; + +import de.flexiprovider.common.math.MathFunctions; + +/** + * This is a utility class containing data type conversions using little-endian + * byte order. + * + * @see BigEndianConversions + */ +public final class LittleEndianConversions { + + /** + * Default constructor (private). + */ + private LittleEndianConversions() { + // empty + } + + /** + * Convert an octet string of length 4 to an integer. No length checking is + * performed. + * + * @param input + * the byte array holding the octet string + * @return an integer representing the octet string input + * @throws ArithmeticException + * if the length of the given octet string is larger than 4. + */ + public static int OS2IP(byte[] input) { + return ((input[0] & 0xff)) | ((input[1] & 0xff) << 8) + | ((input[2] & 0xff) << 16) | ((input[3] & 0xff)) << 24; + } + + /** + * Convert an byte array of length 4 beginning at offset into an + * integer. + * + * @param input + * the byte array + * @param inOff + * the offset into the byte array + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff) { + int result = input[inOff++] & 0xff; + result |= (input[inOff++] & 0xff) << 8; + result |= (input[inOff++] & 0xff) << 16; + result |= (input[inOff] & 0xff) << 24; + return result; + } + + /** + * Convert a byte array of the given length beginning at offset + * into an integer. + * + * @param input + * the byte array + * @param inOff + * the offset into the byte array + * @param inLen + * the length of the encoding + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff, int inLen) { + int result = 0; + for (int i = inLen - 1; i >= 0; i--) { + result |= (input[inOff + i] & 0xff) << (8 * i); + } + return result; + } + + /** + * Convert a byte array of length 8 beginning at inOff into a + * long integer. + * + * @param input + * the byte array + * @param inOff + * the offset into the byte array + * @return the resulting long integer + */ + public static long OS2LIP(byte[] input, int inOff) { + long result = input[inOff++] & 0xff; + result |= (input[inOff++] & 0xff) << 8; + result |= (input[inOff++] & 0xff) << 16; + result |= ((long) input[inOff++] & 0xff) << 24; + result |= ((long) input[inOff++] & 0xff) << 32; + result |= ((long) input[inOff++] & 0xff) << 40; + result |= ((long) input[inOff++] & 0xff) << 48; + result |= ((long) input[inOff++] & 0xff) << 56; + return result; + } + + /** + * Convert an integer to an octet string of length 4. + * + * @param x + * the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(int x) { + byte[] result = new byte[4]; + result[0] = (byte) x; + result[1] = (byte) (x >>> 8); + result[2] = (byte) (x >>> 16); + result[3] = (byte) (x >>> 24); + return result; + } + + /** + * Convert an integer into a byte array beginning at the specified offset. + * + * @param value + * the integer to convert + * @param output + * the byte array to hold the result + * @param outOff + * the integer offset into the byte array + */ + public static void I2OSP(int value, byte[] output, int outOff) { + output[outOff++] = (byte) value; + output[outOff++] = (byte) (value >>> 8); + output[outOff++] = (byte) (value >>> 16); + output[outOff++] = (byte) (value >>> 24); + } + + /** + * Convert an integer to a byte array beginning at the specified offset. No + * length checking is performed (i.e., if the integer cannot be encoded with + * length octets, it is truncated). + * + * @param value + * the integer to convert + * @param output + * the byte array to hold the result + * @param outOff + * the integer offset into the byte array + * @param outLen + * the length of the encoding + */ + public static void I2OSP(int value, byte[] output, int outOff, int outLen) { + for (int i = outLen - 1; i >= 0; i--) { + output[outOff + i] = (byte) (value >>> (8 * i)); + } + } + + /** + * Convert an integer to a byte array of length 8. + * + * @param input + * the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(long input) { + byte[] output = new byte[8]; + output[0] = (byte) input; + output[1] = (byte) (input >>> 8); + output[2] = (byte) (input >>> 16); + output[3] = (byte) (input >>> 24); + output[4] = (byte) (input >>> 32); + output[5] = (byte) (input >>> 40); + output[6] = (byte) (input >>> 48); + output[7] = (byte) (input >>> 56); + return output; + } + + /** + * Convert an integer to a byte array of length 8. + * + * @param input + * the integer to convert + * @param output + * byte array holding the output + * @param outOff + * offset in output array where the result is stored + */ + public static void I2OSP(long input, byte[] output, int outOff) { + output[outOff++] = (byte) input; + output[outOff++] = (byte) (input >>> 8); + output[outOff++] = (byte) (input >>> 16); + output[outOff++] = (byte) (input >>> 24); + output[outOff++] = (byte) (input >>> 32); + output[outOff++] = (byte) (input >>> 40); + output[outOff++] = (byte) (input >>> 48); + output[outOff] = (byte) (input >>> 56); + } + + /** + * Convert an int array to a byte array of the specified length. No length + * checking is performed (i.e., if the last integer cannot be encoded with + * length % 4 octets, it is truncated). + * + * @param input + * the int array + * @param outLen + * the length of the converted array + * @return the converted array + */ + public static byte[] toByteArray(int[] input, int outLen) { + int intLen = input.length; + byte[] result = new byte[outLen]; + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) { + I2OSP(input[i], result, index); + } + I2OSP(input[intLen - 1], result, index, outLen - index); + return result; + } + + /** + * Convert a byte array to an int array. + * + * @param input + * the byte array + * @return the converted array + */ + public static int[] toIntArray(byte[] input) { + int intLen = (input.length + 3) / 4; + int lastLen = input.length & 0x03; + int[] result = new int[intLen]; + + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) { + result[i] = OS2IP(input, index); + } + if (lastLen != 0) { + result[intLen - 1] = OS2IP(input, index, lastLen); + } else { + result[intLen - 1] = OS2IP(input, index); + } + + return result; + } + + + /** + * Converts a byte array of length <= 8 to a long value. + * @param b + * the byte array + * @return the long value represented by the byte array + */ + public static long toLong(byte[] b){ + if(b==null){ + return 0; + } + if(b.length>8){ + throw new ArithmeticException("Conversion to long value not possible: array to long."); + } + byte[] temp=b; + if(b.length<8){ + temp= new byte[8]; + System.arraycopy(b, 0, temp, 0, b.length); + } + long result=OS2LIP(temp, 0); + return result; + } + + /** + * Converts a byte array of length <= 4 to an int value. + * @param b + * the byte array + * @return the int value represented by the byte array + */ + public static int toInt(byte[] b){ + if(b==null){ + return 0; + } + if(b.length>4){ + throw new ArithmeticException("Conversion to int value not possible: array to long."); + } + byte[] temp=b; + if(b.length<4){ + temp= new byte[4]; + System.arraycopy(b, 0, temp, 0, b.length); + } + int result=OS2IP(temp); + return result; + } + + /** + * Converts a long value to a byte array of smallest possible length. + * @param l + * the long value + * @return the byte array representation of l + */ + public static byte[] toVariableByteArray(long l){ + if(l==0){ + return null; + } + byte[] temp=I2OSP(l); + int bytes=8; + if(l>0) + bytes=(int)Math.ceil((MathFunctions.log(l)+1)/8); + if(bytes<8){ + byte[] result=new byte[bytes]; + System.arraycopy(temp, 0, result, 0, bytes); + return result; + }else{ + return temp; + } + } + + /** + * Converts an int value to a byte array of smallest possible length. + * @param l + * the long value + * @return the byte array representation of l + */ + public static byte[] toVariableByteArray(int i){ + if(i==0){ + return null; + } + byte[] temp=I2OSP(i); + int bytes=4; + if(i>0) + bytes=(int)Math.ceil((MathFunctions.log(i)+1)/8); + if(bytes<4){ + byte[] result=new byte[bytes]; + System.arraycopy(temp, 0, result, 0, bytes); + return result; + }else{ + return temp; + } + } + +} diff --git a/src/de/flexiprovider/common/util/StringUtils.java b/src/de/flexiprovider/common/util/StringUtils.java new file mode 100644 index 00000000..9dee426e --- /dev/null +++ b/src/de/flexiprovider/common/util/StringUtils.java @@ -0,0 +1,58 @@ +package de.flexiprovider.common.util; + +/** + * This class is a utility class for manipulating strings. + */ +public final class StringUtils { + + /** + * Default constructor (private) + */ + private StringUtils() { + // empty + } + + /** + * Strip whitespace off a string, returning a new string. + * + * @param str + * the string + * @return the filtered string + */ + public static String filterSpaces(String str) { + StringBuffer buf = new StringBuffer(str); + + for (int i = 0; i < buf.length(); i++) { + if (buf.charAt(i) == ' ') { + buf = buf.deleteCharAt(i); + i--; + } + } + return buf.toString(); + } + + // **************************************************** + // Array utilities + // **************************************************** + + /** + * Compare two String arrays. No null checks are performed. + * + * @param left + * the first String array + * @param right + * the second String array + * @return the result of the comparison + */ + public static boolean equals(String[] left, String[] right) { + if (left.length != right.length) { + return false; + } + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) { + result &= left[i].equals(right[i]); + } + return result; + } + +} diff --git a/src/de/flexiprovider/core/CoreRegistry.java b/src/de/flexiprovider/core/CoreRegistry.java new file mode 100644 index 00000000..1045c4c7 --- /dev/null +++ b/src/de/flexiprovider/core/CoreRegistry.java @@ -0,0 +1,41 @@ +package de.flexiprovider.core; + +import de.flexiprovider.api.Registry; +import de.flexiprovider.core.md.SHA1; +import de.flexiprovider.core.md.SHA224; +import de.flexiprovider.core.md.SHA256; +import de.flexiprovider.core.md.SHA384; +import de.flexiprovider.core.md.SHA512; + +/** + * Register all algorithms of the core package. + */ +public abstract class CoreRegistry extends Registry { + + // flag indicating if algorithms already have been registered + private static boolean registered = false; + + /** + * Register all algorithms of the core package. + */ + public static void registerAlgorithms() { + if (!registered) { + registerSHAfamily(); + registered = true; + } + } + + private static void registerSHAfamily() { + add(MESSAGE_DIGEST, SHA1.class, new String[] { SHA1.ALG_NAME, + SHA1.ALG_NAME2, SHA1.OID }); + add(MESSAGE_DIGEST, SHA224.class, new String[] { SHA224.ALG_NAME, + SHA224.OID }); + add(MESSAGE_DIGEST, SHA256.class, new String[] { SHA256.ALG_NAME, + SHA256.OID }); + add(MESSAGE_DIGEST, SHA384.class, new String[] { SHA384.ALG_NAME, + SHA384.OID }); + add(MESSAGE_DIGEST, SHA512.class, new String[] { SHA512.ALG_NAME, + SHA512.OID }); + } + +} diff --git a/src/de/flexiprovider/core/md/SHA1.java b/src/de/flexiprovider/core/md/SHA1.java new file mode 100644 index 00000000..01b130e6 --- /dev/null +++ b/src/de/flexiprovider/core/md/SHA1.java @@ -0,0 +1,410 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.core.md; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.common.util.BigEndianConversions; + +/** + * SHA1 extends java.security.MessageDigestSpi A class that implements the NIST + * Secure Hash Algorithm - version 1. The Algorithm was implemented according to + * Standard FIPS PUB 180-1. + * + * @author Torsten Ehli + * @author Sylvain Franke + * @author Ralf-Philipp Weinmann + */ +public final class SHA1 extends MessageDigest { + + /** + * The algorithm name. + */ + public static final String ALG_NAME = "SHA1"; + + /** + * An alternative algorithm name. + */ + public static final String ALG_NAME2 = "SHA"; + + /** + * The OID of SHA1 (defined by IEEE P1363). + */ + public static final String OID = "1.3.14.3.2.26"; + + // array to buffer the input before attaining the blocksize that can be + // hashed + private byte[] buffer; + + // counter for the bytes processed thus far + private long count; + + // h0-h5 contain the digest after the original message has been processed + private int h0, h1, h2, h3, h4; + + // array w is a temporary buffer used while computing any block + private int[] w; + + // some constants are used while processing the digest. You might refer to + // them as "The magic". + + private static final int const1 = 0x5a827999; + + private static final int const2 = 0x6ed9eba1; + + private static final int const3 = 0x8f1bbcdc; + + private static final int const4 = 0xca62c1d6; + + // length of the resulting message digest in bytes + private static final int SHA1_DIGEST_LENGTH = 20; + + /** + * Constructor. Create and initialize the arrays needed for computing the + * digest. + */ + public SHA1() { + w = new int[80]; + buffer = new byte[64]; + reset(); + } + + /** + * This function processBlock contains the actual SHA1 Algorithm, which is + * coded in accordance to the definition of the FIPS PUB 180-1 Standard. + */ + private synchronized void processBlock() { + int a, b, c, d, e; + int i; + int register; + + /* step a */ + w[0] = BigEndianConversions.OS2IP(buffer, 0); + w[1] = BigEndianConversions.OS2IP(buffer, 4); + w[2] = BigEndianConversions.OS2IP(buffer, 8); + w[3] = BigEndianConversions.OS2IP(buffer, 12); + w[4] = BigEndianConversions.OS2IP(buffer, 16); + w[5] = BigEndianConversions.OS2IP(buffer, 20); + w[6] = BigEndianConversions.OS2IP(buffer, 24); + w[7] = BigEndianConversions.OS2IP(buffer, 28); + w[8] = BigEndianConversions.OS2IP(buffer, 32); + w[9] = BigEndianConversions.OS2IP(buffer, 36); + w[10] = BigEndianConversions.OS2IP(buffer, 40); + w[11] = BigEndianConversions.OS2IP(buffer, 44); + w[12] = BigEndianConversions.OS2IP(buffer, 48); + w[13] = BigEndianConversions.OS2IP(buffer, 52); + w[14] = BigEndianConversions.OS2IP(buffer, 56); + w[15] = BigEndianConversions.OS2IP(buffer, 60); + + /* step b */ + for (i = 16; i < 80; i++) { + register = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]; + // circular left shift by one bit + w[i] = (register << 1) | (register >>> 31); + } + + /* step c */ + a = h0; + b = h1; + c = h2; + d = h3; + e = h4; + + /* step d */ + + e += const1 + (a << 5 | a >>> 27) + (b & c | ~b & d) + w[0]; + b = b << 30 | b >>> 2; + d += const1 + (e << 5 | e >>> 27) + (a & b | ~a & c) + w[1]; + a = a << 30 | a >>> 2; + c += const1 + (d << 5 | d >>> 27) + (e & a | ~e & b) + w[2]; + e = e << 30 | e >>> 2; + b += const1 + (c << 5 | c >>> 27) + (d & e | ~d & a) + w[3]; + d = d << 30 | d >>> 2; + a += const1 + (b << 5 | b >>> 27) + (c & d | ~c & e) + w[4]; + c = c << 30 | c >>> 2; + e += const1 + (a << 5 | a >>> 27) + (b & c | ~b & d) + w[5]; + b = b << 30 | b >>> 2; + d += const1 + (e << 5 | e >>> 27) + (a & b | ~a & c) + w[6]; + a = a << 30 | a >>> 2; + c += const1 + (d << 5 | d >>> 27) + (e & a | ~e & b) + w[7]; + e = e << 30 | e >>> 2; + b += const1 + (c << 5 | c >>> 27) + (d & e | ~d & a) + w[8]; + d = d << 30 | d >>> 2; + a += const1 + (b << 5 | b >>> 27) + (c & d | ~c & e) + w[9]; + c = c << 30 | c >>> 2; + e += const1 + (a << 5 | a >>> 27) + (b & c | ~b & d) + w[10]; + b = b << 30 | b >>> 2; + d += const1 + (e << 5 | e >>> 27) + (a & b | ~a & c) + w[11]; + a = a << 30 | a >>> 2; + c += const1 + (d << 5 | d >>> 27) + (e & a | ~e & b) + w[12]; + e = e << 30 | e >>> 2; + b += const1 + (c << 5 | c >>> 27) + (d & e | ~d & a) + w[13]; + d = d << 30 | d >>> 2; + a += const1 + (b << 5 | b >>> 27) + (c & d | ~c & e) + w[14]; + c = c << 30 | c >>> 2; + e += const1 + (a << 5 | a >>> 27) + (b & c | ~b & d) + w[15]; + b = b << 30 | b >>> 2; + d += const1 + (e << 5 | e >>> 27) + (a & b | ~a & c) + w[16]; + a = a << 30 | a >>> 2; + c += const1 + (d << 5 | d >>> 27) + (e & a | ~e & b) + w[17]; + e = e << 30 | e >>> 2; + b += const1 + (c << 5 | c >>> 27) + (d & e | ~d & a) + w[18]; + d = d << 30 | d >>> 2; + a += const1 + (b << 5 | b >>> 27) + (c & d | ~c & e) + w[19]; + c = c << 30 | c >>> 2; + e += const2 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[20]; + b = b << 30 | b >>> 2; + d += const2 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[21]; + a = a << 30 | a >>> 2; + c += const2 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[22]; + e = e << 30 | e >>> 2; + b += const2 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[23]; + d = d << 30 | d >>> 2; + a += const2 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[24]; + c = c << 30 | c >>> 2; + e += const2 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[25]; + b = b << 30 | b >>> 2; + d += const2 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[26]; + a = a << 30 | a >>> 2; + c += const2 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[27]; + e = e << 30 | e >>> 2; + b += const2 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[28]; + d = d << 30 | d >>> 2; + a += const2 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[29]; + c = c << 30 | c >>> 2; + e += const2 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[30]; + b = b << 30 | b >>> 2; + d += const2 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[31]; + a = a << 30 | a >>> 2; + c += const2 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[32]; + e = e << 30 | e >>> 2; + b += const2 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[33]; + d = d << 30 | d >>> 2; + a += const2 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[34]; + c = c << 30 | c >>> 2; + e += const2 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[35]; + b = b << 30 | b >>> 2; + d += const2 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[36]; + a = a << 30 | a >>> 2; + c += const2 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[37]; + e = e << 30 | e >>> 2; + b += const2 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[38]; + d = d << 30 | d >>> 2; + a += const2 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[39]; + c = c << 30 | c >>> 2; + e += const3 + (a << 5 | a >>> 27) + (b & c | b & d | c & d) + w[40]; + b = b << 30 | b >>> 2; + d += const3 + (e << 5 | e >>> 27) + (a & b | a & c | b & c) + w[41]; + a = a << 30 | a >>> 2; + c += const3 + (d << 5 | d >>> 27) + (e & a | e & b | a & b) + w[42]; + e = e << 30 | e >>> 2; + b += const3 + (c << 5 | c >>> 27) + (d & e | d & a | e & a) + w[43]; + d = d << 30 | d >>> 2; + a += const3 + (b << 5 | b >>> 27) + (c & d | c & e | d & e) + w[44]; + c = c << 30 | c >>> 2; + e += const3 + (a << 5 | a >>> 27) + (b & c | b & d | c & d) + w[45]; + b = b << 30 | b >>> 2; + d += const3 + (e << 5 | e >>> 27) + (a & b | a & c | b & c) + w[46]; + a = a << 30 | a >>> 2; + c += const3 + (d << 5 | d >>> 27) + (e & a | e & b | a & b) + w[47]; + e = e << 30 | e >>> 2; + b += const3 + (c << 5 | c >>> 27) + (d & e | d & a | e & a) + w[48]; + d = d << 30 | d >>> 2; + a += const3 + (b << 5 | b >>> 27) + (c & d | c & e | d & e) + w[49]; + c = c << 30 | c >>> 2; + e += const3 + (a << 5 | a >>> 27) + (b & c | b & d | c & d) + w[50]; + b = b << 30 | b >>> 2; + d += const3 + (e << 5 | e >>> 27) + (a & b | a & c | b & c) + w[51]; + a = a << 30 | a >>> 2; + c += const3 + (d << 5 | d >>> 27) + (e & a | e & b | a & b) + w[52]; + e = e << 30 | e >>> 2; + b += const3 + (c << 5 | c >>> 27) + (d & e | d & a | e & a) + w[53]; + d = d << 30 | d >>> 2; + a += const3 + (b << 5 | b >>> 27) + (c & d | c & e | d & e) + w[54]; + c = c << 30 | c >>> 2; + e += const3 + (a << 5 | a >>> 27) + (b & c | b & d | c & d) + w[55]; + b = b << 30 | b >>> 2; + d += const3 + (e << 5 | e >>> 27) + (a & b | a & c | b & c) + w[56]; + a = a << 30 | a >>> 2; + c += const3 + (d << 5 | d >>> 27) + (e & a | e & b | a & b) + w[57]; + e = e << 30 | e >>> 2; + b += const3 + (c << 5 | c >>> 27) + (d & e | d & a | e & a) + w[58]; + d = d << 30 | d >>> 2; + a += const3 + (b << 5 | b >>> 27) + (c & d | c & e | d & e) + w[59]; + c = c << 30 | c >>> 2; + e += const4 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[60]; + b = b << 30 | b >>> 2; + d += const4 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[61]; + a = a << 30 | a >>> 2; + c += const4 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[62]; + e = e << 30 | e >>> 2; + b += const4 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[63]; + d = d << 30 | d >>> 2; + a += const4 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[64]; + c = c << 30 | c >>> 2; + e += const4 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[65]; + b = b << 30 | b >>> 2; + d += const4 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[66]; + a = a << 30 | a >>> 2; + c += const4 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[67]; + e = e << 30 | e >>> 2; + b += const4 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[68]; + d = d << 30 | d >>> 2; + a += const4 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[69]; + c = c << 30 | c >>> 2; + e += const4 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[70]; + b = b << 30 | b >>> 2; + d += const4 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[71]; + a = a << 30 | a >>> 2; + c += const4 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[72]; + e = e << 30 | e >>> 2; + b += const4 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[73]; + d = d << 30 | d >>> 2; + a += const4 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[74]; + c = c << 30 | c >>> 2; + e += const4 + (a << 5 | a >>> 27) + (b ^ c ^ d) + w[75]; + b = b << 30 | b >>> 2; + d += const4 + (e << 5 | e >>> 27) + (a ^ b ^ c) + w[76]; + a = a << 30 | a >>> 2; + c += const4 + (d << 5 | d >>> 27) + (e ^ a ^ b) + w[77]; + e = e << 30 | e >>> 2; + b += const4 + (c << 5 | c >>> 27) + (d ^ e ^ a) + w[78]; + d = d << 30 | d >>> 2; + a += const4 + (b << 5 | b >>> 27) + (c ^ d ^ e) + w[79]; + c = c << 30 | c >>> 2; + + /* step e */ + h0 += a; + h1 += b; + h2 += c; + h3 += d; + h4 += e; + } + + /** + * Completes the hash computation by performing final operations such as + * padding. + * + * @return the digest value + */ + public synchronized byte[] digest() { + + pad(); + + byte[] digestValue = new byte[SHA1_DIGEST_LENGTH]; + BigEndianConversions.I2OSP(h0, digestValue, 0); + BigEndianConversions.I2OSP(h1, digestValue, 4); + BigEndianConversions.I2OSP(h2, digestValue, 8); + BigEndianConversions.I2OSP(h3, digestValue, 12); + BigEndianConversions.I2OSP(h4, digestValue, 16); + + reset(); + + return digestValue; + } + + /** + * Returns the digest length in bytes. + * + * @return the digest length in bytes. + */ + public int getDigestLength() { + return SHA1_DIGEST_LENGTH; + } + + /** + * Resets the digest for further use. + */ + public void reset() { + h0 = 0x67452301; + h1 = 0xefcdab89; + h2 = 0x98badcfe; + h3 = 0x10325476; + h4 = 0xc3d2e1f0; + count = 0; + } + + /** + * Updates the digest using the specified array of bytes, starting at the + * specified offset. + * + * @param input + * - the byte[] to use for the update. + * @param offset + * - the offset to start from in the array of bytes. + * @param len + * - the number of bytes to use, starting at offset. + */ + public synchronized void update(byte[] input, int offset, int len) { + int bufOffset = ((int) count) & 0x3f; + int copyLen; + + while (len > 0) { + copyLen = 64 - bufOffset; + copyLen = (len > copyLen) ? copyLen : len; + + System.arraycopy(input, offset, buffer, bufOffset, copyLen); + + len -= copyLen; + offset += copyLen; + count += copyLen; + bufOffset = (bufOffset + copyLen) & 0x3f; + + if (bufOffset == 0) { + processBlock(); + } + } + } + + /** + * Updates the digest using the specified byte. + * + * @param input + * - the byte to use for the update. + */ + public synchronized void update(byte input) { + buffer[(int) count & 0x3f] = input; + + if ((int) (count & 0x3f) == 63) { + processBlock(); + } + + count++; + } + + /** + * This Method performs the padding for the SHA1 algorithm. A single 1-bit + * is appended and then 0-bits, until only 64 bits are left free in the + * final block to enter the total length of the entered message. + */ + private void pad() { + long bitLength = count << 3; + buffer[(int) count & 0x3f] = (byte) 0x80; + count++; + + if ((int) (count & 0x3f) > 56) { + for (int i = (int) count & 0x3f; i < 64; i++) { + buffer[i] = 0; + count++; + } + processBlock(); + } else if ((int) (count & 0x3f) == 0) { + processBlock(); + } + + for (int i = (int) count & 0x3f; i < 56; i++) { + buffer[i] = 0; + } + + BigEndianConversions.I2OSP(bitLength, buffer, 56); + + processBlock(); + } + +} diff --git a/src/de/flexiprovider/core/md/SHA224.java b/src/de/flexiprovider/core/md/SHA224.java new file mode 100644 index 00000000..e7c88f07 --- /dev/null +++ b/src/de/flexiprovider/core/md/SHA224.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ +package de.flexiprovider.core.md; + +/** + * SHA224 is a 224-bit one-way hash function based on SHA-256, but the initial + * value is different and the result is truncated to 224 bits. + * + * For detailed information please refer to RFC 3874. + * + */ +public final class SHA224 extends SHA224_256 { + + /** + * The algorithm name. + */ + public static final String ALG_NAME = "SHA224"; + + /** + * The OID of SHA224 (defined by NIST). + */ + public static final String OID = "2.16.840.1.101.3.4.2.4"; + + // Initial hash value H(0). These were obtained by taking the + // fractional parts of the square roots of the first eight primes. + private static final int[] H0 = { 0xc1059ed8, 0x367cd507, 0x3070dd17, + 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 }; + + // length of the SHA224 message digest in bytes + private static final int SHA224_DIGEST_LENGTH = 28; + + /** + * Default constructor. + */ + public SHA224() { + super(SHA224_DIGEST_LENGTH); + } + + /** + * Reset the digest objects to its initial state. + */ + public void reset() { + initMessageDigest(H0); + } + +} diff --git a/src/de/flexiprovider/core/md/SHA224_256.java b/src/de/flexiprovider/core/md/SHA224_256.java new file mode 100644 index 00000000..883fba62 --- /dev/null +++ b/src/de/flexiprovider/core/md/SHA224_256.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.core.md; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.common.util.BigEndianConversions; + +/** + * Super class for SHA-224 and SHA-256 + * + * @author Ralf-P. Weinmann + */ +public abstract class SHA224_256 extends MessageDigest { + + // Constant words K0...63. These are the first thirty-two bits of + // the fractional parts of the cube roots of the first sixty-four primes. + private static final int[] K = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, + 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, + 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, + 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, + 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, + 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, + 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, + 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, + 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, + 0xc67178f2, }; + + private int[] W; + + // contains the digest value after complete message has been processed + private int[] H; + + // input buffer + private byte[] buffer; + + // number of bytes we already digested + private long count; + + // the digest length + private int digestLength; + + /** + * Constructor. + * + * @param digestLength + * the digest length + */ + protected SHA224_256(int digestLength) { + H = new int[8]; + W = new int[64]; + buffer = new byte[64]; + this.digestLength = digestLength; + reset(); + } + + /** + * Initialize the function with an initial state. + * + * @param initialState + * the initial state + */ + protected void initMessageDigest(int[] initialState) { + count = 0; + System.arraycopy(initialState, 0, H, 0, initialState.length); + } + + /** + * Sigma 0 function. + * + * @param x + * the input + * @return the rotated value + */ + private static int s0(int x) { + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); + } + + /** + * Sigma 1 function. + * + * @param x + * the input + * @return the rotated value + */ + private static int s1(int x) { + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10); + } + + /** + * Compute the hash value of the current block and store it in H + */ + private void processBlock() { + int i, i2; + int T1, T2; + int a, b, c, d, e, f, g, h; + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + + for (i = 0; i < 64; i++) { + + i2 = i << 2; + W[i] = i < 16 ? (((buffer[i2] & 0xff) << 24) + | (buffer[++i2] & 0xff) << 16 | (buffer[++i2] & 0xff) << 8 | (buffer[++i2] & 0xff)) + : s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + T1 = ((e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7)) + + (e & f ^ ~e & g) + h + K[i] + W[i]; + T2 = ((a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10)) + + (a & b ^ a & c ^ b & c); + + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; + + } + + /** + * Pad and hash the value + */ + private void pad() { + // compute length of message in bits + long bitLength = count << 3; + // append single 1-bit trailed by 0-bits to message + + buffer[(int) count & 63] = (byte) 0x80; + count++; + + if ((int) (count & 63) > 56) { + for (int i = (int) count & 63; i < 64; i++) { + buffer[i] = 0; + count++; + } + processBlock(); + } else if ((int) (count & 63) == 0) { + processBlock(); + } + + for (int i = (int) count & 63; i < 56; i++) { + buffer[i] = 0; + } + + // append length of message + BigEndianConversions.I2OSP(bitLength, buffer, 56); + + // chomp last block + processBlock(); + } + + /** + * @return the digest length in bytes + */ + public int getDigestLength() { + return digestLength; + } + + /** + * Update the digest using the specified input byte. + * + * @param input + * the input byte + */ + public synchronized void update(byte input) { + buffer[(int) count & 63] = input; + + if ((int) (count & 63) == 63) { + processBlock(); + } + + count++; + } + + /** + * Update the digest using the specified input byte array. + * + * @param input + * the input + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + */ + public synchronized void update(byte[] input, int inOff, int inLen) { + int bufOff = ((int) count) & 63; + + while (inLen > 0) { + int copyLen = 64 - bufOff; + copyLen = (inLen > copyLen) ? copyLen : inLen; + + System.arraycopy(input, inOff, buffer, bufOff, copyLen); + + inLen -= copyLen; + inOff += copyLen; + count += copyLen; + bufOff = (bufOff + copyLen) & 63; + + if (bufOff == 0) { + processBlock(); + } + } + } + + /** + * Complete the hash computation by performing final operations such as + * padding. + * + * @return the digest value + */ + public synchronized byte[] digest() { + pad(); + + byte[] digestValue = new byte[digestLength]; + for (int i = digestLength >> 2; --i >= 0;) { + BigEndianConversions.I2OSP(H[i], digestValue, i << 2); + } + + reset(); + + return digestValue; + } + +} diff --git a/src/de/flexiprovider/core/md/SHA256.java b/src/de/flexiprovider/core/md/SHA256.java new file mode 100644 index 00000000..75a52de9 --- /dev/null +++ b/src/de/flexiprovider/core/md/SHA256.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.core.md; + +/** + * SHA256 is a 256-bit hash and is meant to provide 128 bits of security against + * collision attacks. + * + * @author Ralf-P. Weinmann + */ +public final class SHA256 extends SHA224_256 { + + /** + * The algorithm name. + */ + public static final String ALG_NAME = "SHA256"; + + /** + * The OID of SHA256 (defined by NIST). + */ + public static final String OID = "2.16.840.1.101.3.4.2.1"; + + // Initial hash value H(0). These were obtained by taking the + // fractional parts of the square roots of the first eight primes. + private static final int[] H0 = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, + 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; + + // length of the SHA256 message digest in bytes + private static final int SHA256_DIGEST_LENGTH = 32; + + /** + * Default constructor. + */ + public SHA256() { + super(SHA256_DIGEST_LENGTH); + } + + /** + * Reset the digest objects to its initial state. + */ + public void reset() { + initMessageDigest(H0); + } + +} diff --git a/src/de/flexiprovider/core/md/SHA384.java b/src/de/flexiprovider/core/md/SHA384.java new file mode 100644 index 00000000..4ccda465 --- /dev/null +++ b/src/de/flexiprovider/core/md/SHA384.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.core.md; + +/** + * SHA-384 is a 384-bit hash and is meant to provide 192 bits of security + * against collision attacks. + * + * To obtain a 384-bit hash value will require truncating the SHA-512 output. + * + * @author Ralf-P. Weinmann + */ +public final class SHA384 extends SHA384_512 { + + /** + * The algorithm name. + */ + public static final String ALG_NAME = "SHA384"; + + /** + * The OID of SHA384 (defined by NIST). + */ + public static final String OID = "2.16.840.1.101.3.4.2.2"; + + // Initial hash value H(0). These were obtained by taking the + // fractional parts of the square roots of the ninth to sixteenth prime. + private static final long[] H0 = { 0xcbbb9d5dc1059ed8L, + 0x629a292a367cd507L, 0x9159015a3070dd17L, 0x152fecd8f70e5939L, + 0x67332667ffc00b31L, 0x8eb44a8768581511L, 0xdb0c2e0d64f98fa7L, + 0x47b5481dbefa4fa4L }; + + // length of the SHA384 message digest in bytes + private static final int SHA384_DIGEST_LENGTH = 48; + + /** + * Constructor. + */ + public SHA384() { + super(SHA384_DIGEST_LENGTH); + } + + /** + * Reset the digest objects to its initial state. + */ + public void reset() { + initMessageDigest(H0); + } + +} diff --git a/src/de/flexiprovider/core/md/SHA384_512.java b/src/de/flexiprovider/core/md/SHA384_512.java new file mode 100644 index 00000000..7b280455 --- /dev/null +++ b/src/de/flexiprovider/core/md/SHA384_512.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.core.md; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.common.util.BigEndianConversions; + +/** + * Abstract class for SHA message digests operating on 64 bit words (SHA-384 and + * SHA-512). + * + * @author Ralf-P. Weinmann + */ +public abstract class SHA384_512 extends MessageDigest { + + // Constant words K0...79. These are the first sixty-four bits of + // the fractional parts of the cube roots of the first eighty primes. + private static final long[] K = { 0x428a2f98d728ae22L, 0x7137449123ef65cdL, + 0xb5c0fbcfec4d3b2fL, 0xe9b5dba58189dbbcL, 0x3956c25bf348b538L, + 0x59f111f1b605d019L, 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L, + 0xd807aa98a3030242L, 0x12835b0145706fbeL, 0x243185be4ee4b28cL, + 0x550c7dc3d5ffb4e2L, 0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, + 0x9bdc06a725c71235L, 0xc19bf174cf692694L, 0xe49b69c19ef14ad2L, + 0xefbe4786384f25e3L, 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L, + 0x2de92c6f592b0275L, 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, + 0x76f988da831153b5L, 0x983e5152ee66dfabL, 0xa831c66d2db43210L, + 0xb00327c898fb213fL, 0xbf597fc7beef0ee4L, 0xc6e00bf33da88fc2L, + 0xd5a79147930aa725L, 0x06ca6351e003826fL, 0x142929670a0e6e70L, + 0x27b70a8546d22ffcL, 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, + 0x53380d139d95b3dfL, 0x650a73548baf63deL, 0x766a0abb3c77b2a8L, + 0x81c2c92e47edaee6L, 0x92722c851482353bL, 0xa2bfe8a14cf10364L, + 0xa81a664bbc423001L, 0xc24b8b70d0f89791L, 0xc76c51a30654be30L, + 0xd192e819d6ef5218L, 0xd69906245565a910L, 0xf40e35855771202aL, + 0x106aa07032bbd1b8L, 0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, + 0x2748774cdf8eeb99L, 0x34b0bcb5e19b48a8L, 0x391c0cb3c5c95a63L, + 0x4ed8aa4ae3418acbL, 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L, + 0x748f82ee5defb2fcL, 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, + 0x8cc702081a6439ecL, 0x90befffa23631e28L, 0xa4506cebde82bde9L, + 0xbef9a3f7b2c67915L, 0xc67178f2e372532bL, 0xca273eceea26619cL, + 0xd186b8c721c0c207L, 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L, + 0x06f067aa72176fbaL, 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, + 0x1b710b35131c471bL, 0x28db77f523047d84L, 0x32caab7b40c72493L, + 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL, 0x4cc5d4becb3e42b6L, + 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L }; + + // input buffer + private byte[] buffer; + + // the number of bytes already digested + private long count; + + // the digest length + private int digestLength; + + // contains the digest value after complete message has been processed + private long[] H; + + private long[] W; + + /** + * Constructor. + * + * @param digestLength + * the digest length + * + */ + protected SHA384_512(int digestLength) { + buffer = new byte[128]; + W = new long[80]; + H = new long[8]; + this.digestLength = digestLength; + reset(); + } + + /** + * Initialize the message digest with an initial state. + * + * @param initialState + * the initial state + */ + protected void initMessageDigest(long[] initialState) { + System.arraycopy(initialState, 0, H, 0, initialState.length); + count = 0; + } + + /** + * Sigma 0 function. + * + * @param x + * the input + * @return the rotated value + */ + private static long sigma0(long x) { + return (x >>> 1 | x << 63) ^ (x >>> 8 | x << 56) ^ (x >>> 7); + } + + /** + * Sigma 1 function. + * + * @param x + * the input + * @return the rotated value + */ + private static long sigma1(long x) { + return (x >>> 19 | x << 45) ^ (x >>> 61 | x << 3) ^ (x >>> 6); + } + + /** + * Compute the hash value of the current block and store it in H. + */ + private void processBlock() { + long a = H[0]; + long b = H[1]; + long c = H[2]; + long d = H[3]; + long e = H[4]; + long f = H[5]; + long g = H[6]; + long h = H[7]; + + for (int i = 0; i < 80; i++) { + + W[i] = i < 16 ? BigEndianConversions.OS2LIP(buffer, i << 3) + : sigma1(W[i - 2]) + W[i - 7] + sigma0(W[i - 15]) + + W[i - 16]; + + long T1 = ((e >>> 14 | e << 50) ^ (e >>> 18 | e << 46) ^ (e >>> 41 | e << 23)) + + (e & f ^ ~e & g) + h + K[i] + W[i]; + + long T2 = ((a >>> 28 | a << 36) ^ (a >>> 34 | a << 30) ^ (a >>> 39 | a << 25)) + + (a & b ^ a & c ^ b & c); + + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; + } + + /** + * Pad and hash the value + */ + private void pad() { + // compute length of message in bits + long bitLength = count << 3; + + // append single 1-bit trailed by 0-bits to message + buffer[(int) count & 127] = (byte) 0x80; + count++; + + if ((count & 127) > 112) { + for (int i = (int) count & 127; i < 128; i++) { + buffer[i] = 0; + count++; + } + processBlock(); + } else if ((count & 127) == 0) { + processBlock(); + } + + for (int i = (int) count & 127; i < 112; i++) { + buffer[i] = 0; + } + + // append length of message + buffer[112] = 0; + buffer[113] = 0; + buffer[114] = 0; + buffer[115] = 0; + buffer[116] = 0; + buffer[117] = 0; + buffer[118] = 0; + buffer[119] = 0; + BigEndianConversions.I2OSP(bitLength, buffer, 120); + + // chomp last block + processBlock(); + } + + /** + * @return the digest length in bytes + */ + public int getDigestLength() { + return digestLength; + } + + /** + * Update the digest using the specified input byte. + * + * @param input + * the input byte + */ + public synchronized void update(byte input) { + buffer[(int) count & 127] = input; + + if ((int) (count & 127) == 127) { + processBlock(); + } + + count++; + } + + /** + * Update the digest using the specified input byte array. + * + * @param input + * the input + * @param inOff + * the offset where the input starts + * @param inLen + * the input length + */ + public synchronized void update(byte[] input, int inOff, int inLen) { + int bufOffset = ((int) count) & 127; + + while (inLen > 0) { + int copyLen = 128 - bufOffset; + copyLen = (inLen > copyLen) ? copyLen : inLen; + + System.arraycopy(input, inOff, buffer, bufOffset, copyLen); + + inLen -= copyLen; + inOff += copyLen; + count += copyLen; + bufOffset = (bufOffset + copyLen) & 127; + + if (bufOffset == 0) { + processBlock(); + } + } + } + + /** + * Completes the hash computation by performing final operations such as + * padding. + * + * @return the digest value + */ + public synchronized byte[] digest() { + pad(); + + byte[] digestValue = new byte[digestLength]; + for (int i = digestLength >> 3; --i >= 0;) { + BigEndianConversions.I2OSP(H[i], digestValue, i << 3); + } + + reset(); + + return digestValue; + } + +} diff --git a/src/de/flexiprovider/core/md/SHA512.java b/src/de/flexiprovider/core/md/SHA512.java new file mode 100644 index 00000000..63dcf29b --- /dev/null +++ b/src/de/flexiprovider/core/md/SHA512.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1998-2003 by The FlexiProvider Group, + * Technische Universitaet Darmstadt + * + * For conditions of usage and distribution please refer to the + * file COPYING in the root directory of this package. + * + */ + +package de.flexiprovider.core.md; + +/** + * SHA-512 is a 512-bit hash and is meant to provide 256 bits of security + * against collision attacks. + * + * @author Ralf-P. Weinmann + */ +public final class SHA512 extends SHA384_512 { + + /** + * The algorithm name. + */ + public static final String ALG_NAME = "SHA512"; + + /** + * The OID of SHA512 (defined by NIST). + */ + public static final String OID = "2.16.840.1.101.3.4.2.3"; + + // Initial hash value H(0). These were obtained by taking the + // fractional parts of the square roots of the first eight primes. + private static final long[] H0 = { 0x6a09e667f3bcc908L, + 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, 0xa54ff53a5f1d36f1L, + 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, 0x1f83d9abfb41bd6bL, + 0x5be0cd19137e2179L }; + + // length of the SHA512 message digest in bytes + private static final int SHA512_DIGEST_LENGTH = 64; + + /** + * Constructor. + */ + public SHA512() { + super(SHA512_DIGEST_LENGTH); + } + + /** + * Reset the digest objects to its initial state. + */ + public void reset() { + initMessageDigest(H0); + } + +} diff --git a/src/de/flexiprovider/pki/AlgorithmIdentifier.java b/src/de/flexiprovider/pki/AlgorithmIdentifier.java new file mode 100644 index 00000000..4de26cd5 --- /dev/null +++ b/src/de/flexiprovider/pki/AlgorithmIdentifier.java @@ -0,0 +1,161 @@ +/* Copyright 2000 Fraunhofer Gesellschaft + * Leonrodstr. 54, 80636 Munich, Germany. + * All rights reserved. + * + * You shall use this software only in accordance with + * the terms of the license agreement you entered into + * with Fraunhofer Gesellschaft. + */ +package de.flexiprovider.pki; + +import java.io.IOException; + +import codec.asn1.ASN1; +import codec.asn1.ASN1Exception; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1Type; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.parameters.AlgorithmParameters; + +/** + * This class represents the ASN.1/DER value of the AlgorithmIdentifier defined + * in Annex D to Recommendation X.509. This structure is extensively used for + * instance in the PKCS standards of RSA Inc. The ASN.1 definition of this + * structure is as given below: + * + *

+ * AlgorithmIdentifier  ::= SEQUENCE{
+ *   algorithm  OBJECT IDENTIFIER,
+ *   parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ * 
+ * + * For this class to work properly, providers need to define the following + * algorithm aliases for the {@link java.security.AlgorithmParameters + * AlgorithmParameters} implementations they provide: + *
    + *
  1. AlgorithmParameters.MyAlg = class + *
  2. Alg.Alias.AlgorithmParameters.1.2.3.4 = MyAlg + *
  3. Alg.Alias.AlgorithmParameters.OID.1.2.3.4 = MyAlg + *
+ * The first property defined the mapping of the JCE compliant standard name of + * the algorithm to the implementing class. The second provider entry allows + * mapping OID to those algorithm names while the third allows mapping those + * names on corresponding OID. + *

+ * The alias definitions are used by this class in order to find an + * AlgorithmParameters implementation for the OID embedded in the X.509 + * AlgorithmIdentifier structure, and to create the OID for a given + * AlgorithmParameters instances. This is done by means of the + * {@link codec.util.JCA JCA} class, which operates on the engine and alias + * definitions of the installed providers. + * + * @author Volker Roth + * @see codec.util.JCA + */ +public class AlgorithmIdentifier extends codec.x509.AlgorithmIdentifier { + + /** + * This method builds the tree of ASN.1 objects used for decoding this + * structure. + */ + public AlgorithmIdentifier() { + super(); + } + + /** + * Creates an instance with the given OID and opaque algorithm parameter + * representation. Both the given OID and the parameter encoding is cloned + * or copied. No side effects occur if these arguments are modified after + * completion of this constructor. + * + * @param oid + * The algorithm object identifier. + * @param b + * The opaque DER encoding of the parameters for the + * algorithm known under the given OID. If no parameters are + * required then null might be passed. In that + * case {@link codec.asn1.ASN1Null ASN.1NULL} is encoded. + * @throws ASN1Exception + * if the opaque representation does not contain a valid DER + * header and contents octets. + */ + public AlgorithmIdentifier(ASN1ObjectIdentifier oid, byte[] b) + throws ASN1Exception { + super(oid, b); + } + + /** + * Creates an instance with the given OID and parameters. The parameters are + * encoded according to DER and stored by means of an opaque type. If the + * given parameters are null then an ASN.1 NULL is encoded. + * + * @param oid + * The OID to use. + * @param params + * The ASN.1 type of which the parameters consist. + * @throws ASN1Exception + * if the given parameters cannot be encoded. This should + * rarely happen. + */ + public AlgorithmIdentifier(ASN1ObjectIdentifier oid, ASN1Type params) + throws ASN1Exception { + super(oid, params); + } + + /** + * This method locates a suitable {@link AlgorithmParameters} implementation + * if it is available from the JCE compliant security providers that are + * installed locally. + *

+ * Such providers need to specify the following aliases for this to work: + *

    + *
  • AlgorithmParameters.MyAlg = class + *
  • Alg.Alias.AlgorithmParameters.1.2.3.4 = MyAlg + *
+ * If you ever want to test a provider for compliance with the JCE and + * cleverness, test it against the FhG-IGD PKCS package. If it + * doesn't work then better demand fixes from the provider's vendor. + *

+ * + * This method may be called only if this instance is initialised properly + * either by specifying AlgorithmParameters in a constructor or by parsing a + * valid ASN.1/DER encoding. + * + * @throws NoSuchAlgorithmException + * if no matching AlgorithmParameters engine is found. + * @throws InvalidAlgorithmParameterException + * if the parameters cannot be decoded properly. + * @return The AlgorithmParameters or null if none are enclosed + * in this structure. + */ + public AlgorithmParameters getParams() throws NoSuchAlgorithmException, + InvalidAlgorithmParameterException { + AlgorithmParameters params; + + if (parameters_.isOptional()) { + return null; + } + + if (parameters_.getTag() == ASN1.TAG_NULL + && parameters_.getTagClass() == ASN1.CLASS_UNIVERSAL) { + return null; + } + + params = Registry.getAlgParams(algorithm_.toString()); + + try { + params.init(parameters_.getEncoded()); + } catch (IOException e) { + throw new InvalidAlgorithmParameterException( + "Caught IOException(\"" + e.getMessage() + "\")"); + } catch (ASN1Exception e) { + throw new InvalidAlgorithmParameterException( + "Caught ASN1Exception(\"" + e.getMessage() + "\")"); + } + return params; + } + +} diff --git a/src/de/flexiprovider/pki/EncodedKeySpec.java b/src/de/flexiprovider/pki/EncodedKeySpec.java new file mode 100644 index 00000000..987c445c --- /dev/null +++ b/src/de/flexiprovider/pki/EncodedKeySpec.java @@ -0,0 +1,11 @@ +package de.flexiprovider.pki; + +import de.flexiprovider.api.keys.KeySpec; + +public interface EncodedKeySpec extends KeySpec { + + byte[] getEncoded(); + + String getFormat(); + +} diff --git a/src/de/flexiprovider/pki/PKCS8EncodedKeySpec.java b/src/de/flexiprovider/pki/PKCS8EncodedKeySpec.java new file mode 100644 index 00000000..5491e16c --- /dev/null +++ b/src/de/flexiprovider/pki/PKCS8EncodedKeySpec.java @@ -0,0 +1,15 @@ +package de.flexiprovider.pki; + +public class PKCS8EncodedKeySpec extends java.security.spec.PKCS8EncodedKeySpec + implements EncodedKeySpec { + + public PKCS8EncodedKeySpec(byte[] encodedKey) { + super(encodedKey); + } + + public PKCS8EncodedKeySpec( + java.security.spec.PKCS8EncodedKeySpec javaKeySpec) { + super(javaKeySpec.getEncoded()); + } + +} diff --git a/src/de/flexiprovider/pki/X509EncodedKeySpec.java b/src/de/flexiprovider/pki/X509EncodedKeySpec.java new file mode 100644 index 00000000..0f637eb4 --- /dev/null +++ b/src/de/flexiprovider/pki/X509EncodedKeySpec.java @@ -0,0 +1,14 @@ +package de.flexiprovider.pki; + +public class X509EncodedKeySpec extends java.security.spec.X509EncodedKeySpec + implements EncodedKeySpec { + + public X509EncodedKeySpec(byte[] encodedKey) { + super(encodedKey); + } + + public X509EncodedKeySpec(java.security.spec.X509EncodedKeySpec javaKeySpec) { + super(javaKeySpec.getEncoded()); + } + +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSKeyFactory.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSKeyFactory.java new file mode 100644 index 00000000..32d2934f --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSKeyFactory.java @@ -0,0 +1,242 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.io.ByteArrayInputStream; + +import codec.CorruptedCodeException; +import codec.asn1.ASN1Type; +import codec.asn1.DERDecoder; +import codec.pkcs8.PrivateKeyInfo; +import codec.x509.SubjectPublicKeyInfo; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.InvalidKeySpecException; +import de.flexiprovider.api.keys.Key; +import de.flexiprovider.api.keys.KeyFactory; +import de.flexiprovider.api.keys.KeySpec; +import de.flexiprovider.api.keys.PrivateKey; +import de.flexiprovider.api.keys.PublicKey; +import de.flexiprovider.pki.PKCS8EncodedKeySpec; +import de.flexiprovider.pki.X509EncodedKeySpec; + +/** + * This class transforms GMSS keys and GMSS key specifications into a form that + * can be used with the FlexiPQCProvider. + * + * @author Sebastian Blume, Michael Schneider + * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKey + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKeySpec + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPublicKey + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPublicKeySpec + */ +public class GMSSKeyFactory extends KeyFactory { + + /** + * The GMSS OID. + */ + public static final String OID = "1.3.6.1.4.1.8301.3.1.3.3"; + + /** + * Converts, if possible, a key specification into a GMSSPublicKey. + * Currently the following key specs are supported: GMSSPublicKeySpec, + * X509EncodedKeySpec. + *

+ * + * @param keySpec + * the key specification + * @return A GMSS public key + * @throws InvalidKeySpecException + * if the KeySpec is not supported + */ + public PublicKey generatePublic(KeySpec keySpec) + throws InvalidKeySpecException { + if (keySpec instanceof GMSSPublicKeySpec) { + GMSSPublicKeySpec gmssPublicKeySpec = (GMSSPublicKeySpec) keySpec; + + return new GMSSPublicKey(gmssPublicKeySpec.getPublicKey(), + gmssPublicKeySpec.getGMSSParameterset()); + } else if (keySpec instanceof X509EncodedKeySpec) { + + // get the DER-encoded Key according to X.509 from the spec + byte[] enc = ((X509EncodedKeySpec) keySpec).getEncoded(); + + // decode the SubjectPublicKeyInfo data structure to the pki object + SubjectPublicKeyInfo pki = new SubjectPublicKeyInfo(); + try { + ByteArrayInputStream bais = new ByteArrayInputStream(enc); + pki.decode(new DERDecoder(bais)); + bais.close(); + } catch (Exception ce) { + ce.printStackTrace(); + throw new InvalidKeySpecException( + "Unable to decode X509EncodedKeySpec"); + } + + // get the inner type inside the BIT STRING + try { + ASN1Type innerType = pki.getDecodedRawKey(); + GMSSPublicKeyASN1 gmssPublicKey = new GMSSPublicKeyASN1( + innerType); + return new GMSSPublicKey(gmssPublicKey.getKeySpec()); + } catch (CorruptedCodeException cce) { + throw new InvalidKeySpecException( + "Unable to decode X509EncodedKeySpec"); + } + } + throw new InvalidKeySpecException("Unknown KeySpec type"); + + } + + /** + * Converts, if possible, a key specification into a GMSSPrivateKey. + * Currently the following key specs are supported: GMSSPrivateKeySpec. + *

+ * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKey + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKeySpec + * @param keySpec + * the key specification + * @return The GMSS private key + * @throws InvalidKeySpecException + * if the KeySpec is not supported + */ + public PrivateKey generatePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + if (keySpec instanceof GMSSPrivateKeySpec) { + GMSSPrivateKeySpec gmssPrivKeySpec = (GMSSPrivateKeySpec) keySpec; + + return new GMSSPrivateKey(gmssPrivKeySpec.getIndex(), + gmssPrivKeySpec.getCurrentSeed(), gmssPrivKeySpec + .getNextNextSeed(), gmssPrivKeySpec + .getCurrentAuthPath(), gmssPrivKeySpec + .getNextAuthPath(), gmssPrivKeySpec.getKeep(), + gmssPrivKeySpec.getCurrentTreehash(), gmssPrivKeySpec + .getNextTreehash(), gmssPrivKeySpec + .getCurrentStack(), gmssPrivKeySpec.getNextStack(), + gmssPrivKeySpec.getCurrentRetain(), gmssPrivKeySpec + .getNextRetain(), + gmssPrivKeySpec.getNextNextLeaf(), gmssPrivKeySpec + .getUpperLeaf(), gmssPrivKeySpec + .getUpperTreehashLeaf(), gmssPrivKeySpec + .getMinTreehash(), gmssPrivKeySpec.getNextRoot(), + gmssPrivKeySpec.getNextNextRoot(), gmssPrivKeySpec + .getCurrentRootSig(), gmssPrivKeySpec + .getNextRootSig(), gmssPrivKeySpec.getGmssPS(), + gmssPrivKeySpec.getAlgNames()); + } else if (keySpec instanceof PKCS8EncodedKeySpec) { + + // get the DER-encoded Key according to PKCS#8 from the spec + byte[] encKey = ((PKCS8EncodedKeySpec) keySpec).getEncoded(); + + // decode the PKCS#8 data structure to the pki object + PrivateKeyInfo pki = new PrivateKeyInfo(); + try { + ByteArrayInputStream bais = new java.io.ByteArrayInputStream( + encKey); + pki.decode(new DERDecoder(bais)); + bais.close(); + } catch (Exception ce2) { + throw new InvalidKeySpecException( + "Unable to decode PKCS8EncodedKeySpec." + + ce2.getMessage()); + } + + // get the inner type inside the OCTET STRING + try { + // --- Build and return the actual key. + ASN1Type innerType = pki.getDecodedRawKey(); + GMSSPrivateKeyASN1 key = new GMSSPrivateKeyASN1(innerType); + return new GMSSPrivateKey(key.getKeySpec()); + + } catch (CorruptedCodeException cce) { + throw new InvalidKeySpecException( + "Unable to decode PKCS8EncodedKeySpec."); + } + } + + throw new InvalidKeySpecException("Unknown KeySpec type."); + } + + /** + * Converts a given key into a key specification, if possible. Currently the + * following specs are supported: + *

    + *
  • for GMSSPublicKey: X509EncodedKeySpec, GMSSPublicKeySpec + *
  • for GMSSPrivateKey: PKCS8EncodedKeySpec, GMSSPrivateKeySpec + *
+ *

+ * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKey + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKeySpec + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPublicKey + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPublicKeySpec + * @param key + * the key + * @param spec + * the class of which type the returned class should be + * @return The specification of the GMSS key + * @throws InvalidKeySpecException + * if the specification is not supported + */ + public KeySpec getKeySpec(Key key, Class spec) + throws InvalidKeySpecException { + if (key instanceof GMSSPrivateKey) { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(spec)) { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } else if (GMSSPrivateKeySpec.class.isAssignableFrom(spec)) { + GMSSPrivateKey gmssPrivateKey = (GMSSPrivateKey) key; + + return new GMSSPrivateKeySpec(gmssPrivateKey.getIndex(), + gmssPrivateKey.getCurrentSeeds(), gmssPrivateKey + .getNextNextSeeds(), gmssPrivateKey + .getCurrentAuthPaths(), gmssPrivateKey + .getNextAuthPaths(), gmssPrivateKey + .getCurrentTreehash(), gmssPrivateKey + .getNextTreehash(), gmssPrivateKey + .getCurrentStack(), gmssPrivateKey + .getNextStack(), gmssPrivateKey + .getCurrentRetain(), gmssPrivateKey + .getNextRetain(), gmssPrivateKey.getKeep(), + gmssPrivateKey.getNextNextLeaf(), gmssPrivateKey + .getUpperLeaf(), gmssPrivateKey + .getUpperTreehashLeaf(), gmssPrivateKey + .getMinTreehash(), + gmssPrivateKey.getNextRoot(), gmssPrivateKey + .getNextNextRoot(), gmssPrivateKey + .getCurrentRootSig(), gmssPrivateKey + .getNextRootSig(), gmssPrivateKey + .getParameterset(), gmssPrivateKey.getName()); + } + } else if (key instanceof GMSSPublicKey) { + if (X509EncodedKeySpec.class.isAssignableFrom(spec)) { + return new X509EncodedKeySpec(key.getEncoded()); + } else if (GMSSPublicKeySpec.class.isAssignableFrom(spec)) { + GMSSPublicKey merkleTreePublicKey = (GMSSPublicKey) key; + return new GMSSPublicKeySpec(merkleTreePublicKey + .getPublicKeyBytes(), merkleTreePublicKey + .getParameterset()); + } + } + throw new InvalidKeySpecException("Unknown KeySpec"); + } + + /** + * Translates a key into a form known by the FlexiProvider. Currently the + * following key types are supported: GMSSPrivateKey, GMSSPublicKey. + *

+ * + * @param key + * the key + * @return A key of a known key type + * @throws InvalidKeyException + * if the key is not supported + */ + public Key translateKey(Key key) throws InvalidKeyException { + if (key instanceof GMSSPrivateKey) { + return key; + } else if (key instanceof GMSSPublicKey) { + return key; + } + + throw new InvalidKeyException("Unsupported key type"); + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSKeyPairGenerator.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSKeyPairGenerator.java new file mode 100644 index 00000000..13573e3c --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSKeyPairGenerator.java @@ -0,0 +1,647 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.util.Vector; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.SignatureException; +import de.flexiprovider.api.keys.KeyPair; +import de.flexiprovider.api.keys.KeyPairGenerator; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class implements key pair generation of the generalized Merkle signature + * scheme (GMSS). The class extends the KeyPairGeneratorSpi class. + *

+ * The GMSSKeyPairGenerator can be used as follows: + *

+ * 1. get instance of GMSS key pair generator:
+ * KeyPairGenerator kpg = + * KeyPairGenerator.getInstance("GMSSwithSHA1", + * "FlexiPQC");
+ * 2. initialize the KPG with the desired Parameterset
+ * kpg.initialize(parameterset);
+ * 3. create GMSS key pair:
+ * KeyPair keyPair = kpg.generateKeyPair();
+ * 4. get the encoded private and public keys from the key pair:
+ * encodedPublicKey = keyPair.getPublic().getEncoded();
+ * encodedPrivateKey = keyPair.getPrivate().getEncoded();
+ * + *

+ * The key pair generator can be initialized with an integer value as well. For + * this purpose call kpg.initialize(keySize);. The integer + * keySize determindes the number of signatures that can be + * created. A value less than 10 creates 2^10 signatures, between 11 and 20 + * creates 2^20 and a keySize greater than 20 creates 2^40 signatures. + * + *

+ * To generate an own parameterSpec for the use with GMSS use the following: + * + *

+ * 1. define int arrays of the desired parameters (defh for the height of the + * single layers of the GMSS tree, w for the Winternitz parameters for each + * layer, K for the parameter for the AuthPath computation)
+ * int[] defh = {10, 10, 10, 10};
+ * int[] defw = {9, 9, 9, 3};
+ * int[] defk = {2, 2, 2, 2};
+ * 2. create a parameterspec
+ * gps = new GMSSParameterSpec(defh.length, defh, defw, defk);
+ * 3. initialize the KPG with the desired Parameterset
+ * kpg.initialize(parameterset);
+ * + * @author Michael Schneider, Sebastian Blume + * @see GMSSSignature + * @see GMSSPrivateKey + * @see GMSSPublicKey + */ +public class GMSSKeyPairGenerator extends KeyPairGenerator { + /* + * Inner classes providing concrete implementations of GMSSKeyPairGenerator + * with a variety of message digests. + */ + + /** + * GMSSKeyPairGenerator with SHA1 + */ + public static class GMSSwithSHA1 extends GMSSKeyPairGenerator { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyFactory.OID + ".1"; + + /** + * Constructor. + */ + public GMSSwithSHA1() { + super(OID, "SHA1", "FlexiCore"); + } + } + + /** + * GMSSKeyPairGenerator with SHA224 + */ + public static class GMSSwithSHA224 extends GMSSKeyPairGenerator { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyFactory.OID + ".2"; + + /** + * Constructor. + */ + public GMSSwithSHA224() { + super(OID, "SHA224", "FlexiCore"); + } + } + + /** + * GMSSKeyPairGenerator with SHA256 + */ + public static class GMSSwithSHA256 extends GMSSKeyPairGenerator { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyFactory.OID + ".3"; + + /** + * Constructor. + */ + public GMSSwithSHA256() { + super(OID, "SHA256", "FlexiCore"); + } + } + + /** + * GMSSKeyPairGenerator with SHA384 + */ + public static class GMSSwithSHA384 extends GMSSKeyPairGenerator { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyFactory.OID + ".4"; + + /** + * Constructor. + */ + public GMSSwithSHA384() { + super(OID, "SHA384", "FlexiCore"); + } + } + + /** + * GMSSKeyPairGenerator with SHA512 + */ + public static class GMSSwithSHA512 extends GMSSKeyPairGenerator { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyFactory.OID + ".5"; + + /** + * Constructor. + */ + public GMSSwithSHA512() { + super(OID, "SHA512", "FlexiCore"); + } + } + + // ////////////////////////////////////////////////////////////////////////////// + + /** + * The source of randomness for OTS private key generation + */ + private GMSSRandom gmssRandom; + + /** + * The hash function used for the construction of the authentication trees + */ + private MessageDigest messDigestTree; + + /** + * An array of the seeds for the PRGN (for main tree, and all current + * subtrees) + */ + private byte[][] currentSeeds; + + /** + * An array of seeds for the PRGN (for all subtrees after next) + */ + private byte[][] nextNextSeeds; + + /** + * An array of the RootSignatures + */ + private byte[][] currentRootSigs; + + /** + * An array of strings containing the name of the hash function used to + * construct the authentication trees and used by the OTS + */ + private String[] algNames = new String[2]; + + /** + * The length of the seed for the PRNG + */ + private int mdLength; + + /** + * the number of Layers + */ + private int numLayer; + + /** + * Instance of GMSSParameterSpec + */ + private GMSSParameterSpec gmssParameterSpec; + + /** + * Flag indicating if the class already has been initialized + */ + private boolean initialized = false; + + /** + * Instance of GMSSParameterset + */ + private GMSSParameterset gmssPS; + + /** + * An array of the heights of the authentication trees of each layer + */ + private int[] heightOfTrees; + + /** + * An array of the Winternitz parameter 'w' of each layer + */ + private int[] otsIndex; + + /** + * The parameter K needed for the authentication path computation + */ + private int[] K; + + /** + * The standard constructor tries to generate the GMSS algorithm identifier + * with the corresponding OID. + *

+ * + * @param oidStr + * string with the oid of the algorithm + * @param mdName + * name of the message digest for the construction of the + * authentication trees + * @param mdProvName + * provider name of the message digest for the construction of + * the the authentication trees and for the OTS + */ + public GMSSKeyPairGenerator(String oidStr, String mdName, String mdProvName) { + String errorMsg; + + CoreRegistry.registerAlgorithms(); + try { + messDigestTree = Registry.getMessageDigest(mdName); + algNames[0] = mdName; + + // set mdLength + this.mdLength = messDigestTree.getDigestLength(); + // construct randomizer + this.gmssRandom = new GMSSRandom(messDigestTree); + + return; + } catch (NoSuchAlgorithmException nsae) { + errorMsg = "message digest " + mdName + " not found in " + + mdProvName + " or key pair generator " + mdName + + " not found in " + mdProvName; + } + throw new RuntimeException("GMSSKeyPairGenerator error: " + errorMsg); + + } + + /** + * Generates the GMSS key pair. The public key is an instance of + * GMSSPublicKey, the private key is an instance of GMSSPrivateKey. + * + * @return Key pair containing a GMSSPublicKey and a GMSSPrivateKey + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKey + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPublicKey + */ + public KeyPair genKeyPair() { + if (!initialized) + initializeDefault(); + + // initialize authenticationPaths and treehash instances + byte[][][] currentAuthPaths = new byte[numLayer][][]; + byte[][][] nextAuthPaths = new byte[numLayer - 1][][]; + Treehash[][] currentTreehash = new Treehash[numLayer][]; + Treehash[][] nextTreehash = new Treehash[numLayer - 1][]; + + Vector[] currentStack = new Vector[numLayer]; + Vector[] nextStack = new Vector[numLayer - 1]; + + Vector[][] currentRetain = new Vector[numLayer][]; + Vector[][] nextRetain = new Vector[numLayer - 1][]; + + for (int i = 0; i < numLayer; i++) { + currentAuthPaths[i] = new byte[heightOfTrees[i]][mdLength]; + currentTreehash[i] = new Treehash[heightOfTrees[i] - K[i]]; + + if (i > 0) { + nextAuthPaths[i - 1] = new byte[heightOfTrees[i]][mdLength]; + nextTreehash[i - 1] = new Treehash[heightOfTrees[i] - K[i]]; + } + + currentStack[i] = new Vector(); + if (i > 0) + nextStack[i - 1] = new Vector(); + } + + // initialize roots + byte[][] currentRoots = new byte[numLayer][mdLength]; + byte[][] nextRoots = new byte[numLayer - 1][mdLength]; + // initialize seeds + byte[][] seeds = new byte[numLayer][mdLength]; + // initialize seeds[] by copying starting-seeds of first trees of each + // layer + for (int i = 0; i < numLayer; i++) { + System.arraycopy(currentSeeds[i], 0, seeds[i], 0, mdLength); + } + + // initialize rootSigs + currentRootSigs = new byte[numLayer - 1][mdLength]; + + // ------------------------- + // ------------------------- + // --- calculation of current authpaths and current rootsigs (AUTHPATHS, + // SIG)------ + // from bottom up to the root + for (int h = numLayer - 1; h >= 0; h--) { + GMSSRootCalc tree = new GMSSRootCalc(this.heightOfTrees[h], + this.K[h], this.algNames); + try { + // on lowest layer no lower root is available, so just call + // the method with null as first parameter + if (h == numLayer - 1) + tree = this.generateCurrentAuthpathAndRoot(null, + currentStack[h], seeds[h], h); + else + // otherwise call the method with the former computed root + // value + tree = this.generateCurrentAuthpathAndRoot( + currentRoots[h + 1], currentStack[h], seeds[h], h); + + } catch (SignatureException e1) { + e1.printStackTrace(); + } + + // set initial values needed for the private key construction + for (int i = 0; i < heightOfTrees[h]; i++) { + System.arraycopy(tree.getAuthPath()[i], 0, + currentAuthPaths[h][i], 0, mdLength); + } + currentRetain[h] = tree.getRetain(); + currentTreehash[h] = tree.getTreehash(); + System.arraycopy(tree.getRoot(), 0, currentRoots[h], 0, mdLength); + } + + // --- calculation of next authpaths and next roots (AUTHPATHS+, ROOTS+) + // ------ + for (int h = numLayer - 2; h >= 0; h--) { + GMSSRootCalc tree = new GMSSRootCalc(this.heightOfTrees[h + 1], + this.K[h + 1], this.algNames); + + tree = this.generateNextAuthpathAndRoot(nextStack[h], seeds[h + 1], + h + 1); + + // set initial values needed for the private key construction + for (int i = 0; i < heightOfTrees[h + 1]; i++) { + System.arraycopy(tree.getAuthPath()[i], 0, nextAuthPaths[h][i], + 0, mdLength); + } + nextRetain[h] = tree.getRetain(); + nextTreehash[h] = tree.getTreehash(); + System.arraycopy(tree.getRoot(), 0, nextRoots[h], 0, mdLength); + + // create seed for the Merkle tree after next (nextNextSeeds) + // SEEDs++ + System.arraycopy(seeds[h + 1], 0, this.nextNextSeeds[h], 0, + mdLength); + } + // ------------ + + // generate GMSSPublicKey + GMSSPublicKey publicKey = new GMSSPublicKey(currentRoots[0], gmssPS); + + // generate the GMSSPrivateKey + GMSSPrivateKey privateKey = new GMSSPrivateKey(currentSeeds, + nextNextSeeds, currentAuthPaths, nextAuthPaths, + currentTreehash, nextTreehash, currentStack, nextStack, + currentRetain, nextRetain, nextRoots, currentRootSigs, gmssPS, + algNames); + + // return the KeyPair + return (new KeyPair(publicKey, privateKey)); + } + + /** + * calculates the authpath for tree in layer h which starts with seed[h] + * additionally computes the rootSignature of underlaying root + * + * @param currentStack + * stack used for the treehash instance created by this method + * @param lowerRoot + * stores the root of the lower tree + * @param seeds + * starting seeds + * @param h + * actual layer + * @throws SignatureException + * if the OTS verifying goes wrong + */ + private GMSSRootCalc generateCurrentAuthpathAndRoot(byte[] lowerRoot, + Vector currentStack, byte[] seed, int h) throws SignatureException { + byte[] help = new byte[mdLength]; + + byte[] OTSseed = new byte[mdLength]; + OTSseed = gmssRandom.nextSeed(seed); + + WinternitzOTSignature ots; + + // data structure that constructs the whole tree and stores + // the initial values for treehash, Auth and retain + GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], + this.K[h], this.algNames); + + treeToConstruct.initialize(currentStack); + + // generate the first leaf + if (h == numLayer - 1) { + ots = new WinternitzOTSignature(OTSseed, algNames, otsIndex[h]); + help = ots.getPublicKey(); + } else { + // for all layers except the lowest, generate the signature of the + // underlying root + // and reuse this signature to compute the first leaf of acual layer + // more efficiently (by verifiing the signature) + ots = new WinternitzOTSignature(OTSseed, algNames, otsIndex[h]); + currentRootSigs[h] = ots.getSignature(lowerRoot); + WinternitzOTSVerify otsver = new WinternitzOTSVerify(algNames, + otsIndex[h]); + help = otsver.Verify(lowerRoot, currentRootSigs[h]); + } + // update the tree with the first leaf + treeToConstruct.update(help); + + int seedForTreehashIndex = 3; + int count = 0; + + // update the tree 2^(H) - 1 times, from the second to the last leaf + for (int i = 1; i < (1 << this.heightOfTrees[h]); i++) { + // initialize the seeds for the leaf generation with index 3 * 2^h + if (i == seedForTreehashIndex + && count < this.heightOfTrees[h] - this.K[h]) { + treeToConstruct.initializeTreehashSeed(seed, count); + seedForTreehashIndex *= 2; + count++; + } + + OTSseed = gmssRandom.nextSeed(seed); + ots = new WinternitzOTSignature(OTSseed, algNames, otsIndex[h]); + treeToConstruct.update(ots.getPublicKey()); + } + + if (treeToConstruct.wasFinished()) { + return treeToConstruct; + } + System.err.println("Baum noch nicht fertig konstruiert!!!"); + return null; + } + + /** + * calculates the authpath and root for tree in layer h which starts with + * seed[h] + * + * @param nextStack + * stack used for the treehash instance created by this method + * @param seeds + * starting seeds + * @param h + * actual layer + * + */ + private GMSSRootCalc generateNextAuthpathAndRoot(Vector nextStack, + byte[] seed, int h) { + byte[] OTSseed = new byte[numLayer]; + WinternitzOTSignature ots; + + // data structure that constructs the whole tree and stores + // the initial values for treehash, Auth and retain + GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], + this.K[h], this.algNames); + treeToConstruct.initialize(nextStack); + + int seedForTreehashIndex = 3; + int count = 0; + + // update the tree 2^(H) times, from the first to the last leaf + for (int i = 0; i < (1 << this.heightOfTrees[h]); i++) { + // initialize the seeds for the leaf generation with index 3 * 2^h + if (i == seedForTreehashIndex + && count < this.heightOfTrees[h] - this.K[h]) { + treeToConstruct.initializeTreehashSeed(seed, count); + seedForTreehashIndex *= 2; + count++; + } + + OTSseed = gmssRandom.nextSeed(seed); + ots = new WinternitzOTSignature(OTSseed, algNames, otsIndex[h]); + treeToConstruct.update(ots.getPublicKey()); + } + + if (treeToConstruct.wasFinished()) + return treeToConstruct; + System.err.println("Nächster Baum noch nicht fertig konstruiert!!!"); + return null; + } + + /** + * This method initializes the GMSS KeyPairGenerator using an integer value + * keySize as input. It provides a simple use of the GMSS for + * testing demands. + *

+ * A given keysize of less than 10 creates an amount 2^10 + * signatures. A keySize between 10 and 20 creates 2^20 signatures. Given an + * integer greater than 20 the key pair generator creates 2^40 signatures. + * + * @param keySize + * Assigns the parameters used for the GMSS signatures. There are + * 3 choices:
+ * 1. keysize <= 10: creates 2^10 signatures using the + * parameterset
+ * P = (2, (5, 5), (3, 3), (3, 3))
+ * 2. keysize > 10 and <= 20: creates 2^20 signatures using the + * parameterset
+ * P = (2, (10, 10), (5, 4), (2, 2))
+ * 3. keysize > 20: creates 2^40 signatures using the + * parameterset
+ * P = (2, (10, 10, 10, 10), (9, 9, 9, 3), (2, 2, 2, 2)) + * + * @param secureRandom + * not used by GMSS, the SHA1PRNG of the SUN Provider is always + * used + */ + public void initialize(int keySize, SecureRandom secureRandom) { + + GMSSParameterSpec gps; + if (keySize <= 10) { // create 2^10 keys + int[] defh = { 10 }; + int[] defw = { 3 }; + int[] defk = { 2 }; + gps = new GMSSParameterSpec(defh.length, defh, defw, defk); + } else if (keySize <= 20) { // create 2^20 keys + int[] defh = { 10, 10 }; + int[] defw = { 5, 4 }; + int[] defk = { 2, 2 }; + gps = new GMSSParameterSpec(defh.length, defh, defw, defk); + } else { // create 2^40 keys, keygen lasts around 80 seconds + int[] defh = { 10, 10, 10, 10 }; + int[] defw = { 9, 9, 9, 3 }; + int[] defk = { 2, 2, 2, 2 }; + gps = new GMSSParameterSpec(defh.length, defh, defw, defk); + } + + // call the initializer with the chosen parameters + try { + this.initialize(gps); + } catch (InvalidAlgorithmParameterException ae) { + } + } + + /** + * Initalizes the key pair generator using a parameter set as input + * + * @param algParamSpec + * an instance of GMSSParameterSpec + * @param secureRandom + * not used in GMSS + * @see de.flexiprovider.pqc.hbc.gmss.GMSSParameterSpec + */ + + public void initialize(AlgorithmParameterSpec algParamSpec, + SecureRandom secureRandom) + throws InvalidAlgorithmParameterException { + this.initialize(algParamSpec); + } + + /** + * Initalizes the key pair generator using a parameter set as input + * + * @param algParamSpec + * an instance of GMSSParameterSpec + * @see de.flexiprovider.pqc.hbc.gmss.GMSSParameterSpec + */ + public void initialize(AlgorithmParameterSpec algParamSpec) + throws InvalidAlgorithmParameterException { + + if (!(algParamSpec instanceof GMSSParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "in GMSSKeyPairGenerator: initialize: params is not " + + "an instance of GMSSParameterSpec"); + } + this.gmssParameterSpec = (GMSSParameterSpec) algParamSpec; + + // generate GMSSParameterset + this.gmssPS = new GMSSParameterset(gmssParameterSpec.getNumOfLayers(), + gmssParameterSpec.getHeightOfTrees(), gmssParameterSpec + .getWinternitzParameter(), gmssParameterSpec.getK()); + + this.numLayer = gmssPS.getNumOfLayers(); + this.heightOfTrees = gmssPS.getHeightOfTrees(); + this.otsIndex = gmssPS.getWinternitzParameter(); + this.K = gmssPS.getK(); + + // seeds + this.currentSeeds = new byte[numLayer][mdLength]; + this.nextNextSeeds = new byte[numLayer - 1][mdLength]; + + byte[] seed; + // construct SecureRandom for initial seed generation + SecureRandom secRan = Registry.getSecureRandom(); + + // generation of initial seeds + for (int i = 0; i < numLayer; i++) { + seed = secRan.generateSeed(mdLength); + System.arraycopy(seed, 0, currentSeeds[i], 0, mdLength); + gmssRandom.nextSeed(currentSeeds[i]); + } + + this.initialized = true; + } + + /** + * This method is called by generateKeyPair() in case that no other + * initialization method has been called by the user + */ + private void initializeDefault() { + int[] defh = { 10, 10, 10, 10 }; + int[] defw = { 3, 3, 3, 3 }; + int[] defk = { 2, 2, 2, 2 }; + + GMSSParameterSpec gps = new GMSSParameterSpec(defh.length, defh, defw, + defk); + + try { + this.initialize(gps); + } catch (InvalidAlgorithmParameterException ae) { + } + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSLeaf.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSLeaf.java new file mode 100644 index 00000000..55ca1c66 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSLeaf.java @@ -0,0 +1,312 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.common.util.ByteUtils; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class implements the distributed computation of the public key of the + * Winternitz one-time signature scheme (OTSS). The class is used by the GMSS + * classes for calculation of upcoming leafs. + * + * @author Sebastian Blume, Michael Schneider + * + */ +public class GMSSLeaf { + + /** + * The hash function used by the OTS and the PRNG + */ + private MessageDigest messDigestOTS; + + /** + * The length of the message digest and private key + */ + private int mdsize, keysize; + + /** + * The source of randomness for OTS private key generation + */ + private GMSSRandom gmssRandom; + + /** + * Byte array for distributed coputation of the upcoming leaf + */ + private byte[] leaf; + + /** + * Byte array for storing the concatenated hashes of private key parts + */ + private byte[] concHashs; + + /** + * indices for distributed computation + */ + private int i, j; + + /** + * storing 2^w + */ + private int two_power_w; + + /** + * Winternitz parameter w + */ + private int w; + + /** + * the amount of distributed computation steps when updateLeaf is called + */ + private int steps; + + /** + * the internal seed + */ + private byte[] seed; + + /** + * the OTS privateKey parts + */ + byte[] privateKeyOTS; + + /** + * This constructor regenerates a prior GMSSLeaf object + * + * @param name + * an array of strings, containing the name of the used hash + * function and PRNG and the name of the corresponding + * provider + * @param statByte + * status bytes + * @param statInt + * status ints + */ + public GMSSLeaf(String[] name, byte[][] statByte, int[] statInt) { + this.i = statInt[0]; + this.j = statInt[1]; + this.steps = statInt[2]; + this.w = statInt[3]; + + CoreRegistry.registerAlgorithms(); + try { + messDigestOTS = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException(": message digest " + name[0] + + " not found in " + name[1]); + } + + gmssRandom = new GMSSRandom(messDigestOTS); + + // calulate keysize for private key and the help array + mdsize = messDigestOTS.getDigestLength(); + int mdsizeBit = mdsize << 3; + int messagesize = (int) Math.ceil((double) (mdsizeBit) / (double) w); + int checksumsize = getLog((messagesize << w) + 1); + this.keysize = messagesize + + (int) Math.ceil((double) checksumsize / (double) w); + this.two_power_w = 1 << w; + + // calculate steps + // ((2^w)-1)*keysize + keysize + 1 / (2^h -1) + + // initialize arrays + this.privateKeyOTS = statByte[0]; + this.seed = statByte[1]; + this.concHashs = statByte[2]; + this.leaf = statByte[3]; + } + + /** + * The constructor precomputes some needed variables for ditributed leaf + * calculation + * + * @param name + * an array of strings, containing the name of the used hash + * function and PRNG and the name of the corresponding + * provider + * @param w + * the winterniz parameter of that tree the leaf is computed + * for + * @param numLeafs + * the number of leafs of the tree from where the distributed + * computation is called + */ + public GMSSLeaf(String[] name, int w, int numLeafs) { + this.w = w; + + CoreRegistry.registerAlgorithms(); + try { + messDigestOTS = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException(": message digest " + name[0] + + " not found in " + name[1]); + } + + gmssRandom = new GMSSRandom(messDigestOTS); + + // calulate keysize for private key and the help array + mdsize = messDigestOTS.getDigestLength(); + int mdsizeBit = mdsize << 3; + int messagesize = (int) Math.ceil((double) (mdsizeBit) / (double) w); + int checksumsize = getLog((messagesize << w) + 1); + this.keysize = messagesize + + (int) Math.ceil((double) checksumsize / (double) w); + this.two_power_w = 1 << w; + + // calculate steps + // ((2^w)-1)*keysize + keysize + 1 / (2^h -1) + this.steps = (int) Math + .ceil((double) (((1 << w) - 1) * keysize + 1 + keysize) + / (double) (numLeafs)); + + // initialize arrays + this.seed = new byte[mdsize]; + this.leaf = new byte[mdsize]; + this.privateKeyOTS = new byte[mdsize]; + this.concHashs = new byte[mdsize * keysize]; + } + + /** + * initialize the distributed leaf calculation reset i,j and compute OTSseed + * with seed0 + * + * @param seed0 + * the starting seed + */ + public void initLeafCalc(byte[] seed0) { + this.i = 0; + this.j = 0; + byte[] dummy = new byte[mdsize]; + System.arraycopy(seed0, 0, dummy, 0, seed.length); + this.seed = gmssRandom.nextSeed(dummy); + } + + /** + * Processes steps steps of distributed leaf calculation + * + * @return true if leaf is completed, else false + */ + public boolean updateLeafCalc() { + // steps times do + for (int s = 0; s < steps; s++) { + + if (i == keysize && j == two_power_w - 1) { // [3] at last hash the + // concatenation + messDigestOTS.update(concHashs); + leaf = messDigestOTS.digest(); + return true; // leaf fineshed + } else if (i == 0 || j == two_power_w - 1) { // [1] at the + // beginning and + // when [2] is + // finished: get the + // next private key + // part + i++; + j = 0; + // get next privKey part + this.privateKeyOTS = gmssRandom.nextSeed(seed); + } else { // [2] hash the privKey part + messDigestOTS.update(privateKeyOTS); + privateKeyOTS = messDigestOTS.digest(); + j++; + if (j == two_power_w - 1) { // after w hashes add to the + // concatenated array + System.arraycopy(privateKeyOTS, 0, concHashs, mdsize + * (i - 1), mdsize); + } + } + } + + return false; // leaf not finished yet + } + + /** + * Returns the leaf value. + * + * @return the leaf value + */ + public byte[] getLeaf() { + return leaf; + } + + /** + * This method returns the least integer that is greater or equal to the + * logarithm to the base 2 of an integer intValue. + * + * @param intValue + * an integer + * @return The least integer greater or equal to the logarithm to the base 2 + * of intValue + */ + private int getLog(int intValue) { + int log = 1; + int i = 2; + while (i < intValue) { + i <<= 1; + log++; + } + return log; + } + + /** + * Returns the status byte array used by the GMSSPrivateKeyASN.1 class + * + * @return The status bytes + */ + public byte[][] getStatByte() { + + byte[][] statByte = new byte[4][]; + statByte[0] = new byte[mdsize]; + statByte[1] = new byte[mdsize]; + statByte[2] = new byte[mdsize * keysize]; + statByte[3] = new byte[mdsize]; + statByte[0] = privateKeyOTS; + statByte[1] = seed; + statByte[2] = concHashs; + statByte[3] = leaf; + + return statByte; + } + + /** + * Returns the status int array used by the GMSSPrivateKeyASN.1 class + * + * @return The status ints + */ + public int[] getStatInt() { + + int[] statInt = new int[4]; + statInt[0] = i; + statInt[1] = j; + statInt[2] = steps; + statInt[3] = w; + return statInt; + } + + /** + * Returns a String representation of the main part of this element + * + * @return a String representation of the main part of this element + */ + public String toString() { + String out = ""; + + for (int i = 0; i < 4; i++) { + out = out + this.getStatInt()[i] + " "; + } + out = out + " " + this.mdsize + " " + this.keysize + " " + + this.two_power_w + " "; + + byte[][] temp = this.getStatByte(); + for (int i = 0; i < 4; i++) { + if (temp[i] != null) { + out = out + ByteUtils.toHexString(temp[i]) + " "; + } else + out = out + "null "; + } + return out; + } + +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSParameterSpec.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSParameterSpec.java new file mode 100644 index 00000000..0a79600e --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSParameterSpec.java @@ -0,0 +1,108 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; + +/** + * This class provides a specification for the GMSS parameters that are used by + * the GMSSKeyPairGenerator and GMSSSignature classes. + * + * @author Sebastian Blume, Michael Schneider + * + * @see GMSSKeyPairGenerator + * @see GMSSSignature + */ +public class GMSSParameterSpec implements AlgorithmParameterSpec { + + /** + * The number of authentication tree layers. numOfLevels is then equal to + * the length of heightOfTrees and to the length winternitzParameter + */ + private int numberOfLayers; + + /** + * The height of the authentication trees of each layer. + */ + private int[] heightOfTrees; + + /** + * The Winternitz Parameter 'w' of each layer. + */ + private int[] winternitzParameter; + + /** + * The parameter K needed for the authentication path computation + */ + private int[] K; + + /** + * The constructor for the parameters of the GMSSKeyPairGenerator. + *

+ * + * @param layers + * the number of authentication tree layers + * @param heightOfTrees + * the height of the authentication trees + * @param winternitzParameter + * the Winternitz Parameter 'w' of each layer + * @param K + * parameter for authpath computation + */ + public GMSSParameterSpec(int layers, int[] heightOfTrees, + int[] winternitzParameter, int[] K) { + this.numberOfLayers = layers; + this.heightOfTrees = heightOfTrees; + this.winternitzParameter = winternitzParameter; + this.K = K; + } + + /** + * The constructor + * + * @param gmssParSet + * an instance of GMSSParameterset + */ + public GMSSParameterSpec(GMSSParameterset gmssParSet) { + this.numberOfLayers = gmssParSet.getNumOfLayers(); + this.heightOfTrees = gmssParSet.getHeightOfTrees(); + this.winternitzParameter = gmssParSet.getWinternitzParameter(); + this.K = gmssParSet.getK(); + } + + /** + * Returns the number of layers of the authentication trees. + * + * @return The number of layers of the authentication trees. + */ + public int getNumOfLayers() { + return numberOfLayers; + } + + /** + * Returns the array of height (for each layer) of the authentication trees + * + * @return The array of height (for each layer) of the authentication trees + */ + public int[] getHeightOfTrees() { + return heightOfTrees; + } + + /** + * Returns the array of Winternitz parameters (for each layer) of the + * authentication trees + * + * @return The array of Winternitz parameters (for each layer) of the + * authentication trees + */ + public int[] getWinternitzParameter() { + return winternitzParameter; + } + + /** + * Returns the parameter K needed for authentication path computation + * + * @return The parameter K needed for authentication path computation + */ + public int[] getK() { + return K; + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSParameterset.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSParameterset.java new file mode 100644 index 00000000..81fcf956 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSParameterset.java @@ -0,0 +1,118 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.exceptions.InvalidParameterException; + +/** + * This class provides a specification for the GMSS parameters that are used by + * the GMSSKeyPairGenerator and GMSSSignature classes. + * + * @author Sebastian Blume, Michael Schneider + * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSKeyPairGenerator + * @see de.flexiprovider.pqc.hbc.gmss.GMSSSignature + */ +public class GMSSParameterset { + + /** + * The number of authentication tree layers. + */ + private int numOfLayers; + + /** + * The height of the authentication trees of each layer. + */ + private int[] heightOfTrees; + + /** + * The Winternitz Parameter 'w' of each layer. + */ + private int[] winternitzParameter; + + /** + * The parameter K needed for the authentication path computation + */ + private int[] K; + + /** + * The constructor for the parameters of the GMSSKeyPairGenerator. + *

+ * + * @param layers + * the number of authentication tree layers + * @param heightOfTrees + * the height of the authentication trees + * @param winternitzParameter + * the Winternitz Parameter 'w' of each layer + * @param K + * parameter for authpath computation + */ + public GMSSParameterset(int layers, int[] heightOfTrees, + int[] winternitzParameter, int[] K) + throws InvalidParameterException { + boolean valid = true; + String errMsg = ""; + this.numOfLayers = layers; + if ((numOfLayers != winternitzParameter.length) + || (numOfLayers != heightOfTrees.length) + || (numOfLayers != K.length)) { + valid = false; + errMsg = "Unexpected parameterset format"; + } + for (int i = 0; i < numOfLayers; i++) { + if ((K[i] < 2) || ((heightOfTrees[i] - K[i]) % 2 != 0)) { + valid = false; + errMsg = "Wrong parameter K (K >= 2 and H-K even required)!"; + } + + if ((heightOfTrees[i] < 4) || (winternitzParameter[i] < 2)) { + valid = false; + errMsg = "Wrong parameter H or w (H > 3 and w > 1 required)!"; + } + } + + if (valid) { + this.heightOfTrees = heightOfTrees; + this.winternitzParameter = winternitzParameter; + this.K = K; + } else + throw new InvalidParameterException(errMsg); + } + + /** + * Returns the number of levels of the authentication trees. + * + * @return The number of levels of the authentication trees. + */ + public int getNumOfLayers() { + return numOfLayers; + } + + /** + * Returns the array of height (for each layer) of the authentication trees + * + * @return The array of height (for each layer) of the authentication trees + */ + public int[] getHeightOfTrees() { + return heightOfTrees; + } + + /** + * Returns the array of WinternitzParameter (for each layer) of the + * authentication trees + * + * @return The array of WinternitzParameter (for each layer) of the + * authentication trees + */ + public int[] getWinternitzParameter() { + return winternitzParameter; + } + + /** + * Returns the parameter K needed for authentication path computation + * + * @return The parameter K needed for authentication path computation + */ + public int[] getK() { + return K; + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKey.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKey.java new file mode 100644 index 00000000..322ca524 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKey.java @@ -0,0 +1,1390 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.util.Vector; + +import codec.asn1.ASN1Null; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1Type; +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.SignatureException; +import de.flexiprovider.api.keys.PrivateKey; +import de.flexiprovider.common.util.ASN1Tools; +import de.flexiprovider.common.util.ByteUtils; + +/** + * This class implements a GMSS private key and is usually initiated by the GMSSKeyPairGenerator. + * + * @author Michael Schneider, Sebastian Blume + * @see de.flexiprovider.pqc.hbc.gmss.GMSSKeyPairGenerator + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKeySpec + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKeyASN1 + */ +public class GMSSPrivateKey extends PrivateKey { + + /** + * An array of tree index of each layer + */ + private int[] index; + + /** + * Array of the seeds for the current trees of each layer + */ + private byte[][] currentSeeds; + + /** + * Array of the seeds for the trees after next (TREE++) of each layer + */ + private byte[][] nextNextSeeds; + + /** + * Array of the authentication paths of the current trees of each layer + */ + private byte[][][] currentAuthPaths; + + /** + * Array of the authentication paths of the next trees (TREE+) of each layer + */ + private byte[][][] nextAuthPaths; + + /** + * TREEHASH instances for the authentication path algorithm + */ + private Treehash[][] currentTreehash; + + /** + * TREEHASH instances for the authentication path algorithm used for the + * following tree (TREE+) + */ + private Treehash[][] nextTreehash; + + /** + * The KEEP arrays for the authentication path algorithm + */ + private byte[][][] keep; + + /** + * The stack for the authentication path algorithm + */ + private Vector[] currentStack; + + /** + * The stack for the authentication path algorithm used for the following + * tree (TREE+) + */ + private Vector[] nextStack; + + /** + * The RETAIN stacks for the authentication path algorithm + */ + private Vector[][] currentRetain; + + /** + * The RETAIN stacks for the authentication path algorithm used for the + * following tree (TREE+) + */ + private Vector[][] nextRetain; + + /** + * An array of the upcoming leaf of the tree after next (TREE++) of each + * layer (LEAF++) + */ + private GMSSLeaf[] nextNextLeaf; + + /** + * An array of the upcoming leaf of the tree over the actual tree + */ + private GMSSLeaf[] upperLeaf; + + /** + * An array of the leafs of the upcoming treehashs of the tree over the + * actual tree + */ + private GMSSLeaf[] upperTreehashLeaf; + + /** + * For each layer, this array depicts which treehash instance is the next + * one to receive an update. + */ + private int[] minTreehash; + + /** + * An array of the upcoming signature of the root of next tree (TREE+) of + * each layer (SIG+) + */ + private GMSSRootSig[] nextRootSig; + + /** + * The GMSS Parameterset + */ + private GMSSParameterset gmssPS; + + /** + * An array of the heights of the authentication trees of each layer + */ + private int[] heightOfTrees; + + /** + * An array of the Winternitz parameter 'w' of each layer + */ + private int[] otsIndex; + + /** + * The parameter K needed for the authentication path computation + */ + private int[] K; + + /** + * the number of Layers + */ + private int numLayer; + + /** + * An array of the roots of the next subtrees (ROOTS+) + */ + private byte[][] nextRoot; + + /** + * An array of the roots of the subtrees after next (ROOTS ++) + */ + private GMSSRootCalc[] nextNextRoot; + + /** + * An array of the signatures of the roots of the current subtrees (SIG) + */ + private byte[][] currentRootSig; + + /** + * The hash function used to construct the authentication trees + */ + private MessageDigest messDigestTrees; + + /** + * The message digest length + */ + private int mdLength; + + /** + * The number of leafs of one tree of each layer + */ + private int[] numLeafs; + + /** + * An array of strings containing the name of the hash function used to + * construct the authentication trees and used by the OTS. + */ + private String[] algNames = new String[2]; + + /** + * The PRNG used for private key generation + */ + private GMSSRandom gmssRandom; + + /** + * Generates a new GMSS private key + * + * @param currentSeed + * seed for the generation of private OTS keys for the + * current subtrees + * @param nextNextSeed + * seed for the generation of private OTS keys for the next + * subtrees + * @param currentAuthPath + * array of current authentication paths + * @param nextAuthPath + * array of next authentication paths + * @param currentTreehash + * array of current treehash instances + * @param nextTreehash + * array of next treehash instances + * @param currentStack + * array of current shared stacks + * @param nextStack + * array of next shared stacks + * @param currentRetain + * array of current retain stacks + * @param nextRetain + * array of next retain stacks + * @param nextRoot + * the roots of the next subtree + * @param currentRootSig + * array of signatures of the roots of the current subtrees + * @param gmssParameterset + * the GMSS Parameterset + * @param algNames + * An array of strings, containing the name of the used hash + * function and the name of the corresponding provider + * @see de.flexiprovider.pqc.hbc.gmss.GMSSKeyPairGenerator + */ + + protected GMSSPrivateKey(byte[][] currentSeed, byte[][] nextNextSeed, + byte[][][] currentAuthPath, byte[][][] nextAuthPath, + Treehash[][] currentTreehash, Treehash[][] nextTreehash, + Vector[] currentStack, Vector[] nextStack, + Vector[][] currentRetain, Vector[][] nextRetain, byte[][] nextRoot, + byte[][] currentRootSig, GMSSParameterset gmssParameterset, + String[] algNames) { + this(null, currentSeed, nextNextSeed, currentAuthPath, nextAuthPath, + null, currentTreehash, nextTreehash, currentStack, nextStack, + currentRetain, nextRetain, null, null, null, null, nextRoot, + null, currentRootSig, null, gmssParameterset, algNames); + } + + /** + * Constructor + * + * @param gmssPrivKeySpec + * a valid GMSS privateKeySpec + */ + protected GMSSPrivateKey(GMSSPrivateKeySpec gmssPrivKeySpec) { + this(gmssPrivKeySpec.getIndex(), gmssPrivKeySpec.getCurrentSeed(), + gmssPrivKeySpec.getNextNextSeed(), gmssPrivKeySpec + .getCurrentAuthPath(), gmssPrivKeySpec + .getNextAuthPath(), gmssPrivKeySpec.getKeep(), + gmssPrivKeySpec.getCurrentTreehash(), gmssPrivKeySpec + .getNextTreehash(), gmssPrivKeySpec.getCurrentStack(), + gmssPrivKeySpec.getNextStack(), gmssPrivKeySpec + .getCurrentRetain(), gmssPrivKeySpec.getNextRetain(), + gmssPrivKeySpec.getNextNextLeaf(), gmssPrivKeySpec + .getUpperLeaf(), + gmssPrivKeySpec.getUpperTreehashLeaf(), gmssPrivKeySpec + .getMinTreehash(), gmssPrivKeySpec.getNextRoot(), + gmssPrivKeySpec.getNextNextRoot(), gmssPrivKeySpec + .getCurrentRootSig(), gmssPrivKeySpec.getNextRootSig(), + gmssPrivKeySpec.getGmssPS(), gmssPrivKeySpec.getAlgNames()); + } + + /** + * Generates a new GMSS private key + * + * @param index + * tree indices + * @param currentSeeds + * seed for the generation of private OTS keys for the + * current subtrees (TREE) + * @param nextNextSeeds + * seed for the generation of private OTS keys for the + * subtrees after next (TREE++) + * @param currentAuthPaths + * array of current authentication paths (AUTHPATH) + * @param nextAuthPaths + * array of next authentication paths (AUTHPATH+) + * @param keep + * keep array for the authPath algorithm + * @param currentTreehash + * treehash for authPath algorithm of current tree + * @param nextTreehash + * treehash for authPath algorithm of next tree (TREE+) + * @param currentStack + * shared stack for authPath algorithm of current tree + * @param nextStack + * shared stack for authPath algorithm of next tree (TREE+) + * @param currentRetain + * retain stack for authPath algorithm of current tree + * @param nextRetain + * retain stack for authPath algorithm of next tree (TREE+) + * @param nextNextLeaf + * array of upcoming leafs of the tree after next (LEAF++) of + * each layer + * @param upperLeaf + * needed for precomputation of upper nodes + * @param upperTreehashLeaf + * needed for precomputation of upper treehash nodes + * @param nextRoot + * the roots of the next trees (ROOT+) + * @param nextNextRoot + * the roots of the tree after next (ROOT++) + * @param currentRootSig + * array of signatures of the roots of the current subtrees + * (SIG) + * @param nextRootSig + * array of signatures of the roots of the next subtree + * (SIG+) + * @param gmssParameterset + * the GMSS Parameterset + * @param algNames + * An array of strings, containing the name of the used hash + * function and the name of the corresponding provider + */ + protected GMSSPrivateKey(int[] index, byte[][] currentSeeds, + byte[][] nextNextSeeds, byte[][][] currentAuthPaths, + byte[][][] nextAuthPaths, byte[][][] keep, + Treehash[][] currentTreehash, Treehash[][] nextTreehash, + Vector[] currentStack, Vector[] nextStack, + Vector[][] currentRetain, Vector[][] nextRetain, + GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf, + GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot, + GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig, + GMSSRootSig[] nextRootSig, GMSSParameterset gmssParameterset, + String[] algNames) { + + // construct message digest + try { + this.messDigestTrees = Registry.getMessageDigest(algNames[0]); + this.mdLength = messDigestTrees.getDigestLength(); + + } catch (NoSuchAlgorithmException nsae) { + String errorMsg = "message digest " + algNames[0] + + " not found in " + algNames[1] + " or secure random " + + algNames[4] + " not found in " + algNames[5]; + throw new RuntimeException(errorMsg); + } + + // Parameter + this.gmssPS = gmssParameterset; + this.otsIndex = gmssParameterset.getWinternitzParameter(); + this.K = gmssParameterset.getK(); + this.heightOfTrees = gmssParameterset.getHeightOfTrees(); + // initialize numLayer + this.numLayer = gmssPS.getNumOfLayers(); + + // initialize index if null + if (index == null) { + this.index = new int[numLayer]; + for (int i = 0; i < numLayer; i++) { + this.index[i] = 0; + } + } else { + this.index = index; + } + + this.currentSeeds = currentSeeds; + this.nextNextSeeds = nextNextSeeds; + + this.currentAuthPaths = currentAuthPaths; + this.nextAuthPaths = nextAuthPaths; + + // initialize keep if null + if (keep == null) { + this.keep = new byte[numLayer][][]; + for (int i = 0; i < numLayer; i++) { + this.keep[i] = new byte[(int) Math.floor(heightOfTrees[i] / 2)][mdLength]; + } + } else { + this.keep = keep; + } + + // initialize stack if null + if (currentStack == null) { + this.currentStack = new Vector[numLayer]; + for (int i = 0; i < numLayer; i++) { + this.currentStack[i] = new Vector(); + } + } else { + this.currentStack = currentStack; + } + + // initialize nextStack if null + if (nextStack == null) { + this.nextStack = new Vector[numLayer - 1]; + for (int i = 0; i < numLayer - 1; i++) { + this.nextStack[i] = new Vector(); + } + } else { + this.nextStack = nextStack; + } + + this.currentTreehash = currentTreehash; + this.nextTreehash = nextTreehash; + + this.currentRetain = currentRetain; + this.nextRetain = nextRetain; + + this.nextRoot = nextRoot; + + this.algNames = algNames; + + if (nextNextRoot == null) { + this.nextNextRoot = new GMSSRootCalc[numLayer - 1]; + for (int i = 0; i < numLayer - 1; i++) { + this.nextNextRoot[i] = new GMSSRootCalc( + this.heightOfTrees[i + 1], this.K[i + 1], this.algNames); + } + } else { + this.nextNextRoot = nextNextRoot; + } + this.currentRootSig = currentRootSig; + + // calculate numLeafs + numLeafs = new int[numLayer]; + for (int i = 0; i < numLayer; i++) { + numLeafs[i] = 1 << heightOfTrees[i]; + } + // construct PRNG + this.gmssRandom = new GMSSRandom(messDigestTrees); + + if (numLayer > 1) { + // construct the nextNextLeaf (LEAFs++) array for upcoming leafs in + // tree after next (TREE++) + if (nextNextLeaf == null) { + this.nextNextLeaf = new GMSSLeaf[numLayer - 2]; + for (int i = 0; i < numLayer - 2; i++) { + this.nextNextLeaf[i] = new GMSSLeaf(algNames, + otsIndex[i + 1], numLeafs[i + 2]); + this.nextNextLeaf[i].initLeafCalc(this.nextNextSeeds[i]); + } + } else { + this.nextNextLeaf = nextNextLeaf; + } + } else { + this.nextNextLeaf = new GMSSLeaf[0]; + } + + // construct the upperLeaf array for upcoming leafs in tree over the + // actual + if (upperLeaf == null) { + this.upperLeaf = new GMSSLeaf[numLayer - 1]; + for (int i = 0; i < numLayer - 1; i++) { + this.upperLeaf[i] = new GMSSLeaf(algNames, otsIndex[i], + numLeafs[i + 1]); + this.upperLeaf[i].initLeafCalc(this.currentSeeds[i]); + } + } else { + this.upperLeaf = upperLeaf; + } + + // construct the leafs for upcoming leafs in treehashs in tree over the + // actual + if (upperTreehashLeaf == null) { + this.upperTreehashLeaf = new GMSSLeaf[numLayer - 1]; + for (int i = 0; i < numLayer - 1; i++) { + this.upperTreehashLeaf[i] = new GMSSLeaf(algNames, otsIndex[i], + numLeafs[i + 1]); + } + } else { + this.upperTreehashLeaf = upperTreehashLeaf; + } + + if (minTreehash == null) { + this.minTreehash = new int[numLayer - 1]; + for (int i = 0; i < numLayer - 1; i++) { + this.minTreehash[i] = -1; + } + } else { + this.minTreehash = minTreehash; + } + + // construct the nextRootSig (RootSig++) + byte[] dummy = new byte[mdLength]; + byte[] OTSseed = new byte[mdLength]; + if (nextRootSig == null) { + this.nextRootSig = new GMSSRootSig[numLayer - 1]; + for (int i = 0; i < numLayer - 1; i++) { + System.arraycopy(currentSeeds[i], 0, dummy, 0, mdLength); + gmssRandom.nextSeed(dummy); + OTSseed = gmssRandom.nextSeed(dummy); + this.nextRootSig[i] = new GMSSRootSig(algNames, otsIndex[i], + heightOfTrees[i + 1]); + this.nextRootSig[i].initSign(OTSseed, nextRoot[i]); + } + } else { + this.nextRootSig = nextRootSig; + } + } + + /** + * This method updates the GMSS private key for the next signature + * + * @param layer + * the layer where the next key is processed + * + */ + public void nextKey(int layer) { + // only for lowest layer ( other layers indices are raised in nextTree() + // method ) + if (layer == numLayer - 1) { + index[layer]++; + } // else System.out.println(" --- nextKey on layer " + layer + " + // index is now : " + index[layer]); + + // if tree of this layer is depleted + if (index[layer] == numLeafs[layer]) { + if (numLayer != 1) { + nextTree(layer); + index[layer] = 0; + } + } else { + updateKey(layer); + } + } + + /** + * Switch to next subtree if the current one is depleted + * + * @param layer + * the layer where the next tree is processed + * + */ + private void nextTree(int layer) { + // System.out.println("NextTree method called on layer " + layer); + // dont create next tree for the top layer + if (layer > 0) { + // raise index for upper layer + index[layer - 1]++; + + // test if it is already the last tree + boolean lastTree = true; + int z = layer; + do { + z--; + if (index[z] < numLeafs[z]) { + lastTree = false; + } + } while (lastTree && (z > 0)); + + // only construct next subtree if last one is not already in use + if (!lastTree) { + gmssRandom.nextSeed(currentSeeds[layer]); + + // last step of distributed signature calculation + try { + nextRootSig[layer - 1].updateSign(); + } catch (SignatureException se) { + } + + // last step of distributed leaf calculation for nextNextLeaf + if (layer > 1) { + nextNextLeaf[layer - 1 - 1].updateLeafCalc(); + } + + // last step of distributed leaf calculation for upper leaf + upperLeaf[layer - 1].updateLeafCalc(); + + // last step of distributed leaf calculation for all treehashs + + if (minTreehash[layer - 1] >= 0) { + this.upperTreehashLeaf[layer - 1].updateLeafCalc(); + byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf(); + // if update is required use the precomputed leaf to update + // treehash + try { + currentTreehash[layer - 1][minTreehash[layer - 1]] + .update(this.gmssRandom, leaf); + // System.out.println("UUUpdated TH " + + // minTreehash[layer - 1]); + if (currentTreehash[layer - 1][minTreehash[layer - 1]] + .wasFinished()) { + // System.out.println("FFFinished TH " + + // minTreehash[layer - 1]); + } + } catch (Exception e) { + System.out.println(e); + } + } + + // last step of nextNextAuthRoot calculation + this.updateNextNextAuthRoot(layer); + + // ******************************************************** / + + // NOW: advance to next tree on layer 'layer' + + // NextRootSig --> currentRootSigs + this.currentRootSig[layer - 1] = nextRootSig[layer - 1] + .getSig(); + + // ----------------------- + + // nextTreehash --> currentTreehash + // nextNextTreehash --> nextTreehash + for (int i = 0; i < heightOfTrees[layer] - K[layer]; i++) { + this.currentTreehash[layer][i] = this.nextTreehash[layer - 1][i]; + this.nextTreehash[layer - 1][i] = this.nextNextRoot[layer - 1] + .getTreehash()[i]; + } + + // NextAuthPath --> currentAuthPath + // nextNextAuthPath --> nextAuthPath + for (int i = 0; i < heightOfTrees[layer]; i++) { + System.arraycopy(nextAuthPaths[layer - 1][i], 0, + currentAuthPaths[layer][i], 0, mdLength); + System.arraycopy(nextNextRoot[layer - 1].getAuthPath()[i], + 0, nextAuthPaths[layer - 1][i], 0, mdLength); + } + + // nextRetain --> currentRetain + // nextNextRetain --> nextRetain + for (int i = 0; i < K[layer] - 1; i++) { + this.currentRetain[layer][i] = this.nextRetain[layer - 1][i]; + this.nextRetain[layer - 1][i] = this.nextNextRoot[layer - 1] + .getRetain()[i]; + } + + // nextStack --> currentStack + this.currentStack[layer] = this.nextStack[layer - 1]; + // nextNextStack --> nextStack + this.nextStack[layer - 1] = this.nextNextRoot[layer - 1] + .getStack(); + + // nextNextRoot --> nextRoot + this.nextRoot[layer - 1] = this.nextNextRoot[layer - 1] + .getRoot(); + // ----------------------- + + // ----------------- + byte[] OTSseed = new byte[mdLength]; + byte[] dummy = new byte[mdLength]; + // gmssRandom.setSeed(currentSeeds[layer]); + System + .arraycopy(currentSeeds[layer - 1], 0, dummy, 0, + mdLength); + OTSseed = gmssRandom.nextSeed(dummy); // only need OTSSeed + OTSseed = gmssRandom.nextSeed(dummy); + OTSseed = gmssRandom.nextSeed(dummy); + // nextWinSig[layer-1]=new + // GMSSWinSig(OTSseed,algNames,otsIndex[layer-1],heightOfTrees[layer],nextRoot[layer-1]); + nextRootSig[layer - 1].initSign(OTSseed, nextRoot[layer - 1]); + + // nextKey for upper layer + nextKey(layer - 1); + } + } + } + + /** + * This method computes the authpath (AUTH) for the current tree, + * Additionally the root signature for the next tree (SIG+), the authpath + * (AUTH++) and root (ROOT++) for the tree after next in layer + * layer, and the LEAF++^1 for the next next tree in the + * layer above are updated This method is used by nextKey() + * + * @param layer + */ + private void updateKey(int layer) { + // ----------current tree processing of actual layer--------- + // compute upcoming authpath for current Tree (AUTH) + computeAuthPaths(layer); + + // -----------distributed calculations part------------ + // not for highest tree layer + if (layer > 0) { + + // compute (partial) next leaf on TREE++ (not on layer 1 and 0) + if (layer > 1) { + nextNextLeaf[layer - 1 - 1].updateLeafCalc(); + } + + // compute (partial) next leaf on tree above (not on layer 0) + upperLeaf[layer - 1].updateLeafCalc(); + + // compute (partial) next leaf for all treehashs on tree above (not + // on layer 0) + + int t = (int) Math + .floor((double) (this.getNumLeafs(layer) * 2) + / (double) (this.heightOfTrees[layer - 1] - this.K[layer - 1])); + + if (index[layer] % t == 1) { + // System.out.println(" layer: " + layer + " index: " + + // index[layer] + " t : " + t); + + // take precomputed node for treehash update + // ------------------------------------------------ + if (index[layer] > 1 && minTreehash[layer - 1] >= 0) { + byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf(); + // if update is required use the precomputed leaf to update + // treehash + try { + currentTreehash[layer - 1][minTreehash[layer - 1]] + .update(this.gmssRandom, leaf); + // System.out.println("Updated TH " + minTreehash[layer + // - 1]); + if (currentTreehash[layer - 1][minTreehash[layer - 1]] + .wasFinished()) { + // System.out.println("Finished TH " + + // minTreehash[layer - 1]); + } + } catch (Exception e) { + System.out.println(e); + } + // ------------------------------------------------ + } + + // initialize next leaf precomputation + // ------------------------------------------------ + + // get lowest index of treehashs + this.minTreehash[layer - 1] = getMinTreehashIndex(layer - 1); + + if (this.minTreehash[layer - 1] >= 0) { + // initialize leaf + byte[] seed = this.currentTreehash[layer - 1][this.minTreehash[layer - 1]] + .getSeedActive(); + this.upperTreehashLeaf[layer - 1] = new GMSSLeaf( + this.algNames, this.otsIndex[layer - 1], t); + this.upperTreehashLeaf[layer - 1].initLeafCalc(seed); + this.upperTreehashLeaf[layer - 1].updateLeafCalc(); + // System.out.println("restarted treehashleaf (" + (layer - + // 1) + "," + this.minTreehash[layer - 1] + ")"); + } + // ------------------------------------------------ + + } else { + // update the upper leaf for the treehash one step + if (this.minTreehash[layer - 1] >= 0) { + this.upperTreehashLeaf[layer - 1].updateLeafCalc(); + // if (minTreehash[layer - 1] > 3) + // System.out.print("#"); + } + } + + // compute (partial) the signature of ROOT+ (RootSig+) (not on top + // layer) + try { + nextRootSig[layer - 1].updateSign(); + } catch (SignatureException se) { + } + + // compute (partial) AUTHPATH++ & ROOT++ (not on top layer) + if (index[layer] == 1) { + // init root and authpath calculation for tree after next + // (AUTH++, ROOT++) + this.nextNextRoot[layer - 1].initialize(new Vector()); + } + + // update root and authpath calculation for tree after next (AUTH++, + // ROOT++) + this.updateNextNextAuthRoot(layer); + } + // ----------- end distributed calculations part----------------- + } + + /** + * This method returns the index of the next Treehash instance that should + * receive an update + * + * @param layer + * the layer of the GMSS tree + * @return index of the treehash instance that should get the update + */ + private int getMinTreehashIndex(int layer) { + int minTreehash = -1; + for (int h = 0; h < heightOfTrees[layer] - K[layer]; h++) { + if (currentTreehash[layer][h].wasInitialized() + && !currentTreehash[layer][h].wasFinished()) { + if (minTreehash == -1) { + minTreehash = h; + } else if (currentTreehash[layer][h].getLowestNodeHeight() < currentTreehash[layer][minTreehash] + .getLowestNodeHeight()) { + minTreehash = h; + } + } + } + return minTreehash; + } + + /** + * Computes the upcoming currentAuthpath of layer layer using + * the revisited authentication path computation of Dahmen/Schneider 2008 + * + * @param layer + * the actual layer + */ + private void computeAuthPaths(int layer) { + + int Phi = index[layer]; + int H = heightOfTrees[layer]; + int K = this.K[layer]; + + // update all nextSeeds for seed scheduling + for (int i = 0; i < H - K; i++) { + currentTreehash[layer][i].updateNextSeed(gmssRandom); + } + + // STEP 1 of Algorithm + int Tau = heightOfPhi(Phi); + + byte[] OTSseed = new byte[mdLength]; + OTSseed = gmssRandom.nextSeed(currentSeeds[layer]); + + // STEP 2 of Algorithm + // if phi's parent on height tau + 1 if left node, store auth_tau + // in keep_tau. + // TODO check it, formerly was + // int L = Phi / (int) Math.floor(Math.pow(2, Tau + 1)); + // L %= 2; + int L = (Phi >>> (Tau + 1)) & 1; + + byte[] tempKeep = new byte[mdLength]; + // store the keep node not in keep[layer][tau/2] because it might be in + // use + // wait until the space is freed in step 4a + if (Tau < H - 1 && L == 0) { + System.arraycopy(currentAuthPaths[layer][Tau], 0, tempKeep, 0, + mdLength); + } + + byte[] help = new byte[mdLength]; + // STEP 3 of Algorithm + // if phi is left child, compute and store leaf for next currentAuthPath + // path, + // (obtained by veriying current signature) + if (Tau == 0) { + // LEAFCALC !!! + if (layer == numLayer - 1) { // lowest layer computes the + // necessary leaf completely at this + // time + WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed, + algNames, otsIndex[layer]); + help = ots.getPublicKey(); + } else { // other layers use the precomputed leafs in + // nextNextLeaf + byte[] dummy = new byte[mdLength]; + System.arraycopy(currentSeeds[layer], 0, dummy, 0, mdLength); + gmssRandom.nextSeed(dummy); + help = upperLeaf[layer].getLeaf(); + this.upperLeaf[layer].initLeafCalc(dummy); + + // WinternitzOTSVerify otsver = new + // WinternitzOTSVerify(algNames, otsIndex[layer]); + // byte[] help2 = otsver.Verify(currentRoot[layer], + // currentRootSig[layer]); + // System.out.println(" --- " + layer + " " + + // ByteUtils.toHexString(help) + " " + + // ByteUtils.toHexString(help2)); + } + System.arraycopy(help, 0, currentAuthPaths[layer][0], 0, mdLength); + } else { + // STEP 4a of Algorithm + // get new left currentAuthPath node on height tau + byte[] toBeHashed = new byte[mdLength << 1]; + System.arraycopy(currentAuthPaths[layer][Tau - 1], 0, toBeHashed, + 0, mdLength); + // free the shared keep[layer][tau/2] + System.arraycopy(keep[layer][(int) Math.floor((Tau - 1) / 2)], 0, + toBeHashed, mdLength, mdLength); + messDigestTrees.update(toBeHashed); + currentAuthPaths[layer][Tau] = messDigestTrees.digest(); + + // STEP 4b and 4c of Algorithm + // copy right nodes to currentAuthPath on height 0..Tau-1 + for (int i = 0; i < Tau; i++) { + + // STEP 4b of Algorithm + // 1st: copy from treehashs + if (i < H - K) { + if (currentTreehash[layer][i].wasFinished()) { + System.arraycopy(currentTreehash[layer][i] + .getFirstNode(), 0, currentAuthPaths[layer][i], + 0, mdLength); + currentTreehash[layer][i].destroy(); + } else { + System.err + .println("Treehash (" + + layer + + "," + + i + + ") not finished when needed in AuthPathComputation"); + } + } + + // 2nd: copy precomputed values from Retain + if (i < H - 1 && i >= H - K) { + if (currentRetain[layer][i - (H - K)].size() > 0) { + // pop element from retain + System.arraycopy(currentRetain[layer][i - (H - K)] + .lastElement(), 0, currentAuthPaths[layer][i], + 0, mdLength); + currentRetain[layer][i - (H - K)] + .removeElementAt(currentRetain[layer][i + - (H - K)].size() - 1); + } + } + + // STEP 4c of Algorithm + // initialize new stack at heights 0..Tau-1 + if (i < H - K) { + // create stacks anew + int startPoint = Phi + 3 * (1 << i); + if (startPoint < numLeafs[layer]) { + // if (layer < 2) { + // System.out.println("initialized TH " + i + " on layer + // " + layer); + // } + currentTreehash[layer][i].initialize(); + } + } + } + } + + // now keep space is free to use + if (Tau < H - 1 && L == 0) { + System.arraycopy(tempKeep, 0, + keep[layer][(int) Math.floor(Tau / 2)], 0, mdLength); + } + + // only update empty stack at height h if all other stacks have + // tailnodes with height >h + // finds active stack with lowest node height, choses lower index in + // case of tie + + // on the lowest layer leafs must be computed at once, no precomputation + // is possible. So all treehash updates are done at once here + if (layer == numLayer - 1) { + for (int tmp = 1; tmp <= (H - K) / 2; tmp++) { + // index of the treehash instance that receives the next update + int minTreehash = getMinTreehashIndex(layer); + + // if active treehash is found update with a leaf + if (minTreehash >= 0) { + try { + byte[] seed = new byte[mdLength]; + System.arraycopy( + this.currentTreehash[layer][minTreehash] + .getSeedActive(), 0, seed, 0, mdLength); + byte[] seed2 = gmssRandom.nextSeed(seed); + WinternitzOTSignature ots = new WinternitzOTSignature( + seed2, this.algNames, this.otsIndex[layer]); + byte[] leaf = ots.getPublicKey(); + currentTreehash[layer][minTreehash].update( + this.gmssRandom, leaf); + } catch (Exception e) { + System.out.println(e); + } + } + } + } else { // on higher layers the updates are done later + this.minTreehash[layer] = getMinTreehashIndex(layer); + } + } + + /** + * Returns the largest h such that 2^h | Phi + * + * @param Phi + * the leaf index + * @return The largest h with 2^h | Phi if + * Phi!=0 else return -1 + */ + private int heightOfPhi(int Phi) { + if (Phi == 0) { + return -1; + } + int Tau = 0; + int modul = 1; + while (Phi % modul == 0) { + modul *= 2; + Tau += 1; + } + return Tau - 1; + } + + /** + * Updates the authentication path and root calculation for the tree after + * next (AUTH++, ROOT++) in layer layer + * + * @param layer + */ + private void updateNextNextAuthRoot(int layer) { + + byte[] OTSseed = new byte[mdLength]; + OTSseed = gmssRandom.nextSeed(nextNextSeeds[layer - 1]); + + // get the necessary leaf + if (layer == numLayer - 1) { // lowest layer computes the necessary + // leaf completely at this time + WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed, + algNames, otsIndex[layer]); + this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], ots + .getPublicKey()); + } else { // other layers use the precomputed leafs in nextNextLeaf + this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], + nextNextLeaf[layer - 1].getLeaf()); + this.nextNextLeaf[layer - 1].initLeafCalc(nextNextSeeds[layer - 1]); + } + } + + /** + * @return The name of the algorithm + */ + public String getAlgorithm() { + return "GMSS"; + } + + /** + * @return The detailed name of the algorithm + */ + public String[] getName() { + + return algNames; + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected ASN1ObjectIdentifier getOID() { + return new ASN1ObjectIdentifier(GMSSKeyFactory.OID); + } + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected ASN1Type getAlgParams() { + return new ASN1Null(); + } + + /** + * @return the keyData to encode in the SubjectPublicKeyInfo structure + */ + protected byte[] getKeyData() { + GMSSPrivateKeyASN1 mtsPrivateKey = new GMSSPrivateKeyASN1(index, + currentSeeds, nextNextSeeds, currentAuthPaths, nextAuthPaths, + keep, currentTreehash, nextTreehash, currentStack, nextStack, + currentRetain, nextRetain, nextNextLeaf, upperLeaf, + upperTreehashLeaf, minTreehash, nextRoot, nextNextRoot, + currentRootSig, nextRootSig, gmssPS, algNames); + return ASN1Tools.derEncode(mtsPrivateKey); + } + + /*************************************************************************** + * / Returns an initialized one-time signature object + * + * @return An initialized WinternitzOTSignature object + */ + /* + * public WinternitzOTSignature getOTSInstance() { //forwards currentSeed + * //gmssRandom.setSeed(currentSeeds[currentSeeds.length-1]);//secureRandom.setSeed(currentSeeds[currentSeeds.length-1]); + * byte[] OTSseed = new byte[mdLength]; byte[] dummy = new byte[mdLength]; + * System.arraycopy(currentSeeds[numLayer-1], 0, dummy, 0, mdLength); + * OTSseed = + * gmssRandom.nextSeed(dummy);//secureRandom.nextBytes(currentSeeds[currentSeeds.length-1]);secureRandom.nextBytes(OTSseed); + * + * WinternitzOTSignature ots = new WinternitzOTSignature( OTSseed, algNames, + * gmssPS.getWinternitzParameter()[currentSeeds.length-1]); //update 'first' + * every second time if (index[numLayer-1] % 2 == 0) { } return ots; } + */ + + /** + * @return The current indices array + */ + protected int[] getIndex() { + return index; + } + + /** + * @return The current index of layer i + */ + protected int getIndex(int i) { + return index[i]; + } + + /** + * @return The array of current seeds + */ + protected byte[][] getCurrentSeeds() { + return currentSeeds; + } + + /** + * @return The array of seeds after next (SEED++) + */ + protected byte[][] getNextNextSeeds() { + return nextNextSeeds; + } + + /** + * @return The current authentication path array + */ + protected byte[][][] getCurrentAuthPaths() { + return currentAuthPaths; + } + + /** + * @return The next authentication path array + */ + protected byte[][][] getNextAuthPaths() { + return nextAuthPaths; + } + + /** + * @return The current treehash instances + */ + protected Treehash[][] getCurrentTreehash() { + return currentTreehash; + } + + /** + * @return The next treehash instances + */ + protected Treehash[][] getNextTreehash() { + return nextTreehash; + } + + /** + * @return The current treehash instances + */ + protected Vector[] getCurrentStack() { + return currentStack; + } + + /** + * @return The next treehash instances + */ + protected Vector[] getNextStack() { + return nextStack; + } + + /** + * @return The current treehash instances + */ + protected Vector[][] getCurrentRetain() { + return currentRetain; + } + + /** + * @return The next treehash instances + */ + protected Vector[][] getNextRetain() { + return nextRetain; + } + + /** + * @return The number of leafs of each tree of layer i + */ + protected int getNumLeafs(int i) { + return numLeafs[i]; + } + + /** + * @return The array of number of leafs of a tree of each layer + */ + protected int[] getNumLeafs() { + return numLeafs; + } + + /** + * @return The stack array keep + */ + protected byte[][][] getKeep() { + return keep; + } + + /** + * @return An array of the GMSSLeafs of the tree after next of each layer + * (LEAF++) + */ + protected GMSSLeaf[] getNextNextLeaf() { + return nextNextLeaf; + } + + /** + * @return An array of the GMSSLeafs of the tree after next of each layer + * (LEAF++) + */ + protected GMSSLeaf[] getUpperLeaf() { + return upperLeaf; + } + + /** + * @return An array of the GMSSLeafs of the tree after next of each layer + * (LEAF++) + */ + protected GMSSLeaf[] getUpperTreehashLeaf() { + return upperTreehashLeaf; + } + + /** + * @return An array of the indices of the next treehashs to receive updates + */ + protected int[] getMinTreehash() { + return minTreehash; + } + + /** + * @return An array of roots of the next subtree of each layer (ROOT+) + */ + protected byte[][] getNextRoot() { + return nextRoot; + } + + /** + * @return An array of roots of the subtree after next of each layer + * (ROOT++) + */ + protected GMSSRootCalc[] getNextNextRoot() { + return nextNextRoot; + } + + /** + * @return An array of signatures of the current subtree roots of each layer + */ + protected byte[][] getCurrentRootSig() { + return currentRootSig; + } + + /** + * @return the GMSSParameterset + */ + protected GMSSParameterset getParameterset() { + return gmssPS; + } + + /** + * @return The one-time signature of the root of the current subtree + */ + protected byte[] getSubtreeRootSig(int i) { + return currentRootSig[i]; + } + + /** + * @return The one-time signatures of the next root (SIG+) + */ + + protected GMSSRootSig[] getNextRootSig() { + return nextRootSig; + } + + /** + * @return A human readable representation of main part of the key + */ + public String toString() { + GMSSUtilities gmssUtil = new GMSSUtilities(); + + String out = ""; + out = out + "tree indices : \n"; + for (int i = 0; i < index.length; i++) { + out = out + " " + i + ". index : " + index[i] + "\n"; + } + out = out + "current tree seeds : \n"; + for (int i = 0; i < currentSeeds.length; i++) { + out = out + " " + i + ". currentSeed : " + + ByteUtils.toHexString(currentSeeds[i]) + "\n"; + } + out = out + "next next tree seeds : \n"; + for (int i = 0; i < nextNextSeeds.length; i++) { + out = out + " " + i + ". nextNextSeed : " + + ByteUtils.toHexString(nextNextSeeds[i]) + "\n"; + } + out = out + "current tree authPaths : \n"; + for (int i = 0; i < currentAuthPaths.length; i++) { + out = out + + " " + + i + + ". currentAuthPath : " + + ByteUtils.toHexString(gmssUtil + .concatenateArray(currentAuthPaths[i])) + "\n"; + } + out = out + "next tree authPaths : \n"; + for (int i = 0; i < nextAuthPaths.length; i++) { + out = out + + " " + + i + + ". nextAuthPath : " + + ByteUtils.toHexString(gmssUtil + .concatenateArray(nextAuthPaths[i])) + "\n"; + } + out = out + "currentTreehash :\n"; + for (int i = 0; i < currentTreehash.length; i++) { + for (int j = 0; j < currentTreehash[i].length; j++) { + out = out + " (" + i + "," + j + ")." + + currentTreehash[i][j].toString() + "\n"; + } + } + out = out + "nextTreehash :\n"; + for (int i = 0; i < nextTreehash.length; i++) { + for (int j = 0; j < nextTreehash[i].length; j++) { + out = out + " (" + i + "," + j + ")." + + nextTreehash[i][j].toString() + "\n"; + } + } + out = out + "current tree stack : \n"; + for (int i = 0; i < currentStack.length; i++) { + out = out + " " + i; + for (int j = 0; j < currentStack[i].size(); j++) { + out = out + + " " + + j + + ". currentStack : " + + ByteUtils.toHexString((byte[]) currentStack[i] + .elementAt(j)); + } + out = out + "\n"; + } + out = out + "next tree stack : \n"; + for (int i = 0; i < nextStack.length; i++) { + out = out + " " + i; + for (int j = 0; j < nextStack[i].size(); j++) { + out = out + + " " + + j + + ". nextStack : " + + ByteUtils.toHexString((byte[]) nextStack[i] + .elementAt(j)); + } + out = out + "\n"; + } + String help; + out = out + "current tree retain : \n"; + for (int i = 0; i < currentRetain.length; i++) { + for (int j = 0; j < currentRetain[i].length; j++) { + help = ""; + for (int k = 0; k < currentRetain[i][j].size(); k++) { + help = help + + ByteUtils + .toHexString((byte[]) currentRetain[i][j] + .elementAt(k)); + } + out = out + " (" + i + "," + j + "). currentRetain : " + + help + "\n"; + } + } + out = out + "next tree retain : \n"; + for (int i = 0; i < nextRetain.length; i++) { + for (int j = 0; j < nextRetain[i].length; j++) { + help = ""; + for (int k = 0; k < nextRetain[i][j].size(); k++) { + help = help + + ByteUtils.toHexString((byte[]) nextRetain[i][j] + .elementAt(k)); + } + out = out + " (" + i + "," + j + "). nextRetain : " + + help + "\n"; + } + } + out = out + "keep : \n"; + for (int i = 0; i < keep.length; i++) { + out = out + " " + i + ". keep : " + + ByteUtils.toHexString(gmssUtil.concatenateArray(keep[i])) + + "\n"; + } + out = out + "subtree Roots : \n"; + for (int i = 0; i < currentRootSig.length; i++) { + out = out + " " + i + ". currentRootSig : " + + ByteUtils.toHexString(currentRootSig[i]) + "\n"; + } + out = out + "next next Roots (distributed) : \n"; + for (int i = 0; i < nextNextRoot.length; i++) { + out = out + " " + nextNextRoot[i].toString() + "\n"; + } + out = out + "Name of " + "\n" + " message digest (trees) : " + + algNames[0] + " [" + algNames[1] + "]" + "\n"; + + out = out + "nextNextLeaf :\n"; + for (int i = 0; i < nextNextLeaf.length; i++) { + out = out + " " + i + ". " + nextNextLeaf[i].toString() + "\n"; + } + out = out + "upperLeaf :\n"; + for (int i = 0; i < upperLeaf.length; i++) { + out = out + " " + i + ". " + upperLeaf[i].toString() + "\n"; + } + out = out + "upperTreehashLeaf :\n"; + for (int i = 0; i < upperTreehashLeaf.length; i++) { + out = out + " " + i + ". " + upperTreehashLeaf[i].toString() + + "\n"; + } + out = out + "minTreehash : \n"; + for (int i = 0; i < minTreehash.length; i++) { + out = out + " " + i + ". minTreehash : " + + minTreehash[i] + "\n"; + } + out = out + "nextRootSig : \n"; + for (int i = 0; i < nextRootSig.length; i++) { + out = out + " " + i + ". " + nextRootSig[i].toString() + "\n"; + } + + return out; + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKeyASN1.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKeyASN1.java new file mode 100644 index 00000000..91ea1f58 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKeyASN1.java @@ -0,0 +1,1416 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.util.Vector; + +import codec.asn1.ASN1IA5String; +import codec.asn1.ASN1Integer; +import codec.asn1.ASN1OctetString; +import codec.asn1.ASN1Sequence; +import codec.asn1.ASN1SequenceOf; +import codec.asn1.ASN1Type; +import de.flexiprovider.common.util.ASN1Tools; + +/** + * This class represents an ASN.1 encoded GMSSPrivateKey. + *

+ * The ASN.1 definition of this structure is + *

+ * + *

+ *     GMSSPrivateKey		::= SEQUENCE {
+ * 

+ * algorithm OBJECT IDENTIFIER + * index SEQUENCE OF INTEGER + * curSeeds SEQUENCE OF OCTET STRING + * nextNextSeeds SEQUENCE OF OCTET STRING + * curAuth SEQUENCE OF AuthPath + * nextAuth SEQUENCE OF AuthPath + * curTreehash SEQUENCE OF TreehashStack + * nextTreehash SEQUENCE OF TreehashStack + * StackKeep SEQUENCE OF Stack + * + * curStack SEQUENCE OF Stack + * nextStack SEQUENCE OF Stack + * curRetain SEQUENCE OF Retain + * nextRetain SEQUENCE OF Retain + * + * nextNextLeaf SEQUENCE OF DistrLeaf + * upperLeaf SEQUENCE OF DistrLeaf + * upperTHLeaf SEQUENCE OF DistrLeaf + * minTreehash SEQUENCE OF INTEGER + * + * nextRoot SEQUENCE OF OCTET STRING + * nextNextRoot SEQUENCE OF DistrRoot + * curRootSig SEQUENCE OF OCTET STRING + * nextRootSig SEQUENCE OF DistrRootSig + * + * Parameterset ParSet + * names SEQUENCE OF ASN1IA5String + * } + * + * DistrLeaf ::= SEQUENCE { + * name SEQUENCE OF ASN1IA5String + * statBytes SEQUENCE OF OCTET STRING + * statInts SEQUENCE OF INTEGER + * } + * DistrRootSig ::= SEQUENCE { + * name SEQUENCE OF ASN1IA5String + * statBytes SEQUENCE OF OCTET STRING + * statInts SEQUENCE OF INTEGER + * } + * DistrRoot ::= SEQUENCE { + * name SEQUENCE OF ASN1IA5String + * statBytes SEQUENCE OF OCTET STRING + * statInts SEQUENCE OF INTEGER + * treeH SEQUENCE OF Treehash + * ret SEQUENCE OF Retain + * } + * TreehashStack ::= SEQUENCE OF Treehash + * Treehash ::= SEQUENCE { + * name SEQUENCE OF ASN1IA5String + * statBytes SEQUENCE OF OCTET STRING + * statInts SEQUENCE OF INTEGER + * } + * + * ParSet ::= SEQUENCE { + * T INTEGER + * h SEQUENCE OF INTEGER + * w SEQUENCE OF INTEGER + * K SEQUENCE OF INTEGER + * } + * Retain ::= SEQUENCE OF Stack + * AuthPath ::= SEQUENCE OF OCTET STRING + * Stack ::= SEQUENCE OF OCTET STRING + *

+ * + * @author Michael Schneider, Sebastian Blume + */ +public class GMSSPrivateKeyASN1 extends ASN1Sequence { + + private GMSSPrivateKeySpec keySpec; + + /** + * The Constructor + * + * @param encoded + * The key in binary representation + */ + public GMSSPrivateKeyASN1(ASN1Type encoded) { + + ASN1Sequence mtsPrivateKey = (ASN1Sequence) encoded; + + // --- Decode . + ASN1Sequence indexPart = (ASN1Sequence) mtsPrivateKey.get(0); + int[] index = new int[indexPart.size()]; + for (int i = 0; i < indexPart.size(); i++) { + index[i] = ASN1Tools.getFlexiBigInt((ASN1Integer) indexPart.get(i)) + .intValue(); + } + + // --- Decode . + ASN1Sequence curSeedsPart = (ASN1Sequence) mtsPrivateKey.get(1); + byte[][] curSeeds = new byte[curSeedsPart.size()][]; + for (int i = 0; i < curSeeds.length; i++) { + curSeeds[i] = ((ASN1OctetString) curSeedsPart.get(i)) + .getByteArray(); + } + + // --- Decode . + ASN1Sequence nextNextSeedsPart = (ASN1Sequence) mtsPrivateKey.get(2); + byte[][] nextNextSeeds = new byte[nextNextSeedsPart.size()][]; + for (int i = 0; i < nextNextSeeds.length; i++) { + nextNextSeeds[i] = ((ASN1OctetString) nextNextSeedsPart.get(i)) + .getByteArray(); + } + + // --- Decode . + ASN1Sequence curAuthPart0 = (ASN1Sequence) mtsPrivateKey.get(3); + ASN1Sequence curAuthPart1; + + byte[][][] curAuth = new byte[curAuthPart0.size()][][]; + for (int i = 0; i < curAuth.length; i++) { + curAuthPart1 = (ASN1Sequence) curAuthPart0.get(i); + curAuth[i] = new byte[curAuthPart1.size()][]; + for (int j = 0; j < curAuth[i].length; j++) { + curAuth[i][j] = ((ASN1OctetString) curAuthPart1.get(j)) + .getByteArray(); + } + } + + // --- Decode . + ASN1Sequence nextAuthPart0 = (ASN1Sequence) mtsPrivateKey.get(4); + ASN1Sequence nextAuthPart1; + + byte[][][] nextAuth = new byte[nextAuthPart0.size()][][]; + for (int i = 0; i < nextAuth.length; i++) { + nextAuthPart1 = (ASN1Sequence) nextAuthPart0.get(i); + nextAuth[i] = new byte[nextAuthPart1.size()][]; + for (int j = 0; j < nextAuth[i].length; j++) { + nextAuth[i][j] = ((ASN1OctetString) nextAuthPart1.get(j)) + .getByteArray(); + } + } + + // --- Decode . + ASN1Sequence seqOfcurTreehash0 = (ASN1Sequence) mtsPrivateKey.get(5); + ASN1Sequence seqOfcurTreehash1; + ASN1Sequence seqOfcurTreehashStat; + ASN1Sequence seqOfcurTreehashBytes; + ASN1Sequence seqOfcurTreehashInts; + ASN1Sequence seqOfcurTreehashString; + + Treehash[][] curTreehash = new Treehash[seqOfcurTreehash0.size()][]; + + for (int i = 0; i < curTreehash.length; i++) { + seqOfcurTreehash1 = (ASN1Sequence) seqOfcurTreehash0.get(i); + curTreehash[i] = new Treehash[seqOfcurTreehash1.size()]; + for (int j = 0; j < curTreehash[i].length; j++) { + seqOfcurTreehashStat = (ASN1Sequence) seqOfcurTreehash1.get(j); + seqOfcurTreehashString = (ASN1Sequence) seqOfcurTreehashStat + .get(0); + seqOfcurTreehashBytes = (ASN1Sequence) seqOfcurTreehashStat + .get(1); + seqOfcurTreehashInts = (ASN1Sequence) seqOfcurTreehashStat + .get(2); + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfcurTreehashString.get(0)) + .getString(); + name[1] = ((ASN1IA5String) seqOfcurTreehashString.get(1)) + .getString(); + + int tailLength = ASN1Tools.getFlexiBigInt( + (ASN1Integer) seqOfcurTreehashInts.get(1)).intValue(); + byte[][] statByte = new byte[3 + tailLength][]; + statByte[0] = ((ASN1OctetString) seqOfcurTreehashBytes.get(0)) + .getByteArray(); + if (statByte[0].length == 0) { // if null was encoded + statByte[0] = null; + } + + statByte[1] = ((ASN1OctetString) seqOfcurTreehashBytes.get(1)) + .getByteArray(); + statByte[2] = ((ASN1OctetString) seqOfcurTreehashBytes.get(2)) + .getByteArray(); + for (int k = 0; k < tailLength; k++) { + statByte[3 + k] = ((ASN1OctetString) seqOfcurTreehashBytes + .get(3 + k)).getByteArray(); + } + int[] statInt = new int[6 + tailLength]; + statInt[0] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfcurTreehashInts.get(0))).intValue(); + statInt[1] = tailLength; + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfcurTreehashInts.get(2))).intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfcurTreehashInts.get(3))).intValue(); + statInt[4] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfcurTreehashInts.get(4))).intValue(); + statInt[5] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfcurTreehashInts.get(5))).intValue(); + for (int k = 0; k < tailLength; k++) { + statInt[6 + k] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfcurTreehashInts.get(6 + k))) + .intValue(); + } + curTreehash[i][j] = new Treehash(name, statByte, statInt); + } + } + + // --- Decode . + ASN1Sequence seqOfNextTreehash0 = (ASN1Sequence) mtsPrivateKey.get(6); + ASN1Sequence seqOfNextTreehash1; + ASN1Sequence seqOfNextTreehashStat; + ASN1Sequence seqOfNextTreehashBytes; + ASN1Sequence seqOfNextTreehashInts; + ASN1Sequence seqOfNextTreehashString; + + Treehash[][] nextTreehash = new Treehash[seqOfNextTreehash0.size()][]; + + for (int i = 0; i < nextTreehash.length; i++) { + seqOfNextTreehash1 = (ASN1Sequence) seqOfNextTreehash0.get(i); + nextTreehash[i] = new Treehash[seqOfNextTreehash1.size()]; + for (int j = 0; j < nextTreehash[i].length; j++) { + seqOfNextTreehashStat = (ASN1Sequence) seqOfNextTreehash1 + .get(j); + seqOfNextTreehashString = (ASN1Sequence) seqOfNextTreehashStat + .get(0); + seqOfNextTreehashBytes = (ASN1Sequence) seqOfNextTreehashStat + .get(1); + seqOfNextTreehashInts = (ASN1Sequence) seqOfNextTreehashStat + .get(2); + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfNextTreehashString.get(0)) + .getString(); + name[1] = ((ASN1IA5String) seqOfNextTreehashString.get(1)) + .getString(); + + int tailLength = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfNextTreehashInts.get(1))) + .intValue(); + byte[][] statByte = new byte[3 + tailLength][]; + statByte[0] = ((ASN1OctetString) seqOfNextTreehashBytes.get(0)) + .getByteArray(); + if (statByte[0].length == 0) { // if null was encoded + statByte[0] = null; + } + + statByte[1] = ((ASN1OctetString) seqOfNextTreehashBytes.get(1)) + .getByteArray(); + statByte[2] = ((ASN1OctetString) seqOfNextTreehashBytes.get(2)) + .getByteArray(); + for (int k = 0; k < tailLength; k++) { + statByte[3 + k] = ((ASN1OctetString) seqOfNextTreehashBytes + .get(3 + k)).getByteArray(); + } + int[] statInt = new int[6 + tailLength]; + statInt[0] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfNextTreehashInts.get(0))) + .intValue(); + statInt[1] = tailLength; + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfNextTreehashInts.get(2))) + .intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfNextTreehashInts.get(3))) + .intValue(); + statInt[4] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfNextTreehashInts.get(4))) + .intValue(); + statInt[5] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfNextTreehashInts.get(5))) + .intValue(); + for (int k = 0; k < tailLength; k++) { + statInt[6 + k] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfNextTreehashInts.get(6 + k))) + .intValue(); + } + nextTreehash[i][j] = new Treehash(name, statByte, statInt); + } + } + + // --- Decode . + ASN1Sequence keepPart0 = (ASN1Sequence) mtsPrivateKey.get(7); + ASN1Sequence keepPart1; + + byte[][][] keep = new byte[keepPart0.size()][][]; + for (int i = 0; i < keep.length; i++) { + keepPart1 = (ASN1Sequence) keepPart0.get(i); + keep[i] = new byte[keepPart1.size()][]; + for (int j = 0; j < keep[i].length; j++) { + keep[i][j] = ((ASN1OctetString) keepPart1.get(j)) + .getByteArray(); + } + } + + // --- Decode . + ASN1Sequence curStackPart0 = (ASN1Sequence) mtsPrivateKey.get(8); + ASN1Sequence curStackPart1; + + Vector[] curStack = new Vector[curStackPart0.size()]; + for (int i = 0; i < curStack.length; i++) { + curStackPart1 = (ASN1Sequence) curStackPart0.get(i); + curStack[i] = new Vector(); + for (int j = 0; j < curStackPart1.size(); j++) { + curStack[i].addElement(((ASN1OctetString) curStackPart1.get(j)) + .getByteArray()); + } + } + + // --- Decode . + ASN1Sequence nextStackPart0 = (ASN1Sequence) mtsPrivateKey.get(9); + ASN1Sequence nextStackPart1; + + Vector[] nextStack = new Vector[nextStackPart0.size()]; + for (int i = 0; i < nextStack.length; i++) { + nextStackPart1 = (ASN1Sequence) nextStackPart0.get(i); + nextStack[i] = new Vector(); + for (int j = 0; j < nextStackPart1.size(); j++) { + nextStack[i].addElement(((ASN1OctetString) nextStackPart1 + .get(j)).getByteArray()); + } + } + + // --- Decode . + ASN1Sequence curRetainPart0 = (ASN1Sequence) mtsPrivateKey.get(10); + ASN1Sequence curRetainPart1; + ASN1Sequence curRetainPart2; + + Vector[][] curRetain = new Vector[curRetainPart0.size()][]; + for (int i = 0; i < curRetain.length; i++) { + curRetainPart1 = (ASN1Sequence) curRetainPart0.get(i); + curRetain[i] = new Vector[curRetainPart1.size()]; + for (int j = 0; j < curRetain[i].length; j++) { + curRetainPart2 = (ASN1Sequence) curRetainPart1.get(j); + curRetain[i][j] = new Vector(); + for (int k = 0; k < curRetainPart2.size(); k++) { + curRetain[i][j] + .addElement(((ASN1OctetString) curRetainPart2 + .get(k)).getByteArray()); + } + } + } + + // --- Decode . + ASN1Sequence nextRetainPart0 = (ASN1Sequence) mtsPrivateKey.get(11); + ASN1Sequence nextRetainPart1; + ASN1Sequence nextRetainPart2; + + Vector[][] nextRetain = new Vector[nextRetainPart0.size()][]; + for (int i = 0; i < nextRetain.length; i++) { + nextRetainPart1 = (ASN1Sequence) nextRetainPart0.get(i); + nextRetain[i] = new Vector[nextRetainPart1.size()]; + for (int j = 0; j < nextRetain[i].length; j++) { + nextRetainPart2 = (ASN1Sequence) nextRetainPart1.get(j); + nextRetain[i][j] = new Vector(); + for (int k = 0; k < nextRetainPart2.size(); k++) { + nextRetain[i][j] + .addElement(((ASN1OctetString) nextRetainPart2 + .get(k)).getByteArray()); + } + } + } + + // --- Decode . + ASN1Sequence seqOfLeafs = (ASN1Sequence) mtsPrivateKey.get(12); + ASN1Sequence seqOfLeafStat; + ASN1Sequence seqOfLeafBytes; + ASN1Sequence seqOfLeafInts; + ASN1Sequence seqOfLeafString; + + GMSSLeaf[] nextNextLeaf = new GMSSLeaf[seqOfLeafs.size()]; + + for (int i = 0; i < nextNextLeaf.length; i++) { + seqOfLeafStat = (ASN1Sequence) seqOfLeafs.get(i); + // nextNextAuth[i]= new byte[nextNextAuthPart1.size()][]; + seqOfLeafString = (ASN1Sequence) seqOfLeafStat.get(0); + seqOfLeafBytes = (ASN1Sequence) seqOfLeafStat.get(1); + seqOfLeafInts = (ASN1Sequence) seqOfLeafStat.get(2); + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfLeafString.get(0)).getString(); + name[1] = ((ASN1IA5String) seqOfLeafString.get(1)).getString(); + byte[][] statByte = new byte[4][]; + statByte[0] = ((ASN1OctetString) seqOfLeafBytes.get(0)) + .getByteArray(); + statByte[1] = ((ASN1OctetString) seqOfLeafBytes.get(1)) + .getByteArray(); + statByte[2] = ((ASN1OctetString) seqOfLeafBytes.get(2)) + .getByteArray(); + statByte[3] = ((ASN1OctetString) seqOfLeafBytes.get(3)) + .getByteArray(); + int[] statInt = new int[4]; + statInt[0] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfLeafInts.get(0))).intValue(); + statInt[1] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfLeafInts.get(1))).intValue(); + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfLeafInts.get(2))).intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfLeafInts.get(3))).intValue(); + nextNextLeaf[i] = new GMSSLeaf(name, statByte, statInt); + } + + // --- Decode . + ASN1Sequence seqOfUpperLeafs = (ASN1Sequence) mtsPrivateKey.get(13); + ASN1Sequence seqOfUpperLeafStat; + ASN1Sequence seqOfUpperLeafBytes; + ASN1Sequence seqOfUpperLeafInts; + ASN1Sequence seqOfUpperLeafString; + + GMSSLeaf[] upperLeaf = new GMSSLeaf[seqOfUpperLeafs.size()]; + + for (int i = 0; i < upperLeaf.length; i++) { + seqOfUpperLeafStat = (ASN1Sequence) seqOfUpperLeafs.get(i); + seqOfUpperLeafString = (ASN1Sequence) seqOfUpperLeafStat.get(0); + seqOfUpperLeafBytes = (ASN1Sequence) seqOfUpperLeafStat.get(1); + seqOfUpperLeafInts = (ASN1Sequence) seqOfUpperLeafStat.get(2); + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfUpperLeafString.get(0)).getString(); + name[1] = ((ASN1IA5String) seqOfUpperLeafString.get(1)).getString(); + byte[][] statByte = new byte[4][]; + statByte[0] = ((ASN1OctetString) seqOfUpperLeafBytes.get(0)) + .getByteArray(); + statByte[1] = ((ASN1OctetString) seqOfUpperLeafBytes.get(1)) + .getByteArray(); + statByte[2] = ((ASN1OctetString) seqOfUpperLeafBytes.get(2)) + .getByteArray(); + statByte[3] = ((ASN1OctetString) seqOfUpperLeafBytes.get(3)) + .getByteArray(); + int[] statInt = new int[4]; + statInt[0] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperLeafInts.get(0))).intValue(); + statInt[1] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperLeafInts.get(1))).intValue(); + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperLeafInts.get(2))).intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperLeafInts.get(3))).intValue(); + upperLeaf[i] = new GMSSLeaf(name, statByte, statInt); + } + + // --- Decode . + ASN1Sequence seqOfUpperTHLeafs = (ASN1Sequence) mtsPrivateKey.get(14); + ASN1Sequence seqOfUpperTHLeafStat; + ASN1Sequence seqOfUpperTHLeafBytes; + ASN1Sequence seqOfUpperTHLeafInts; + ASN1Sequence seqOfUpperTHLeafString; + + GMSSLeaf[] upperTHLeaf = new GMSSLeaf[seqOfUpperTHLeafs.size()]; + + for (int i = 0; i < upperTHLeaf.length; i++) { + seqOfUpperTHLeafStat = (ASN1Sequence) seqOfUpperTHLeafs.get(i); + seqOfUpperTHLeafString = (ASN1Sequence) seqOfUpperTHLeafStat.get(0); + seqOfUpperTHLeafBytes = (ASN1Sequence) seqOfUpperTHLeafStat.get(1); + seqOfUpperTHLeafInts = (ASN1Sequence) seqOfUpperTHLeafStat.get(2); + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfUpperTHLeafString.get(0)) + .getString(); + name[1] = ((ASN1IA5String) seqOfUpperTHLeafString.get(1)) + .getString(); + byte[][] statByte = new byte[4][]; + statByte[0] = ((ASN1OctetString) seqOfUpperTHLeafBytes.get(0)) + .getByteArray(); + statByte[1] = ((ASN1OctetString) seqOfUpperTHLeafBytes.get(1)) + .getByteArray(); + statByte[2] = ((ASN1OctetString) seqOfUpperTHLeafBytes.get(2)) + .getByteArray(); + statByte[3] = ((ASN1OctetString) seqOfUpperTHLeafBytes.get(3)) + .getByteArray(); + int[] statInt = new int[4]; + statInt[0] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperTHLeafInts.get(0))).intValue(); + statInt[1] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperTHLeafInts.get(1))).intValue(); + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperTHLeafInts.get(2))).intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfUpperTHLeafInts.get(3))).intValue(); + upperTHLeaf[i] = new GMSSLeaf(name, statByte, statInt); + } + + // --- Decode . + ASN1Sequence minTreehashPart = (ASN1Sequence) mtsPrivateKey.get(15); + int[] minTreehash = new int[minTreehashPart.size()]; + for (int i = 0; i < minTreehashPart.size(); i++) { + minTreehash[i] = ASN1Tools.getFlexiBigInt( + (ASN1Integer) minTreehashPart.get(i)).intValue(); + } + + // --- Decode . + ASN1Sequence seqOfnextRoots = (ASN1Sequence) mtsPrivateKey.get(16); + byte[][] nextRoot = new byte[seqOfnextRoots.size()][]; + for (int i = 0; i < nextRoot.length; i++) { + nextRoot[i] = ((ASN1OctetString) seqOfnextRoots.get(i)) + .getByteArray(); + } + + // --- Decode . + ASN1Sequence seqOfnextNextRoot = (ASN1Sequence) mtsPrivateKey.get(17); + ASN1Sequence seqOfnextNextRootStat; + ASN1Sequence seqOfnextNextRootBytes; + ASN1Sequence seqOfnextNextRootInts; + ASN1Sequence seqOfnextNextRootString; + ASN1Sequence seqOfnextNextRootTreeH; + ASN1Sequence seqOfnextNextRootRetain; + + GMSSRootCalc[] nextNextRoot = new GMSSRootCalc[seqOfnextNextRoot.size()]; + + for (int i = 0; i < nextNextRoot.length; i++) { + seqOfnextNextRootStat = (ASN1Sequence) seqOfnextNextRoot.get(i); + seqOfnextNextRootString = (ASN1Sequence) seqOfnextNextRootStat + .get(0); + seqOfnextNextRootBytes = (ASN1Sequence) seqOfnextNextRootStat + .get(1); + seqOfnextNextRootInts = (ASN1Sequence) seqOfnextNextRootStat.get(2); + seqOfnextNextRootTreeH = (ASN1Sequence) seqOfnextNextRootStat + .get(3); + seqOfnextNextRootRetain = (ASN1Sequence) seqOfnextNextRootStat + .get(4); + + // decode treehash of nextNextRoot + // --------------------------------- + ASN1Sequence seqOfnextNextRootTreeHStat; + ASN1Sequence seqOfnextNextRootTreeHBytes; + ASN1Sequence seqOfnextNextRootTreeHInts; + ASN1Sequence seqOfnextNextRootTreeHString; + + Treehash[] nnRTreehash = new Treehash[seqOfnextNextRootTreeH.size()]; + + for (int k = 0; k < nnRTreehash.length; k++) { + seqOfnextNextRootTreeHStat = (ASN1Sequence) seqOfnextNextRootTreeH + .get(k); + seqOfnextNextRootTreeHString = (ASN1Sequence) seqOfnextNextRootTreeHStat + .get(0); + seqOfnextNextRootTreeHBytes = (ASN1Sequence) seqOfnextNextRootTreeHStat + .get(1); + seqOfnextNextRootTreeHInts = (ASN1Sequence) seqOfnextNextRootTreeHStat + .get(2); + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfnextNextRootTreeHString.get(0)) + .getString(); + name[1] = ((ASN1IA5String) seqOfnextNextRootTreeHString.get(1)) + .getString(); + + int tailLength = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootTreeHInts.get(1))) + .intValue(); + byte[][] statByte = new byte[3 + tailLength][]; + statByte[0] = ((ASN1OctetString) seqOfnextNextRootTreeHBytes + .get(0)).getByteArray(); + if (statByte[0].length == 0) { // if null was encoded + statByte[0] = null; + } + + statByte[1] = ((ASN1OctetString) seqOfnextNextRootTreeHBytes + .get(1)).getByteArray(); + statByte[2] = ((ASN1OctetString) seqOfnextNextRootTreeHBytes + .get(2)).getByteArray(); + for (int j = 0; j < tailLength; j++) { + statByte[3 + j] = ((ASN1OctetString) seqOfnextNextRootTreeHBytes + .get(3 + j)).getByteArray(); + } + int[] statInt = new int[6 + tailLength]; + statInt[0] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootTreeHInts.get(0))) + .intValue(); + statInt[1] = tailLength; + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootTreeHInts.get(2))) + .intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootTreeHInts.get(3))) + .intValue(); + statInt[4] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootTreeHInts.get(4))) + .intValue(); + statInt[5] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootTreeHInts.get(5))) + .intValue(); + for (int j = 0; j < tailLength; j++) { + statInt[6 + j] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootTreeHInts + .get(6 + j))).intValue(); + } + nnRTreehash[k] = new Treehash(name, statByte, statInt); + } + // --------------------------------- + + // decode retain of nextNextRoot + // --------------------------------- + // ASN1Sequence seqOfnextNextRootRetainPart0 = + // (ASN1Sequence)seqOfnextNextRootRetain.get(0); + ASN1Sequence seqOfnextNextRootRetainPart1; + + Vector[] nnRRetain = new Vector[seqOfnextNextRootRetain.size()]; + for (int j = 0; j < nnRRetain.length; j++) { + seqOfnextNextRootRetainPart1 = (ASN1Sequence) seqOfnextNextRootRetain + .get(j); + nnRRetain[j] = new Vector(); + for (int k = 0; k < seqOfnextNextRootRetainPart1.size(); k++) { + nnRRetain[j] + .addElement(((ASN1OctetString) seqOfnextNextRootRetainPart1 + .get(k)).getByteArray()); + } + } + // --------------------------------- + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfnextNextRootString.get(0)) + .getString(); + name[1] = ((ASN1IA5String) seqOfnextNextRootString.get(1)) + .getString(); + + int heightOfTree = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(0))).intValue(); + int tailLength = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(7))).intValue(); + byte[][] statByte = new byte[1 + heightOfTree + tailLength][]; + statByte[0] = ((ASN1OctetString) seqOfnextNextRootBytes.get(0)) + .getByteArray(); + for (int j = 0; j < heightOfTree; j++) { + statByte[1 + j] = ((ASN1OctetString) seqOfnextNextRootBytes + .get(1 + j)).getByteArray(); + } + for (int j = 0; j < tailLength; j++) { + statByte[1 + heightOfTree + j] = ((ASN1OctetString) seqOfnextNextRootBytes + .get(1 + heightOfTree + j)).getByteArray(); + } + int[] statInt = new int[8 + heightOfTree + tailLength]; + statInt[0] = heightOfTree; + statInt[1] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(1))).intValue(); + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(2))).intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(3))).intValue(); + statInt[4] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(4))).intValue(); + statInt[5] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(5))).intValue(); + statInt[6] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(6))).intValue(); + statInt[7] = tailLength; + for (int j = 0; j < heightOfTree; j++) { + statInt[8 + j] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(8 + j))) + .intValue(); + } + for (int j = 0; j < tailLength; j++) { + statInt[8 + heightOfTree + j] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnextNextRootInts.get(8 + + heightOfTree + j))).intValue(); + } + nextNextRoot[i] = new GMSSRootCalc(name, statByte, statInt, + nnRTreehash, nnRRetain); + } + + // --- Decode . + ASN1Sequence seqOfcurRootSig = (ASN1Sequence) mtsPrivateKey.get(18); + byte[][] curRootSig = new byte[seqOfcurRootSig.size()][]; + for (int i = 0; i < curRootSig.length; i++) { + curRootSig[i] = ((ASN1OctetString) seqOfcurRootSig.get(i)) + .getByteArray(); + } + + // --- Decode . + ASN1Sequence seqOfnextRootSigs = (ASN1Sequence) mtsPrivateKey.get(19); + ASN1Sequence seqOfnRSStats; + ASN1Sequence seqOfnRSStrings; + ASN1Sequence seqOfnRSInts; + ASN1Sequence seqOfnRSBytes; + + GMSSRootSig[] nextRootSig = new GMSSRootSig[seqOfnextRootSigs.size()]; + + for (int i = 0; i < nextRootSig.length; i++) { + seqOfnRSStats = (ASN1Sequence) seqOfnextRootSigs.get(i); + // nextNextAuth[i]= new byte[nextNextAuthPart1.size()][]; + seqOfnRSStrings = (ASN1Sequence) seqOfnRSStats.get(0); + seqOfnRSBytes = (ASN1Sequence) seqOfnRSStats.get(1); + seqOfnRSInts = (ASN1Sequence) seqOfnRSStats.get(2); + + String[] name = new String[2]; + name[0] = ((ASN1IA5String) seqOfnRSStrings.get(0)).getString(); + name[1] = ((ASN1IA5String) seqOfnRSStrings.get(1)).getString(); + byte[][] statByte = new byte[5][]; + statByte[0] = ((ASN1OctetString) seqOfnRSBytes.get(0)) + .getByteArray(); + statByte[1] = ((ASN1OctetString) seqOfnRSBytes.get(1)) + .getByteArray(); + statByte[2] = ((ASN1OctetString) seqOfnRSBytes.get(2)) + .getByteArray(); + statByte[3] = ((ASN1OctetString) seqOfnRSBytes.get(3)) + .getByteArray(); + statByte[4] = ((ASN1OctetString) seqOfnRSBytes.get(4)) + .getByteArray(); + int[] statInt = new int[9]; + statInt[0] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(0))).intValue(); + statInt[1] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(1))).intValue(); + statInt[2] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(2))).intValue(); + statInt[3] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(3))).intValue(); + statInt[4] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(4))).intValue(); + statInt[5] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(5))).intValue(); + statInt[6] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(6))).intValue(); + statInt[7] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(7))).intValue(); + statInt[8] = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfnRSInts.get(8))).intValue(); + nextRootSig[i] = new GMSSRootSig(name, statByte, statInt); + } + + // --- Decode . + + ASN1Sequence seqOfParams = (ASN1Sequence) mtsPrivateKey.get(20); + int numLayer = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfParams.get(0))).intValue(); + ASN1Sequence seqOfPSh = (ASN1Sequence) seqOfParams.get(1); + ASN1Sequence seqOfPSw = (ASN1Sequence) seqOfParams.get(2); + ASN1Sequence seqOfPSK = (ASN1Sequence) seqOfParams.get(3); + + int[] h = new int[seqOfPSh.size()]; + int[] w = new int[seqOfPSw.size()]; + int[] K = new int[seqOfPSK.size()]; + + for (int i = 0; i < h.length; i++) { + h[i] = ASN1Tools.getFlexiBigInt(((ASN1Integer) seqOfPSh.get(i))) + .intValue(); + w[i] = ASN1Tools.getFlexiBigInt(((ASN1Integer) seqOfPSw.get(i))) + .intValue(); + K[i] = ASN1Tools.getFlexiBigInt(((ASN1Integer) seqOfPSK.get(i))) + .intValue(); + } + GMSSParameterset parSet = new GMSSParameterset(numLayer, h, w, K); + + // --- Decode . + ASN1Sequence namePart = (ASN1Sequence) mtsPrivateKey.get(21); + String[] name = new String[namePart.size()]; + for (int i = 0; i < name.length; i++) { + name[i] = ((ASN1IA5String) namePart.get(i)).getString(); + } + + // --- Store the key spec. + keySpec = new GMSSPrivateKeySpec(index, curSeeds, nextNextSeeds, + curAuth, nextAuth, curTreehash, nextTreehash, curStack, + nextStack, curRetain, nextRetain, keep, nextNextLeaf, + upperLeaf, upperTHLeaf, minTreehash, nextRoot, nextNextRoot, + curRootSig, nextRootSig, parSet, name); + } + + /** + * @param index + * tree indices + * @param currentSeeds + * seed for the generation of private OTS keys for the + * current subtrees (TREE) + * @param nextNextSeeds + * seed for the generation of private OTS keys for the + * subtrees after next (TREE++) + * @param currentAuthPaths + * array of current authentication paths (AUTHPATH) + * @param nextAuthPaths + * array of next authentication paths (AUTHPATH+) + * @param keep + * keep array for the authPath algorithm + * @param currentTreehash + * treehash for authPath algorithm of current tree + * @param nextTreehash + * treehash for authPath algorithm of next tree (TREE+) + * @param currentStack + * shared stack for authPath algorithm of current tree + * @param nextStack + * shared stack for authPath algorithm of next tree (TREE+) + * @param currentRetain + * retain stack for authPath algorithm of current tree + * @param nextRetain + * retain stack for authPath algorithm of next tree (TREE+) + * @param nextNextLeaf + * array of upcoming leafs of the tree after next (LEAF++) of + * each layer + * @param upperLeaf + * needed for precomputation of upper nodes + * @param upperTreehashLeaf + * needed for precomputation of upper treehash nodes + * @param minTreehash + * index of next treehash instance to receive an update + * @param nextRoot + * the roots of the next trees (ROOT+) + * @param nextNextRoot + * the roots of the tree after next (ROOT++) + * @param currentRootSig + * array of signatures of the roots of the current subtrees + * (SIG) + * @param nextRootSig + * array of signatures of the roots of the next subtree + * (SIG+) + * @param gmssParameterset + * the GMSS Parameterset + * @param algNames + * An array of strings, containing the name of the used hash + * function and the name of the corresponding provider + */ + public GMSSPrivateKeyASN1(int[] index, byte[][] currentSeeds, + byte[][] nextNextSeeds, byte[][][] currentAuthPaths, + byte[][][] nextAuthPaths, byte[][][] keep, + Treehash[][] currentTreehash, Treehash[][] nextTreehash, + Vector[] currentStack, Vector[] nextStack, + Vector[][] currentRetain, Vector[][] nextRetain, + GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf, + GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot, + GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig, + GMSSRootSig[] nextRootSig, GMSSParameterset gmssParameterset, + String[] algNames) { + + // --- Encode . + ASN1SequenceOf indexPart = new ASN1SequenceOf(ASN1Integer.class); + for (int i = 0; i < index.length; i++) { + indexPart.add(new ASN1Integer(index[i])); + } + add(indexPart); + + // --- Encode . + ASN1SequenceOf curSeedsPart = new ASN1SequenceOf(ASN1OctetString.class); + for (int i = 0; i < currentSeeds.length; i++) { + curSeedsPart.add(new ASN1OctetString(currentSeeds[i])); + } + add(curSeedsPart); + + // --- Encode . + ASN1SequenceOf nextNextSeedsPart = new ASN1SequenceOf( + ASN1OctetString.class); + for (int i = 0; i < nextNextSeeds.length; i++) { + nextNextSeedsPart.add(new ASN1OctetString(nextNextSeeds[i])); + } + add(nextNextSeedsPart); + + // --- Encode . + ASN1SequenceOf curAuthPart0 = new ASN1SequenceOf(ASN1OctetString.class); + ASN1SequenceOf curAuthPart1 = new ASN1SequenceOf(ASN1Sequence.class); + for (int i = 0; i < currentAuthPaths.length; i++) { + for (int j = 0; j < currentAuthPaths[i].length; j++) { + curAuthPart0.add(new ASN1OctetString(currentAuthPaths[i][j])); + } + curAuthPart1.add(curAuthPart0); + curAuthPart0 = new ASN1SequenceOf(ASN1OctetString.class); + } + add(curAuthPart1); + + // --- Encode . + ASN1SequenceOf nextAuthPart0 = new ASN1SequenceOf(ASN1OctetString.class); + ASN1SequenceOf nextAuthPart1 = new ASN1SequenceOf(ASN1Sequence.class); + for (int i = 0; i < nextAuthPaths.length; i++) { + for (int j = 0; j < nextAuthPaths[i].length; j++) { + nextAuthPart0.add(new ASN1OctetString(nextAuthPaths[i][j])); + } + nextAuthPart1.add(nextAuthPart0); + nextAuthPart0 = new ASN1SequenceOf(ASN1OctetString.class); + } + add(nextAuthPart1); + + // --- Encode . + ASN1SequenceOf seqOfTreehash0 = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf seqOfTreehash1 = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + ASN1SequenceOf seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + for (int i = 0; i < currentTreehash.length; i++) { + for (int j = 0; j < currentTreehash[i].length; j++) { + seqOfString.add(new ASN1IA5String(algNames[0])); + seqOfString.add(new ASN1IA5String("")); + seqOfStat.add(seqOfString); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + int tailLength = currentTreehash[i][j].getStatInt()[1]; + + seqOfByte.add(new ASN1OctetString(currentTreehash[i][j] + .getStatByte()[0])); + seqOfByte.add(new ASN1OctetString(currentTreehash[i][j] + .getStatByte()[1])); + seqOfByte.add(new ASN1OctetString(currentTreehash[i][j] + .getStatByte()[2])); + for (int k = 0; k < tailLength; k++) { + seqOfByte.add(new ASN1OctetString(currentTreehash[i][j] + .getStatByte()[3 + k])); + } + seqOfStat.add(seqOfByte); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + + seqOfInt.add(new ASN1Integer( + currentTreehash[i][j].getStatInt()[0])); + seqOfInt.add(new ASN1Integer(tailLength)); + seqOfInt.add(new ASN1Integer( + currentTreehash[i][j].getStatInt()[2])); + seqOfInt.add(new ASN1Integer( + currentTreehash[i][j].getStatInt()[3])); + seqOfInt.add(new ASN1Integer( + currentTreehash[i][j].getStatInt()[4])); + seqOfInt.add(new ASN1Integer( + currentTreehash[i][j].getStatInt()[5])); + for (int k = 0; k < tailLength; k++) { + seqOfInt.add(new ASN1Integer(currentTreehash[i][j] + .getStatInt()[6 + k])); + } + seqOfStat.add(seqOfInt); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + + seqOfTreehash1.add(seqOfStat); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + } + seqOfTreehash0.add(seqOfTreehash1); + seqOfTreehash1 = new ASN1SequenceOf(ASN1Sequence.class); + } + add(seqOfTreehash0); + + // --- Encode . + seqOfTreehash0 = new ASN1SequenceOf(ASN1Sequence.class); + seqOfTreehash1 = new ASN1SequenceOf(ASN1Sequence.class); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + for (int i = 0; i < nextTreehash.length; i++) { + for (int j = 0; j < nextTreehash[i].length; j++) { + seqOfString.add(new ASN1IA5String(algNames[0])); + seqOfString.add(new ASN1IA5String("")); + seqOfStat.add(seqOfString); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + int tailLength = nextTreehash[i][j].getStatInt()[1]; + + seqOfByte.add(new ASN1OctetString(nextTreehash[i][j] + .getStatByte()[0])); + seqOfByte.add(new ASN1OctetString(nextTreehash[i][j] + .getStatByte()[1])); + seqOfByte.add(new ASN1OctetString(nextTreehash[i][j] + .getStatByte()[2])); + for (int k = 0; k < tailLength; k++) { + seqOfByte.add(new ASN1OctetString(nextTreehash[i][j] + .getStatByte()[3 + k])); + } + seqOfStat.add(seqOfByte); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + + seqOfInt + .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[0])); + seqOfInt.add(new ASN1Integer(tailLength)); + seqOfInt + .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[2])); + seqOfInt + .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[3])); + seqOfInt + .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[4])); + seqOfInt + .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[5])); + for (int k = 0; k < tailLength; k++) { + seqOfInt.add(new ASN1Integer(nextTreehash[i][j] + .getStatInt()[6 + k])); + } + seqOfStat.add(seqOfInt); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + + seqOfTreehash1.add(seqOfStat); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + } + seqOfTreehash0.add(seqOfTreehash1); + seqOfTreehash1 = new ASN1SequenceOf(ASN1Sequence.class); + } + add(seqOfTreehash0); + + // --- Encode . + ASN1SequenceOf keepPart0 = new ASN1SequenceOf(ASN1OctetString.class); + ASN1SequenceOf keepPart1 = new ASN1SequenceOf(ASN1Sequence.class); + for (int i = 0; i < keep.length; i++) { + for (int j = 0; j < keep[i].length; j++) { + keepPart0.add(new ASN1OctetString(keep[i][j])); + } + keepPart1.add(keepPart0); + keepPart0 = new ASN1SequenceOf(ASN1OctetString.class); + } + add(keepPart1); + + // --- Encode . + ASN1SequenceOf curStackPart0 = new ASN1SequenceOf(ASN1OctetString.class); + ASN1SequenceOf curStackPart1 = new ASN1SequenceOf(ASN1Sequence.class); + for (int i = 0; i < currentStack.length; i++) { + for (int j = 0; j < currentStack[i].size(); j++) { + curStackPart0.add(new ASN1OctetString((byte[]) currentStack[i] + .elementAt(j))); + } + curStackPart1.add(curStackPart0); + curStackPart0 = new ASN1SequenceOf(ASN1OctetString.class); + } + add(curStackPart1); + + // --- Encode . + ASN1SequenceOf nextStackPart0 = new ASN1SequenceOf( + ASN1OctetString.class); + ASN1SequenceOf nextStackPart1 = new ASN1SequenceOf(ASN1Sequence.class); + for (int i = 0; i < nextStack.length; i++) { + for (int j = 0; j < nextStack[i].size(); j++) { + nextStackPart0.add(new ASN1OctetString((byte[]) nextStack[i] + .elementAt(j))); + } + nextStackPart1.add(nextStackPart0); + nextStackPart0 = new ASN1SequenceOf(ASN1OctetString.class); + } + add(nextStackPart1); + + // --- Encode . + ASN1SequenceOf currentRetainPart0 = new ASN1SequenceOf( + ASN1OctetString.class); + ASN1SequenceOf currentRetainPart1 = new ASN1SequenceOf( + ASN1Sequence.class); + ASN1SequenceOf currentRetainPart2 = new ASN1SequenceOf( + ASN1Sequence.class); + for (int i = 0; i < currentRetain.length; i++) { + for (int j = 0; j < currentRetain[i].length; j++) { + for (int k = 0; k < currentRetain[i][j].size(); k++) { + currentRetainPart0.add(new ASN1OctetString( + (byte[]) currentRetain[i][j].elementAt(k))); + } + currentRetainPart1.add(currentRetainPart0); + currentRetainPart0 = new ASN1SequenceOf(ASN1OctetString.class); + } + currentRetainPart2.add(currentRetainPart1); + currentRetainPart1 = new ASN1SequenceOf(ASN1OctetString.class); + } + add(currentRetainPart2); + + // --- Encode . + ASN1SequenceOf nextRetainPart0 = new ASN1SequenceOf( + ASN1OctetString.class); + ASN1SequenceOf nextRetainPart1 = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf nextRetainPart2 = new ASN1SequenceOf(ASN1Sequence.class); + for (int i = 0; i < nextRetain.length; i++) { + for (int j = 0; j < nextRetain[i].length; j++) { + for (int k = 0; k < nextRetain[i][j].size(); k++) { + nextRetainPart0.add(new ASN1OctetString( + (byte[]) nextRetain[i][j].elementAt(k))); + } + nextRetainPart1.add(nextRetainPart0); + nextRetainPart0 = new ASN1SequenceOf(ASN1OctetString.class); + } + nextRetainPart2.add(nextRetainPart1); + nextRetainPart1 = new ASN1SequenceOf(ASN1OctetString.class); + } + add(nextRetainPart2); + + // --- Encode . + ASN1SequenceOf seqOfLeaf = new ASN1SequenceOf(ASN1Sequence.class); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + for (int i = 0; i < nextNextLeaf.length; i++) { + seqOfString.add(new ASN1IA5String(algNames[0])); + seqOfString.add(new ASN1IA5String("")); + seqOfStat.add(seqOfString); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + byte[][] tempByte = nextNextLeaf[i].getStatByte(); + seqOfByte.add(new ASN1OctetString(tempByte[0])); + seqOfByte.add(new ASN1OctetString(tempByte[1])); + seqOfByte.add(new ASN1OctetString(tempByte[2])); + seqOfByte.add(new ASN1OctetString(tempByte[3])); + seqOfStat.add(seqOfByte); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + + int[] tempInt = nextNextLeaf[i].getStatInt(); + seqOfInt.add(new ASN1Integer(tempInt[0])); + seqOfInt.add(new ASN1Integer(tempInt[1])); + seqOfInt.add(new ASN1Integer(tempInt[2])); + seqOfInt.add(new ASN1Integer(tempInt[3])); + seqOfStat.add(seqOfInt); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + + seqOfLeaf.add(seqOfStat); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + } + add(seqOfLeaf); + + // --- Encode . + ASN1SequenceOf seqOfUpperLeaf = new ASN1SequenceOf(ASN1Sequence.class); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + for (int i = 0; i < upperLeaf.length; i++) { + seqOfString.add(new ASN1IA5String(algNames[0])); + seqOfString.add(new ASN1IA5String("")); + seqOfStat.add(seqOfString); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + byte[][] tempByte = upperLeaf[i].getStatByte(); + seqOfByte.add(new ASN1OctetString(tempByte[0])); + seqOfByte.add(new ASN1OctetString(tempByte[1])); + seqOfByte.add(new ASN1OctetString(tempByte[2])); + seqOfByte.add(new ASN1OctetString(tempByte[3])); + seqOfStat.add(seqOfByte); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + + int[] tempInt = upperLeaf[i].getStatInt(); + seqOfInt.add(new ASN1Integer(tempInt[0])); + seqOfInt.add(new ASN1Integer(tempInt[1])); + seqOfInt.add(new ASN1Integer(tempInt[2])); + seqOfInt.add(new ASN1Integer(tempInt[3])); + seqOfStat.add(seqOfInt); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + + seqOfUpperLeaf.add(seqOfStat); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + } + add(seqOfUpperLeaf); + + // encode + ASN1SequenceOf seqOfUpperTreehashLeaf = new ASN1SequenceOf( + ASN1Sequence.class); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + for (int i = 0; i < upperTreehashLeaf.length; i++) { + seqOfString.add(new ASN1IA5String(algNames[0])); + seqOfString.add(new ASN1IA5String("")); + seqOfStat.add(seqOfString); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + byte[][] tempByte = upperTreehashLeaf[i].getStatByte(); + seqOfByte.add(new ASN1OctetString(tempByte[0])); + seqOfByte.add(new ASN1OctetString(tempByte[1])); + seqOfByte.add(new ASN1OctetString(tempByte[2])); + seqOfByte.add(new ASN1OctetString(tempByte[3])); + seqOfStat.add(seqOfByte); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + + int[] tempInt = upperTreehashLeaf[i].getStatInt(); + seqOfInt.add(new ASN1Integer(tempInt[0])); + seqOfInt.add(new ASN1Integer(tempInt[1])); + seqOfInt.add(new ASN1Integer(tempInt[2])); + seqOfInt.add(new ASN1Integer(tempInt[3])); + seqOfStat.add(seqOfInt); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + + seqOfUpperTreehashLeaf.add(seqOfStat); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + } + add(seqOfUpperTreehashLeaf); + + // --- Encode . + ASN1SequenceOf minTreehashPart = new ASN1SequenceOf(ASN1Integer.class); + for (int i = 0; i < minTreehash.length; i++) { + minTreehashPart.add(new ASN1Integer(minTreehash[i])); + } + add(minTreehashPart); + + // --- Encode . + ASN1SequenceOf nextRootPart = new ASN1SequenceOf(ASN1OctetString.class); + for (int i = 0; i < nextRoot.length; i++) { + nextRootPart.add(new ASN1OctetString(nextRoot[i])); + } + add(nextRootPart); + + // --- Encode . + ASN1SequenceOf seqOfnextNextRoot = new ASN1SequenceOf( + ASN1Sequence.class); + ASN1SequenceOf seqOfnnRStats = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf seqOfnnRStrings = new ASN1SequenceOf(ASN1IA5String.class); + ASN1SequenceOf seqOfnnRBytes = new ASN1SequenceOf(ASN1OctetString.class); + ASN1SequenceOf seqOfnnRInts = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf seqOfnnRTreehash = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf seqOfnnRRetain = new ASN1SequenceOf(ASN1Integer.class); + + for (int i = 0; i < nextNextRoot.length; i++) { + seqOfnnRStrings.add(new ASN1IA5String(algNames[0])); + seqOfnnRStrings.add(new ASN1IA5String("")); + seqOfnnRStats.add(seqOfnnRStrings); + seqOfnnRStrings = new ASN1SequenceOf(ASN1IA5String.class); + + int heightOfTree = nextNextRoot[i].getStatInt()[0]; + int tailLength = nextNextRoot[i].getStatInt()[7]; + + seqOfnnRBytes.add(new ASN1OctetString( + nextNextRoot[i].getStatByte()[0])); + for (int j = 0; j < heightOfTree; j++) { + seqOfnnRBytes.add(new ASN1OctetString(nextNextRoot[i] + .getStatByte()[1 + j])); + } + for (int j = 0; j < tailLength; j++) { + seqOfnnRBytes.add(new ASN1OctetString(nextNextRoot[i] + .getStatByte()[1 + heightOfTree + j])); + } + + seqOfnnRStats.add(seqOfnnRBytes); + seqOfnnRBytes = new ASN1SequenceOf(ASN1OctetString.class); + + seqOfnnRInts.add(new ASN1Integer(heightOfTree)); + seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[1])); + seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[2])); + seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[3])); + seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[4])); + seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[5])); + seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[6])); + seqOfnnRInts.add(new ASN1Integer(tailLength)); + for (int j = 0; j < heightOfTree; j++) { + seqOfnnRInts.add(new ASN1Integer( + nextNextRoot[i].getStatInt()[8 + j])); + } + for (int j = 0; j < tailLength; j++) { + seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[8 + + heightOfTree + j])); + } + + seqOfnnRStats.add(seqOfnnRInts); + seqOfnnRInts = new ASN1SequenceOf(ASN1Integer.class); + + // add treehash of nextNextRoot object + // ---------------------------- + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + if (nextNextRoot[i].getTreehash() != null) { + for (int j = 0; j < nextNextRoot[i].getTreehash().length; j++) { + seqOfString.add(new ASN1IA5String(algNames[0])); + seqOfString.add(new ASN1IA5String("")); + seqOfStat.add(seqOfString); + seqOfString = new ASN1SequenceOf(ASN1IA5String.class); + + tailLength = nextNextRoot[i].getTreehash()[j].getStatInt()[1]; + + seqOfByte.add(new ASN1OctetString(nextNextRoot[i] + .getTreehash()[j].getStatByte()[0])); + seqOfByte.add(new ASN1OctetString(nextNextRoot[i] + .getTreehash()[j].getStatByte()[1])); + seqOfByte.add(new ASN1OctetString(nextNextRoot[i] + .getTreehash()[j].getStatByte()[2])); + for (int k = 0; k < tailLength; k++) { + seqOfByte.add(new ASN1OctetString(nextNextRoot[i] + .getTreehash()[j].getStatByte()[3 + k])); + } + seqOfStat.add(seqOfByte); + seqOfByte = new ASN1SequenceOf(ASN1OctetString.class); + + seqOfInt.add(new ASN1Integer( + nextNextRoot[i].getTreehash()[j].getStatInt()[0])); + seqOfInt.add(new ASN1Integer(tailLength)); + seqOfInt.add(new ASN1Integer( + nextNextRoot[i].getTreehash()[j].getStatInt()[2])); + seqOfInt.add(new ASN1Integer( + nextNextRoot[i].getTreehash()[j].getStatInt()[3])); + seqOfInt.add(new ASN1Integer( + nextNextRoot[i].getTreehash()[j].getStatInt()[4])); + seqOfInt.add(new ASN1Integer( + nextNextRoot[i].getTreehash()[j].getStatInt()[5])); + for (int k = 0; k < tailLength; k++) { + seqOfInt.add(new ASN1Integer(nextNextRoot[i] + .getTreehash()[j].getStatInt()[6 + k])); + } + seqOfStat.add(seqOfInt); + seqOfInt = new ASN1SequenceOf(ASN1Integer.class); + + seqOfnnRTreehash.add(seqOfStat); + seqOfStat = new ASN1SequenceOf(ASN1Sequence.class); + } + } + // ---------------------------- + seqOfnnRStats.add(seqOfnnRTreehash); + seqOfnnRTreehash = new ASN1SequenceOf(ASN1Integer.class); + + // encode retain of nextNextRoot + // ---------------------------- + // --- Encode . + currentRetainPart0 = new ASN1SequenceOf(ASN1OctetString.class); + if (nextNextRoot[i].getRetain() != null) { + for (int j = 0; j < nextNextRoot[i].getRetain().length; j++) { + for (int k = 0; k < nextNextRoot[i].getRetain()[j].size(); k++) { + currentRetainPart0.add(new ASN1OctetString( + (byte[]) nextNextRoot[i].getRetain()[j] + .elementAt(k))); + } + seqOfnnRRetain.add(currentRetainPart0); + currentRetainPart0 = new ASN1SequenceOf( + ASN1OctetString.class); + } + } + // ---------------------------- + seqOfnnRStats.add(seqOfnnRRetain); + seqOfnnRRetain = new ASN1SequenceOf(ASN1Integer.class); + + seqOfnextNextRoot.add(seqOfnnRStats); + seqOfnnRStats = new ASN1SequenceOf(ASN1Sequence.class); + } + add(seqOfnextNextRoot); + + // --- Encode . + ASN1SequenceOf curRootSigPart = new ASN1SequenceOf( + ASN1OctetString.class); + for (int i = 0; i < currentRootSig.length; i++) { + curRootSigPart.add(new ASN1OctetString(currentRootSig[i])); + } + add(curRootSigPart); + + // --- Encode . + ASN1SequenceOf seqOfnextRootSigs = new ASN1SequenceOf( + ASN1Sequence.class); + ASN1SequenceOf seqOfnRSStats = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf seqOfnRSStrings = new ASN1SequenceOf(ASN1IA5String.class); + ASN1SequenceOf seqOfnRSBytes = new ASN1SequenceOf(ASN1OctetString.class); + ASN1SequenceOf seqOfnRSInts = new ASN1SequenceOf(ASN1Integer.class); + + for (int i = 0; i < nextRootSig.length; i++) { + seqOfnRSStrings.add(new ASN1IA5String(algNames[0])); + seqOfnRSStrings.add(new ASN1IA5String("")); + seqOfnRSStats.add(seqOfnRSStrings); + seqOfnRSStrings = new ASN1SequenceOf(ASN1IA5String.class); + + seqOfnRSBytes.add(new ASN1OctetString( + nextRootSig[i].getStatByte()[0])); + seqOfnRSBytes.add(new ASN1OctetString( + nextRootSig[i].getStatByte()[1])); + seqOfnRSBytes.add(new ASN1OctetString( + nextRootSig[i].getStatByte()[2])); + seqOfnRSBytes.add(new ASN1OctetString( + nextRootSig[i].getStatByte()[3])); + seqOfnRSBytes.add(new ASN1OctetString( + nextRootSig[i].getStatByte()[4])); + + seqOfnRSStats.add(seqOfnRSBytes); + seqOfnRSBytes = new ASN1SequenceOf(ASN1OctetString.class); + + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[0])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[1])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[2])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[3])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[4])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[5])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[6])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[7])); + seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[8])); + + seqOfnRSStats.add(seqOfnRSInts); + seqOfnRSInts = new ASN1SequenceOf(ASN1Integer.class); + + seqOfnextRootSigs.add(seqOfnRSStats); + seqOfnRSStats = new ASN1SequenceOf(ASN1Sequence.class); + } + add(seqOfnextRootSigs); + + // --- Encode . + ASN1SequenceOf parSetPart0 = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf parSetPart1 = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf parSetPart2 = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf parSetPart3 = new ASN1SequenceOf(ASN1Integer.class); + + for (int i = 0; i < gmssParameterset.getHeightOfTrees().length; i++) { + parSetPart1.add(new ASN1Integer( + gmssParameterset.getHeightOfTrees()[i])); + parSetPart2.add(new ASN1Integer(gmssParameterset + .getWinternitzParameter()[i])); + parSetPart3.add(new ASN1Integer(gmssParameterset.getK()[i])); + } + parSetPart0.add(new ASN1Integer(gmssParameterset.getNumOfLayers())); + parSetPart0.add(parSetPart1); + parSetPart0.add(parSetPart2); + parSetPart0.add(parSetPart3); + add(parSetPart0); + + // --- Encode . + ASN1SequenceOf namesPart = new ASN1SequenceOf(ASN1IA5String.class); + + for (int i = 0; i < algNames.length; i++) { + String name = algNames[i]; + if (name != null) { + namesPart.add(new ASN1IA5String(algNames[i])); + } else { + namesPart.add(new ASN1IA5String("")); + } + + } + add(namesPart); + + } + + /** + * @return The GMSSPrivateKeySpec + */ + public GMSSPrivateKeySpec getKeySpec() { + return keySpec; + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKeySpec.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKeySpec.java new file mode 100644 index 00000000..5228bd30 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPrivateKeySpec.java @@ -0,0 +1,227 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.util.Vector; + +import de.flexiprovider.api.keys.KeySpec; + +/** + * This class provides a specification for a GMSS private key. + * + * @author Michael Schneider, Sebastian Blume; + * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPrivateKey + */ +public class GMSSPrivateKeySpec implements KeySpec { + + private int[] index; + + private byte[][] currentSeed; + private byte[][] nextNextSeed; + + private byte[][][] currentAuthPath; + private byte[][][] nextAuthPath; + + private Treehash[][] currentTreehash; + private Treehash[][] nextTreehash; + + private Vector[] currentStack; + private Vector[] nextStack; + + private Vector[][] currentRetain; + private Vector[][] nextRetain; + + private byte[][][] keep; + + private GMSSLeaf[] nextNextLeaf; + private GMSSLeaf[] upperLeaf; + private GMSSLeaf[] upperTreehashLeaf; + + private int[] minTreehash; + + private GMSSParameterset gmssPS; + + private byte[][] nextRoot; + private GMSSRootCalc[] nextNextRoot; + + private byte[][] currentRootSig; + private GMSSRootSig[] nextRootSig; + + private String[] algNames; + + /** + * /** + * + * @param index + * tree indices + * @param currentSeed + * seed for the generation of private OTS keys for the + * current subtrees (TREE) + * @param nextNextSeed + * seed for the generation of private OTS keys for the + * subtrees after next (TREE++) + * @param currentAuthPath + * array of current authentication paths (AUTHPATH) + * @param nextAuthPath + * array of next authentication paths (AUTHPATH+) + * @param keep + * keep array for the authPath algorithm + * @param currentTreehash + * treehash for authPath algorithm of current tree + * @param nextTreehash + * treehash for authPath algorithm of next tree (TREE+) + * @param currentStack + * shared stack for authPath algorithm of current tree + * @param nextStack + * shared stack for authPath algorithm of next tree (TREE+) + * @param currentRetain + * retain stack for authPath algorithm of current tree + * @param nextRetain + * retain stack for authPath algorithm of next tree (TREE+) + * @param nextNextLeaf + * array of upcoming leafs of the tree after next (LEAF++) of + * each layer + * @param upperLeaf + * needed for precomputation of upper nodes + * @param upperTreehashLeaf + * needed for precomputation of upper treehash nodes + * @param minTreehash + * index of next treehash instance to receive an update + * @param nextRoot + * the roots of the next trees (ROOT+) + * @param nextNextRoot + * the roots of the tree after next (ROOT++) + * @param currentRootSig + * array of signatures of the roots of the current subtrees + * (SIG) + * @param nextRootSig + * array of signatures of the roots of the next subtree + * (SIG+) + * @param gmssParameterset + * the GMSS Parameterset + * @param algNames + * An array of strings, containing the name of the used hash + * function and the name of the corresponding provider + */ + public GMSSPrivateKeySpec(int[] index, byte[][] currentSeed, + byte[][] nextNextSeed, byte[][][] currentAuthPath, + byte[][][] nextAuthPath, Treehash[][] currentTreehash, + Treehash[][] nextTreehash, Vector[] currentStack, + Vector[] nextStack, Vector[][] currentRetain, + Vector[][] nextRetain, byte[][][] keep, GMSSLeaf[] nextNextLeaf, + GMSSLeaf[] upperLeaf, GMSSLeaf[] upperTreehashLeaf, + int[] minTreehash, byte[][] nextRoot, GMSSRootCalc[] nextNextRoot, + byte[][] currentRootSig, GMSSRootSig[] nextRootSig, + GMSSParameterset gmssParameterset, String[] algNames) { + + this.index = index; + this.currentSeed = currentSeed; + this.nextNextSeed = nextNextSeed; + this.currentAuthPath = currentAuthPath; + this.nextAuthPath = nextAuthPath; + this.currentTreehash = currentTreehash; + this.nextTreehash = nextTreehash; + this.currentStack = currentStack; + this.nextStack = nextStack; + this.currentRetain = currentRetain; + this.nextRetain = nextRetain; + this.keep = keep; + this.nextNextLeaf = nextNextLeaf; + this.upperLeaf = upperLeaf; + this.upperTreehashLeaf = upperTreehashLeaf; + this.minTreehash = minTreehash; + this.nextRoot = nextRoot; + this.nextNextRoot = nextNextRoot; + this.currentRootSig = currentRootSig; + this.nextRootSig = nextRootSig; + this.gmssPS = gmssParameterset; + this.algNames = algNames; + } + + public int[] getIndex() { + return index; + } + + public byte[][] getCurrentSeed() { + return currentSeed; + } + + public byte[][] getNextNextSeed() { + return nextNextSeed; + } + + public byte[][][] getCurrentAuthPath() { + return currentAuthPath; + } + + public byte[][][] getNextAuthPath() { + return nextAuthPath; + } + + public Treehash[][] getCurrentTreehash() { + return currentTreehash; + } + + public Treehash[][] getNextTreehash() { + return nextTreehash; + } + + public byte[][][] getKeep() { + return keep; + } + + public Vector[] getCurrentStack() { + return currentStack; + } + + public Vector[] getNextStack() { + return nextStack; + } + + public Vector[][] getCurrentRetain() { + return currentRetain; + } + + public Vector[][] getNextRetain() { + return nextRetain; + } + + public GMSSLeaf[] getNextNextLeaf() { + return nextNextLeaf; + } + + public GMSSLeaf[] getUpperLeaf() { + return upperLeaf; + } + + public GMSSLeaf[] getUpperTreehashLeaf() { + return upperTreehashLeaf; + } + + public int[] getMinTreehash() { + return minTreehash; + } + + public GMSSRootSig[] getNextRootSig() { + return nextRootSig; + } + + public GMSSParameterset getGmssPS() { + return gmssPS; + } + + public byte[][] getNextRoot() { + return nextRoot; + } + + public GMSSRootCalc[] getNextNextRoot() { + return nextNextRoot; + } + + public byte[][] getCurrentRootSig() { + return currentRootSig; + } + + public String[] getAlgNames() { + return algNames; + } +} \ No newline at end of file diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKey.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKey.java new file mode 100644 index 00000000..8b7fbe0b --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKey.java @@ -0,0 +1,125 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import codec.asn1.ASN1Null; +import codec.asn1.ASN1ObjectIdentifier; +import codec.asn1.ASN1Type; +import de.flexiprovider.api.keys.PublicKey; +import de.flexiprovider.common.util.ASN1Tools; +import de.flexiprovider.common.util.ByteUtils; + +/** + * This class implements the GMSS public key and is usually initiated by the GMSSKeyPairGenerator. + * + * @author Sebastian Blume + * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSKeyPairGenerator + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPublicKeySpec + */ +public class GMSSPublicKey extends PublicKey { + + /** + * The GMSS public key + */ + private byte[] publicKeyBytes; + + /** + * The GMSSParameterset + */ + private GMSSParameterset gmssParameterset; + + /** + * The constructor + * + * @param pub + * a raw GMSS public key + * + * @param gmssParameterset + * an instance of GMSS Parameterset + * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSKeyPairGenerator + */ + protected GMSSPublicKey(byte[] pub, GMSSParameterset gmssParameterset) { + + this.gmssParameterset = gmssParameterset; + this.publicKeyBytes = pub; + } + + /** + * The constructor + * + * @param keySpec + * a GMSS key specification + */ + protected GMSSPublicKey(GMSSPublicKeySpec keySpec) { + this(keySpec.getPublicKey(), keySpec.getGMSSParameterset()); + } + + /** + * Returns the name of the algorithm + * + * @return "GMSS" + */ + public String getAlgorithm() { + return "GMSS"; + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected ASN1ObjectIdentifier getOID() { + return new ASN1ObjectIdentifier(GMSSKeyFactory.OID); + } + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected ASN1Type getAlgParams() { + return new ASN1Null(); + } + + /** + * @return the keyData to encode in the SubjectPublicKeyInfo structure + */ + protected byte[] getKeyData() { + GMSSPublicKeyASN1 key = new GMSSPublicKeyASN1(publicKeyBytes, + gmssParameterset); + return ASN1Tools.derEncode(key); + } + + /** + * @return The GMSS public key byte array + */ + protected byte[] getPublicKeyBytes() { + return publicKeyBytes; + } + + /** + * @return The GMSS Parameterset + */ + protected GMSSParameterset getParameterset() { + return gmssParameterset; + } + + /** + * Returns a human readable form of the GMSS public key + * + * @return A human readable form of the GMSS public key + */ + public String toString() { + String out = "GMSS public key : " + + ByteUtils.toHexString(publicKeyBytes) + "\n" + + "Height of Trees: \n"; + + for (int i = 0; i < gmssParameterset.getHeightOfTrees().length; i++) { + out = out + "Layer " + i + " : " + + gmssParameterset.getHeightOfTrees()[i] + + " WinternitzParameter: " + + gmssParameterset.getWinternitzParameter()[i] + " K: " + + gmssParameterset.getK()[i] + "\n"; + } + return out; + } + +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKeyASN1.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKeyASN1.java new file mode 100644 index 00000000..199d9622 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKeyASN1.java @@ -0,0 +1,117 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import codec.asn1.ASN1Integer; +import codec.asn1.ASN1OctetString; +import codec.asn1.ASN1Sequence; +import codec.asn1.ASN1SequenceOf; +import codec.asn1.ASN1Type; +import de.flexiprovider.common.util.ASN1Tools; + +/** + * This class implements an ASN.1 encoded GMSS public key. The ASN.1 definition + * of this structure is: + * + *
+ *  GMSSPublicKey		::= SEQUENCE{
+ *   	publicKey		SEQUENCE OF OCTET STRING 
+ *  	heightOfTrees	SEQUENCE OF INTEGER
+ *  	Parameterset	ParSet
+ * 	}
+ * 	ParSet				::= SEQUENCE {
+ * 		T				INTEGER
+ * 		h				SEQUENCE OF INTEGER
+ * 		w				SEQUENCE OF INTEGER		
+ * 	K				SEQUENCE OF INTEGER
+ * 	}
+ * 
+ * + * @author Sebastian Blume, Michael Schneider + */ +public class GMSSPublicKeyASN1 extends ASN1Sequence { + + /** + * A key specification of the GMSS public key + */ + private GMSSPublicKeySpec keySpec; + + /** + * The Constructor + * + * @param encoded + * a ASN.1 encoded GMSS public key + */ + public GMSSPublicKeyASN1(ASN1Type encoded) { + + ASN1Sequence mtsPublicKey = (ASN1Sequence) encoded; + + // --- Decode . + byte[] publicKey = ((ASN1OctetString) mtsPublicKey.get(0)) + .getByteArray(); + + // --- Decode . + + ASN1Sequence seqOfParams = (ASN1Sequence) mtsPublicKey.get(1); + int numLayer = ASN1Tools.getFlexiBigInt( + ((ASN1Integer) seqOfParams.get(0))).intValue(); + ASN1Sequence seqOfPSh = (ASN1Sequence) seqOfParams.get(1); + ASN1Sequence seqOfPSw = (ASN1Sequence) seqOfParams.get(2); + ASN1Sequence seqOfPSK = (ASN1Sequence) seqOfParams.get(3); + + int[] h = new int[seqOfPSh.size()]; + int[] w = new int[seqOfPSw.size()]; + int[] K = new int[seqOfPSK.size()]; + + for (int i = 0; i < h.length; i++) { + h[i] = ASN1Tools.getFlexiBigInt(((ASN1Integer) seqOfPSh.get(i))) + .intValue(); + w[i] = ASN1Tools.getFlexiBigInt(((ASN1Integer) seqOfPSw.get(i))) + .intValue(); + K[i] = ASN1Tools.getFlexiBigInt(((ASN1Integer) seqOfPSK.get(i))) + .intValue(); + } + GMSSParameterset parSet = new GMSSParameterset(numLayer, h, w, K); + + keySpec = new GMSSPublicKeySpec(publicKey, parSet); + } + + /** + * The Constructor + * + * @param publicKey + * a raw GMSS public key + * @param parSet + * GMSS parameter set + */ + public GMSSPublicKeyASN1(byte[] publicKey, GMSSParameterset parSet) { + + // --- Encode . + add(new ASN1OctetString(publicKey)); + + // --- Encode . + ASN1SequenceOf parSetPart0 = new ASN1SequenceOf(ASN1Sequence.class); + ASN1SequenceOf parSetPart1 = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf parSetPart2 = new ASN1SequenceOf(ASN1Integer.class); + ASN1SequenceOf parSetPart3 = new ASN1SequenceOf(ASN1Integer.class); + + for (int i = 0; i < parSet.getHeightOfTrees().length; i++) { + parSetPart1.add(new ASN1Integer(parSet.getHeightOfTrees()[i])); + parSetPart2 + .add(new ASN1Integer(parSet.getWinternitzParameter()[i])); + parSetPart3.add(new ASN1Integer(parSet.getK()[i])); + } + parSetPart0.add(new ASN1Integer(parSet.getNumOfLayers())); + parSetPart0.add(parSetPart1); + parSetPart0.add(parSetPart2); + parSetPart0.add(parSetPart3); + add(parSetPart0); + } + + /** + * Returns the GMSS public key specification + * + * @return the GMSS public key specification + */ + public GMSSPublicKeySpec getKeySpec() { + return keySpec; + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKeySpec.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKeySpec.java new file mode 100644 index 00000000..2385bac4 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSPublicKeySpec.java @@ -0,0 +1,55 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.keys.KeySpec; + +/** + * This class provides a specification for a GMSS public key. + * + * @author Sebastian Blume + * + * @see de.flexiprovider.pqc.hbc.gmss.GMSSPublicKey + */ +public class GMSSPublicKeySpec implements KeySpec { + + /** + * The GMSS public key + */ + private byte[] gmssPublicKey; + + /** + * The GMSSParameterset + */ + private GMSSParameterset gmssParameterset; + + /** + * The constructor. + * + * @param key + * a raw GMSS public key + * @param gmssParameterset + * an instance of GMSSParameterset + */ + public GMSSPublicKeySpec(byte[] key, GMSSParameterset gmssParameterset) { + this.gmssPublicKey = key; + this.gmssParameterset = gmssParameterset; + } + + /** + * Returns the GMSS public key + * + * @return The GMSS public key + */ + public byte[] getPublicKey() { + return gmssPublicKey; + } + + /** + * Returns the GMSS parameterset + * + * @return The GMSS parameterset + */ + public GMSSParameterset getGMSSParameterset() { + return gmssParameterset; + } + +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSRandom.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSRandom.java new file mode 100644 index 00000000..14a2d770 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSRandom.java @@ -0,0 +1,82 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.MessageDigest; + +/** + * This class provides a PRNG for GMSS + * + * @author Sebastian Blume, Michael Schneider + */ +public class GMSSRandom { + + /** + * Hash function for the construction of the authentication trees + */ + private MessageDigest messDigestTree; + + /** + * Constructor + * + * @param messDigestTree + */ + public GMSSRandom(MessageDigest messDigestTree) { + + this.messDigestTree = messDigestTree; + } + + /** + * computes the next seed value, returns a random byte array and sets + * outseed to the next value + * + * @param outseed + * byte array in which ((1 + SEEDin +RAND) mod 2^n) will be + * stored + * @return byte array of H(SEEDin) + */ + public byte[] nextSeed(byte[] outseed) { + + // byte array value "1" + byte[] one = new byte[outseed.length]; + one[0] = 1; + for (int i = 1; i < outseed.length; i++) { + one[i] = 0; + } + // RAND <-- H(SEEDin) + byte[] rand = new byte[outseed.length]; + messDigestTree.update(outseed); + rand = messDigestTree.digest(); + + // SEEDout <-- (1 + SEEDin +RAND) mod 2^n + addByteArrays(outseed, rand); + addOne(outseed); + + // System.arraycopy(outseed, 0, outseed, 0, outseed.length); + + return rand; + } + + private void addByteArrays(byte[] a, byte[] b) { + + byte overflow = 0; + int temp; + + for (int i = 0; i < a.length; i++) { + temp = (0xFF & a[i]) + (0xFF & b[i]) + overflow; + a[i] = (byte) temp; + overflow = (byte) (temp >> 8); + } + } + + private void addOne(byte[] a) { + + byte overflow = 1; + int temp; + + for (int i = 0; i < a.length; i++) { + temp = (0xFF & a[i]) + overflow; + a[i] = (byte) temp; + overflow = (byte) (temp >> 8); + } + } + +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSRootCalc.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSRootCalc.java new file mode 100644 index 00000000..fbe1de29 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSRootCalc.java @@ -0,0 +1,528 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.util.Vector; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.common.util.ByteUtils; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class computes a whole Merkle tree and saves the needed values for + * AuthPath computation. It is used for precomputation of the root of a + * following tree. After initialization, 2^H updates are required to complete + * the root. Every update requires one leaf value as parameter. While computing + * the root all initial values for the authentication path algorithm (treehash, + * auth, retain) are stored for later use. + * + * @author Michael Schneider + */ +class GMSSRootCalc { + + /** + * max height of the tree + */ + private int heightOfTree; + + /** + * length of the messageDigest + */ + private int mdLength; + + /** + * the treehash instances of the tree + */ + private Treehash[] treehash; + + /** + * stores the retain nodes for authPath computation + */ + private Vector[] retain; + + /** + * finally stores the root of the tree when finished + */ + private byte[] root; + + /** + * stores the authentication path y_1(i), i = 0..H-1 + */ + private byte[][] AuthPath; + + /** + * the value K for the authentication path computation + */ + private int K; + + /** + * Vector element that stores the nodes on the stack + */ + private Vector tailStack; + + /** + * stores the height of all nodes laying on the tailStack + */ + private Vector heightOfNodes; + /** + * The hash function used for the construction of the authentication trees + */ + private MessageDigest messDigestTree; + + /** + * An array of strings containing the name of the hash function used to + * construct the authentication trees and used by the OTS. + */ + private String[] algNames = new String[2]; + + /** + * stores the index of the current node on each height of the tree + */ + private int[] index; + + /** + * true if instance was already initialized, false otherwise + */ + private boolean isInitialized; + + /** + * true it instance was finished + */ + private boolean isFinished; + + /** + * Integer that stores the index of the next seed that has to be omitted to + * the treehashs + */ + private int indexForNextSeed; + + /** + * temporary integer that stores the height of the next treehash instance + * that gets initialized with a seed + */ + private int heightOfNextSeed; + + /** + * This constructor regenerates a prior treehash object + * + * @param name + * an array of strings, containing the name of the used hash + * function and PRNG and the name of the corresponding + * provider + * @param statByte + * status bytes + * @param statInt + * status ints + */ + public GMSSRootCalc(String[] name, byte[][] statByte, int[] statInt, + Treehash[] treeH, Vector[] ret) { + // decode name + CoreRegistry.registerAlgorithms(); + try { + messDigestTree = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + System.err.println(": message digest " + name[0] + " not found in " + + name[1]); + } + + this.algNames = name; + // decode statInt + this.heightOfTree = statInt[0]; + this.mdLength = statInt[1]; + this.K = statInt[2]; + this.indexForNextSeed = statInt[3]; + this.heightOfNextSeed = statInt[4]; + if (statInt[5] == 1) + this.isFinished = true; + else + this.isFinished = false; + if (statInt[6] == 1) + this.isInitialized = true; + else + this.isInitialized = false; + + int tailLength = statInt[7]; + + this.index = new int[heightOfTree]; + for (int i = 0; i < heightOfTree; i++) { + this.index[i] = statInt[8 + i]; + } + + this.heightOfNodes = new Vector(); + for (int i = 0; i < tailLength; i++) { + this.heightOfNodes.addElement(new Integer(statInt[8 + heightOfTree + + i])); + } + + // decode statByte + this.root = statByte[0]; + + this.AuthPath = new byte[heightOfTree][mdLength]; + for (int i = 0; i < heightOfTree; i++) { + this.AuthPath[i] = statByte[1 + i]; + } + + this.tailStack = new Vector(); + for (int i = 0; i < tailLength; i++) { + this.tailStack.addElement(statByte[1 + heightOfTree + i]); + } + + // decode treeH + this.treehash = treeH; + + // decode ret + this.retain = ret; + } + + /** + * Constructor + * + * @param heightOfTree + * maximal height of the tree + * @param algNames + * an array of strings, containing the name of the used hash + * function and PRNG and the name of the corresponding + * provider + */ + public GMSSRootCalc(int heightOfTree, int K, String[] algNames) { + this.heightOfTree = heightOfTree; + CoreRegistry.registerAlgorithms(); + try { + messDigestTree = Registry.getMessageDigest(algNames[0]); + } catch (NoSuchAlgorithmException nsae) { + System.err.println(": message digest " + algNames[0] + + " not found in " + algNames[1]); + } + this.algNames = algNames; + this.mdLength = messDigestTree.getDigestLength(); + this.K = K; + this.index = new int[heightOfTree]; + this.AuthPath = new byte[heightOfTree][mdLength]; + this.root = new byte[mdLength]; + // this.treehash = new Treehash[this.heightOfTree - this.K]; + this.retain = new Vector[this.K - 1]; + for (int i = 0; i < K - 1; i++) + this.retain[i] = new Vector(); + + } + + /** + * Initializes the calculation of a new root + * + * @param sharedStack + * the stack shared by all treehash instances of this tree + */ + public void initialize(Vector sharedStack) { + this.treehash = new Treehash[this.heightOfTree - this.K]; + for (int i = 0; i < this.heightOfTree - this.K; i++) + this.treehash[i] = new Treehash(sharedStack, i, this.algNames); + + this.index = new int[heightOfTree]; + this.AuthPath = new byte[heightOfTree][mdLength]; + this.root = new byte[mdLength]; + + this.tailStack = new Vector(); + this.heightOfNodes = new Vector(); + this.isInitialized = true; + this.isFinished = false; + + for (int i = 0; i < heightOfTree; i++) + this.index[i] = -1; + + this.retain = new Vector[this.K - 1]; + for (int i = 0; i < K - 1; i++) + this.retain[i] = new Vector(); + + this.indexForNextSeed = 3; + this.heightOfNextSeed = 0; + } + + /** + * updates the root with one leaf and stores needed values in retain, + * treehash or authpath. Additionally counts the seeds used. This method is + * used when performing the updates for TREE++. + * + * @param seed + * the initial seed for treehash: seedNext + * @param leaf + * the height of the treehash + */ + public void update(byte[] seed, byte[] leaf) { + if (this.heightOfNextSeed < (this.heightOfTree - this.K) + && this.indexForNextSeed - 2 == index[0]) { + this.initializeTreehashSeed(seed, this.heightOfNextSeed); + this.heightOfNextSeed++; + this.indexForNextSeed *= 2; + } + // now call the simple update + this.update(leaf); + } + + /** + * Updates the root with one leaf and stores the needed values in retain, + * treehash or authpath + */ + public void update(byte[] leaf) { + + if (isFinished) { + System.out.print("Too much updates for Tree!!"); + return; + } + if (!isInitialized) { + System.err.println("GMSSRootCalc not initialized!"); + return; + } + + // a new leaf was omitted, so raise index on lowest layer + index[0]++; + + // store the nodes on the lowest layer in treehash or authpath + if (index[0] == 1) { + System.arraycopy(leaf, 0, AuthPath[0], 0, mdLength); + } else if (index[0] == 3) { + // store in treehash only if K < H + if (heightOfTree > K) + treehash[0].setFirstNode(leaf); + } + + if ((index[0] - 3) % 2 == 0 && index[0] >= 3) { + // store in retain if K = H + if (heightOfTree == K) + // TODO: check it + retain[0].insertElementAt(leaf, 0); + } + + // if first update to this tree is made + if (index[0] == 0) { + tailStack.addElement(leaf); + heightOfNodes.addElement(new Integer(0)); + } else { + + byte[] help = new byte[mdLength]; + byte[] toBeHashed = new byte[mdLength << 1]; + + // store the new leaf in help + System.arraycopy(leaf, 0, help, 0, mdLength); + int helpHeight = 0; + // while top to nodes have same height + while (tailStack.size() > 0 + && helpHeight == ((Integer) heightOfNodes.lastElement()) + .intValue()) { + + // help <-- hash(stack top element || help) + System.arraycopy(tailStack.lastElement(), 0, toBeHashed, 0, + mdLength); + tailStack.removeElementAt(tailStack.size() - 1); + heightOfNodes.removeElementAt(heightOfNodes.size() - 1); + System.arraycopy(help, 0, toBeHashed, mdLength, mdLength); + + messDigestTree.update(toBeHashed); + help = messDigestTree.digest(); + + // the new help node is one step higher + helpHeight++; + if (helpHeight < heightOfTree) { + index[helpHeight]++; + + // add index 1 element to initial authpath + if (index[helpHeight] == 1) { + System.arraycopy(help, 0, AuthPath[helpHeight], 0, + mdLength); + } + + if (helpHeight >= heightOfTree - K) { + if (helpHeight == 0) + System.out.println("MÖÖÖP"); + // add help element to retain stack if it is a right + // node + // and not stored in treehash + if ((index[helpHeight] - 3) % 2 == 0 + && index[helpHeight] >= 3) + // TODO: check it + retain[helpHeight - (heightOfTree - K)] + .insertElementAt(help, 0); + } else { + // if element is third in his line add it to treehash + if (index[helpHeight] == 3) { + treehash[helpHeight].setFirstNode(help); + } + } + } + } + // push help element to the stack + tailStack.addElement(help); + heightOfNodes.addElement(new Integer(helpHeight)); + + // is the root calculation finished? + if (helpHeight == heightOfTree) { + isFinished = true; + isInitialized = false; + root = (byte[]) tailStack.lastElement(); + } + } + + } + + /** + * initializes the seeds for the treehashs of the tree precomputed by this + * class + * + * @param seed + * the initial seed for treehash: seedNext + * @param index + * the height of the treehash + */ + public void initializeTreehashSeed(byte[] seed, int index) { + treehash[index].initializeSeed(seed); + } + + /** + * Method to check whether the instance has been initialized or not + * + * @return true if treehash was already initialized + */ + public boolean wasInitialized() { + return isInitialized; + } + + /** + * Method to check whether the instance has been finished or not + * + * @return true if tree has reached its maximum height + */ + public boolean wasFinished() { + return isFinished; + } + + /** + * returns the authentication path of the first leaf of the tree + * + * @return the authentication path of the first leaf of the tree + */ + public byte[][] getAuthPath() { + return AuthPath; + } + + /** + * returns the initial treehash instances, storing value y_3(i) + * + * @return the initial treehash instances, storing value y_3(i) + */ + public Treehash[] getTreehash() { + return treehash; + } + + /** + * returns the retain stacks storing all right nodes near to the root + * + * @return the retain stacks storing all right nodes near to the root + */ + public Vector[] getRetain() { + return retain; + } + + /** + * returns the finished root value + * + * @return the finished root value + */ + public byte[] getRoot() { + return root; + } + + /** + * returns the shared stack + * + * @return the shared stack + */ + public Vector getStack() { + return tailStack; + } + + /** + * Returns the status byte array used by the GMSSPrivateKeyASN.1 class + * + * @return The status bytes + */ + public byte[][] getStatByte() { + + int tailLength; + if (tailStack == null) + tailLength = 0; + else + tailLength = tailStack.size(); + byte[][] statByte = new byte[1 + heightOfTree + tailLength][messDigestTree + .getDigestLength()]; + statByte[0] = root; + + for (int i = 0; i < heightOfTree; i++) { + statByte[1 + i] = AuthPath[i]; + } + for (int i = 0; i < tailLength; i++) { + statByte[1 + heightOfTree + i] = (byte[]) tailStack.elementAt(i); + } + + return statByte; + } + + /** + * Returns the status int array used by the GMSSPrivateKeyASN.1 class + * + * @return The status ints + */ + public int[] getStatInt() { + + int tailLength; + if (tailStack == null) { + tailLength = 0; + } else { + tailLength = tailStack.size(); + } + int[] statInt = new int[8 + heightOfTree + tailLength]; + statInt[0] = heightOfTree; + statInt[1] = mdLength; + statInt[2] = K; + statInt[3] = indexForNextSeed; + statInt[4] = heightOfNextSeed; + if (isFinished) + statInt[5] = 1; + else + statInt[5] = 0; + if (isInitialized) + statInt[6] = 1; + else + statInt[6] = 0; + statInt[7] = tailLength; + + for (int i = 0; i < heightOfTree; i++) { + statInt[8 + i] = index[i]; + } + for (int i = 0; i < tailLength; i++) { + statInt[8 + heightOfTree + i] = ((Integer) heightOfNodes + .elementAt(i)).intValue(); + } + + return statInt; + } + + /** + * @return a human readable version of the structure + */ + public String toString() { + String out = ""; + int tailLength; + if (tailStack == null) + tailLength = 0; + else + tailLength = tailStack.size(); + + for (int i = 0; i < 8 + heightOfTree + tailLength; i++) { + out = out + getStatInt()[i] + " "; + } + for (int i = 0; i < 1 + heightOfTree + tailLength; i++) { + out = out + ByteUtils.toHexString(getStatByte()[i]) + " "; + } + out = out + " " + algNames[0] + "( " + algNames[1] + " ) " + mdLength; + return out; + } +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSRootSig.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSRootSig.java new file mode 100644 index 00000000..a593e7ee --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSRootSig.java @@ -0,0 +1,626 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.SignatureException; +import de.flexiprovider.common.util.ByteUtils; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class implements the distributed signature generation of the Winternitz + * one-time signature scheme (OTSS), described in C.Dods, N.P. Smart, and M. + * Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages 96–115, + * 2005. The class is used by the GMSS classes. + * + * @author Sebastian Blume, Michael Schneider + * + */ +public class GMSSRootSig { + + /** + * The hash function used by the OTS + */ + private MessageDigest messDigestOTS; + + /** + * The length of the message digest and private key + */ + private int mdsize, keysize; + + /** + * The private key + */ + private byte[] privateKeyOTS; + + /** + * The message bytes + */ + private byte[] hash; + + /** + * The signature bytes + */ + private byte[] sign; + + /** + * The Winternitz parameter + */ + private int w; + + /** + * The source of randomness for OTS private key generation + */ + private GMSSRandom gmssRandom; + + /** + * Sizes of the message + */ + private int messagesize; + + /** + * Some precalculated values + */ + private int k; + + /** + * Some variables for storing the actual status of distributed signing + */ + private int r, test, counter, ii; + + /** + * variables for storing big numbers for the actual status of distributed + * signing + */ + private long test8, big8; + + /** + * The necessary steps of each updateSign() call + */ + private int steps; + + /** + * The checksum part + */ + private int checksum; + + /** + * The height of the tree + */ + private int height; + + /** + * The current intern OTSseed + */ + private byte[] seed; + + /** + * This constructor regenerates a prior GMSSRootSig object used by the + * GMSSPrivateKeyASN.1 class + * + * @param name + * an array of strings, containing the name of the used hash + * function, the name of the PRGN and the names of the + * corresponding providers + * @param statByte + * status byte array + * @param statInt + * status int array + */ + public GMSSRootSig(String[] name, byte[][] statByte, int[] statInt) { + + CoreRegistry.registerAlgorithms(); + try { + messDigestOTS = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + System.err.println(": message digest " + name[0] + " not found in " + + name[1]); + } + + gmssRandom = new GMSSRandom(messDigestOTS); + + this.counter = statInt[0]; + this.test = statInt[1]; + this.ii = statInt[2]; + this.r = statInt[3]; + this.steps = statInt[4]; + this.keysize = statInt[5]; + this.height = statInt[6]; + this.w = statInt[7]; + this.checksum = statInt[8]; + + this.mdsize = messDigestOTS.getDigestLength(); + + this.k = (1 << w) - 1; + + int mdsizeBit = mdsize << 3; + this.messagesize = (int) Math.ceil((double) (mdsizeBit) / (double) w); + + this.privateKeyOTS = statByte[0]; + this.seed = statByte[1]; + this.hash = statByte[2]; + + this.sign = statByte[3]; + + this.test8 = ((statByte[4][0] & 0xff)) + | ((long) (statByte[4][1] & 0xff) << 8) + | ((long) (statByte[4][2] & 0xff) << 16) + | ((long) (statByte[4][3] & 0xff)) << 24 + | ((long) (statByte[4][4] & 0xff)) << 32 + | ((long) (statByte[4][5] & 0xff)) << 40 + | ((long) (statByte[4][6] & 0xff)) << 48 + | ((long) (statByte[4][7] & 0xff)) << 56; + + this.big8 = ((statByte[4][8] & 0xff)) + | ((long) (statByte[4][9] & 0xff) << 8) + | ((long) (statByte[4][10] & 0xff) << 16) + | ((long) (statByte[4][11] & 0xff)) << 24 + | ((long) (statByte[4][12] & 0xff)) << 32 + | ((long) (statByte[4][13] & 0xff)) << 40 + | ((long) (statByte[4][14] & 0xff)) << 48 + | ((long) (statByte[4][15] & 0xff)) << 56; + } + + /** + * The constructor generates the PRNG and initializes some variables + * + * @param name + * an array of strings, containing the name of the used hash + * function, the name of the PRGN and the names of the + * corresponding providers + * @param w + * the winternitz parameter + * @param height + * the heigth of the tree + */ + public GMSSRootSig(String[] name, int w, int height) { + + CoreRegistry.registerAlgorithms(); + try { + messDigestOTS = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + System.err.println(": message digest " + name[0] + " not found in " + + name[1]); + } + + gmssRandom = new GMSSRandom(messDigestOTS); + + this.mdsize = messDigestOTS.getDigestLength(); + this.w = w; + this.height = height; + + this.k = (1 << w) - 1; + + int mdsizeBit = mdsize << 3; + this.messagesize = (int) Math.ceil((double) (mdsizeBit) / (double) w); + } + + /** + * This method initializes the distributed sigature calculation. Variables + * are reseted and necessary steps are calculated + * + * @param seed0 + * the initial OTSseed + * @param message + * the massage which will be signed + */ + public void initSign(byte[] seed0, byte[] message) { + + // create hash of message m + this.hash = new byte[mdsize]; + messDigestOTS.update(message); + this.hash = messDigestOTS.digest(); + + // variables for calculation of steps + byte[] messPart = new byte[mdsize]; + System.arraycopy(hash, 0, messPart, 0, mdsize); + int checkPart = 0; + int sumH = 0; + int checksumsize = getLog((messagesize << w) + 1); + + // ------- calculation of necessary steps ------ + if (8 % w == 0) { + int dt = 8 / w; + // message part + for (int a = 0; a < mdsize; a++) { + // count necessary hashs in 'sumH' + for (int b = 0; b < dt; b++) { + sumH += messPart[a] & k; + messPart[a] = (byte) (messPart[a] >>> w); + } + } + // checksum part + this.checksum = (messagesize << w) - sumH; + checkPart = checksum; + // count necessary hashs in 'sumH' + for (int b = 0; b < checksumsize; b += w) { + sumH += checkPart & k; + checkPart >>>= w; + } + } // end if ( 8 % w == 0 ) + else if (w < 8) { + long big8; + int ii = 0; + int dt = mdsize / w; + + // first d*w bytes of hash (main message part) + for (int i = 0; i < dt; i++) { + big8 = 0; + for (int j = 0; j < w; j++) { + big8 ^= (messPart[ii] & 0xff) << (j << 3); + ii++; + } + // count necessary hashs in 'sumH' + for (int j = 0; j < 8; j++) { + sumH += (int) (big8 & k); + big8 >>>= w; + } + } + // rest of message part + dt = mdsize % w; + big8 = 0; + for (int j = 0; j < dt; j++) { + big8 ^= (messPart[ii] & 0xff) << (j << 3); + ii++; + } + dt <<= 3; + // count necessary hashs in 'sumH' + for (int j = 0; j < dt; j += w) { + sumH += (int) (big8 & k); + big8 >>>= w; + } + // checksum part + this.checksum = (messagesize << w) - sumH; + checkPart = checksum; + // count necessary hashs in 'sumH' + for (int i = 0; i < checksumsize; i += w) { + sumH += checkPart & k; + checkPart >>>= w; + } + }// end if(w<8) + else if (w < 57) { + long big8; + int r = 0; + int s, f, rest, ii; + + // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w (main + // message part) + while (r <= ((mdsize << 3) - w)) { + s = r >>> 3; + rest = r % 8; + r += w; + f = (r + 7) >>> 3; + big8 = 0; + ii = 0; + for (int j = s; j < f; j++) { + big8 ^= (messPart[j] & 0xff) << (ii << 3); + ii++; + } + big8 >>>= rest; + // count necessary hashs in 'sumH' + sumH += (big8 & k); + + } + // rest of message part + s = r >>> 3; + if (s < mdsize) { + rest = r % 8; + big8 = 0; + ii = 0; + for (int j = s; j < mdsize; j++) { + big8 ^= (messPart[j] & 0xff) << (ii << 3); + ii++; + } + + big8 >>>= rest; + // count necessary hashs in 'sumH' + sumH += (big8 & k); + } + // checksum part + this.checksum = (messagesize << w) - sumH; + checkPart = checksum; + // count necessary hashs in 'sumH' + for (int i = 0; i < checksumsize; i += w) { + sumH += (checkPart & k); + checkPart >>>= w; + } + }// end if(w<57) + + // calculate keysize + this.keysize = messagesize + + (int) Math.ceil((double) checksumsize / (double) w); + + // calculate steps: 'keysize' times PRNG, 'sumH' times hashing, + // (1<steps steps of distributed signature + * calculaion + * + * @return true if signature is generated completly, else false + * @throws SignatureException + */ + public boolean updateSign() throws SignatureException { + // steps times do + + for (int s = 0; s < steps; s++) { // do 'step' times + + if (counter < keysize) { // generate the private key or perform + // the next hash + oneStep(); + } + if (counter == keysize) {// finish + return true; + } + } + + return false; // leaf not finished yet + } + + /** + * @return The private OTS key + */ + public byte[] getSig() { + + return sign; + } + + /** + * @return The one-time signature of the message, generated step by step + */ + private void oneStep() throws SignatureException { + // -------- if (8 % w == 0) ---------- + if (8 % w == 0) { + if (test == 0) { + // get current OTSprivateKey + this.privateKeyOTS = gmssRandom.nextSeed(seed); + // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize); + + if (ii < mdsize) { // for main message part + test = hash[ii] & k; + hash[ii] = (byte) (hash[ii] >>> w); + } else { // for checksum part + test = checksum & k; + checksum >>>= w; + } + } else if (test > 0) { // hash the private Key 'test' times (on + // time each step) + messDigestOTS.update(privateKeyOTS); + privateKeyOTS = messDigestOTS.digest(); + test--; + } + if (test == 0) { // if all hashes done copy result to siganture + // array + System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, + mdsize); + counter++; + + if (counter % (8 / w) == 0) { // raise array index for main + // massage part + ii++; + } + } + + }// ----- end if (8 % w == 0) ----- + // ---------- if ( w < 8 ) ---------------- + else if (w < 8) { + + if (test == 0) { + if (counter % 8 == 0 && ii < mdsize) { // after every 8th "add + // to signature"-step + big8 = 0; + if (counter < ((mdsize / w) << 3)) {// main massage + // (generate w*8 Bits + // every time) part + for (int j = 0; j < w; j++) { + big8 ^= (hash[ii] & 0xff) << (j << 3); + ii++; + } + } else { // rest of massage part (once) + for (int j = 0; j < mdsize % w; j++) { + big8 ^= (hash[ii] & 0xff) << (j << 3); + ii++; + } + } + } + if (counter == messagesize) { // checksum part (once) + big8 = checksum; + } + + test = (int) (big8 & k); + // generate current OTSprivateKey + this.privateKeyOTS = gmssRandom.nextSeed(seed); + // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize); + + } else if (test > 0) { // hash the private Key 'test' times (on + // time each step) + messDigestOTS.update(privateKeyOTS); + privateKeyOTS = messDigestOTS.digest(); + test--; + } + if (test == 0) { // if all hashes done copy result to siganture + // array + System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, + mdsize); + big8 >>>= w; + counter++; + } + + }// ------- end if(w<8)-------------------------------- + // --------- if w < 57 ----------------------------- + else if (w < 57) { + + if (test8 == 0) { + int s, f, rest; + big8 = 0; + ii = 0; + rest = r % 8; + s = r >>> 3; + // --- message part--- + if (s < mdsize) { + if (r <= ((mdsize << 3) - w)) { // first message part + r += w; + f = (r + 7) >>> 3; + } else { // rest of message part (once) + f = mdsize; + r += w; + } + // generate long 'big8' with minimum w next bits of the + // message array + for (int i = s; i < f; i++) { + big8 ^= (hash[i] & 0xff) << (ii << 3); + ii++; + } + // delete bits on the right side, which were used already by + // the last loop + big8 >>>= rest; + test8 = (big8 & k); + } + // --- checksum part + else { + test8 = (checksum & k); + checksum >>>= w; + } + // generate current OTSprivateKey + this.privateKeyOTS = gmssRandom.nextSeed(seed); + // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize); + + } else if (test8 > 0) { // hash the private Key 'test' times (on + // time each step) + messDigestOTS.update(privateKeyOTS); + privateKeyOTS = messDigestOTS.digest(); + test8--; + } + if (test8 == 0) { // if all hashes done copy result to siganture + // array + System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, + mdsize); + counter++; + } + + } + } + + /** + * This method returns the least integer that is greater or equal to the + * logarithm to the base 2 of an integer intValue. + * + * @param intValue + * an integer + * @return The least integer greater or equal to the logarithm to the base 2 + * of intValue + */ + public int getLog(int intValue) { + int log = 1; + int i = 2; + while (i < intValue) { + i <<= 1; + log++; + } + return log; + } + + /** + * This method returns the status byte array + * + * @return statBytes + */ + public byte[][] getStatByte() { + + byte[][] statByte = new byte[5][mdsize]; + statByte[0] = privateKeyOTS; + statByte[1] = seed; + statByte[2] = hash; + statByte[3] = sign; + statByte[4] = this.getStatLong(); + + return statByte; + } + + /** + * This method returns the status int array + * + * @return statInt + */ + public int[] getStatInt() { + int[] statInt = new int[9]; + statInt[0] = counter; + statInt[1] = test; + statInt[2] = ii; + statInt[3] = r; + statInt[4] = steps; + statInt[5] = keysize; + statInt[6] = height; + statInt[7] = w; + statInt[8] = checksum; + return statInt; + } + + /** + * Converts the long parameters into byte arrays to store it in + * statByte-Array + */ + public byte[] getStatLong() { + byte[] bytes = new byte[16]; + + bytes[0] = (byte) ((test8) & 0xff); + bytes[1] = (byte) ((test8 >> 8) & 0xff); + bytes[2] = (byte) ((test8 >> 16) & 0xff); + bytes[3] = (byte) ((test8 >> 24) & 0xff); + bytes[4] = (byte) ((test8) >> 32 & 0xff); + bytes[5] = (byte) ((test8 >> 40) & 0xff); + bytes[6] = (byte) ((test8 >> 48) & 0xff); + bytes[7] = (byte) ((test8 >> 56) & 0xff); + + bytes[8] = (byte) ((big8) & 0xff); + bytes[9] = (byte) ((big8 >> 8) & 0xff); + bytes[10] = (byte) ((big8 >> 16) & 0xff); + bytes[11] = (byte) ((big8 >> 24) & 0xff); + bytes[12] = (byte) ((big8) >> 32 & 0xff); + bytes[13] = (byte) ((big8 >> 40) & 0xff); + bytes[14] = (byte) ((big8 >> 48) & 0xff); + bytes[15] = (byte) ((big8 >> 56) & 0xff); + + return bytes; + } + + /** + * returns a string representation of the instance + * + * @return a string representation of the instance + */ + public String toString() { + String out = "" + this.big8 + " "; + int[] statInt = new int[9]; + statInt = this.getStatInt(); + byte[][] statByte = new byte[5][mdsize]; + statByte = this.getStatByte(); + for (int i = 0; i < 9; i++) { + out = out + statInt[i] + " "; + } + for (int i = 0; i < 5; i++) { + out = out + ByteUtils.toHexString(statByte[i]) + " "; + } + + return out; + } + +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSSignature.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSSignature.java new file mode 100644 index 00000000..a72b2f03 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSSignature.java @@ -0,0 +1,641 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.SecureRandom; +import de.flexiprovider.api.Signature; +import de.flexiprovider.api.exceptions.InvalidAlgorithmParameterException; +import de.flexiprovider.api.exceptions.InvalidKeyException; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.SignatureException; +import de.flexiprovider.api.keys.PrivateKey; +import de.flexiprovider.api.keys.PublicKey; +import de.flexiprovider.api.parameters.AlgorithmParameterSpec; +import de.flexiprovider.common.util.ByteUtils; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class implements the GMSS signature scheme. The class extends the + * SignatureSpi class. + *

+ * The GMSSSignature can be used as follows: + *

+ * Signature generation: + *

+ * 1. generate KeySpec from encoded GMSS private key:
+ * KeySpec privateKeySpec = new PKCS8EncodedKeySpec(encPrivateKey);
+ * 2. get instance of GMSS key factory:
+ * KeyFactory keyFactory = KeyFactory.getInstance("GMSS","FlexiPQC");
+ * 3. decode GMSS private key:
+ * PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
+ * 4. get instance of a GMSS signature:
Signature cmmsSig = + * Signature.getInstance("GMSSwithSHA1","FlexiPQC");
+ * 5. initialize signing:
gmssSig.initSign(privateKey);
+ * 6. sign message:
gmssSig.update(message.getBytes());
+ * signature = gmssSig.sign();
+ * return signature;
+ *

+ * Signature verification: + *

+ * 1. generate KeySpec from encoded GMSS public key:
+ * KeySpec publicKeySpec = new X509EncodedKeySpec(encPublicKey);
+ * 2. decode GMSS public key:
+ * PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
+ * 3. initialize verifying:
gmssSig.initVerify(publicKey);
+ * 4. Verify the signature:
gmssSig.update(message.getBytes());
+ * return gmssSig.verify(signature);
+ * + * @author Michael Schneider, Sebastian Blume + * @see GMSSKeyPairGenerator + */ +public class GMSSSignature extends Signature { + /* + * Inner classes providing concrete implementations of MerkleOTSSignature + * with a variety of message digests. + */ + + // ////////////////////////////////////////////////////////////////////////////// + /** + * GMSSSignature with SHA1 message digest + */ + public static class GMSSwithSHA1 extends GMSSSignature { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyPairGenerator.GMSSwithSHA1.OID; + + /** + * Constructor. + */ + public GMSSwithSHA1() { + super(OID, "SHA1", "FlexiCore"); + } + } + + /** + * GMSSSignature with SHA224 message digest + */ + public static class GMSSwithSHA224 extends GMSSSignature { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyPairGenerator.GMSSwithSHA224.OID; + + /** + * Constructor. + */ + public GMSSwithSHA224() { + super(OID, "SHA224", "FlexiCore"); + } + } + + /** + * GMSSSignature with SHA256 message digest + */ + public static class GMSSwithSHA256 extends GMSSSignature { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyPairGenerator.GMSSwithSHA256.OID; + + /** + * Constructor. + */ + public GMSSwithSHA256() { + super(OID, "SHA256", "FlexiCore"); + } + } + + /** + * GMSSSignature with SHA384 message digest + */ + public static class GMSSwithSHA384 extends GMSSSignature { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyPairGenerator.GMSSwithSHA384.OID; + + /** + * Constructor. + */ + public GMSSwithSHA384() { + super(OID, "SHA384", "FlexiCore"); + } + } + + /** + * GMSSSignature with SHA512 message digest + */ + public static class GMSSwithSHA512 extends GMSSSignature { + + /** + * The OID of the algorithm. + */ + public static final String OID = GMSSKeyPairGenerator.GMSSwithSHA512.OID; + + /** + * Constructor. + */ + public GMSSwithSHA512() { + super(OID, "SHA512", "FlexiCore"); + } + } + + // ////////////////////////////////////////////////////////////////////////////// + + /** + * Instance of GMSSParameterSpec + */ + private GMSSParameterSpec gmssParameterSpec; + + /** + * Instance of GMSSUtilities + */ + private GMSSUtilities gmssUtil = new GMSSUtilities(); + + /** + * The GMSS private key + */ + private GMSSPrivateKey gmssPrivateKey; + + /** + * The GMSS public key + */ + private GMSSPublicKey gmssPublicKey; + + /** + * The raw GMSS public key + */ + private byte[] pubKeyBytes; + + /** + * Hash function for the construction of the authentication trees + */ + private MessageDigest messDigestTrees; + + /** + * The length of the hash function output + */ + private int mdLength; + + /** + * The number of tree layers + */ + private int numLayer; + + /** + * The hash function used by the OTS + */ + private MessageDigest messDigestOTS; + + /** + * An instance of the Winternitz one-time signature + */ + private WinternitzOTSignature ots; + + /** + * Array of strings containing the name of the hash function used by the OTS + * and the corresponding provider name + */ + private String[] algNames = new String[2]; + + /** + * The current main tree and subtree indices + */ + private int[] index; + + /** + * Array of the authentication paths for the current trees of all layers + */ + private byte[][][] currentAuthPaths; + + /** + * The one-time signature of the roots of the current subtrees + */ + private byte[][] subtreeRootSig; + + /** + * The ByteArrayOutputStream holding the messages + */ + private ByteArrayOutputStream baos; + + /** + * The GMSSParameterset + */ + private GMSSParameterset gmssPS; + + /** + * The PRNG + */ + private GMSSRandom gmssRandom; + + /** + * The standard constructor tries to generate the MerkleTree Algorithm + * identifier with the corresponding OID. + * + * @param oidStr + * string with the oid of the algorithm + * @param mdName + * name of the message digest for the authentication trees + * @param mdProvName + * provider name of the message digest for the authentication + * trees + */ + public GMSSSignature(String oidStr, String mdName, String mdProvName) { + algNames[0] = mdName; + algNames[1] = mdProvName; + CoreRegistry.registerAlgorithms(); + + // construct message digest + try { + messDigestTrees = Registry.getMessageDigest(mdName); + messDigestOTS = messDigestTrees; + mdLength = messDigestTrees.getDigestLength(); + gmssRandom = new GMSSRandom(messDigestTrees); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException("message digest " + mdName + + " not found in " + mdProvName + " or signature " + mdName + + " not found in " + mdProvName); + } + + } + + /** + * Feeds a message byte to the message digest. + * + * @param data + * array of message bytes + * @throws SignatureException + * if the signature object is not initialized correctly. + */ + public void update(byte data) throws SignatureException { + baos.write(data); + } + + /** + * Feeds message bytes to the message digest. + * + * @param data + * array of message bytes + * @param offset + * index of message start + * @param length + * number of message bytes + * @throws SignatureException + * if the signature object is not initialized correctly. + */ + + public void update(byte[] data, int offset, int length) + throws SignatureException { + baos.write(data, offset, length); + } + + /** + * This method returns the date contained in the ByteArrayOutputStream + * closes the stream + */ + private byte[] getData() { + byte[] data = baos.toByteArray(); + + try { + baos.close(); + } catch (IOException ioe) { + System.out.println("Can not close ByteArrayOutputStream"); + } + baos.reset(); + return data; + } + + /** + * This method creates a ByteArrayOutputStream + */ + private void initValues() { + + /* + * int logValue = merkleOperations.lookupLog(digestLength)+4; + * logValue/=8; logValue++; keySize = (digestLength+3*logValue)<<2; + * helpSize = digestLength; + */ + + baos = new ByteArrayOutputStream(); + + } + + /** + * Initializes the signature algorithm for signing a message. + *

+ * + * @param privateKey + * the private key of the signer. + * @throws InvalidKeyException + * if the key is not an instance of OTSPrivateKey. + */ + public void initSign(PrivateKey privateKey, SecureRandom sr) + throws InvalidKeyException { + if (privateKey instanceof GMSSPrivateKey) { + messDigestTrees.reset(); + initValues(); + // set private key and take from it ots key, auth, tree and key + // counter, rootSign + gmssPrivateKey = (GMSSPrivateKey) privateKey; + + // check if last signature has been generated + if (gmssPrivateKey.getIndex(0) >= gmssPrivateKey.getNumLeafs(0)) { + throw new RuntimeException( + "No more signatures can be generated"); + } + + // get Parameterset + this.gmssPS = gmssPrivateKey.getParameterset(); + // get numLayer + this.numLayer = gmssPS.getNumOfLayers(); + + // get OTS Instance of lowest layer + byte[] seed = gmssPrivateKey.getCurrentSeeds()[numLayer - 1]; + byte[] OTSSeed = new byte[mdLength]; + byte[] dummy = new byte[mdLength]; + System.arraycopy(seed, 0, dummy, 0, mdLength); + OTSSeed = gmssRandom.nextSeed(dummy); // secureRandom.nextBytes(currentSeeds[currentSeeds.length-1]);secureRandom.nextBytes(OTSseed); + this.ots = new WinternitzOTSignature(OTSSeed, algNames, gmssPS + .getWinternitzParameter()[numLayer - 1]); + + byte[][][] helpCurrentAuthPaths = gmssPrivateKey + .getCurrentAuthPaths(); + currentAuthPaths = new byte[numLayer][][]; + + // copy the main tree authentication path + for (int j = 0; j < numLayer; j++) { + currentAuthPaths[j] = new byte[helpCurrentAuthPaths[j].length][mdLength]; + for (int i = 0; i < helpCurrentAuthPaths[j].length; i++) { + System.arraycopy(helpCurrentAuthPaths[j][i], 0, + currentAuthPaths[j][i], 0, mdLength); + } + } + + // copy index + index = new int[numLayer]; + System.arraycopy(gmssPrivateKey.getIndex(), 0, index, 0, numLayer); + + // copy subtreeRootSig + byte[] helpSubtreeRootSig; + subtreeRootSig = new byte[numLayer - 1][]; + for (int i = 0; i < numLayer - 1; i++) { + helpSubtreeRootSig = gmssPrivateKey.getSubtreeRootSig(i); + subtreeRootSig[i] = new byte[helpSubtreeRootSig.length]; + System.arraycopy(helpSubtreeRootSig, 0, subtreeRootSig[i], 0, + helpSubtreeRootSig.length); + } + + // change private key for next signature + gmssPrivateKey.nextKey(numLayer - 1); + + } else + throw new InvalidKeyException("Key is not a GMSSPrivateKey."); + } + + /** + * Signs a message. + *

+ * + * @return the signature. + * @throws SignatureException + * if the signature is not initialized properly. + */ + public byte[] sign() throws SignatureException { + + byte[] message; + byte[] otsSig = new byte[mdLength]; + byte[] authPathBytes; + byte[] indexBytes; + + // --- first part of this signature + // get the data which should be signed + message = getData(); + + otsSig = ots.getSignature(message); + + // get concatenated lowest layer tree authentication path + authPathBytes = gmssUtil + .concatenateArray(currentAuthPaths[numLayer - 1]); + + // put lowest layer index into a byte array + indexBytes = gmssUtil.intToBytesLittleEndian(index[numLayer - 1]); + + // create first part of GMSS signature + byte[] gmssSigFirstPart = new byte[indexBytes.length + otsSig.length + + authPathBytes.length]; + System.arraycopy(indexBytes, 0, gmssSigFirstPart, 0, indexBytes.length); + System.arraycopy(otsSig, 0, gmssSigFirstPart, indexBytes.length, + otsSig.length); + System.arraycopy(authPathBytes, 0, gmssSigFirstPart, + (indexBytes.length + otsSig.length), authPathBytes.length); + // --- end first part + + // --- next parts of the signature + // create initial array with length 0 for iteration + byte[] gmssSigNextPart = new byte[0]; + + for (int i = numLayer - 1 - 1; i >= 0; i--) { + + // get concatenated next tree authentication path + authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[i]); + + // put next tree index into a byte array + indexBytes = gmssUtil.intToBytesLittleEndian(index[i]); + + // create next part of GMSS signature + + // create help array and copy actual gmssSig into it + byte[] helpGmssSig = new byte[gmssSigNextPart.length]; + System.arraycopy(gmssSigNextPart, 0, helpGmssSig, 0, + gmssSigNextPart.length); + // adjust length of gmssSigNextPart for adding next part + gmssSigNextPart = new byte[helpGmssSig.length + indexBytes.length + + subtreeRootSig[i].length + authPathBytes.length]; + + // copy old data (help array) and new data in gmssSigNextPart + System.arraycopy(helpGmssSig, 0, gmssSigNextPart, 0, + helpGmssSig.length); + System.arraycopy(indexBytes, 0, gmssSigNextPart, + helpGmssSig.length, indexBytes.length); + System.arraycopy(subtreeRootSig[i], 0, gmssSigNextPart, + (helpGmssSig.length + indexBytes.length), + subtreeRootSig[i].length); + System + .arraycopy( + authPathBytes, + 0, + gmssSigNextPart, + (helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length), + authPathBytes.length); + + } + // --- end next parts + + // concatenate the two parts of the GMSS signature + byte[] gmssSig = new byte[gmssSigFirstPart.length + + gmssSigNextPart.length]; + System.arraycopy(gmssSigFirstPart, 0, gmssSig, 0, + gmssSigFirstPart.length); + System.arraycopy(gmssSigNextPart, 0, gmssSig, gmssSigFirstPart.length, + gmssSigNextPart.length); + + // return the GMSS signature + return gmssSig; + } + + /** + * Initializes the signature algorithm for verifying a signature. + * + * @param publicKey + * the public key of the signer. + * @throws InvalidKeyException + * if the public key is not an instance of GMSSPublicKey. + */ + public void initVerify(PublicKey publicKey) throws InvalidKeyException { + if (publicKey instanceof GMSSPublicKey) { + messDigestTrees.reset(); + + gmssPublicKey = (GMSSPublicKey) publicKey; + pubKeyBytes = gmssPublicKey.getPublicKeyBytes(); + gmssPS = gmssPublicKey.getParameterset(); + // get numLayer + this.numLayer = gmssPS.getNumOfLayers(); + initValues(); + + } + + else + throw new InvalidKeyException("Key is not a GMSSPublicKey"); + } + + /** + * Verifies a signature. + * + * @param signature + * the signature to be verified. + * @return TRUE if the signature is correct, + * FALSE otherwise + */ + public boolean verify(byte[] signature) throws SignatureException { + + boolean success = false; + // int halfSigLength = signature.length >>> 1; + messDigestOTS.reset(); + WinternitzOTSVerify otsVerify; + int otsSigLength; + + // get the message that was signed + byte[] help = getData(); + + byte[] message; + byte[] otsSig; + byte[] otsPublicKey; + byte[][] authPath; + byte[] dest; + int nextEntry = 0; + int index; + // Verify signature + + // --- begin with message = 'message that was signed' + // and then in each step message = subtree root + for (int j = numLayer - 1; j >= 0; j--) { + otsVerify = new WinternitzOTSVerify(algNames, gmssPS + .getWinternitzParameter()[j]); + otsSigLength = otsVerify.getSignatureLength(); + + message = help; + // get the subtree index + index = gmssUtil.bytesToIntLittleEndian(signature, nextEntry); + + // 4 is the number of bytes in integer + nextEntry += 4; + + // get one-time signature + otsSig = new byte[otsSigLength]; + System.arraycopy(signature, nextEntry, otsSig, 0, otsSigLength); + nextEntry += otsSigLength; + + // compute public OTS key from the one-time signature + otsPublicKey = otsVerify.Verify(message, otsSig); + + // test if OTSsignature is correct + if (otsPublicKey == null) { + System.err + .println("OTS Public Key is null in GMSSSignature.verify"); + return false; + } + + // get authentication path from the signature + authPath = new byte[gmssPS.getHeightOfTrees()[j]][mdLength]; + for (int i = 0; i < authPath.length; i++) { + System + .arraycopy(signature, nextEntry, authPath[i], 0, + mdLength); + nextEntry = nextEntry + mdLength; + } + + // compute the root of the subtree from the authentication path + help = new byte[mdLength]; + + help = otsPublicKey; + + int count = 1 << authPath.length; + count = count + index; + + for (int i = 0; i < authPath.length; i++) { + dest = new byte[mdLength << 1]; + + if ((count % 2) == 0) { + System.arraycopy(help, 0, dest, 0, mdLength); + System.arraycopy(authPath[i], 0, dest, mdLength, mdLength); + count = count / 2; + } else { + System.arraycopy(authPath[i], 0, dest, 0, mdLength); + System.arraycopy(help, 0, dest, mdLength, help.length); + count = (count - 1) / 2; + } + messDigestTrees.update(dest); + help = messDigestTrees.digest(); + } + } + + // now help contains the root of the maintree + + // test if help is equal to the GMSS public key + if (ByteUtils.equals(pubKeyBytes, help)) { + success = true; + } + + return success; + } + + /** + * Sets the parameters for GMSSSignature + * + * @param algParamSpec + * parameter specification for MerkleTree. + * @see de.flexiprovider.pqc.hbc.gmss.GMSSParameterSpec + */ + public void setParameters(AlgorithmParameterSpec algParamSpec) + throws InvalidAlgorithmParameterException { + if (algParamSpec == null) { + return; + } + + if (!(algParamSpec instanceof GMSSParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "in GMSSSignature: initialize: params is not " + + "an instance of GMSSParameterSpec"); + } + this.gmssParameterSpec = (GMSSParameterSpec) algParamSpec; + } + +} \ No newline at end of file diff --git a/src/de/flexiprovider/pqc/hbc/gmss/GMSSUtilities.java b/src/de/flexiprovider/pqc/hbc/gmss/GMSSUtilities.java new file mode 100644 index 00000000..3e16f575 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/GMSSUtilities.java @@ -0,0 +1,173 @@ +package de.flexiprovider.pqc.hbc.gmss; + +/** + * This class provides several methods that are required by the GMSS classes. + * + * @author Elena Klintsevich + */ +public class GMSSUtilities { + + /** + * Converts a 32 bit integer into a byte array beginning at + * offset (little-endian representation) + * + * @param value + * the integer to convert + */ + public byte[] intToBytesLittleEndian(int value) { + byte[] bytes = new byte[4]; + + bytes[0] = (byte) ((value) & 0xff); + bytes[1] = (byte) ((value >> 8) & 0xff); + bytes[2] = (byte) ((value >> 16) & 0xff); + bytes[3] = (byte) ((value >> 24) & 0xff); + return bytes; + } + + /** + * Converts a byte array beginning at offset into a 32 bit + * integer (little-endian representation) + * + * @param bytes + * the byte array + * @return The resulting integer + */ + public int bytesToIntLittleEndian(byte[] bytes) { + + return ((bytes[0] & 0xff)) | ((bytes[1] & 0xff) << 8) + | ((bytes[2] & 0xff) << 16) | ((bytes[3] & 0xff)) << 24; + } + + /** + * Converts a byte array beginning at offset into a 32 bit + * integer (little-endian representation) + * + * @param bytes + * the byte array + * @param offset + * the integer offset into the byte array + * @return The resulting integer + */ + public int bytesToIntLittleEndian(byte[] bytes, int offset) { + return ((bytes[offset++] & 0xff)) | ((bytes[offset++] & 0xff) << 8) + | ((bytes[offset++] & 0xff) << 16) + | ((bytes[offset] & 0xff)) << 24; + } + + /** + * This method concatenates a 2-dimensional byte array into a 1-dimensional + * byte array + * + * @param arraycp + * a 2-dimensional byte array. + * @return 1-dimensional byte array with concatenated input array + */ + public byte[] concatenateArray(byte[][] arraycp) { + byte[] dest = new byte[arraycp.length * arraycp[0].length]; + int indx = 0; + for (int i = 0; i < arraycp.length; i++) { + System.arraycopy(arraycp[i], 0, dest, indx, arraycp[i].length); + indx = indx + arraycp[i].length; + } + return dest; + } + + /** + * This method prints the values of a 2-dimensional byte array + * + * @param text + * a String + * @param array + * a 2-dimensional byte array + */ + public void printArray(String text, byte[][] array) { + System.out.println(text); + int counter = 0; + for (int i = 0; i < array.length; i++) { + for (int j = 0; j < array[0].length; j++) { + System.out.println(counter + "; " + array[i][j]); + counter++; + } + } + } + + /** + * This method prints the values of a 1-dimensional byte array + * + * @param text + * a String + * @param array + * a 1-dimensional byte array. + */ + public void printArray(String text, byte[] array) { + System.out.println(text); + int counter = 0; + for (int i = 0; i < array.length; i++) { + System.out.println(counter + "; " + array[i]); + counter++; + } + } + + /** + * This method tests if an integer is a power of 2. + * + * @param testValue + * an integer + * @return TRUE if testValue is a power of 2, + * FALSE otherwise + */ + public boolean testPowerOfTwo(int testValue) { + int a = 1; + while (a < testValue) + a <<= 1; + if (testValue == a) + return true; + + return false; + } + + /** + * This method returns the least integer that is greater or equal to the + * logarithm to the base 2 of an integer intValue. + * + * @param intValue + * an integer + * @return The least integer greater or equal to the logarithm to the base 2 + * of intValue + */ + public int getLog(int intValue) { + int log = 1; + int i = 2; + while (i < intValue) { + i <<= 1; + log++; + } + return log; + } + + + + /** + * This Method calculates the actual index of the tree in level height from the global index byte array glIndex + * + * @param glIndex + * global Index byte array + * @param height + * height of resulting tree index + * + * @return the actual index of the tree at level height + * + * + */ + /* + public int[] calcIndex(byte[] glIndex,int[] heightOfTrees){ + int[] index; + byte[]temp; + //index[n]=globalIndex MOD numleafs[n] + System.arraycopy(glIndex, 0, , temp, glIndex.length - heightOfTrees) + index[n]= + + + return index; + }*/ +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/Treehash.java b/src/de/flexiprovider/pqc/hbc/gmss/Treehash.java new file mode 100644 index 00000000..79dcdd55 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/Treehash.java @@ -0,0 +1,479 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import java.util.Vector; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.common.util.ByteUtils; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class implements a treehash instance for the Merkle tree traversal + * algorithm. The first node of the stack is stored in this instance itself, + * additional tail nodes are stored on a tailstack. + * + * @author Michael Schneider + */ +class Treehash { + + /** + * max height of current treehash instance. + */ + private int maxHeight; + + /** + * Vector element that stores the nodes on the stack + */ + private Vector tailStack; + + /** + * Vector element that stores the height of the nodes on the stack + */ + private Vector heightOfNodes; + + /** + * the first node is stored in the treehash instance itself, not on stack + */ + private byte[] firstNode; + + /** + * seedActive needed for the actual node + */ + private byte[] seedActive; + + /** + * the seed needed for the next re-initialization of the treehash instance + */ + private byte[] seedNext; + + /** + * number of nodes stored on the stack and belonging to this treehash + * instance + */ + private int tailLength; + + /** + * the height in the tree of the first node stored in treehash + */ + private int firstNodeHeight; + + /** + * true if treehash instance was already initialized, false otherwise + */ + private boolean isInitialized; + + /** + * true if the first node's height equals the maxHeight of the treehash + */ + private boolean isFinished; + + /** + * true if the nextSeed has been initialized with index 3*2^h needed for the + * seed scheduling + */ + private boolean seedInitialized; + + /** + * denotes the Message Digest used by the tree to create nodes + */ + private MessageDigest messDigestTree; + + /** + * This constructor regenerates a prior treehash object + * + * @param name + * an array of strings, containing the name of the used hash + * function and PRNG and the name of the corresponding provider + * @param statByte + * status bytes + * @param statInt + * status ints + */ + public Treehash(String[] name, byte[][] statByte, int[] statInt) { + + // decode name + CoreRegistry.registerAlgorithms(); + try { + messDigestTree = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException(": message digest " + name[0] + + " not found in " + name[1]); + } + + // decode statInt + this.maxHeight = statInt[0]; + this.tailLength = statInt[1]; + this.firstNodeHeight = statInt[2]; + + if (statInt[3] == 1) + this.isFinished = true; + else + this.isFinished = false; + if (statInt[4] == 1) + this.isInitialized = true; + else + this.isInitialized = false; + if (statInt[5] == 1) + this.seedInitialized = true; + else + this.seedInitialized = false; + + this.heightOfNodes = new Vector(); + for (int i = 0; i < tailLength; i++) { + this.heightOfNodes.addElement(new Integer(statInt[6 + i])); + } + + // decode statByte + this.firstNode = statByte[0]; + this.seedActive = statByte[1]; + this.seedNext = statByte[2]; + + this.tailStack = new Vector(); + for (int i = 0; i < tailLength; i++) { + this.tailStack.addElement(statByte[3 + i]); + } + } + + /** + * Constructor + * + * @param tailStack + * a vector element where the stack nodes are stored + * @param maxHeight + * maximal height of the treehash instance + * @param name + * an array of strings, containing the name of the used hash + * function and PRNG and the name of the corresponding provider + */ + public Treehash(Vector tailStack, int maxHeight, String[] name) { + this.tailStack = tailStack; + this.maxHeight = maxHeight; + this.firstNode = null; + this.isInitialized = false; + this.isFinished = false; + this.seedInitialized = false; + + CoreRegistry.registerAlgorithms(); + try { + messDigestTree = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException(": message digest " + name[0] + + " not found in " + name[1]); + } + + this.seedNext = new byte[messDigestTree.getDigestLength()]; + this.seedActive = new byte[messDigestTree.getDigestLength()]; + } + + /** + * Method to initialize the seeds needed for the precomputation of right + * nodes. Should be initialized with index 3*2^i for treehash_i + * + * @param seedIn + */ + public void initializeSeed(byte[] seedIn) { + System.arraycopy(seedIn, 0, this.seedNext, 0, this.messDigestTree + .getDigestLength()); + this.seedInitialized = true; + } + + /** + * initializes the treehash instance. The seeds must already have been + * initialized to work correctly. + */ + public void initialize() { + if (!this.seedInitialized) { + System.err.println("Seed " + this.maxHeight + " not initialized"); + return; + } + + this.heightOfNodes = new Vector(); + this.tailLength = 0; + this.firstNode = null; + this.firstNodeHeight = -1; + this.isInitialized = true; + System.arraycopy(this.seedNext, 0, this.seedActive, 0, messDigestTree + .getDigestLength()); + } + + /** + * Calculates one update of the treehash instance, i.e. creates a new leaf + * and hashes if possible + * + * @param gmssRandom + * an instance of the PRNG + * @param leaf + * The byte value of the leaf needed for the update + */ + public void update(GMSSRandom gmssRandom, byte[] leaf) { + + if (this.isFinished) { + System.err + .println("No more update possible for treehash instance!"); + return; + } + if (!this.isInitialized) { + System.err + .println("Treehash instance not initialized before update"); + return; + } + + byte[] help = new byte[this.messDigestTree.getDigestLength()]; + int helpHeight = -1; + + gmssRandom.nextSeed(this.seedActive); + + // if treehash gets first update + if (this.firstNode == null) { + this.firstNode = leaf; + this.firstNodeHeight = 0; + } else { + // store the new node in help array, do not push it on the stack + help = leaf; + helpHeight = 0; + + // hash the nodes on the stack if possible + while (this.tailLength > 0 + && helpHeight == ((Integer) heightOfNodes.lastElement()) + .intValue()) { + // put top element of the stack and help node in array + // 'tobehashed' + // and hash them together, put result again in help array + byte[] toBeHashed = new byte[this.messDigestTree + .getDigestLength() << 1]; + + // pop element from stack + System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed, + 0, this.messDigestTree.getDigestLength()); + this.tailStack.removeElementAt(this.tailStack.size() - 1); + this.heightOfNodes + .removeElementAt(this.heightOfNodes.size() - 1); + + System.arraycopy(help, 0, toBeHashed, this.messDigestTree + .getDigestLength(), this.messDigestTree + .getDigestLength()); + messDigestTree.update(toBeHashed); + help = messDigestTree.digest(); + + // increase help height, stack was reduced by one element + helpHeight++; + this.tailLength--; + } + + // push the new node on the stack + this.tailStack.addElement(help); + this.heightOfNodes.addElement(new Integer(helpHeight)); + this.tailLength++; + + // finally check whether the top node on stack and the first node + // in treehash have same height. If so hash them together + // and store them in treehash + if (((Integer) heightOfNodes.lastElement()).intValue() == this.firstNodeHeight) { + byte[] toBeHashed = new byte[this.messDigestTree + .getDigestLength() << 1]; + System.arraycopy(this.firstNode, 0, toBeHashed, 0, + this.messDigestTree.getDigestLength()); + + // pop element from tailStack and copy it into help2 array + System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed, + this.messDigestTree.getDigestLength(), + this.messDigestTree.getDigestLength()); + this.tailStack.removeElementAt(this.tailStack.size() - 1); + this.heightOfNodes + .removeElementAt(this.heightOfNodes.size() - 1); + + // store new element in firstNode, stack is then empty + messDigestTree.update(toBeHashed); + this.firstNode = messDigestTree.digest(); + this.firstNodeHeight++; + + // empty the stack + this.tailLength = 0; + } + } + + // check if treehash instance is completed + if (this.firstNodeHeight == this.maxHeight) { + this.isFinished = true; + } + } + + /** + * Destroys a treehash instance after the top node was taken for + * authentication path. + */ + public void destroy() { + this.isInitialized = false; + this.isFinished = false; + this.firstNode = null; + this.tailLength = 0; + this.firstNodeHeight = -1; + } + + /** + * Returns the height of the lowest node stored either in treehash or on the + * stack. It must not be set to infinity (as mentioned in the paper) because + * this cases are considered in the computeAuthPaths method of + * GMSSPrivateKey + * + * @return Height of the lowest node + */ + public int getLowestNodeHeight() { + if (this.firstNode == null) { + return this.maxHeight; + } else if (this.tailLength == 0) { + return this.firstNodeHeight; + } else { + return Math.min(this.firstNodeHeight, ((Integer) heightOfNodes + .lastElement()).intValue()); + } + } + + /** + * Returns the top node height + * + * @return Height of the first node, the top node + */ + public int getFirstNodeHeight() { + if (firstNode == null) { + return maxHeight; + } + return firstNodeHeight; + } + + /** + * Method to check whether the instance has been initialized or not + * + * @return true if treehash was already initialized + */ + public boolean wasInitialized() { + return this.isInitialized; + } + + /** + * Method to check whether the instance has been finished or not + * + * @return true if treehash has reached its maximum height + */ + public boolean wasFinished() { + return this.isFinished; + } + + /** + * returns the first node stored in treehash instance itself + * + * @return the first node stored in treehash instance itself + */ + public byte[] getFirstNode() { + return this.firstNode; + } + + /** + * returns the active seed + * + * @return the active seed + */ + public byte[] getSeedActive() { + return this.seedActive; + } + + /** + * This method sets the first node stored in the treehash instance itself + * + * @param hash + */ + public void setFirstNode(byte[] hash) { + if (!this.isInitialized) + this.initialize(); + this.firstNode = hash; + this.firstNodeHeight = this.maxHeight; + this.isFinished = true; + } + + /** + * updates the nextSeed of this treehash instance one step needed for the + * schedulng of the seeds + * + * @param gmssRandom + * the prng used for the seeds + */ + public void updateNextSeed(GMSSRandom gmssRandom) { + gmssRandom.nextSeed(seedNext); + } + + /** + * Returns the tailstack + * + * @return the tailstack + */ + public Vector getTailStack() { + return this.tailStack; + } + + /** + * Returns the status byte array used by the GMSSPrivateKeyASN.1 class + * + * @return The status bytes + */ + public byte[][] getStatByte() { + + byte[][] statByte = new byte[3 + tailLength][this.messDigestTree + .getDigestLength()]; + statByte[0] = firstNode; + statByte[1] = seedActive; + statByte[2] = seedNext; + for (int i = 0; i < tailLength; i++) { + statByte[3 + i] = (byte[]) tailStack.elementAt(i); + } + return statByte; + } + + /** + * Returns the status int array used by the GMSSPrivateKeyASN.1 class + * + * @return The status ints + */ + public int[] getStatInt() { + + int[] statInt = new int[6 + tailLength]; + statInt[0] = maxHeight; + statInt[1] = tailLength; + statInt[2] = firstNodeHeight; + if (this.isFinished) + statInt[3] = 1; + else + statInt[3] = 0; + if (this.isInitialized) + statInt[4] = 1; + else + statInt[4] = 0; + if (this.seedInitialized) + statInt[5] = 1; + else + statInt[5] = 0; + for (int i = 0; i < tailLength; i++) { + statInt[6 + i] = ((Integer) heightOfNodes.elementAt(i)).intValue(); + } + return statInt; + } + + /** + * returns a String representation of the treehash instance + */ + public String toString() { + String out = "Treehash : "; + for (int i = 0; i < 6 + tailLength; i++) { + out = out + this.getStatInt()[i] + " "; + } + for (int i = 0; i < 3 + tailLength; i++) { + if (this.getStatByte()[i] != null) { + out = out + ByteUtils.toHexString(this.getStatByte()[i]) + " "; + } else + out = out + "null "; + } + out = out + " " + this.messDigestTree.getDigestLength(); + return out; + } + +} \ No newline at end of file diff --git a/src/de/flexiprovider/pqc/hbc/gmss/WinternitzOTSVerify.java b/src/de/flexiprovider/pqc/hbc/gmss/WinternitzOTSVerify.java new file mode 100644 index 00000000..37076be7 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/WinternitzOTSVerify.java @@ -0,0 +1,325 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.SignatureException; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class implements signature verification of the Winternitz one-time + * signature scheme (OTSS), described in C.Dods, N.P. Smart, and M. Stam, "Hash + * Based Digital Signature Schemes", LNCS 3796, pages 96–115, 2005. The + * class is used by the GMSS classes. + * + * @author Elena Klintsevich + * + */ +public class WinternitzOTSVerify { + + private MessageDigest messDigestOTS; + + /** + * The Winternitz parameter + */ + private int w; + + /** + * The constructor + *

+ * + * @param algNames + * the name of the hash function used by the OTS and the provider + * name of the hash function + * @param w + * the Winternitz parameter + */ + public WinternitzOTSVerify(String[] algNames, int w) { + this.w = w; + + CoreRegistry.registerAlgorithms(); + try { + messDigestOTS = Registry.getMessageDigest(algNames[0]); + } catch (NoSuchAlgorithmException nsae) { + System.err.println(": message digest " + algNames[0] + + " not found in " + algNames[1]); + } + + } + + /** + * @return The length of the one-time signature + */ + public int getSignatureLength() { + int mdsize = messDigestOTS.getDigestLength(); + int size = ((mdsize << 3) + (w - 1)) / w; + int logs = getLog((size << w) + 1); + size += (logs + w - 1) / w; + + return mdsize * size; + } + + /** + * This method computes the public OTS key from the one-time signature of a + * message. This is *NOT* a complete OTS signature verification, but it + * suffices for usage with CMSS. + * + * @param message + * the message + * @param signature + * the one-time signature + * @return The public OTS key + * @throws SignatureException + */ + + public byte[] Verify(byte[] message, byte[] signature) + throws SignatureException { + + int mdsize = messDigestOTS.getDigestLength(); + byte[] hash = new byte[mdsize]; // hash of message m + + // create hash of message m + messDigestOTS.update(message); + hash = messDigestOTS.digest(); + + int size = ((mdsize << 3) + (w - 1)) / w; + int logs = getLog((size << w) + 1); + int keysize = size + (logs + w - 1) / w; + + int testKeySize = mdsize * keysize; + + if (testKeySize != signature.length) { + return null; + } + + byte[] testKey = new byte[testKeySize]; + + int c = 0; + int counter = 0; + int test; + + if (8 % w == 0) { + int d = 8 / w; + int k = (1 << w) - 1; + byte[] hlp = new byte[mdsize]; + + // verify signature + for (int i = 0; i < hash.length; i++) + for (int j = 0; j < d; j++) { + test = hash[i] & k; + c += test; + + System.arraycopy(signature, counter * mdsize, hlp, 0, + mdsize); + + while (test < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test++; + } + + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + hash[i] = (byte) (hash[i] >>> w); + counter++; + } + + c = (size << w) - c; + for (int i = 0; i < logs; i += w) { + test = c & k; + + System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize); + + while (test < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test++; + } + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + c >>>= w; + counter++; + } + } else if (w < 8) { + int d = mdsize / w; + int k = (1 << w) - 1; + byte[] hlp = new byte[mdsize]; + long big8; + int ii = 0; + // create signature + // first d*w bytes of hash + for (int i = 0; i < d; i++) { + big8 = 0; + for (int j = 0; j < w; j++) { + big8 ^= (hash[ii] & 0xff) << (j << 3); + ii++; + } + for (int j = 0; j < 8; j++) { + test = (int) (big8 & k); + c += test; + + System.arraycopy(signature, counter * mdsize, hlp, 0, + mdsize); + + while (test < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test++; + } + + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + big8 >>>= w; + counter++; + } + } + // rest of hash + d = mdsize % w; + big8 = 0; + for (int j = 0; j < d; j++) { + big8 ^= (hash[ii] & 0xff) << (j << 3); + ii++; + } + d <<= 3; + for (int j = 0; j < d; j += w) { + test = (int) (big8 & k); + c += test; + + System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize); + + while (test < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test++; + } + + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + big8 >>>= w; + counter++; + } + + // check bytes + c = (size << w) - c; + for (int i = 0; i < logs; i += w) { + test = c & k; + + System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize); + + while (test < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test++; + } + + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + c >>>= w; + counter++; + } + }// end if(w<8) + else if (w < 57) { + int d = (mdsize << 3) - w; + int k = (1 << w) - 1; + byte[] hlp = new byte[mdsize]; + long big8, test8; + int r = 0; + int s, f, rest, ii; + // create signature + // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w + while (r <= d) { + s = r >>> 3; + rest = r % 8; + r += w; + f = (r + 7) >>> 3; + big8 = 0; + ii = 0; + for (int j = s; j < f; j++) { + big8 ^= (hash[j] & 0xff) << (ii << 3); + ii++; + } + + big8 >>>= rest; + test8 = (big8 & k); + c += test8; + + System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize); + + while (test8 < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test8++; + } + + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + counter++; + + } + // rest of hash + s = r >>> 3; + if (s < mdsize) { + rest = r % 8; + big8 = 0; + ii = 0; + for (int j = s; j < mdsize; j++) { + big8 ^= (hash[j] & 0xff) << (ii << 3); + ii++; + } + + big8 >>>= rest; + test8 = (big8 & k); + c += test8; + + System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize); + + while (test8 < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test8++; + } + + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + counter++; + } + // check bytes + c = (size << w) - c; + for (int i = 0; i < logs; i += w) { + test8 = (c & k); + + System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize); + + while (test8 < k) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test8++; + } + + System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize); + c >>>= w; + counter++; + } + }// end if(w<57) + + byte[] TKey = new byte[mdsize]; + messDigestOTS.update(testKey); + TKey = messDigestOTS.digest(); + + return TKey; + + } + + /** + * This method returns the least integer that is greater or equal to the + * logarithm to the base 2 of an integer intValue. + * + * @param intValue + * an integer + * @return The least integer greater or equal to the logarithm to the base + * 256 of intValue + */ + public int getLog(int intValue) { + int log = 1; + int i = 2; + while (i < intValue) { + i <<= 1; + log++; + } + return log; + } + +} diff --git a/src/de/flexiprovider/pqc/hbc/gmss/WinternitzOTSignature.java b/src/de/flexiprovider/pqc/hbc/gmss/WinternitzOTSignature.java new file mode 100644 index 00000000..74e8f0d4 --- /dev/null +++ b/src/de/flexiprovider/pqc/hbc/gmss/WinternitzOTSignature.java @@ -0,0 +1,371 @@ +package de.flexiprovider.pqc.hbc.gmss; + +import de.flexiprovider.api.MessageDigest; +import de.flexiprovider.api.Registry; +import de.flexiprovider.api.exceptions.NoSuchAlgorithmException; +import de.flexiprovider.api.exceptions.SignatureException; +import de.flexiprovider.core.CoreRegistry; + +/** + * This class implements key pair generation and signature generation of the + * Winternitz one-time signature scheme (OTSS), described in C.Dods, N.P. Smart, + * and M. Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages + * 96–115, 2005. The class is used by the GMSS classes. + * + * @author Elena Klintsevich + * + */ + +public class WinternitzOTSignature { + + /** + * The hash function used by the OTS + */ + private MessageDigest messDigestOTS; + + /** + * The length of the message digest and private key + */ + private int mdsize, keysize; + + /** + * An array of strings, containing the name of the used hash function, the + * name of the PRGN and the names of the corresponding providers + */ + // private String[] name = new String[2]; + /** + * The private key + */ + private byte[][] privateKeyOTS; + + /** + * The Winternitz parameter + */ + private int w; + + /** + * The source of randomness for OTS private key generation + */ + private GMSSRandom gmssRandom; + + /** + * Sizes of the message and the checksum, both + */ + private int messagesize, checksumsize; + + /** + * The constructor generates an OTS key pair, using seed0 and + * the PRNG + *

+ * + * @param seed0 + * the seed for the PRGN + * @param name + * an array of strings, containing the name of the used hash + * function, the name of the PRGN and the names of the + * corresponding providers + * @param w + * the Winternitz parameter + */ + public WinternitzOTSignature(byte[] seed0, String[] name, int w) { + // this.name = name; + this.w = w; + + CoreRegistry.registerAlgorithms(); + try { + messDigestOTS = Registry.getMessageDigest(name[0]); + } catch (NoSuchAlgorithmException nsae) { + System.err.println(": message digest " + name[0] + " not found in " + + name[1]); + } + + gmssRandom = new GMSSRandom(messDigestOTS); + + // calulate keysize for private and public key and also the help + // array + + mdsize = messDigestOTS.getDigestLength(); + int mdsizeBit = mdsize << 3; + messagesize = (int) Math.ceil((double) (mdsizeBit) / (double) w); + + checksumsize = getLog((messagesize << w) + 1); + + keysize = messagesize + + (int) Math.ceil((double) checksumsize / (double) w); + + /* + * mdsize = messDigestOTS.getDigestLength(); messagesize = + * ((mdsize<<3)+(w-1))/w; + * + * checksumsize = getlog((messagesize< 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + hash[i] = (byte) (hash[i] >>> w); + counter++; + } + + c = (messagesize << w) - c; + for (int i = 0; i < checksumsize; i += w) { + test = c & k; + + System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize); + + while (test > 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + c >>>= w; + counter++; + } + } else if (w < 8) { + int d = mdsize / w; + int k = (1 << w) - 1; + byte[] hlp = new byte[mdsize]; + long big8; + int ii = 0; + // create signature + // first d*w bytes of hash + for (int i = 0; i < d; i++) { + big8 = 0; + for (int j = 0; j < w; j++) { + big8 ^= (hash[ii] & 0xff) << (j << 3); + ii++; + } + for (int j = 0; j < 8; j++) { + test = (int) (big8 & k); + c += test; + + System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize); + + while (test > 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + big8 >>>= w; + counter++; + } + } + // rest of hash + d = mdsize % w; + big8 = 0; + for (int j = 0; j < d; j++) { + big8 ^= (hash[ii] & 0xff) << (j << 3); + ii++; + } + d <<= 3; + for (int j = 0; j < d; j += w) { + test = (int) (big8 & k); + c += test; + + System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize); + + while (test > 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + big8 >>>= w; + counter++; + } + + // check bytes + c = (messagesize << w) - c; + for (int i = 0; i < checksumsize; i += w) { + test = c & k; + + System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize); + + while (test > 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + c >>>= w; + counter++; + } + }// end if(w<8) + else if (w < 57) { + int d = (mdsize << 3) - w; + int k = (1 << w) - 1; + byte[] hlp = new byte[mdsize]; + long big8, test8; + int r = 0; + int s, f, rest, ii; + // create signature + // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w + while (r <= d) { + s = r >>> 3; + rest = r % 8; + r += w; + f = (r + 7) >>> 3; + big8 = 0; + ii = 0; + for (int j = s; j < f; j++) { + big8 ^= (hash[j] & 0xff) << (ii << 3); + ii++; + } + + big8 >>>= rest; + test8 = (big8 & k); + c += test8; + + System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize); + while (test8 > 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test8--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + counter++; + + } + // rest of hash + s = r >>> 3; + if (s < mdsize) { + rest = r % 8; + big8 = 0; + ii = 0; + for (int j = s; j < mdsize; j++) { + big8 ^= (hash[j] & 0xff) << (ii << 3); + ii++; + } + + big8 >>>= rest; + test8 = (big8 & k); + c += test8; + + System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize); + while (test8 > 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test8--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + counter++; + } + // check bytes + c = (messagesize << w) - c; + for (int i = 0; i < checksumsize; i += w) { + test8 = (c & k); + + System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize); + + while (test8 > 0) { + messDigestOTS.update(hlp); + hlp = messDigestOTS.digest(); + test8--; + } + System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize); + c >>>= w; + counter++; + } + }// end if(w<57) + + return sign; + } + + /** + * This method returns the least integer that is greater or equal to the + * logarithm to the base 2 of an integer intValue. + * + * @param intValue + * an integer + * @return The least integer greater or equal to the logarithm to the base 2 + * of intValue + */ + public int getLog(int intValue) { + int log = 1; + int i = 2; + while (i < intValue) { + i <<= 1; + log++; + } + return log; + } + +} diff --git a/src/i2p/bote/I2PBote.java b/src/i2p/bote/I2PBote.java index 4382e7bd..c1f2d98f 100644 --- a/src/i2p/bote/I2PBote.java +++ b/src/i2p/bote/I2PBote.java @@ -279,7 +279,7 @@ public class I2PBote implements NetworkStatusSource { expirationThread.addExpirationListener(relayPacketSender); backgroundThreads.add(expirationThread); - outboxProcessor = new OutboxProcessor(dht, outbox, peerManager, relayPacketFolder, configuration, this); + outboxProcessor = new OutboxProcessor(dht, outbox, peerManager, relayPacketFolder, identities, configuration, this); outboxProcessor.addOutboxListener(new OutboxListener() { /** Moves sent emails to the "sent" folder */ @Override @@ -400,7 +400,7 @@ public class I2PBote implements NetworkStatusSource { EmailIdentity senderIdentity = identities.extractIdentity(sender); if (senderIdentity == null) throw new MessagingException(_("No identity matches the sender/from field: " + sender)); - email.sign(senderIdentity); + email.sign(senderIdentity, identities); } outbox.add(email); diff --git a/src/i2p/bote/crypto/CryptoFactory.java b/src/i2p/bote/crypto/CryptoFactory.java index 8bbd3494..f3b0f208 100644 --- a/src/i2p/bote/crypto/CryptoFactory.java +++ b/src/i2p/bote/crypto/CryptoFactory.java @@ -58,6 +58,6 @@ public class CryptoFactory { Log log = new Log(CryptoFactory.class); log.error("Error creating ECDH256_ECDSA256 or ECDH521_ECDSA521.", e); } - instances.add(new NTRUEncrypt1087_NTRUSign349()); + instances.add(new NTRUEncrypt1087_GMSS512()); } } \ No newline at end of file diff --git a/src/i2p/bote/crypto/CryptoImplementation.java b/src/i2p/bote/crypto/CryptoImplementation.java index c822e1f4..b7f2085a 100644 --- a/src/i2p/bote/crypto/CryptoImplementation.java +++ b/src/i2p/bote/crypto/CryptoImplementation.java @@ -127,8 +127,14 @@ public interface CryptoImplementation { /** This method takes a public key in addition to the private key because some algorithms need the public key for decryption. */ byte[] decrypt(byte[] data, PublicKey publicKey, PrivateKey privateKey) throws GeneralSecurityException, InvalidCipherTextException; - /** This method takes a public key in addition to the private key because some algorithms need the public key for signing. */ - byte[] sign(byte[] data, PublicKey publicKey, PrivateKey privateKey) throws GeneralSecurityException; + /** + * @param data + * @param privateKey + * @param keyUpdateHandler Called if the signature algorithm alters the private key + * @return + * @throws GeneralSecurityException + */ + byte[] sign(byte[] data, PrivateKey privateKey, KeyUpdateHandler keyUpdateHandler) throws GeneralSecurityException; boolean verify(byte[] data, byte[] signature, PublicKey key) throws GeneralSecurityException; } \ No newline at end of file diff --git a/src/i2p/bote/crypto/ECDH_ECDSA.java b/src/i2p/bote/crypto/ECDH_ECDSA.java index 0d2d97a6..73912c04 100644 --- a/src/i2p/bote/crypto/ECDH_ECDSA.java +++ b/src/i2p/bote/crypto/ECDH_ECDSA.java @@ -398,7 +398,7 @@ public abstract class ECDH_ECDSA implements CryptoImplementation { protected abstract ECPublicKeySpec createPublicKeySpec(byte[] encodedKey) throws InvalidKeySpecException, NoSuchAlgorithmException; @Override - public byte[] sign(byte[] data, PublicKey publicKey, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + public byte[] sign(byte[] data, PrivateKey privateKey, KeyUpdateHandler keyupdateHandler) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { BouncyECDSASigner signatureAlg = new BouncyECDSASigner(); signatureAlg.initSign(privateKey); signatureAlg.update(data); diff --git a/src/i2p/bote/crypto/ElGamal2048_DSA1024.java b/src/i2p/bote/crypto/ElGamal2048_DSA1024.java index 76d89e09..a726ad56 100644 --- a/src/i2p/bote/crypto/ElGamal2048_DSA1024.java +++ b/src/i2p/bote/crypto/ElGamal2048_DSA1024.java @@ -234,7 +234,7 @@ public class ElGamal2048_DSA1024 implements CryptoImplementation { /** Only accepts DSAPrivateKeys. */ @Override - public byte[] sign(byte[] data, PublicKey publicKey, PrivateKey privateKey) throws GeneralSecurityException { + public byte[] sign(byte[] data, PrivateKey privateKey, KeyUpdateHandler keyupdateHandler) throws GeneralSecurityException { DSAPrivateKey dsaKey = castToDSA(privateKey); Signature signature = DSAEngine.getInstance().sign(data, dsaKey.getI2PKey()); return signature.toByteArray(); diff --git a/src/i2p/bote/crypto/KeyUpdateHandler.java b/src/i2p/bote/crypto/KeyUpdateHandler.java new file mode 100644 index 00000000..28465b25 --- /dev/null +++ b/src/i2p/bote/crypto/KeyUpdateHandler.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2009 HungryHobo@mail.i2p + * + * The GPG fingerprint for HungryHobo@mail.i2p is: + * 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12 + * + * This file is part of I2P-Bote. + * I2P-Bote 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 3 of the License, or + * (at your option) any later version. + * + * I2P-Bote 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 I2P-Bote. If not, see . + */ + +package i2p.bote.crypto; + +import i2p.bote.fileencryption.PasswordException; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +/** + * Called when a private key in an Email Identity was changed and + * needs to be written back to the identities file. + */ +public interface KeyUpdateHandler { + + void updateKey() throws GeneralSecurityException, PasswordException, IOException; +} \ No newline at end of file diff --git a/src/i2p/bote/crypto/NTRUEncrypt1087_NTRUSign349.java b/src/i2p/bote/crypto/NTRUEncrypt1087_GMSS512.java similarity index 53% rename from src/i2p/bote/crypto/NTRUEncrypt1087_NTRUSign349.java rename to src/i2p/bote/crypto/NTRUEncrypt1087_GMSS512.java index 47d96350..f20e873f 100644 --- a/src/i2p/bote/crypto/NTRUEncrypt1087_NTRUSign349.java +++ b/src/i2p/bote/crypto/NTRUEncrypt1087_GMSS512.java @@ -22,11 +22,15 @@ package i2p.bote.crypto; import i2p.bote.Util; +import i2p.bote.fileencryption.PasswordException; +import java.io.IOException; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; import java.security.KeyException; import java.security.KeyPair; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; @@ -40,34 +44,70 @@ import net.sf.ntru.EncryptionParameters; import net.sf.ntru.EncryptionPrivateKey; import net.sf.ntru.EncryptionPublicKey; import net.sf.ntru.NtruEncrypt; -import net.sf.ntru.NtruSign; -import net.sf.ntru.SignatureKeyPair; -import net.sf.ntru.SignatureParameters; -import net.sf.ntru.SignaturePrivateKey; -import net.sf.ntru.SignaturePublicKey; +import de.flexiprovider.api.exceptions.InvalidKeySpecException; +import de.flexiprovider.api.keys.KeySpec; +import de.flexiprovider.pki.PKCS8EncodedKeySpec; +import de.flexiprovider.pqc.hbc.gmss.GMSSKeyFactory; +import de.flexiprovider.pqc.hbc.gmss.GMSSKeyPairGenerator; +import de.flexiprovider.pqc.hbc.gmss.GMSSParameterSpec; +import de.flexiprovider.pqc.hbc.gmss.GMSSParameterset; +import de.flexiprovider.pqc.hbc.gmss.GMSSPublicKeySpec; +import de.flexiprovider.pqc.hbc.gmss.GMSSSignature; /** - * NTRUEncrypt with N=1087 and NTRUSign with N=349. + * NTRUEncrypt with N=1087 and GMSS with SHA512. *

- * Both algorithms provide 256 bits (???) of security and are considered safe from + * Both algorithms provide 256 bits of security and are considered safe from * quantum computer attacks.
- * NTRU uses public keys that are shorter than ElGamal keys (at the same security - * level) but longer than ECC keys. + * NTRUEncrypt uses public keys that are shorter than ElGamal keys (at the same + * security level) but longer than ECC keys. GMSS public keys are roughly the + * same length as ECC keys. + *

+ * Key generation with this CryptoImplementation takes some time, + * almost all of which is spent on GMSS key generation. On fast computers, it + * takes a minute or less; on slower machines, it takes several minutes. + *

+ * One GMSS key can only create 2^(Σ HEIGHTS) signatures. + * Since HEIGHTS is [6, 6, 5, 5], a single email + * identity can be used to send 100 emails a day to 10 recipients each for 167 + * years before the email identity is used up. + *

+ * GMSS is a key-evolving signature scheme, meaning the private key changes + * after every signature. This affects the length of the private key. The + * maximum amount by which the key can grow can be calculated using Lemma 7 in + * + * Merkle Signatures with Virtually Unlimited Signature Capacity. + * Because the private key is variable in length, it is padded to the maximum + * length so the CryptoImplementation can be assigned a key length + * that identifies it. + *

+ * GMSS signatures are much longer than ElGamal or ECC signatures. GMSS allows + * a trade-off between signature size, private key size, and key generation + * time. Parameters for this CryptoImplementation were chosen to + * keep signature size and key generation time low at the expense of private + * key size. Signatures are 13712 bytes and private keys are 71584 bytes + * (including padding). */ -public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { +public class NTRUEncrypt1087_GMSS512 implements CryptoImplementation { private static final EncryptionParameters NTRUENCRYPT_PARAMETERS = EncryptionParameters.EES1087EP2; - private static final SignatureParameters NTRUSIGN_PARAMETERS = SignatureParameters.T349; + private static final int[] HEIGHTS = new int[] {6, 6, 5, 5}; // GMSS tree heights + private static final int[] WINTERNITZ = new int[] {12, 11, 11, 11}; // Winternitz parameters for GMSS + private static final int[] AUTH_PATH_PARAMETERS = new int[] {2, 2, 3, 3}; // the parameter K in GMSS + private static final GMSSParameterset GMSS_PARAMETERS = new GMSSParameterset(HEIGHTS.length, HEIGHTS, WINTERNITZ, AUTH_PATH_PARAMETERS); + private static final int BASE64_PRIVATE_KEY_PAIR_LENGTH = 95734; private static final int PUBLIC_ENCRYPTION_KEY_BYTES = 1495; - private static final int PUBLIC_SIGNING_KEY_BYTES = 393; + private static final int PUBLIC_SIGNING_KEY_BYTES = 64; private static final int PRIVATE_ENCRYPTION_KEY_BYTES = 216; - private static final int PRIVATE_SIGNING_KEY_BYTES = 673; + private static final int PRIVATE_SIGNING_KEY_BYTES = 57180 + 4 + 14400; // ASN1-encoded private key + 4 length bytes + padding private static final int ENCRYPTED_LENGTH_BYTES = PUBLIC_ENCRYPTION_KEY_BYTES; // length of an NTRU-encrypted message (no AES) private static final int BLOCK_SIZE = 16; // length of the AES initialization vector; also the AES block size for padding. Not to be confused with the AES key size. private I2PAppContext appContext; + private GMSSKeyFactory gmssKeyFactory; - NTRUEncrypt1087_NTRUSign349() { + public NTRUEncrypt1087_GMSS512() { appContext = new I2PAppContext(); + gmssKeyFactory = new GMSSKeyFactory(); } @Override @@ -82,14 +122,12 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { @Override public int getBase64PublicKeyPairLength() { - // #base64 chars = ceil(#bytes*8/6) - return (getByteArrayPublicKeyPairLength()*8+5) / 6; + return 2079; } @Override public int getBase64CompleteKeySetLength() { - int privateKeyPairLength = ((PRIVATE_ENCRYPTION_KEY_BYTES+PRIVATE_SIGNING_KEY_BYTES)*8+5) / 6; - return getBase64PublicKeyPairLength() + privateKeyPairLength; + return getBase64PublicKeyPairLength() + BASE64_PRIVATE_KEY_PAIR_LENGTH; } @Override @@ -98,27 +136,27 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { } @Override - public PublicKeyPair createPublicKeyPair(byte[] bytes) { + public PublicKeyPair createPublicKeyPair(byte[] bytes) throws InvalidKeySpecException { PublicKeyPair keyPair = new PublicKeyPair(); byte[] encryptionKeyBytes = Arrays.copyOf(bytes, PUBLIC_ENCRYPTION_KEY_BYTES); keyPair.encryptionKey = new NtruEncrypt1087PublicKey(encryptionKeyBytes); - byte[] signingKeyBytes = Arrays.copyOfRange(bytes, PUBLIC_ENCRYPTION_KEY_BYTES, PUBLIC_ENCRYPTION_KEY_BYTES+PUBLIC_ENCRYPTION_KEY_BYTES); - keyPair.signingKey = new NtruSign349PublicKey(signingKeyBytes); + byte[] signingKeyBytes = Arrays.copyOfRange(bytes, PUBLIC_ENCRYPTION_KEY_BYTES, PUBLIC_ENCRYPTION_KEY_BYTES+PUBLIC_SIGNING_KEY_BYTES); + keyPair.signingKey = new Gmss512PublicKey(signingKeyBytes); return keyPair; } @Override - public PrivateKeyPair createPrivateKeyPair(byte[] bytes) { + public PrivateKeyPair createPrivateKeyPair(byte[] bytes) throws InvalidKeySpecException { PrivateKeyPair keyPair = new PrivateKeyPair(); byte[] encryptionKeyBytes = Arrays.copyOf(bytes, PRIVATE_ENCRYPTION_KEY_BYTES); keyPair.encryptionKey = new NtruEncrypt1087PrivateKey(encryptionKeyBytes); byte[] signingKeyBytes = Arrays.copyOfRange(bytes, PRIVATE_ENCRYPTION_KEY_BYTES, bytes.length); - keyPair.signingKey = new NtruSign349PrivateKey(signingKeyBytes); + keyPair.signingKey = new Gmss512PrivateKey(signingKeyBytes); return keyPair; } @@ -126,21 +164,21 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { @Override public String toBase64(PublicKeyPair keyPair) { String base64 = Base64.encode(toByteArray(keyPair)); - // the last two chars are always '==', so drop them - return base64.substring(0, base64.length()-2); + // the last char is always '=', so drop it + return base64.substring(0, base64.length()-1); } @Override public String toBase64(PrivateKeyPair keyPair) { String base64 = Base64.encode(toByteArray(keyPair)); - // the last two chars are always '==', so drop them + // the last two chars are always "==", so drop them return base64.substring(0, base64.length()-2); } @Override public PublicKeyPair createPublicKeyPair(String base64) throws GeneralSecurityException { - // append the "==" that is omitted in the encoding - base64 += "=="; + // append the '=' that is omitted in the encoding + base64 += '='; byte[] keyBytes = Base64.decode(base64); return createPublicKeyPair(keyBytes); } @@ -166,7 +204,9 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { @Override public byte[] toByteArray(PrivateKeyPair keyPair) { byte[] encKey = keyPair.encryptionKey.getEncoded(); + byte[] sigKey = keyPair.signingKey.getEncoded(); + byte[] encodedKeys = new byte[encKey.length + sigKey.length]; System.arraycopy(encKey, 0, encodedKeys, 0, encKey.length); System.arraycopy(sigKey, 0, encodedKeys, encKey.length, sigKey.length); @@ -183,14 +223,16 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { } @Override - public KeyPair generateSigningKeyPair() throws KeyException { - SignatureKeyPair sigKeyPair = NtruSign.generateKeyPair(NTRUSIGN_PARAMETERS); - PublicKey publicKey = new NtruSign349PublicKey(sigKeyPair.pub); - PrivateKey privateKey = new NtruSign349PrivateKey(sigKeyPair.priv); + public KeyPair generateSigningKeyPair() throws KeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + GMSSKeyPairGenerator gmssKeyPairGenerator = new GMSSKeyPairGenerator.GMSSwithSHA512(); + gmssKeyPairGenerator.initialize(new GMSSParameterSpec(GMSS_PARAMETERS), appContext.random()); + de.flexiprovider.api.keys.KeyPair flexiKeyPair = gmssKeyPairGenerator.genKeyPair(); + Gmss512PublicKey publicKey = new Gmss512PublicKey(flexiKeyPair.getPublic()); + Gmss512PrivateKey privateKey = new Gmss512PrivateKey(flexiKeyPair.getPrivate()); return new KeyPair(publicKey, privateKey); } - + /** * Only accepts Ntru1087PublicKeys. * @throws NoSuchAlgorithmException @@ -255,35 +297,46 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { throw new IllegalArgumentException(" must be a " + NtruEncrypt1087PrivateKey.class.getName()); } - /** Only accepts NtruSign349PublicKeys and NtruSign349PrivateKeys. */ + /** Only accepts Gmss512PrivateKeys. */ @Override - public byte[] sign(byte[] data, PublicKey publicKey, PrivateKey privateKey) { - NtruSign349PublicKey publicNtruKey = castToNtruSignKey(publicKey); - NtruSign349PrivateKey privateNtruKey = castToNtruSignKey(privateKey); - return NtruSign.sign(data, privateNtruKey.key, publicNtruKey.key, NTRUSIGN_PARAMETERS); + public byte[] sign(byte[] data, PrivateKey key, KeyUpdateHandler keyUpdateHandler) throws GeneralSecurityException, PasswordException { + Gmss512PrivateKey gmssKey = castToGMSS(key); + GMSSSignature signer = new GMSSSignature.GMSSwithSHA512(); + signer.initSign(gmssKey.key); + signer.update(data); + byte[] signature = signer.sign(); + try { + keyUpdateHandler.updateKey(); + } catch (IOException e) { + throw new KeyStoreException("Error updating GMSS key after signing.", e); + } + return signature; } - private NtruSign349PublicKey castToNtruSignKey(PublicKey key) { - if (key instanceof NtruSign349PublicKey) - return (NtruSign349PublicKey)key; + private Gmss512PrivateKey castToGMSS(PrivateKey key) { + if (key instanceof Gmss512PrivateKey) + return (Gmss512PrivateKey)key; else - throw new IllegalArgumentException(" must be a " + NtruSign349PublicKey.class.getName()); + throw new IllegalArgumentException(" must be a " + Gmss512PrivateKey.class.getName()); } - private NtruSign349PrivateKey castToNtruSignKey(PrivateKey key) { - if (key instanceof NtruSign349PrivateKey) - return (NtruSign349PrivateKey)key; - else - throw new IllegalArgumentException(" must be a " + NtruSign349PrivateKey.class.getName()); - } - - /** Only accepts NtruSign349PublicKeys. */ + /** Only accepts Gmss512PublicKeys. */ @Override - public boolean verify(byte[] data, byte[] signature, PublicKey key) { - NtruSign349PublicKey publicNtruKey = castToNtruSignKey(key); - return NtruSign.verify(data, signature, publicNtruKey.key, NTRUSIGN_PARAMETERS); + public boolean verify(byte[] data, byte[] signature, PublicKey key) throws GeneralSecurityException { + Gmss512PublicKey gmssKey = castToGMSS(key); + GMSSSignature signer = new GMSSSignature.GMSSwithSHA512(); + signer.initVerify(gmssKey.key); + signer.update(data); + return signer.verify(signature); } + private Gmss512PublicKey castToGMSS(PublicKey key) { + if (key instanceof Gmss512PublicKey) + return (Gmss512PublicKey)key; + else + throw new IllegalArgumentException(" must be a " + Gmss512PublicKey.class.getName()); + } + private class NtruEncrypt1087PublicKey implements PublicKey { private static final long serialVersionUID = 8103999492335873827L; @@ -311,6 +364,15 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { public byte[] getEncoded() { return key.getEncoded(); } + + @Override + public boolean equals(Object anotherObj) { + if (anotherObj==null || !NtruEncrypt1087PublicKey.class.equals(anotherObj.getClass())) + return false; + + NtruEncrypt1087PublicKey otherKey = (NtruEncrypt1087PublicKey)anotherObj; + return Arrays.equals(getEncoded(), otherKey.getEncoded()); + } } private class NtruEncrypt1087PrivateKey implements PrivateKey { @@ -340,24 +402,34 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { public byte[] getEncoded() { return key.getEncoded(); } + + @Override + public boolean equals(Object anotherObj) { + if (anotherObj==null || !NtruEncrypt1087PrivateKey.class.equals(anotherObj.getClass())) + return false; + + NtruEncrypt1087PrivateKey otherKey = (NtruEncrypt1087PrivateKey)anotherObj; + return Arrays.equals(getEncoded(), otherKey.getEncoded()); + } } - - private class NtruSign349PublicKey implements PublicKey { - private static final long serialVersionUID = 8103999492335873827L; + + private class Gmss512PublicKey implements PublicKey { + private static final long serialVersionUID = 6542076074673466836L; - private SignaturePublicKey key; + private de.flexiprovider.api.keys.PublicKey key; - public NtruSign349PublicKey(SignaturePublicKey key) { + public Gmss512PublicKey(de.flexiprovider.api.keys.PublicKey key) throws InvalidKeySpecException { this.key = key; } - public NtruSign349PublicKey(byte[] keyBytes) { - key = new SignaturePublicKey(keyBytes, NTRUSIGN_PARAMETERS); + public Gmss512PublicKey(byte[] keyBytes) throws InvalidKeySpecException { + GMSSPublicKeySpec keySpec = new GMSSPublicKeySpec(keyBytes, GMSS_PARAMETERS); + key = gmssKeyFactory.generatePublic(keySpec); } @Override public String getAlgorithm() { - return "NTRUSign-349"; + return "GMSS-512"; } @Override @@ -367,26 +439,41 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { @Override public byte[] getEncoded() { - return key.getEncoded(); + byte[] encodedKey = key.getEncoded(); + return Arrays.copyOfRange(encodedKey, 27, 91); // strip everything but the key itself + } + + @Override + public boolean equals(Object anotherObj) { + if (anotherObj==null || !Gmss512PublicKey.class.equals(anotherObj.getClass())) + return false; + + Gmss512PublicKey otherKey = (Gmss512PublicKey)anotherObj; + return Arrays.equals(getEncoded(), otherKey.getEncoded()); } } - - private class NtruSign349PrivateKey implements PrivateKey { - private static final long serialVersionUID = 8103999492335873827L; + + private class Gmss512PrivateKey implements PrivateKey { + private static final long serialVersionUID = -8488638051563793833L; - private SignaturePrivateKey key; + private de.flexiprovider.api.keys.PrivateKey key; - public NtruSign349PrivateKey(SignaturePrivateKey key) { + public Gmss512PrivateKey(de.flexiprovider.api.keys.PrivateKey key) { this.key = key; } - - public NtruSign349PrivateKey(byte[] keyBytes) { - key = new SignaturePrivateKey(keyBytes, NTRUSIGN_PARAMETERS); + + public Gmss512PrivateKey(byte[] keyBytes) throws InvalidKeySpecException { + ByteBuffer paddedSigningKey = ByteBuffer.wrap(keyBytes); + int sigKeySize = paddedSigningKey.getInt(); + byte[] signingKeyBytes = new byte[sigKeySize]; + paddedSigningKey.get(signingKeyBytes); + KeySpec signingKeySpec = new PKCS8EncodedKeySpec(signingKeyBytes); + key = gmssKeyFactory.generatePrivate(signingKeySpec); } @Override public String getAlgorithm() { - return "NTRUSign-349"; + return "GMSS-512"; } @Override @@ -396,7 +483,22 @@ public class NTRUEncrypt1087_NTRUSign349 implements CryptoImplementation { @Override public byte[] getEncoded() { - return key.getEncoded(); + byte[] encodedKey = key.getEncoded(); + ByteBuffer paddedSigKey = ByteBuffer.allocate(4 + encodedKey.length); + paddedSigKey.putInt(encodedKey.length); + paddedSigKey.put(encodedKey); + encodedKey = paddedSigKey.array(); + encodedKey = Arrays.copyOf(encodedKey, PRIVATE_SIGNING_KEY_BYTES); + return encodedKey; + } + + @Override + public boolean equals(Object anotherObj) { + if (anotherObj==null || !Gmss512PrivateKey.class.equals(anotherObj.getClass())) + return false; + + Gmss512PrivateKey otherKey = (Gmss512PrivateKey)anotherObj; + return Arrays.equals(getEncoded(), otherKey.getEncoded()); } } } \ No newline at end of file diff --git a/src/i2p/bote/email/Email.java b/src/i2p/bote/email/Email.java index 0dfa7709..33e789d0 100644 --- a/src/i2p/bote/email/Email.java +++ b/src/i2p/bote/email/Email.java @@ -26,6 +26,7 @@ import i2p.bote.UniqueId; import i2p.bote.Util; import i2p.bote.crypto.CryptoFactory; import i2p.bote.crypto.CryptoImplementation; +import i2p.bote.crypto.KeyUpdateHandler; import i2p.bote.fileencryption.EncryptedInputStream; import i2p.bote.fileencryption.PasswordException; import i2p.bote.fileencryption.PasswordHolder; @@ -42,7 +43,6 @@ import java.io.OutputStream; import java.net.URLConnection; import java.security.GeneralSecurityException; import java.security.PrivateKey; -import java.security.PublicKey; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -309,16 +309,16 @@ public class Email extends MimeMessage { * The signature includes the ID number of the {@link CryptoImplementation} * used (signature lengths can be different for the same algorithm). * @param senderIdentity + * @param keyUpdateHandler Needed for updating the signature key after signing (see {@link CryptoImplementation#sign(byte[], PrivateKey, KeyUpdateHandler)}) * @throws MessagingException * @throws GeneralSecurityException */ - public void sign(EmailIdentity senderIdentity) throws MessagingException, GeneralSecurityException { + public void sign(EmailIdentity senderIdentity, KeyUpdateHandler keyUpdateHandler) throws MessagingException, GeneralSecurityException { removeHeader(SIGNATURE_HEADER); // make sure there is no existing signature which would make the new signature invalid removeHeader(SIGNATURE_VALID_HEADER); // remove the signature validity flag before signing CryptoImplementation cryptoImpl = senderIdentity.getCryptoImpl(); - PublicKey publicSigningKey = senderIdentity.getPublicSigningKey(); PrivateKey privateSigningKey = senderIdentity.getPrivateSigningKey(); - byte[] signature = cryptoImpl.sign(toByteArray(), publicSigningKey, privateSigningKey); + byte[] signature = cryptoImpl.sign(toByteArray(), privateSigningKey, keyUpdateHandler); setHeader(SIGNATURE_HEADER, cryptoImpl.getId() + "_" + Base64.encode(signature)); } @@ -581,12 +581,13 @@ public class Email extends MimeMessage { * If an error occurs, an empty Collection is returned. * * @param senderIdentity The sender's Email Identity, or null for anonymous emails + * @param keyUpdateHandler Needed for updating the signature key after signing (see {@link CryptoImplementation#sign(byte[], PrivateKey, KeyUpdateHandler)}) * @param bccToKeep All BCC fields in the header section of the email are removed, except this field. If this parameter is null, all BCC fields are written. * @param maxPacketSize The size limit in bytes * @throws MessagingException * @throws GeneralSecurityException If the email cannot be signed */ - public Collection createEmailPackets(EmailIdentity senderIdentity, String bccToKeep, int maxPacketSize) throws MessagingException, GeneralSecurityException { + public Collection createEmailPackets(EmailIdentity senderIdentity, KeyUpdateHandler keyUpdateHandler, String bccToKeep, int maxPacketSize) throws MessagingException, GeneralSecurityException { ArrayList packets = new ArrayList(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -599,7 +600,7 @@ public class Email extends MimeMessage { else removeHeader("BCC"); if (!isAnonymous()) - sign(senderIdentity); + sign(senderIdentity, keyUpdateHandler); compressTo(outputStream); } catch (IOException e) { throw new MessagingException("Can't write the email to an OutputStream.", e); diff --git a/src/i2p/bote/email/EmailIdentity.java b/src/i2p/bote/email/EmailIdentity.java index 979706c3..a2c0e54a 100644 --- a/src/i2p/bote/email/EmailIdentity.java +++ b/src/i2p/bote/email/EmailIdentity.java @@ -73,7 +73,7 @@ public class EmailIdentity extends EmailDestination { } } if (cryptoImpl == null) - throw new InvalidKeyException("Not a valid Email Identity: <" + base64Key + ">"); + throw new InvalidKeyException("Not a valid Email Identity: <" + base64Key + ">"); PublicKeyPair publicKeys = cryptoImpl.createPublicKeyPair(base64Key); String base64PrivateKeys = base64Key.substring(cryptoImpl.getBase64PublicKeyPairLength()); // the two private keys start after the two public keys diff --git a/src/i2p/bote/email/Identities.java b/src/i2p/bote/email/Identities.java index d13afad3..642e0a8c 100644 --- a/src/i2p/bote/email/Identities.java +++ b/src/i2p/bote/email/Identities.java @@ -22,6 +22,7 @@ package i2p.bote.email; import i2p.bote.Util; +import i2p.bote.crypto.KeyUpdateHandler; import i2p.bote.fileencryption.DerivedKey; import i2p.bote.fileencryption.EncryptedInputStream; import i2p.bote.fileencryption.EncryptedOutputStream; @@ -54,14 +55,15 @@ import net.i2p.util.Log; * Holds a set of {@link EmailIdentity} objects that are sorted by name.
* The Email Identities can be written to, and read from, a password-encrypted file. */ -public class Identities implements Iterable { +public class Identities implements Iterable, KeyUpdateHandler { private Log log = new Log(Identities.class); private File identitiesFile; private PasswordHolder passwordHolder; private SortedSet identities; // null until file has been read successfully /** - * Constructs a new empty Identities object. + * Constructs a new empty Identities object. The identitiesFile + * is lazy-loaded. * @param identitiesFile * @param passwordHolder */ @@ -160,10 +162,10 @@ public class Identities implements Iterable { return identity; } catch (PatternSyntaxException e) { - log.error("Unparseable email identity: <" + emailIdentityString + ">"); + log.error("Unparseable email identity: <" + emailIdentityString + ">", e); return null; } catch (GeneralSecurityException e) { - log.error("Invalid email identity: <" + fields[0] + ">"); + log.error("Invalid email identity: <" + fields[0] + ">", e); return null; } } @@ -399,7 +401,7 @@ public class Identities implements Iterable { private class IdentityComparator implements Comparator { @Override public int compare(EmailIdentity identity1, EmailIdentity identity2) { - int nameComparison = String.CASE_INSENSITIVE_ORDER.compare(identity1.getPublicName(), identity2.getPublicName()); + int nameComparison = compareNames(identity1, identity2); if (nameComparison == 0) { // if the names are the same, compare destination keys String key1 = identity1.getKey(); @@ -409,5 +411,22 @@ public class Identities implements Iterable { else return nameComparison; } + + /** Null-safe comparison of public names */ + private int compareNames(EmailIdentity identity1, EmailIdentity identity2) { + String name1 = identity1.getPublicName(); + String name2 = identity2.getPublicName(); + if (name1 == null) + return name2==null ? 0 : -1; + else if (name2 == null) + return 1; + else + return name1.compareToIgnoreCase(name2); + } + } + + @Override + public void updateKey() throws GeneralSecurityException, PasswordException, IOException { + save(); } } \ No newline at end of file diff --git a/src/i2p/bote/service/OutboxProcessor.java b/src/i2p/bote/service/OutboxProcessor.java index 7b660c05..1b2a7878 100644 --- a/src/i2p/bote/service/OutboxProcessor.java +++ b/src/i2p/bote/service/OutboxProcessor.java @@ -23,10 +23,10 @@ package i2p.bote.service; import static i2p.bote.Util._; import i2p.bote.Configuration; -import i2p.bote.I2PBote; import i2p.bote.email.Email; import i2p.bote.email.EmailDestination; import i2p.bote.email.EmailIdentity; +import i2p.bote.email.Identities; import i2p.bote.fileencryption.PasswordException; import i2p.bote.folder.Outbox; import i2p.bote.folder.RelayPacketFolder; @@ -65,17 +65,19 @@ public class OutboxProcessor extends I2PBoteThread { private Outbox outbox; private RelayPeerManager peerManager; private RelayPacketFolder relayPacketFolder; + private Identities identities; private Configuration configuration; private NetworkStatusSource networkStatusSource; private CountDownLatch wakeupSignal; // tells the thread to interrupt the current wait and resume the loop private List outboxListeners; - public OutboxProcessor(DHT dht, Outbox outbox, RelayPeerManager peerManager, RelayPacketFolder relayPacketFolder, Configuration configuration, NetworkStatusSource networkStatusSource) { + public OutboxProcessor(DHT dht, Outbox outbox, RelayPeerManager peerManager, RelayPacketFolder relayPacketFolder, Identities identities, Configuration configuration, NetworkStatusSource networkStatusSource) { super("OutboxProcsr"); this.dht = dht; this.outbox = outbox; this.peerManager = peerManager; this.relayPacketFolder = relayPacketFolder; + this.identities = identities; this.configuration = configuration; this.networkStatusSource = networkStatusSource; wakeupSignal = new CountDownLatch(1); @@ -138,7 +140,7 @@ public class OutboxProcessor extends I2PBoteThread { EmailIdentity senderIdentity = null; if (!email.isAnonymous()) { String sender = email.getSender().toString(); - senderIdentity = I2PBote.getInstance().getIdentities().extractIdentity(sender); + senderIdentity = identities.extractIdentity(sender); if (senderIdentity == null) { log.error("No identity matches the sender/from field: " + sender + " in email: " + email); outbox.setStatus(email, _("No identity matches the sender/from field: " + sender)); @@ -189,7 +191,7 @@ public class OutboxProcessor extends I2PBoteThread { int hops = configuration.getNumStoreHops(); int maxPacketSize = getMaxEmailPacketSize(hops); - Collection emailPackets = email.createEmailPackets(senderIdentity, recipient, maxPacketSize); + Collection emailPackets = email.createEmailPackets(senderIdentity, identities, recipient, maxPacketSize); IndexPacket indexPacket = new IndexPacket(recipientDest); for (UnencryptedEmailPacket unencryptedPacket: emailPackets) { diff --git a/test/i2p/bote/AllTests.java b/test/i2p/bote/AllTests.java index 448d32a2..cce22306 100644 --- a/test/i2p/bote/AllTests.java +++ b/test/i2p/bote/AllTests.java @@ -25,6 +25,7 @@ import i2p.bote.crypto.CryptoImplementationTest; import i2p.bote.email.EmailIdentityTest; import i2p.bote.email.EmailMetadataTest; import i2p.bote.email.EmailTest; +import i2p.bote.email.IdentitiesTest; import i2p.bote.fileencryption.EncryptedStreamTest; import i2p.bote.fileencryption.FileEncryptionUtilTest; import i2p.bote.fileencryption.PasswordCacheTest; @@ -82,6 +83,7 @@ import org.junit.runners.Suite; EmailTest.class, EmailMetadataTest.class, EmailIdentityTest.class, + IdentitiesTest.class, KBucketTest.class, BucketManagerTest.class, CryptoImplementationTest.class, diff --git a/test/i2p/bote/TestUtil.java b/test/i2p/bote/TestUtil.java index d5f57f4e..5289bc14 100644 --- a/test/i2p/bote/TestUtil.java +++ b/test/i2p/bote/TestUtil.java @@ -23,13 +23,27 @@ package i2p.bote; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import i2p.bote.crypto.CryptoImplementation; +import i2p.bote.crypto.ECDH256_ECDSA256; +import i2p.bote.crypto.ECDH521_ECDSA521; +import i2p.bote.crypto.ElGamal2048_DSA1024; +import i2p.bote.crypto.KeyUpdateHandler; +import i2p.bote.crypto.NTRUEncrypt1087_GMSS512; +import i2p.bote.crypto.PrivateKeyPair; +import i2p.bote.crypto.PublicKeyPair; import i2p.bote.email.Email; +import i2p.bote.email.EmailIdentity; import i2p.bote.fileencryption.PasswordCache; +import i2p.bote.fileencryption.PasswordException; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import javax.mail.MessagingException; @@ -99,4 +113,173 @@ public class TestUtil { return configuration; } + + /** Returns a KeyUpdateHandler that does nothing */ + public static KeyUpdateHandler createDummyKeyUpdateHandler() { + return new KeyUpdateHandler() { + @Override + public void updateKey() throws GeneralSecurityException, PasswordException, IOException { + } + }; + } + + /** Returns a KeyUpdateHandler that must be called exactly numExpectedCalls times */ + public static KeyUpdateHandler createVerifyingKeyUpdateHandler(final int numExpectedCalls) throws GeneralSecurityException, IOException { + Mockery mockery = new Mockery(); + final KeyUpdateHandler keyUpdateHandler = mockery.mock(KeyUpdateHandler.class); + mockery.checking(new Expectations() {{ + exactly(numExpectedCalls).of(keyUpdateHandler).updateKey(); + }}); + return keyUpdateHandler; + } + + /** Returns email identities for all CryptoImplementations. */ + public static List createTestIdentities() throws GeneralSecurityException { + List identities = new ArrayList(); + + String elGamal2048PublicKeyPair = "-GygBJmy3XXPaDCD6uG0a7c23udye7H9jVFQ2WCeCnmls353ewLyITt7D3oneFYBsM1dHm~ciORrLtgZUCRqeJwIJIjzzKMVL93FSuMD8PQB9IX~F2l-Jn~5oBJCJWK~rnkNX7yBl-uUrylzPidfZ-NpW0U6wJREOQTx4oGvcGNv2oDkHBL44Oqencuw9NXxHJ9SjapuSgo2vg8YN6BP67oHR5-SlaIN6bHaF9T5tjJMkf32frT-qmWTcyB~0OgXXL3Z9cTERqVihYIBmk4EaTPa5oB~sOUIhUv5DqedBD~BDY5P4d7TroNWoW4FOhnfGqtTD-cS-qEn0ww3tHn7JEppbWGcgKrbdb4F4Qt8VYBd-ogATOXFbbo-PG~PgmUOa2QWGIi4RSXK1L3NoYO4ha7SkQMJpKj8ySi-ixk3ivofk6lRgoZ4WhbReaB352pF1iMXqp4p7-mnLMPZUX41ibHPeWrq7TyNqb-ouyn9ZfqORlko3bi04eXkfzkeDVuf"; + String elGamal2048PrivateKeyPair = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADClvCJzneZ4R4yUJbm6zOP3wOh~CTfnWx3MSR7QDZS22-njK3KHuZHBbCK7HbyQLr"; + TestIdentity elGamal2048 = new TestIdentity(elGamal2048PublicKeyPair, elGamal2048PrivateKeyPair, new ElGamal2048_DSA1024()); + identities.add(elGamal2048); + + String ecdh256PublicKeyPair = "xE1fQK3nPfcmABNpYrHEDVHLj1sq01mmtrDrrIKAZcMnK9vmbiBZ4ygsksNpe5rV-TILTQUUTIUry5qt8q5ybB"; + String ecdh256PrivateKeyPair = "M3yBbveBPFwfd59UY06RtJnfZtHU8yC7RZMYCITBwdRGkyPsftKS7M2~OSMsmHWUfejRolqztJ4lf4keS~KSge"; + TestIdentity ecdh256 = new TestIdentity(ecdh256PublicKeyPair, ecdh256PrivateKeyPair, new ECDH256_ECDSA256()); + identities.add(ecdh256); + + String ecdh521PublicKeyPair = "m-5~1dZ0MrGdyAWu-C2ecNAB5LCCsHQpeSfjn-r~mqMfNvroR98~BRmReUDmb0la-r-pBHLMtflrJE7aTrGwDTBm5~AJFEm-9SJPZnyGs-ed5pOj4Db65yJml1y1n77qr1~mM4GITl6KuIoxg8YwvPrCIlXe2hiiDCoC-uY9-np9UY"; + String ecdh521PrivatePublicKeyPair = "YujtjOOwCqXPH9PIbcZeFRkegbOxw5G6I7M4-TZBFbxYDtaew6HX9hnQEGWHkaapq2kTTB3Hmv0Uyo64jvcfMmSRcPng3J1Ho5mHgnzsH0qxQemnBcw7Lfc9fU8xRz858uyiQ8J8XH3T8S7k2~8L7awSgaT7uHQgpV~Rs0p1ofJ70g"; + TestIdentity ecdh521 = new TestIdentity(ecdh521PublicKeyPair, ecdh521PrivatePublicKeyPair, new ECDH521_ECDSA521()); + identities.add(ecdh521); + + String ntru1087PublicKeyPair = "vkf7~LRP0uSIf01eE4iPJ0MsDZvXYGwNqpa7DTPMm9mDsylDAuS-98WsFyQCbS~83U0cTeliDICpCJxddfp45-7kRzy~K~pnaxeHGt9FCzdccErKHGzkauGpHRicrQXnL6uudqea8gdxp6gsPVoJTxpsq4-XrdhoangfH5i6pNTTE1077mJ4Mjde4oYibDMhi40tqXC3ZSzj-qLiBBuVlOHMQH~WixT9bLYL5R1rzKgTdgbDVfAGFrDwbi9pmfGZbTo0mzAmPGNAg~ObYn~tG7QvYnV1dZt3FqEvmdfckyjyaF6B4K0y7tt7J~JJ5r7EuMqcbW3Y5scb7C~o6P0V1GEjpt7RnX7fnrs463feekWWvghoqpIwIr8btMAYkezH7UTPJqod-xkQMpRjLmV1-yhKkIpFsigqOrrFHnEwP0JoBLPdAuDJ~JbdR6OIMroKJQmpgn1CM5i2D9AhMEajqN4X2ZIUom292HssS6ajEoS2PybUPDwGTk~jBbDSTZg7lgq2ryolvVJaAycHSnaGDfuo9GGSL4-qbuEWptiDu45aRL8W2RCUtwFykjmUL5FrJgLAgioWj6eD~iAUb3DRX9Ea7pqbu3hUi--J5LnakXb~2lOMkunEjvhlMI5voHJdiQtelbCsyAyBSFaCmSotUfXuOahPmvcLRjlCRKWDkDWcJTheXY3E5bMHMpVproE~XtaVz9lA9RBCFujh8-hmSIou3q3hK3E2H-4IU4ypKU0KKt24FLCMsdG5fv2kyrv2aY1HNCLMxRMzapm5INqKXcv-LIWtR9XP8gdTMtFjIVU5fbiLTV72QTvdWLVQ-onK~IXZtj9mhYg-XZrNuDLfEo2tq7TPXaEwGLwYCNf5N08CpzC7mZcvfCugPP6Xd3gygnL8MrJzPdxZuw36cWo1pdSuWZmcF0B2~SdJpB19Q3TtuQN4R3aYFnkCMJSWu81hlhuCCiF7bk8YJTRkBhHxefoDQCAQWpDKOZAmtLTa" + + "Z3MObZmdj57cc9-3DdVCEaA02ChKxKsInOyXjK17dRRcuWVR8NRccppCchsvbHolUtlUVxvXhQSMRJf76vIJbfS9WRxgEdvFvjI8G9ZkoBxEM8ZifmQN5YDRGwh4GW503qxoGXV2jXtgOaloeLsYhcEu2v3~aPG3bzSBZVvBBPP8FLDQd6eNzCEMT9XNWTIqS3rzDaNbQaWO~OI2ergJAB4t6gNpCNH99bXufReKrB2Ca2QnuVMSTaq7g6uIH1UkIHhlJk3MjwLzA~rpNU6MfSozRvdsxKFb2rPgeKpxJ1XeHJwqMNOerraRIx-w4sDMKdzB1uGtttsMe~0BcXiUOeuWtDoBb9PGnI59y55Edw9q6dMP-hl5GMzyyNnIj5rGMr7KJK4eoNXnhwPjN3ug5p6Rfuytae74i1h3xEmy04F6EWEuCdBLJ59qLcSJblx89b2j4eGMcQ2y2YN7Lwjv1vymu1myP5oRo1kf7ZzjHwoU8aCwK2Kp68MHczP8whwtD0rFVKfIImFJ0d~EQo2Ovq~ZYsz957gJ48HNjyPnE1~bZU1WyjQoL3SEuhkY0MAJewDPTX0JoNw6S6D3SMjnIYn~fPTsbNGH1pjKV3miWez7AYVPtwKIVO6IWttcAGGyt0QNTMecIfbaNv9h55i0~s-nP36go8qWNxypQEWBue4DDCqTtNlli7YfSDP58pbGFD1PHSTUHw0ufVXyHb0MR0aoDVErQqyL09avvrAITYhsTGhs-~zDbC8x1lk9gVfXbraFkDOVnvqpPj94SE7SwALvefe3ripmD5XAZatW8gfEoIzsgwIO6hmJN3boGJ4O-kv6URlcCQ1ZJTlkx2BP8Aa3iaKL4d8WlmY621bZM~cxjhvHKFxKZk~DXB9VyIlA5pmOT0HlSqTM6p~Tz9Nl6jeibqufI4P-hTwvltD8Kwey9ctIIj6k2h5Z31c6NIDn9~nv4a5cjBI2~pDyMV3tKOk4cF9ABurLH~RUopNh" + + "YL06SZ30eubfHzEzCPp--dyGgWBGuQ-PawGJurlzNe~mlX8EU2Yw5w~YKoRSinflsWKY4g-rqbNuoPg"; + String ntru1087PrivateKeyPair = "ATRwEyR1UmhXgY0NiFFZvqVAUSsShHwqWHl4~C8L-l3zdofk8HXpP9aS~vPwwSnQi7cgeucBFRN4ha~SemsB6BZeQjAYtPCg~CG~MYOtxwvLWJYa1MfG2q-6szhHTbaxWpg33ctDi53lBeIZAe3J0MeacL2D2-E9Gnrl6x8wafBGMT~CREQmLneeYVSC6VRRyml-by6TUEbMKCOM39vFvtcbIjKKe1b-Sbs27C5yiexBLmJTUMyDEK07JeNa8RzFksSMY8CvrNd6pKzBovWEL7voa6UFxoYIAADfXDCC31gCAQAwDwYLKwYBBAHAbQMBAwMFAASC30Awgt88MAwCAQACAQACAQACAQAwggEIBECrx9sA2n4SsEwGlZHZuZJ2AR6W5zWG2duDdh3f~LUMOK4rP3x297dR3Iktmbn9HxuuEgl~PELsztoRIRlsjEDKBEAOP6~8exKGdja2VV4tNFyCbdjynanrrBgvMRQhZztkzYWzhu7MPEV0Gm44PeeofgLn-ne1b8HJZmsUducUUxigBEAyLW8AXwLjGLbbkpEmsmc7DZ~5MFHjkMhOzC-kx4Fz6JCK3xgOA1Pd4zImI2iC4oZUu-UYhJj4K97ngr56YAK7BEBkmghqN6XSgM9fvYFllcM5SJDm5gPpN9AhP2ZAUYN7WJvFRqrt10ykePVlyNnsw7sOqwr6olurPjStag4l324uMIHGBECJG5UNTVKOwZdOCAEnGGC429V9KGIPGcyTbGfxZzUgTetdLSyLf1-B~3OAQcSk~vqUEIY40S-WPTRmjmgSwTM3BEAvW85HRb42ZQ8o-C0fHYkcurSs~tce3P2uXU9QNRjMMKsgtlqd5SxdwVztJXG2OVjCvxdfGFcj2nms8uMgQL4BBECVNCQiDbcl1KHOq~7BGGFc~pNxwTLUILHjyHxQfyRXUpgteGxOkMXyEwL4VuTdSV4ObC56GnKWtV-J8sUaA-LKMIIFvDCCAYwEQNeOH3pO" + + "j0fvYj66ov3h~8OeBTT4CNSnK-qpOPcg2BBEDfb4zbrs4~cPIHyyvbV6ZGYJtfpRRNRaQERiB3o1vx0EQFieUTWFuRd7nMEbyVDJvE8ITkyi02EctJgsDIEYS9LiSJAOFGE6~m3L3z7ZdefzIWN18AXjZrmc4kGeDy0lIlUEQH7xbSE~tl2GVYZ3cASbR5ZvgAg1ELWxLgfHZlBwfyI6E2~8~Cd7FzeQ-iTbVopMrRVo9Kp1icmVqqON3zqvofUEQKama3rhaudZwgtNwL9zt2VJ6TMpJQy~9vK-G8TMg02oFNFg9RC62KWfHTCUHYepXeh45X2CC-OJpDIu4o7XuawEQJotGeCglUfjHriOC6RDqb1tA1FhIloyZzno9QaQgOiPyC0wE1~XJH0yV9JjhtS8FehJWdTTtcQf6EyK6Jobih8EQFIJ6sruYo53zBsxEp1cQ5BIVQSbThilGC26HrQwd3H5T3m5HAoWGEyL6krEb~PgAB306ZbVW8K5NxvUum2FNzIwggGMBEATmQ011NKMqP5Ae4oZTUx6qAP8Zgg3COHHCAzyJO0AshzFMcxfjA8ZF2FDheF1KsO2~fJVThwLpn7WaClZvTtNBECNVAAgrlS5RBTT5GL1qMj1xB3g6x~W90CtIv7u-F1qop8Ldc033Siil-p-7ERTDpqSiu2jMpUXWoM-lL6htKDEBEDXck2QCKjhkSC5vRpxggAvue4nRfLB~rNC-UNMlCYuFFefaLfKk9W19rcTwsu-rW9X1sk1QQjRKfZMPU8X22dlBEBCAX-G9fbskzKVjB4l~3KJvs04lur856~Cuh3go9ls1DA24fxTc~bc4AZMwk6xWEoSaVwtiikKOQ6P~YfWZ-XRBEAcK4LSdy5pXx0x8LO00MB-M2laNuEx-QnHPPxm-g3Vgx5OIi6JoW0vPJ3-sYUefcJBQ1tEEC5hxa2Eaqccz6g-BECqplqnrw9ddRYiw~xZO91cLQIGe5yP9C~S" + + "FiTMstcAldeDufmoDuvao09zh-BXdg6pyfPL1iLgL7pXmSFdy90yMIIBSgRAlD2X42qVAS4WF0cRDoEdghesMySzpXdUHHADwDkxdI6lUxc8Vg~PjzBlMwcAiXQXHM7KlHm6C-xksEs3rMgOiARALQHZNkENWiN~70PiSr65L8BVFS6ow0Z4UlW5OUY-785GYmt4fnfRgYAPENdO0VA6MbHLW5aEBY~ntCsO9~ei7ARAIrWm5EkWS72PAx0WJqBX48nuETA~CllX2afftYeV2iAbNSDA5sMZZJj0v5npr9dMdooX3BE4UvRDzRvG~2N-vwRAfdeshMQpkNfkaEvN9WnybLE2pDJ9NgSnV5Zwv71CTKeAqCax2GLW58wryJhB2w6RSLguRYlH5sXWjpR1AIzXMwRAIKAooGNBVoSCCsaS21~BaIACzgXHa1Ic1V7vx3Z9o4loPSRO~Q6Cm70LQk-NNxbDbNjTfGNlOg5gSTYaG~f0kDCCAUoEQAAM3hGAIJXmSwy5Egs-zqpv6GKhBanai-RpRxZaaZEegVi6HI78bu7Yc2qvdfS55QA1Yumk2JHp-gwCMeoYx9QEQFLVOfjS9rERqeAJaOhaeWYdDGg8fkWWu5fOYSuPNAtLoTbVed0Citig5JU0VEYAkbJEx9NCOiPtdeF9Qg8Xgu8EQPRMdwoJfpHi-UBOjuJ5P82UrgUtGRf-5MOqgPypjqW-Aa831LM~5FUpeFQjzBp4LAsoTOxDfMlA9VedkGVJNX8EQJsXcIjM4f3mpLsGsvYpJyNryZZ54s2AnxxnLcQ16~snBhv7dTbdzSJec-gsSoispKxR-VTAWMH-c2y5LyAScuoEQLfIMsfBhRoj19vquPzHbCltSRfz~M-YzPO1T0RV2h4AKEpifjRDpMst45W8thnE9XDFd6wVIIleKClyPpWerT0wggQsMIIBjARA0KjFfYBPqHEVzb1zmw9LkS0AbQYMdswLgG3vOJc1EdJf" + + "EMnaqXUbYfgwykv~YbHODQZ~uEiHwa1Whp9GucNPIQRAfyAV3qhiR~fRRnWxCmWd1ITAfxSb6gFYF1YyTojS3kNfGTWrVodcQfPVgca9-YGqdLAJZxpdgYlNEM6gDFF0fgRAr8xoLuuo~-fBB1ZbWmn3JXyJaFhW-lSIGr4SygLU8v4Z1W8Amf4c9a~875rTVka9KxxeNqJZmhRe~gnRwPB5uARA8icup6jIwth7EpZfGN0yQUFfu26qq8-BUBflLXZUbY7U0IXutsBf5s6V-fjZoPNrjDEFAMfQb7fTwxtRX2PhSARAJzYWW93-9yN9LaZLGJThoR68LhG6vq-B6mwHHuYLYn3jdky-PQ8q7iiyHkQ61xjcpfK23NokXMjj8ohhdzEqCQRA-JMWLrJM1idYZJgjnjr68MIzyIV6byHOULJg34mCcGcpo7bJog59uYLV2iHwGqI8eQlpFkoHEnwMIGx4~NkQnTCCAUoEQIJevz3lh~vnswDrogcb-IIPj18-hAflLAnd5wSI2AkAz6lEQKJjFm-RhDyoKrtVDI~DBLuH-2P-IlW8OgbdmmEEQFakV197uU3peE0Jrvzkeau2odysDOe0gKh8XQ0FyzyQuXacDRAsfdSRNDjXGgHkj0-vGSuqSIiZXAPkedYU7DoEQBP~uc8ssREdGbJsHN7PFFryJBAH8HcVeaFWPeu4Q6wtWbKm61Ug0exfluktGV9Xulyh8buJ3Xg2QKmbiNu0YfsEQMLK9TBga8RHCJy~WwFQJUsnBMQ-mOypKZdOwZiTsrTaW0ltc2CiS6yGXA0uUskJjmWzla-OSmEm5BELpPIoM0UEQASKEimKOleD9u7D4Ung2Tek9Ns6Vq6~3SmKjFO8ntyH61DgWO45WdK~roYXIRC0qRR3zwboVoNdTd75nbHSF5owggFKBECbPNInzn8bp1cKv3pox4EQyP6Mgovbs-zKknZxFmnmx~wPW2h1XBHZqmfvsIwM6PD9" + + "h~PMHy-gpSctvuFo3lB9BEBLHKMtRzZpiHUMh4oQ34d55Hsyd4qNJxQldRSwjBgjznWChzRJ3HonfI3iVk6qrbvljTahMdAssvmohVv6FUveBECvtbkPPSQK01IYrjEAcS6QowM68ApC6drE-A6IfyrPlHiJHA8YGn~P4ir9usYpmy2NRvUkP0WLNA3PZlp-lmTSBEAIgB4fIip5oFqcAEN6gTb1Mc3bxqGzV46UrLuhSGghEctqiY312LS7jcDrTuxegaQByXKlFfov~-FTO1AAr4wwBEBz20s8snf-mhESEfLWyvm8Y9BwipBhqF5yKdnxK-591lVxQWcTWX02esGJJ3swyOigvc6ZXUIoMoVm-9IaKSXMMIILIDCCA7AwgekwChYGU0hBNTEyFgAwgcYEQAbGSo3oOWYyuoDqespOewi1p9ek6hhawzVbw4oXu7wZ2nefxfo9VWtYfghMQeix7l2X1T8FzHBTwahoOEuefX0EQBGULuDYQLBM5p-eTbo-JlmVHlPFUFBi1BAtmTE~mnjRdjvhX-GDLO1vUmMxmJ6-y8rboabNt6JLrzY33YG-P~IEQBGULuDYQLBM5p-eTbo-JlmVHlPFUFBi1BAtmTE~mnjRdjvhX-GDLO1vUmMxmJ6-y8rboabNt6JLrzY33YG-P~IwEgIBAAIBAAIBAAIBAQIBAQIBATCB6TAKFgZTSEE1MTIWADCBxgRAr1u-5nPIA2yKoQc8z7x6kjczhid4V~9KH42KK-jp5fXyz9QKEfs8UzYH1jaZOmWtR-IiUMCnYVhVOrtZXBy1iwRASS5blDY67VzojG5JpaLdJszzDIBz0E1s9OO9oiwiPhnbWonf9~Pw1TYMDltmKxKjm-ZyOyE8kX1Sfb-sRx7kzARASS5blDY67VzojG5JpaLdJszzDIBz0E1s9OO9oiwiPhnbWonf9~Pw1TYMDltmKxKjm-ZyOyE8kX1Sfb-sRx7kzDASAgEBAgEAAgEB" + + "AgEBAgEBAgEBMIHpMAoWBlNIQTUxMhYAMIHGBECZ8GM8j4ASF4DNDzoHHEeDe6qJSE2glR3kb7-7oZcODz7o3TvnfCWxG3zrl41vUz3zewW4cMwsKCujR3rEGRiGBEC1xDiqVSOTKaq3b3wH1xVTitIoJrgl~DU1~PaYm6f2yXLIsA-sCUcExibYPaZKd2Lmpu8yy9NqZr82DlbB0WrlBEC1xDiqVSOTKaq3b3wH1xVTitIoJrgl~DU1~PaYm6f2yXLIsA-sCUcExibYPaZKd2Lmpu8yy9NqZr82DlbB0WrlMBICAQICAQACAQICAQECAQECAQEwgekwChYGU0hBNTEyFgAwgcYEQMYqF8la03mipwultDjb8RzmRRj0yesYUyNAtKc5LLE1i2EIVsWPYV6bsyHj2zVRG3yNO1SXwzRpTr5FvksM3loEQFtZw3xZebo2NaP~jlefIr7P0CoinsrrYzV0aGSk95eEprCAQFVmWigSsFAkE2U-m6UE4IvJPiCFrNh6KO1sXg4EQFtZw3xZebo2NaP~jlefIr7P0CoinsrrYzV0aGSk95eEprCAQFVmWigSsFAkE2U-m6UE4IvJPiCFrNh6KO1sXg4wEgIBAwIBAAIBAwIBAQIBAQIBATCCA7AwgekwChYGU0hBNTEyFgAwgcYEQIDeO04lSz-El2lXOAwSuScnvFKRj7GhYB4RKdLhkwS5Sz538D9SVS6YxrcPS1-2yNDIE6AAuqO3VKy2NTth1u4EQPy1MLtGkKffda8io6JuhM-ZN3kkoHdbrIUREwDBxxhiVgpeDpPaoKHqkAdhUnUsENsVNq-OpWjSwA7IEZwXCJUEQPy1MLtGkKffda8io6JuhM-ZN3kkoHdbrIUREwDBxxhiVgpeDpPaoKHqkAdhUnUsENsVNq-OpWjSwA7IEZwXCJUwEgIBAAIBAAIBAAIBAQIBAQIBATCB6TAKFgZTSEE1MTIWADCBxgRAFSqfJNvIZw-N" + + "nkXkdARywKMxXFQTkSyWm9~CSL0P5xsw0DFgo54zW32FUJ25zwT3TwdH64wEGZQX8QeqxQm5TQRAlNrpnPnzszwJmo4OZXiBdgqMHwkg-GURXrRGrM7LvQF0nfDY7ZeTG4KcKLTMa5W9hOGewXY-xtzQe09R8sTw8ARAlNrpnPnzszwJmo4OZXiBdgqMHwkg-GURXrRGrM7LvQF0nfDY7ZeTG4KcKLTMa5W9hOGewXY-xtzQe09R8sTw8DASAgEBAgEAAgEBAgEBAgEBAgEBMIHpMAoWBlNIQTUxMhYAMIHGBEAHLN6CLwqaT93kaeg9i-ZSTvZGkLYXsLmk-w~gjr9LV~BCtgMD4AiNhSlQrV1HAl66561vxjvtm5bzCvthKQlmBEAxGFHQ8vI-SrXQYg5RUMHECbWs15sXyvX5LSTR1bII1Di8OliBNPGdOtLwBvc9DI5afrOz3~J-PnWcOzwFs6XLBEAxGFHQ8vI-SrXQYg5RUMHECbWs15sXyvX5LSTR1bII1Di8OliBNPGdOtLwBvc9DI5afrOz3~J-PnWcOzwFs6XLMBICAQICAQACAQICAQECAQECAQEwgekwChYGU0hBNTEyFgAwgcYEQJwPazvt4G6EQqk-7JfqMr7YlnhYFOfRHwUzCZSPgmnO~eCwmvMBaj2R25l5gHn~tm3~avBLyp4rOIRbb3hPe14EQPgDOhNtdL0OfROh5b3Cpb5IA0jkGtc3aZ4Kc-4I4oHK98YvB1yUes2wWiqXIQDjuOz19GzyXUwUzDREPM-IcUAEQPgDOhNtdL0OfROh5b3Cpb5IA0jkGtc3aZ4Kc-4I4oHK98YvB1yUes2wWiqXIQDjuOz19GzyXUwUzDREPM-IcUAwEgIBAwIBAAIBAwIBAQIBAQIBATCCAdgwgekwChYGU0hBNTEyFgAwgcYEQIsMNWKJZFJSOk-9qloukYKqCK3GSPUJsWG3Tz76HFqYejcRVnFNXiiY0ZxUI4uJ" + + "B1EKywqPyiVHBi86~rjoL~8EQIbMQupGcmgeFjE76eMPMnhn0TvHZDLGdIQ46fe0SVPnWaz3e3QM~ACEbpAXK92SttkLo0CVqy10nPFlsoVXEKwEQIbMQupGcmgeFjE76eMPMnhn0TvHZDLGdIQ46fe0SVPnWaz3e3QM~ACEbpAXK92SttkLo0CVqy10nPFlsoVXEKwwEgIBAAIBAAIBAAIBAQIBAQIBATCB6TAKFgZTSEE1MTIWADCBxgRA7x4EHmLQgSD-KVRdpS-iWTQnjhlPunEKWYjQRwbo02w6VuQeBEKSqQkH1rJl2nqXhhiD2bwK3lxfdD~A2DZ-GwRAyMD9icFG4asyeQTE1-WUHBdYWh4fHQhJijKT1x3wmJnfFyw3YOXQU96nZAGCkkobei-rteWzO3BYx2E5bMGVcwRAyMD9icFG4asyeQTE1-WUHBdYWh4fHQhJijKT1x3wmJnfFyw3YOXQU96nZAGCkkobei-rteWzO3BYx2E5bMGVczASAgEBAgEAAgEBAgEBAgEBAgEBMIIB2DCB6TAKFgZTSEE1MTIWADCBxgRATAb3dduGkH88GVIxzybR~WyipyGiuh3hpxrJcGjcgJzRTYpuPGwNY6eWF7Tj3P2CmRKnoGO46n1vcAN2js9ePARAzAXlor5brE9ihoxHF0nE~Zp659CnkGVIzDwOu9m8YGtJkJpfRDsJWaQyDzvDNmCsgMiixajjuazB6ka8ddTwXwRAzAXlor5brE9ihoxHF0nE~Zp659CnkGVIzDwOu9m8YGtJkJpfRDsJWaQyDzvDNmCsgMiixajjuazB6ka8ddTwXzASAgEAAgEAAgEAAgEBAgEBAgEBMIHpMAoWBlNIQTUxMhYAMIHGBEBYtoPqBM4-uZKDkFUIkiiA5hYpL3nr1MJqMUvqfcHY-O0Ww8quM29p7SyvSUyXpUVVEmdj9cFydKmvd8GiXiJiBEAnHS1fOHJYP4vhkE11F97GxJ5z" + + "xsU2Wd-sFLhyK~dHFxGUgY2yIVnOCFv-cM6VCr3GFpmBwotx~MHRrapNATVuBEAnHS1fOHJYP4vhkE11F97GxJ5zxsU2Wd-sFLhyK~dHFxGUgY2yIVnOCFv-cM6VCr3GFpmBwotx~MHRrapNATVuMBICAQECAQACAQECAQECAQECAQEwggdsMIIDsDCB6TAKFgZTSEE1MTIWADCBxgRApI5MjN495mTQNbguIZiZMM0DjCQVtQt3jweYJ2lMS6C4AMgGih8ieR5NkzW9U1534XKLu5j1sFZQwKQrM43M8QRAc2bvCfkV2Hlu4GaFBYikFzX0hq2KZJBOwY4rL93D3fZf3hHc7gWXHYykYiETmIz4tEPEiiEefX0wHWFh1UcV1gRAc2bvCfkV2Hlu4GaFBYikFzX0hq2KZJBOwY4rL93D3fZf3hHc7gWXHYykYiETmIz4tEPEiiEefX0wHWFh1UcV1jASAgEAAgEAAgEAAgEBAgEBAgEBMIHpMAoWBlNIQTUxMhYAMIHGBECj07XlG1f~DLq~1VEgxQPMtp0A91al7jZ-3e4Lcc5VEodqXFBk6cYreQLgK5SVQtOJEkdAohOimFq6Lr1DymhhBEDYM8mzmNUqUSe31dAiMLFBVjikKqMtdzVXzZm3uF0Ln0cetb1rdpktnZWn~3NEh-reKz8lBXF~FcL5QEIb11zFBEDYM8mzmNUqUSe31dAiMLFBVjikKqMtdzVXzZm3uF0Ln0cetb1rdpktnZWn~3NEh-reKz8lBXF~FcL5QEIb11zFMBICAQECAQACAQECAQECAQECAQEwgekwChYGU0hBNTEyFgAwgcYEQJVJQKcQuCHnH3UkDYK~Gbfz37SnPQHE5XlQIt-8GupqTIn8gZV0jbN31Q1NOA99byk0DlW1Kh-8SGuENy67LncEQA0ZZoSH2RvZBSRse60Hm6L-1ZN128jAiAIIb-VI~5WW46b3o3axHaxAIp2q3Py6wK9ICKyW" + + "E3WZvGFCo0wn5oAEQA0ZZoSH2RvZBSRse60Hm6L-1ZN128jAiAIIb-VI~5WW46b3o3axHaxAIp2q3Py6wK9ICKyWE3WZvGFCo0wn5oAwEgIBAgIBAAIBAgIBAQIBAQIBATCB6TAKFgZTSEE1MTIWADCBxgRA4O~0aS7Fxgktfxze-oXDYjjIhnGh5jEMKWLJSfrtbWtcMfmgHu6C4RRti6PUAj1Ao84SEU86GJ1nFY7NVnktEgRAI6PoPfDmi3FqUlSYUk4vJzQC76B~TJthJkHWH1M1Z2jzivlLEqMqGF~KMgBs1Kdz2A2hGR4563gmjQURkYg5GQRAI6PoPfDmi3FqUlSYUk4vJzQC76B~TJthJkHWH1M1Z2jzivlLEqMqGF~KMgBs1Kdz2A2hGR4563gmjQURkYg5GTASAgEDAgEAAgEDAgEBAgEBAgEBMIIB2DCB6TAKFgZTSEE1MTIWADCBxgRABjtK~L0yzPkuLdEn3egjYxtx2H6P0KnqyfFxobspUThP0WWnmkv5zsVbzUlnIHeP6Q4oShVox8CnPD2y89XYeQRAbI9vS886PgvlSzl7e~YH-~ftSXlDh0pCTuRZrowu9bxUIa6~cvo3OyFXnhPXTpFGyvqPFu~Mc~d-kg-K2bEf~ARAbI9vS886PgvlSzl7e~YH-~ftSXlDh0pCTuRZrowu9bxUIa6~cvo3OyFXnhPXTpFGyvqPFu~Mc~d-kg-K2bEf~DASAgEAAgEAAgEAAgEBAgEBAgEBMIHpMAoWBlNIQTUxMhYAMIHGBEBjYBNZIRel2MESE-1rG-6Or5abkN0n7UjeLOVqUOe65Y2n3mDa-KLoR8o7txCG8m8TkDtgnfEDYuCwfzZ4wtqlBEBIzSovHOeT0eOj-6-dqP6Ff2tVj0NyjLMVRAiU8p~YPJq1cIlrdfeIy4VXhE5spewC8xZRvgNhtK3TI3fr0027BEBIzSovHOeT0eOj-6-dqP6Ff2tVj0NyjLMV" + + "RAiU8p~YPJq1cIlrdfeIy4VXhE5spewC8xZRvgNhtK3TI3fr0027MBICAQECAQACAQECAQECAQECAQEwggHYMIHpMAoWBlNIQTUxMhYAMIHGBEDJSC0uQ1bogJgnYy5l59JxoqSU7iwqz41QGuxLEHkWC7VD7H8mgRL98HEwGIOMuUG-3sf8fgAxmWR-AB5KrHEFBECs~HaejTMPQcjZBa456uXwPxxcfTwjXN83j9PdYYB5Nmvo3pOpN6HdcDTpxyf83C17tgh8IsH1wJDSSeMEHijlBECs~HaejTMPQcjZBa456uXwPxxcfTwjXN83j9PdYYB5Nmvo3pOpN6HdcDTpxyf83C17tgh8IsH1wJDSSeMEHijlMBICAQACAQACAQACAQECAQECAQEwgekwChYGU0hBNTEyFgAwgcYEQA-LrHFb-vUTPefI2b4Co7nvLUTvCK~mqa1t9gkJGGFFwdbLvOptYZ56sXPv9eVBgQl4NCAtj7nk1rkrdVOubAAEQCLIym9S83DC~G8vokKR4CPB0xoHeapS-s86wVnUgLnua8~w6VZIXOPnN0zMkJJdCPbuXZl-a5fN~bTAY83wzo0EQCLIym9S83DC~G8vokKR4CPB0xoHeapS-s86wVnUgLnua8~w6VZIXOPnN0zMkJJdCPbuXZl-a5fN~bTAY83wzo0wEgIBAQIBAAIBAQIBAQIBAQIBATCCAqAwgcYEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwgcYEQAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwgYQEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwgYQEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwCDAAMAAwADAAMAYwADAAMAAwggKuMEQwQgRAN5EkP9bhfWIvH-Bk9ZAhSsICrqi0AOa-WDF1CKuZbIZwaDMKDGmrGs5Xiz5WIfR8Yxkg9fphN6uLfBfw3PaREjBEMEIEQGypJW~rU7ik9hWhJwxWBhdkNE0x9M1IFcOsH216HEwDYWI8cMZrqcb58qOcKeZ7jBc50WhTi5fPzCgOMHBvKkAwggENMIHGBECI3~bsLWlhloqm5JmWhzT9s~ssU~d2R3Tiwvu10Kj-DYalbBebysmGf8qd8Bq0Y8dApaZCRetdhmOM-Kom838BBECS3DX-JzbPYJwvZqQwaEgo5ibo0OmSvkEY4sj1tHY-Wj7N~uUhXk1WFu72EQK51TS~-4Wu" + + "01y9EWl~f5Nk~5lyBECUc4GbZRevXQyPCLqtYmCONiqH5VGeknwo2LE063x81y5gUguwinqy4gAMgGLBuhdCCIjrB7OwKxDTQ6S7wspIMEIEQNn2NRg5z2~7OhUnZARAYo4NmG5jqbATRqcpSoodmnNQ6lBAbRx~9gckXBkd0Py9BJIEnz-pKOmd9kd6OC1UK64wggENMIHGBEAWOcYRK9nKvbBWUa3rh02QFGW0rkKuaEKD72TJXRxrwqXNenOF5JUk7IswAQH8Pq34RyR9y0Hkq3DZrkjrxjnqBED2SLrc7xMgvk4mTBDHWRMA44G~yk61st8iKU3mKWMdu6n~P1bZPFq2pXtmk4eqonbDGM0ejSma2RKyUDoPl453BEDZm3NfQywxwoWq1HTZYS74cl49D8drCke4ytlAMmlS56GP8L63DeHrhDKwZbDa7mj~42Y8dfb3sa-nEtjvRRFzMEIEQPm1mZDDBKpVHPYYAxorc2Sh0cD4BAUIXb0HIVERR1AHBw23f5YJSW7zct23vdFIyrC1sHsQpasfIP5eKsmG6k8wggJoMEQwQgRAmO4s4~pQwGQeVM9UGxm8JSwVU~tvCRfd8407Nn-pjuvRVTl~DIvoKWiaLFYf47bfPx7fnwQSaWiXaHnn--w~xzCCAQ0wgcYEQJishvawNeMtsdoOUoFREBKLSJSmp8wWC92Ey2ZEl8h1cgNjX7dFYBpZWhMmafQD~qpEkQ2aHS2vuB8NbMpORasEQLEsNAKEkhu3dX1dc-i~iMuE9db3L5aWol4ytfowuRs704w2TgtqRpFVecmRDI89P5~DwS3LZLuUf39EqE1CuykEQL-nSXUM5CyX2CIrn9eq9zga7G0L7USRdkmNpwJhg~2rGEk7NaElVxt~~XaApa~1qh1p9jPLUgXK8LDkT5~FesUwQgRAjLQ6nUbl9oYpKtUOo~Otd~nyQSokXIkAJ3EWGhhj0C8Ka4TfezUX4sTa1nImOE92" + + "EYQJkd6K3ISCmeRo78lhBTCCAQ0wgcYEQD6ViJJ8s1jDnJVGL9cFFeCm8~WkhUPTuO3hq4NbrhcBvssuVo8wzmjoY7Du-PEvIhGXrNnMLFvg1KYZuFY50NgEQNCet38a7s2XaKtHO7RPrwMd5MJMUYjDcaEBDytaPvrHQrdfI-6BPqKu5w~q~xo333mUNuDtAU~0iLuez68UtFgEQMSKoJfkzW5ZgWn30cX4ChVILxGYheSy~I-eAyA4FrDmXRf967xpOhLZgOvb3aolVK73voPQFGZd18fdCwmghZQwQgRA6sKzsvmgeBSPOSesf90~1x~44G2pNpF14gnlvHUWHxBrGeyFJQQedHLWEGih2Ojrad5UdFhj-1Yhh0V~7EuWXjCCGlowgg0pMAoWBlNIQTUxMhYAMIINCgRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAZW82VT0bTSxmAjHt9~NLYl5XVhGiIUyDzNhjK9Tzp4icEm7mth3eCMZsvmDW6g~cWy7ZCX00EAPFffMd~0tAVASCDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMA0CAQACAQACAgxBAgELMIINKTAKFgZTSEE1MTIWADCCDQoEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAEQJgnEcK4gg-3BDqmWrtmrJ9VQt2K0syrR9Y4KQKf3ag0Sk76wNmWYs3sFlpCNtXxb9N7xdkpaHiSwxpGIkFFW1kEggxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADANAgEAAgEAAgIMQQIBCzCCJocwggwpMAoWBlNIQTUxMhYAMIIMCgRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAdDQYoMPk66YZcO8SlfeCKBuKYCktQ2ac62zKRLtLhijw-PKkrgk73QlgH0PH2PVtY~Ek2Gj0cq-kSdE4eVdZmASCC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwDQIBAAIBAAICC0ECAQwwgg0pMAoWBlNIQTUxMhYAMIINCgRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAcDscfnEhfgppzF76400jBJLdsY8ngk5f3QMH1BXhgUY4ICEnq52FfBbjeHt3OUwZToWBp~Fv5c6VRiR~7~VT7ASCDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAMA0CAQACAQACAgxBAgELMIINKTAKFgZTSEE1MTIWADCCDQoEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQP0l8DLIxnwmC~zwa7A1DuSH21tHRidQyDC9JOUSHpeZXgj0q7at6nwjEqhQRN6cp4og~F2KX3o4RcqMR5V5ffcEggxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADANAgEAAgEAAgIMQQIBCzCCJocwggwpMAoWBlNIQTUxMhYAMIIMCgRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASCC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwDQIBAAIBAAICC0ECAQwwgg0pMAoWBlNIQTUxMhYAMIINCgRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASCDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMA0CAQACAQACAgxBAgELMIINKTAKFgZTSEE1MTIWADCCDQoEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEggxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADANAgEAAgEAAgIMQQIBCzAJAgH~AgH~AgH~MIHGBEACR8bNKqSRp5piUT8mXTQVh6M0oCgG0BaQrW97NV72xt1ROdEXck7YCS5A7Keg1FvN1n26OXU3JdQdfkhN~SFABEAAxUPhmtF~XgnccBUcK0Wfvug8UQ6np0WyNHZuE6h6fAVoVdTCgR10Np4TzDchWQUCKXsGf9S4aLkP858VZTWWBEBWPfH82ox147yPeH8zei2b3hBKHyPCtw3lf1~djjK7vuSbVCsVTxzfVu-k8jV~h-QUm~ThXJuDq0T~pWA1J-KnMIIFtjCCAhAwChYGU0hB" + + "NTEyFgAwggHOBEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCoCAQYCAUACAQICAQACAQACAQACAQACAQACAQACAQACAQACAQACAQACAQAwADACMAAwggHNMAoWBlNIQTUxMhYAMIIBjARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAnAgEFAgFAAgEDAgEAAgEAAgEAAgEAAgEAAgEAAgEAAgEAAgEAAgEAMAAwBDAAMAAwggHNMAoWBlNIQTUxMhYAMIIBjARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAnAgEFAgFAAgEDAgEAAgEAAgEAAgEAAgEAAgEAAgEAAgEAAgEAAgEAMAAwBDAAMAAwgiPMBIILQFR89fTkEsVm2iV0xGY85s8YpfJUTmLj5m0kIoY~coeJnhC2P1uIfvU9" + + "CkgOpTIaTpcK83oxTXF8BId5ZpXSP54XnyKKumKFPGZe3fywNEY0lAzrHzaFwbwcZNb8ekH4PIjfSwkZJCdap~VG~PtQ9T16yuTxL78G5bv7OrwEYZbhHnjXtgKuT1xcRwPgR5venjF8nCHDxV8mjPL9A-TPT8iRZTonSyji0jov-qHNvWXqDD2ZPwgg6MiOLiCwRuwXeuJRt4~6tLbEVbaf-CF6wazK10PaBIAiomJMh6xsv8RM~1AT04OQ5JYMXCLAk~xAcew-qTUwRqP2xgFHQBi5G1WrjMMa1DYNRgFoT5fBvBHAE9HnEqc7D7H20C2gz~Q-gAenuBJddrEo7ooDuuVU700OcPm24i2PDLTmJMH56LB76479vGIXBE872Qb4nFq2ScFEFWLLulOXUQnhCqLERquPAZwgf6r1cy5fk16X-9aBEoFHaCR8g7rrsbIVxkCe0m4Jv29xm2wJgpal4KFFw5VLcug4CI1GyGIjiswVddoBUgCTrGFTrRbbmPtPkdwJh0sO9l6aYWjnjEgV01AWPqpZXMtY8tFenhHkZsHZtNLFDfDVfJs6ab4rabf1Adcwu74t08czr3wlNAylu~YoakaPGaAzmgrZK8mkLEGV-Gbivi-MDxoI~8ofR0X-Yo2FPoV23T4AyVMOl49PHBpQCjAxwrmAinp5-eoBDhZBmbebcaCaJektCgQcEbVXb-ni9KKYdMTnHUBjROYONiOnoGjVvL10hhY1f36FdAGeFXUb7k2KtxHUZ92OP9nS0wew~jsj9m1pYrHwz2XkdoHx594UrGrq3YbZ0wSmPmZzjk43ToP~CRCbHPaZoumpcNqsANTVYRMcmPuHd7Ty3V2HyNqJ1aAu31iVuv7E6Arns~HuCP3hWSQgwP05XXa1kzoZRt5ir1xUt1XS-RxuyFAtMurHF07kWl9WPDk10TTnZY6QsmapbkrO-mP39AtVCW57SwzFkFaAfyFSSCkn-LYvL-xW392cXPGu" + + "QhKZa6UdTx7tiqw7skSXvqOkw-WtfTXEHu7cYbo7NfnSb8g03Bp7yAmvMsjGqt5zCuwphs3SiI926NE1WJ3EF8TcK0aztl7NUBWIpVrvOaJIn6lueeaiwFbGlXZtmn552UxtTjAgiOHWX44OAzBonrYE0DlQ4C7ADQ9mcM51-jyn06uMUwTm3ulGT6KLe2ENZbOSrCQ1DgCxfxermq-P9G9r05wYFkjk~IgQMVYulXbNsNzF~p39wcb~DvQ0eozI9GejFYvmZDGlmnrM1amo6KawP4XEdWq2oyhR5Tb08eM9TY66vR38PrcZve-EpNgdoOjgQWUc5M1gFAl-~ObxyTBS8IG2oSwhPCPfs8eHkIs69LlFbQe8XxzDNKcUCRMRMuSjrbR7-avKuz2r0c9~zHCW5YhsGj7ZAa22Ro7ygRVdwbdlO9TstGE3k3ashaFDlG3jwO~jv7jKT0b~7nyr-LUMkGML9r0h1IXpk7NlzhmUnq7bKOCxbCLthcPhX8UiJse9b0xuB2YK7zCKyJMcsbMIINUdCNKla2hO4UYYJMjnEigdknWRnSkFj9K-6ftghcVWFW--nHSmpxC22yYDckNzCQeQOkd0BCkuOYltBJRU289I9g7NDAwq4XQIxQRB9~mGNwxoOXX-RnQ~YpKRbUG7Xg-E26q7f7R2XvrbQ1ca7gMqXOLIEp~6KJinTusYupFZ~eEdLKJ83tgi9gRTBWXMROINHby1G7BEuXinfRjqU6FmJbAUAz1hKtOYgu0MboNpxd~jUX3D6ymZl79uBa3piO7tcPnuXKG1qwmAYBsZExXuYbKrLYjtQLO5CjRaQoehOB0bzdTkvgWaNqGZqbPRXv04QevXifbNTgYzrQ9egri94qrvhJaW0GlQKVGNuycmVIM-NO6HUdzEZMXanIFBCmHW97UwCXYqyzELW2fkAW548-BjiNvkEIzSaydFGHW3JeMzy4H2aXnfmx3iCstX33hW3SWC93UiQVOZ" + + "POAP4mY0p-jVIP~OA5lOxyuvC6UQBeVlfK660BkMg7-~ZQk0GL-rr5O7tPKVhrixP8lLho6Fb4CTZwja2RTUct0rFtTvL426edok7o7eLTf-GC56ZH4YH2p8n5Xx776Xu-oJsV9lZ5Zftd5uXkBwgZjJRIPTtO8i8Lmx-eQvmrhyy9OzghvzsQh5kj3tv7eVTExPDJM-fpc8b4aAMoqIVXhBrSxEdOXRBb5jzZDwLzLx1OPKVLBKSzKe9MG~eCr~svuljr0N8nz9lOxh~YBAoDTCmz8tTpMQ30bhiJtIy3D1tAGHiksWWMYJMwQwUuvD4jUPYM3~bSUJCP0o-U6A2iygHo49Fvh7~V~F7Jr9rkkgvOhNWjPulcATmjBHMdzuhoi~y-vdcv2mi829a9Co~Z2TiH50806d1Z9O~FSLRNqDEW9a7BnzjF-tuxdcK9Qmdq9cH0tQFyV-6KrlWvdRSRAcAJt9oc0lJo2L0bwGp6t0CH2ZUFiqWMwLC5QJPSMwOUlibdLLlGPDDzU2tAWjD5elLCarnkz~5-sR9-ZeOUXSaSzxLuWRM1BQCizFWFQlzlNcD7JTcesnZZXv4muLZ1nRsZACa-k0kNyIw1vuSqBdXoauaJfEOA~oOnslJQThRqM6GFq43a6QlLfdErCfiBK4JPX9D3XmdEAjv7MpBO-CtpA7UiGJRV4EmU-i6CZIU6zEK6rrHEx~m6iA5Z-t-x1nOqVV~SVF2cvTebQDjh3nAannlsEG-5BMsJDl8eS18knrrmnJBrA3mCHr7iWyThffvZwLwN5kU1LeiBV01pSUAz8k7RNTrBg6pGu~nEYm6k4Jk59XAnUOujdRUaqmbCTxIj9Qlic0KiD8gOz4utbLngC4bAzGfgt2YDwTiQICOejoegHHpbaP36hUAIfqwAHRmVlpdfoVgoN-~q2ZZCJW0C6tYIklbOdK2qL9TTWSstyZokFSB6jb0qusUcCo2fxD5c-96jvsJ6qFjngy" + + "7Y5W1pmVYpYGD-f3R5U6MmWiGKCoxPfSyaE22wnIcnqNMa6XU7TeGzXAl6ifshImFvaV8Jq8XbVJfkS07uIeuwvejaK4rzN5Id7vBVW8xCWwCQb85R7o5jek1ZTQSzuTj708Jz0hnnXglBakn1E7ifW6FiAdvkL5BlUuEPdnqKPQFKSK8-A0Pzkh21LuRnC4rJ8IZRELQi-tcw~sgLWELYjZD7nu-dcfZZiH8vnLDUAX~ZiHXWIZoJzIl2I76PTYAbjUK81wvhYuviNuzyOYU3nICMfsJy-ugc3vylKw8RQoqPH9vYTuIDVGPqcwN~YdhD1ikQjL-wmJmrv2vmJzekbzA8f15alC1vzB8ICWENME3tQJlZ02kX5vOxiZi1dfo86hn0l61NP350u4ouKg6d7k5kxVCpp1wKKDr05DVRGoYho7D1LsTHgLx0miKwB7JN6wo0y-AuJ2Hguk2BMHoGc0ayad0JPVWAvikOJuiYpB3MfqaJRQQTaA6kEHaVmYDgrWseYf-2Uo-oWh2Z1EPHg7DdzB3w9xClP~W7-w8Y8rs~l-MgISEloLcOx2XdxdpowrCR0LqgWD3EB6-V4mIPJRfPRGkX9oIQAOWk5kIFLcFxCAY7AspH32IWHbNNWt0HsQIfwB7BB9CxJC3sWTIJSZSl-Dc7MYcNsxStnyplmU-cQfFv0RU236ohbvtJPhJoHXS5WDYodRTrOjz-BAhSH~jEWtStpG8j-Zp355J-F0cL6HPrN~3mTVT5~THkWLL9mKR1tuSuG4gw8xkwSCDEDdOgxvFEZo5Zx4TUnmXMj70rHuZ4ronbc74TzqaAj2VaheUzVwbBe8tnoXWZU4Y3lpeKmqDxH2jWJbGtsH1ya6HWJ6R~4tTMjSVF2xWA4sGFN0~npOe85CDRixc2-UsdRbLmo4Wr-QSH9eWBhR9A-qrlZCrqH3xLIi~aUYLdGEFeeCSKlpZbjdS1oGno86rDO0aQCFRhjv6FehbSV4" + + "jFLgEeINjGYVURuooih1rFgIjFSYDH1wPSXR8HJRodnH4p71uBHh9urCCT6i6FiITm2UOHQDjT484ueUtTNogflUDKR6RWF0AEtZhHjr0CSLWXhqxskGKkK~I99O7jKDfPUe8OGHoNCTWJ4QUdBadBwXQNSpQbBZWlcklcla86oG58gTehs-a6PDZO9ha9jCnxDIv34QJFsytD3fD4Wbbomdl7dJ4mXDFdFQzi5GvHXXUw-tmFChgy8Ep37n2fnqpkT2wxbQAJvAWHFnQH-A4POKxIWLTqkWrDx-Y05dRdfZ-9qnS21XCC0b84Awn~~JICCJBiX0~9GpedrQAYoMCddMrITlryw4~Wx0kAle-7KnIPPDIVmoAgjPolVWCCRg8Ynbma1GbF9WZsaKWaBVTU4yLu5sxUYs9ncPgAJ4tCQGcaTAskOIa1jW4Kd3QefeYe2IAAvuVNvJt8MkOiQZtC~sX5gsIHkr7vodHJGiMIRxk9GPQwYdNXiaHPHS167RROX5f2nViu~ybC5Tm6MGRoRUgGfcPuAAuURVcbItp4hPnfrIrkT~cbW7gWWFc31bOV4Q-f7qvpPHbh~qEHwQmiRBN8XQmHUVU4xolSTLWUcrScxX67h3QAwn-Dw9C7Tz6LZkB6GkEeic9tQA24ZeOYUyz6inXErpD36nWPdLcPeKQVn8Y~xO~7bdzArHKGJMzGaOGJIx7kwTf0aCINQXAQh-SgXE8T5sp8HacDHqO16Wp4~m--smr189HPoJDnz7FeGcetgd2pH4XrBvG4ipsg9wsY-cqIzTvLdV802Cc3oXlx-BjHtaV~p-RqcdyJ5ozjH33cskEkG5gXC-6CtdM27HLqxe2MvZnBUUXZ-4KBY5zPP2BAnGKbzo40dxgAOztXZWCHdg8-HjMABdHYwsHY~vxKGgoKg-zvHwqRewTRtBKYc9OgG1ho0~k6v4-US23HGyBYjT5igD6HSarzaU~DjSxUkQ01RrqBCsMHeU" + + "EaJR99DXuiquBhPcEPiX5Ceodi8XN62XcPnvpsED~DxtzVJB-vhI7le5XZZdNMVdxy04xKR~M14KrUODuq2svqJ2~8j0lwG~iKqjtRbUhaGHyp-9gvG8jaOMtPa7yI7S4ZzZ6oxf8YiEP7fzK9~ZANin4HU24HiP0enuMmVb~XHhaTxKU6H-JPqV06KhF~VFV1s7WmPWAKyiGC59itNqDxMQgRDUAx0mwCjnzWuUbsS1CLCMjEipkoFU2nVVuVkgZXNwJwIc4s~F~ZH0CW-M-LOKLWZ7WBazg~o~h16b8mQ7I3Rl4j8HkkSYFdqal28FxhtoTZzjjpCnXNBR9fJvlOSuYx8~lYViPfJuwTS159EACUpp1qKSjLhkg1GI4s~Ky60huthZ9nWHrlni2qC3GVOOFYUsoqVTKaZAgG~8LvnmwiZ6Jf6qBDX8suhg7CglqP2m7vZZoUeSTNwqPr6rD6Oo1oysIzasbqoVS7AR~2kTidr7yyO1EVBrxDT2w~o1SbaAv73HZlrOGhyi7oA1ZsaFzD2h3vgFlldCUC6I4uaHfb9jzwF6aKLmQNtmADgnhIgEkH5MsR6FR4E5ukrJ-AAgGinugf1TpsomGa5ujmILKCJEtuOGN60RPzWuPfT4WmFnxuutLtsSwv2i~rawqCTp~SxJqHn09tnjia5KkY4MMWaNE~OwdvVeStG7a3CmRZYtDTn-fb6cb5xUaXzD1exgQZsw1IswxEJrWKy0WeBIIXLwpo~SRH1~aEXFaUqgStkmoaYcAA6AAZxvCcOHZqxf5eerpBIxa3r2AKCOa6UKti-ov964CYkyH2xusJT0kErF1-O5rbJ-KaOP0iU0P0QPp57BZjdx7k~rbIXbD7uNa6Cj1DG8nbiv5VK0zlvewduFdk-fa7omRrXjDDiYB~Y4NAmTphRSWWvIU8Am07uBpfUAy9KobAtRvwrIuJhed01Z~mDdNFDFU4NDhej4H02w2AXtBBd58LzIS0I7" + + "RnOmFqJLQHsep6kIbs5CjGmsXk6dta05WtXo8xeDkkM6bGl-YzuSfySUpm-XXefRNji2zx4jDxiYSz9B-Qh~HnhfEJBUqxmAnDexpZO9zYwtj9YLD2ezt3W8c9DYK24EBhwdFas4CGGqtXFPHeDcchWlTP7msT4Mwd0WL8z94UfNobDfqVufxE8ohW1g9OFp0RlXlGzMvg5WKyHIR7egktcUTBDQx09iHdeOgSK6FCVO5l9UyiCU5n9EfINykMowd54s4~NXkA~0TdVRkXLPHcM~9wf4DEUP2BkGkQbMU5M4Cq6mDhJEU02gwNNUU~zK59-pWBOKrmR77rH~MuYZ3yU1oZd8DXr-5V8atuCgDIMsaeURCEU~aWGcVLuz7DA7xKl4lcxd0nfMtfIdAZVoi5EOoPxHA76X8Jxw0SC-vyuU1SSZr0SR2wWGG16VRyG~0NSHltLKr-oqCmyWN2pEtcmUdTJMZu1T6KscUlPbh4x36dyTpeXKAfO3gTsD4rpagftycgmKdzez7k77MdsEO0~o~fG~eJrPoBvxHM~5ccQ~s5osjus34093Gj3Eh0Xlt~8GsPVEPWoSjN631QUjSysDvH0FGQePelmZogGe9OaHlonOq4s69F-nP5eL84o5ZyABdB9cWQK2SAYpcNRkZ3cgtpoVpn8mFs7XAxhv2Ira3cgOhxHSrEFzRCKp7oup1pMkEu4kfopPsHvWpTLiNi7CA69VhoNCz5DU61KeH~KtVT9oAZYG9zOcYHxRWJ2hAMbCCsjTgubfZzbWACPVrhyyJghcFt4WMJupN0eO-g23TOyqTfVxpiZDDlg8KWOSOBjniSxrQ0RhuPbeqbt2whi5yBDIpDLKanyshx6o36v~QpMbpzq55y9fiymbEpxiV0csruit55wA-X54XSgKxTjEvxIg5fvVltjj9~9A3qchuS84-BTKLTgwrl0qmqO5S911CcURbiX9pRusaD8g12v4gHIMbToF8nNVics3" + + "Nak6xjMVHyPP1NHzhv3XUdE0otS71x~vM3JV3uSmRoE9nz13z19yEQcJe-DPQJ0LiUOiooO~18QIc1O7SrIOg14ut8IHsFOIKSPNAwOImZpxvCgtetmoABzCIfJY72mru6GeKeeUMb4wxlKldwPZ7Rzb5IHRTGMWTigmyluBZuqVtgk27T-NXSVyhXYG1JBBynx6h0TZHfKQNv8j~VyrgEkxwVAtdaO6PsJ8NrsD0NwIwkHkT2vmTwBq8jdd66LFJHZX1889lF0x39~H8FuW3Mxf~VuJmlEpYrFZWX1H4g09cwRkDVqFM~~8H0xZ4zrBs9RQZ0bwPjPK-oKoFOdNQS4LbA7KDP6gQl~wGmK9O5ys4CMQkeicyzU4NZiLvdfzo~K0toVjytkyQG5IgvmS7SNPQm23syO6nlfdlDp0qOjQiqpgUvOEYrAygo1WPeQMyTmkhvc1ydJv3Y~MEs4R5H2B1k-l4tBUheJZe1Jq6GRuTWCfclUooGK2KyWd-j8xXrq4CgQG2KERi4BnWSqt7ImJPhVWckWQk1ayvj4XKowCRAZeDNI9n~Y5kyLTyVvelfYPtoPSreclgDZhFAINEsdCVbIky6wb5BI006-LmhpcGZwUcIz~rRweVFUjp~wRO8Z-wA7mFeBDlrOg240nEEV14kyBP~VXhLg7L9Fks7GDZbQ2u5KsD9ZR7z5s0xniVOEx11ojCpqN7u7oYiew2VUf3bjH8TjLddgaevFBENm-1~9gtNyIWMpCjBzCK2~T1RwFOcVZ--zdeIZ35c3cdzyELs257zUUyOZBRKvv-Kt2MvKUpDZvRX1vorS1ypiXrudpMuCF7TgKuY2WgC59CQihLuoq7I17A~aeQU0fxDAiwfa9N26HYUd-YvEHbKFd09xAqcNDPDlK0bznWdg6UD6Gk7Jf3PIzXlRr90D4kTl80YAh~Zkgq905b6Lqt4wNlTqRajoQZAMwBIIMQDRqWzWVSF4OXzjzsWijbNYa" + + "psETeoNAZip4LM1qSbNOApq9Hk-PcNe-yYDZW4rG~xbABmrsWkctxUmUa4cpMb1a4Dxdw4ZwJSVMoInMtFwfJAIOZA37HREqRnnE~P-qQxpWV2LroKndECEGtb8epNhC62T6pX5oqpMwYW1KUkn98W0qq4o0ZIAEtkph4Baq3MVEV8H5dMlgHnflCjWzaakZKycNAwoanuaqdYCEXIGC-jnWp1fHxzQFcxH3QJyMm6YCwYKJgbQrLglEOnetPl7~0PXT1dSEBNBwAfHg2qKBKl~Ljgt-t6ABWdtQm-mVFaPb3yNE2yXJy49Of4fJivUQZshuBBc2nNykscVvxSBKwbGuN5m7QcEtKmdNaVA9natYmPozEroV1xB4BLwFD1pODBrTdrn~GlsQ~bsP8WfVG~2ySM0Y2hDWgRWrJ-bnNIdSr2-LIKCDHTeNh472BXC3RWtairG95ZDeOPn9a7ntZfbioRH-RTMSiGYdYWsqjCDDmWqatQgmer3CVB8iZi8Oywi8xuSyeT-fITE10ONZwlHdWzJPp4lC6LK9HYxhYHT6woGQudJxWRkzmU7MI8qmvDLVvDUs6UWPQA2gt4oPoTn0d0lcFMuW-Yz1YBDextRxVrYmAtC81EpJq1r--qJaO8nHiRYwPSQScjra~hC0k8QS0ayfFiKMXHADM~03S2s550soSO9awvVSwJKfLSK5efVF2nCAFm2qlbTiLOHdu7qZLaId8~-TLn1qtbycnm1kXUFBmllmHF2FiaHyjRf4CxlR~A9Ebv4XGI70yqg5eGARVtSC~xL4Kn4UwX2ELxBn-W2VLUZp~LgAtpIZPZ36t0j0rwOz9sVJvgVBMGvrjFPh7MVtaYZbezszNlsQ5z0an36IMBgdGLHhO7BkBj9PkMkE4UndaFJaOVfZRLiPFGY7ldHJ1MD6n8O8OCBxZGzzf0ew0wtSw2NxbtkY0X3d0HMsHmYpoM-FMi-icWMvTzvEYmdona4Jy5XV19Mb" + + "eqT95~tgOF6gPO9u8PNkCNoMOInMySv2IJvIulNYLnhuq4e56KndMOyCJrdq7ttOPahY5sMHaG~c31BtBuf1XyjTqu5mgDtNLmBMOlmztit8nA4E3GwG0XfPeSSIJImNwGG2HCjoL~VGBVaXYUwUw~IAje7RkuRqpJW99Zop2ZPbhHqX3CbSvOIUJUGPDt9YjaLYN5dx8jDpyqxJLqWlug9Eo2T5oqK772USfryJAs7xYhEa0u8JNkAurvr-djYRak486PxbMMGnmXVTd3m7aAT9g5IZF6onz72s5f9eM72YgQUFoDA-G0MDcP89v0HoSqQg4YVYaHzrKPJkoYZnBj2eVctmBW1By2opIDihvIYXYIlSH3l0zDACYLeeG7AkJ3Volw5yJDZdg88JztDhjykO8p-QAeMi0beeH-tRPPPR12CHnof~DQqQN8VftXP6FigrZEjzbvlrIKtn3SNQ3rNyShhdHVEFnwx9nv6ioUoXzcieUX1zgC6GvZFUbIXzN8zq-JAWf7Zqs3Yu3xzZpbTNd-vh3q659XJ02-spbJBdPzQ4oKS7A7~iIjmSLEgHeomLUEwwAbCUlp-~1iz-jUFC7jVmGDnoMROKsN0XG~aP-XcanLYx7Cggc3sB1RLN6HKr8Cznj7DL5YxyryHuK-O~5YkgBvsb2mzIxCHUV3HiyISqblyAzdaBzhGPajbLipDus4cV0rSQ2sjLDWRJS0FSj6SeRiYPpZeonZbPC1lr-lGxzORTbetUKMLz~vIGytLuq9hDXaoNdDV8Kd27wyla4ZMg6sIgI-LlHgy4la2o8dvOjE42lZr9weEVdCQasYjsAHZK2BJsrYDJCi3M0gXaW5YtvqQEFoFRm9OxGlk1cpRbCxilPWelV75IS1LaXeRcTj~OlIv2xNj49qkdRHyHzDAlL3HZUxEw3o12QK48NMbbOIuPnzqRVy92OYUaV8iqtVSy4c-WFjc0nJOHox9D2vSeK7eGtLwvyhHG" + + "YgUvC0ev17b-aMpRfkbOh2NY4lmJLAjdDNjc6dLVithNCSpfg6EdOWYshtHivN~ONthV3Q8RCPOJUnSwfkphc1wcaEZ0pZnvk-FpUID82EyHQ-01jQFh5VJf1-LQ0OZORw2XNuwAIjYq6a~eXqhBGLvYTrCVdQIiIzbAlSMxcbyhPI95s9NHUG~9BPYEYpGl6LDtlomlVcmu3dI0YJs2uHbJqzzmyF8c22bk~sof0j-aPN2xodYWCqDtnQKJtjEtD5xUiWiw5qFV2EXMELP7dYbt2xTYAlbflScb4JJZVs-qcbeVavT~g2R-LK4-MLImrTt4Q5ZeRTafyl9MpycRLb6X-bPppAEaE~YNriRGR1qHqS~wJcdQKvf4jvKqQ2YV5yYEkvBQrP13M9uPg69sKKvjfqxf-XcXQV4-9PmRwU~VR-2BwMUczI73aY7Uoqa5SoxOwyuJi1NS4TG-SGlwZg3i9CUNDta~qdUo2JqVmgA7CYFkaeMjyfmxCJ2aZHxNvff6KAT633nWJG94mCnYliNn9LtGkae6CdgHWcR-QXfdVQCbNbVq2rnGtfBFzfZg~tKKlc6c4H3v-qFd9rPB4Jk5T5Vs9jyXpJViZnVlfD98QLrNYAsuq6ZUXXttOWy-NQGzBovo4CQP8APm8oEUezmoV-X6I8xIY6Qdv4Xv8TahnU78hmPldXrJacTNm09AOC-R~~U6bF8GEkW345l07RvaLNC4-28Xae3fBeaZfeAqSmLytXiTEYBjdZOL8HMqSRW2lwjqZEZJTq9IZ50N-gWHJS6DO2dwaZGr0M7Fmbgbx1h~1i2Atzf~zYT0lUv1dSCl6lA2PAzVU-Uad5gzkaHHReCMih8lChLSdL3PTy7lchCzAk~d0mDU6ZgzEYWQ8P6PFHOwNcho7AsrqUGBqK~xlk9Y9w38tNJi2YbAAImyR3Vu25YxbuIyKvMJZSrrNJbSw6yvx~gHaodahbZlu5f3l4LyY5Kopkz~AoW4" + + "OjE5RMH0djQyeHPzFiC4pGW3FrotevATAMR48wHA~hrhW4yz7jyh1rptByCWlgrggXuUBNp~VZwUpaYB07Ww2uWQfNAS7j6-9KY~S2ZgiJbApgzfFuZJsybtTEqV-~HpiNSE-lIR74S9xSf0gmEn7d99zXu~SS6j3hf2EeVvMlHuu4TnKX8zaW~vgbz6cg7Znh-fblOyJ8mzgy91DeIJGYbpVqfxAwDI6yBfeOxPGx33t4a7VS8c3OYboyV5EJkSxPH1klCR5RW~FI5UuraVid-t3ndSjoaK-Nw3toTRg2EUdQ25PNxvYEgtFQNABN1FtMnr7Hv7~vRtolAYwB5uDXTfyaASik3Df3KIUNvxEKDkyCzehXu6WB6GpQQPpKoC~PBMCEzWPrIwCUafGt6cXcwY~l925TmG6RbDvx2F9x-JZfh2mVtGnf0JnRjzve9op7x04dplbOVH7jh4qrX9SqxrL1xEY36KRPIZJ9IaPKubANN1uXq8L6W6MifXTB6nBK31ehLxBNiRixde3kw8aCSiMvdWIQOw5LxXsGyqbrq3G7ZIfmvsWyV-l1IxkYbc1p7j~5Bb9O6kdXkwxc4WRp0erBUcVmirFbC3FImqFxJKCGFXeWmSCvf2KBZyrmvRMJWllYAvFPjnTVSYhfKiUMF~oQfFot4JK~ADC~FmbDZfXFOTY3amHK0Huw-jIAav3CZq6ul5RLXoq8RmG7uBeF6WbBkCsSw4hMsLdAqP99ep1nlOp3ZEjEr0BQM7a3RDYnnNsrlykdZEMA7~s3fZKOpNtyJwZylkw5v6w-XvjfJKfU5Ukx6vF8ky2kvUEKHNIL1S2JzOO2zGSr5Oj03osjtrOoHrP9yQjeIfN1GANc7UPhF6c-N12OMLw47Lt797uWhmhVa0xF3Y1FxNdL0NWiS0g8klpaVKVq2nuND4SJKcdKZIjG40hIUoTMlcaYG1CImmWf0ZbpChPeH10yqcnlEMZC5KfSFywEYm4OEM" + + "FGySGjNjzqPJzYBwMVYqyweItlDhd4HlkZRqXP~gXJCVVvz4MfXCb5Ntp2cTE7bHA2IqtC7D9sUwQnqUzuOZiUJvSj~lXKnhNuidNUMvjF2VBqQhruqzmrINbTvHRVVK4UtdhqKFVIav9BdzSStetcuI2XGDn~swgibwMIIMTDAKFgZTSEE1MTIWADCCDBwEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQBVBd2deuRDLYqfWaMMB6CXbU6C64ALnMwWrD51xlgtGXv5yflvvZ-V~0QSv5VpVmqJAscITcsUBHaRsc4MwNCoEQInbBJ1WuN9gnpTKwhLqwWD3ZjCORZQo-~hfe9ShlEb7WZQfi-f5K7zZUg05SrsyNeltE3a0BuDDrpqmIT-mH-0EggtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAMB4CAQACAQACAQACAQACAgUCAgEtAgEGAgEMAgMBcc4wgg1MMAoWBlNIQTUxMhYAMIINHARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAGs6oEJRXMLzTZljjcMRIm31c66tsKDCP4sgmjfYSUf3ZYqveFlLKzf3SejwkuombYqb0Tjn~F2suvNjHWJ32HARA8SUFdvrLpWsMe4DSu~a0m~Vhyi~4-wJfsZssZkRIGmZgnzdPvesl0xvdEgYjs-gARNthOLUxv~74GVg2JbXWcgSCDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAMB4CAQACAQACAQACAQACAgYDAgExAgEFAgELAgMAucQwgg1MMAoWBlNIQTUxMhYAMIINHARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAARAB14UMXvdeAqAsml1KEWuLndXZLmwMxTbA9xySPvg7FOIA9FTdMSa63kQsjx8c7CgTSka8aDN51gFCYLOY7il0wRAuSEnIZkGVtHiMLxZuAfra6jXWgh4OKgJsfCj02hEE7JLLxtJUpSGSGgPR2pI4G3oLzKdXfhYklYSfc017WErbASCDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAMB4CAQACAQACAQACAQACAgXDAgExAgEFAgELAgMAwD8wLQIBBDAMAgEGAgEGAgEFAgEFMAwCAQwCAQsCAQsCAQswDAIBAgIBAgIBAwIBAzAKFgZTSEE1MTIWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + TestIdentity ntru1087 = new TestIdentity(ntru1087PublicKeyPair, ntru1087PrivateKeyPair, new NTRUEncrypt1087_GMSS512()); + identities.add(ntru1087); + + return identities; + } + + /** Contains a CryptoImplementation and a set of keys */ + public static class TestIdentity { + public String base64PublicKeyPair; + public String base64PrivateKeyPair; + public CryptoImplementation cryptoImpl; + public PublicKeyPair publicKeys; + public PrivateKeyPair privateKeys; + public KeyPair encryptionKeys; + public KeyPair signingKeys; + public EmailIdentity identity; + + private TestIdentity(String base64PublicKeyPair, String base64PrivateKeyPair, CryptoImplementation cryptoImpl) throws GeneralSecurityException { + this.base64PublicKeyPair = base64PublicKeyPair; + this.base64PrivateKeyPair = base64PrivateKeyPair; + this.cryptoImpl = cryptoImpl; + + publicKeys = cryptoImpl.createPublicKeyPair(base64PublicKeyPair); + privateKeys = cryptoImpl.createPrivateKeyPair(base64PrivateKeyPair); + signingKeys = new KeyPair(publicKeys.signingKey, privateKeys.signingKey); + encryptionKeys = new KeyPair(publicKeys.encryptionKey, privateKeys.encryptionKey); + + identity = new EmailIdentity(base64PublicKeyPair + base64PrivateKeyPair); + } + } } \ No newline at end of file diff --git a/test/i2p/bote/crypto/CryptoImplementationTest.java b/test/i2p/bote/crypto/CryptoImplementationTest.java index 60057b2c..1123ebbd 100644 --- a/test/i2p/bote/crypto/CryptoImplementationTest.java +++ b/test/i2p/bote/crypto/CryptoImplementationTest.java @@ -23,7 +23,10 @@ package i2p.bote.crypto; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import i2p.bote.TestUtil; +import i2p.bote.TestUtil.TestIdentity; +import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.util.ArrayList; @@ -37,44 +40,11 @@ import org.junit.Test; /** Tests all CryptoImplementations */ public class CryptoImplementationTest { - private List testConfigurationSet; private List testMessages; + private List testIdentities; @Before public void setUp() throws Exception { - testConfigurationSet = new ArrayList(); - - TestConfiguration elGamal2048TestData = new TestConfiguration(); - elGamal2048TestData.cryptoImpl = new ElGamal2048_DSA1024(); - elGamal2048TestData.base64PublicKeyPair = "-GygBJmy3XXPaDCD6uG0a7c23udye7H9jVFQ2WCeCnmls353ewLyITt7D3oneFYBsM1dHm~ciORrLtgZUCRqeJwIJIjzzKMVL93FSuMD8PQB9IX~F2l-Jn~5oBJCJWK~rnkNX7yBl-uUrylzPidfZ-NpW0U6wJREOQTx4oGvcGNv2oDkHBL44Oqencuw9NXxHJ9SjapuSgo2vg8YN6BP67oHR5-SlaIN6bHaF9T5tjJMkf32frT-qmWTcyB~0OgXXL3Z9cTERqVihYIBmk4EaTPa5oB~sOUIhUv5DqedBD~BDY5P4d7TroNWoW4FOhnfGqtTD-cS-qEn0ww3tHn7JEppbWGcgKrbdb4F4Qt8VYBd-ogATOXFbbo-PG~PgmUOa2QWGIi4RSXK1L3NoYO4ha7SkQMJpKj8ySi-ixk3ivofk6lRgoZ4WhbReaB352pF1iMXqp4p7-mnLMPZUX41ibHPeWrq7TyNqb-ouyn9ZfqORlko3bi04eXkfzkeDVuf"; - elGamal2048TestData.base64PrivateKeyPair = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADClvCJzneZ4R4yUJbm6zOP3wOh~CTfnWx3MSR7QDZS22-njK3KHuZHBbCK7HbyQLr"; - testConfigurationSet.add(elGamal2048TestData); - - TestConfiguration ecdh256TestData = new TestConfiguration(); - ecdh256TestData.cryptoImpl = new ECDH256_ECDSA256(); - ecdh256TestData.base64PublicKeyPair = "xE1fQK3nPfcmABNpYrHEDVHLj1sq01mmtrDrrIKAZcMnK9vmbiBZ4ygsksNpe5rV-TILTQUUTIUry5qt8q5ybB"; - ecdh256TestData.base64PrivateKeyPair = "M3yBbveBPFwfd59UY06RtJnfZtHU8yC7RZMYCITBwdRGkyPsftKS7M2~OSMsmHWUfejRolqztJ4lf4keS~KSge"; - testConfigurationSet.add(ecdh256TestData); - - TestConfiguration ecdh521TestData = new TestConfiguration(); - ecdh521TestData.cryptoImpl = new ECDH521_ECDSA521(); - ecdh521TestData.base64PublicKeyPair = "m-5~1dZ0MrGdyAWu-C2ecNAB5LCCsHQpeSfjn-r~mqMfNvroR98~BRmReUDmb0la-r-pBHLMtflrJE7aTrGwDTBm5~AJFEm-9SJPZnyGs-ed5pOj4Db65yJml1y1n77qr1~mM4GITl6KuIoxg8YwvPrCIlXe2hiiDCoC-uY9-np9UY"; - ecdh521TestData.base64PrivateKeyPair = "YujtjOOwCqXPH9PIbcZeFRkegbOxw5G6I7M4-TZBFbxYDtaew6HX9hnQEGWHkaapq2kTTB3Hmv0Uyo64jvcfMmSRcPng3J1Ho5mHgnzsH0qxQemnBcw7Lfc9fU8xRz858uyiQ8J8XH3T8S7k2~8L7awSgaT7uHQgpV~Rs0p1ofJ70g"; - testConfigurationSet.add(ecdh521TestData); - - TestConfiguration ntru1087TestData = new TestConfiguration(); - ntru1087TestData.cryptoImpl = new NTRUEncrypt1087_NTRUSign349(); - ntru1087TestData.base64PublicKeyPair = "573-aZRkI9gAGisC9X7Kh0UfYhb0OoS4atMN27aCcJfOnUehQSnFonPPKcPXFGck0EtRnUPOwjABU0kiQdcnfq05LT7XaLv2bNlUAlpD4RM3dSoepmx5~lytrn4KkhEjTm7G45fHBbZzS-wfQHB6UDc4ABZYPz-g3aMQ9etuS8mMfgH~UprGYxwHd8Lfx46LJkU-YQ7OVE~aqSJ3UaVTwkjgPKH5EbGuVHM72zZxNnEw3mvCFGNix-jewg4esp5lEbuhWxmcnPc8FNiXSYGnGnfERJphuteYHUF2KzbN21kmbGm19L4SbKYiMZaABWxWgA~R-zRpjIAlrr4yVditnBfBVSVyK-M6AUeXFImqT6hshQwYf0nLa5EW1A8PmTW8ZjuCIsFQQVjF9I5ppRD1gq3jEv0JEgaF8F7TERmA~1mc5ylMm6hUXYcwB3vtdXKvwBBD8MG4JlNb2lbiQP6UxOau8NfslCT~YkAPrZZB-cEk0r2jsh6wi6eTbsa0mjS1i0ssO~8ThkrGrbBXP2BNoRoHbKb7RBHsfWpjn5qum7Co4i7Sp2wyXdO3JI3z2CEBgbC-wpLPpkWEG1ODv1LJr~NXcl0zrlqhxExgoJ~NuZ-GMZn3XNPQcEiHHddtdA4PADfZbfqzZeJKtIjZl~UAiGBcnPKglmKRrPF3zRLAiMwlWkHcAuV2NWjkj~WyGUFMMw3oUV9JR6Y84ZiaNkVPkfQtislNt7~fGmYz7LzwYfvhZC1zm3VNLs12~Xp0LUmFu~BsIgX-WPW8mQgWEiuiHj9sFg6KUbRfpzDAGp-mU4XrD4OoNg75CVgMna2W8~6VDh1bB7Xjg5QgyyNHbUUrB-uaGe8LTwhc7XPBh8PgvKGb1~v7NYIwUX10kt3Ohhtmk7tyteJcOEP-8X6htCKlayfTgL8uEkr5685okK1JSfeI1uDGRBpL0zkuBkNTz6ukHJj613dg5zrVjK0mME7y7hV29gWEPedqU01mHB6wOGssn4Z2~X3y8cr3FF2xfTu~W-hjnC50LUokleJPKOTcleHwuylMoVk9gPRE9BV5cbpK-1tiBWC0yeZgJVAEkJF-lEeGyu0U4YgIpyH1JvSJRWXqJRdLOI4FSy4r0fJwmbFuf-uItc3dzwIRE5dMc4AeDSZ31OYq5TLov6vYoDpyu0tWgfrXAMLwY7jwjPunTOxYjwqKicF2j0ha3FxsRq17KD~TZ3ouu8HQkUuuOwCRnUtzKc-Y8IEavRWiFxM3K8Cg2wBoXoXttuaZwvRNuUW24trbFFiH2jBUnPsb0kRril65tQaHWBEZCMpEmQ8mDEENAQwzoyvP~sl0pKS4cyAb-v2jFcWRLRXozx2fR8KIa3IQT9pMGgKbiOjh6pWtz3vL2QWIoWMs-XFQaUP5Dzr1LbVUx1SFX3iJN3wldffnLTRZS9OmL9IoBPOCiQEw-C5KWbkrew~4lOXp4Pz065-6~s0PbTkbzqchKl1Z9W0cZjSesotkyPFlUq5QXWbjSZ-3Y9VxdmFgXALJaR9MmmFoeSiwxURAtihMHVvJ2LC0nARPvLMJoYhiHkzZRFg62bnRR2XsI4RMWn~fTwgbPCBy-N9ia9~RmkgWnPMpo8pRcKcYsslEgdsnt8uLy3Rn1oZRU2uKzi5WFTa-wBO83tl-ZD17NN~HbdiWeO4KphSGYYXscBRsDB1ebbp~ykgHzKK9W7rpbqTYe0oRVPD24LqOwk4UJtFZ7vbwb6O58FUvdAu4ZHTo79eNPAuP44RCcvc7lEeaxSUe5pUxIp6WH1ywdR36neh2FCUSnz9YCJetw8-~EkOWGWB9ESixov-m0husxDrti~~u0pnhgw5~NNQ-w37CKd9NUOsHzpMGYzzx8ed8tyzmvsTzUnzXr6CNsc49sibTzroUHkcJWOkqmwpq-48uYELgqsxUiceTnkj0NdpR96XjbpkHJVMxfo7giOMJKxmESQjObk4ItukpR06wQyQKRbKKB0N2BofVoPUZV3IP4srT05yuQ6j-1ZKrmErcf2fB2-dvNb5jz9r9VNwBQtY57cuJ3uQCVjIu-5~E92uKhqHAfyVlHlSGmecyWJ66TRQgyT2W-uXhfDDsg8yQPJia~FTNtAQvtrrUVEBQiB5fAg9gej1I3nvLjX1O~SGpg-K5rWNWDFzSyjd83fFY96rLpBRKW0P~OHk2lByk9imEvbNEirSNCvFkrn2G8KT7u~ScdF7piBr8Z~ZudbknH-tAwxxuFZN9lZRDoQiJER1dhpYc-f2NFCJXg-agduSxna0drNI7ALTKyRB8ovlqP1BYf49Og01e2810IxvqqYPIX-Fkl2JZHIgw1iwM2GB2SfpjtTvnczeGN-bxDGX39PMb91rVi8H-FB6WDEpY5CjOpGRYzjhtau6lYQXQRwXW9w1LysOxx2HTvAgVewyfq6Qiyx5a2qzVWdxTuchdDGZd2Wep9syNc8fyjMj7pUvix1F76cllr-FHMApdrA~uSNKlC6jJjR-haC5DCr17Gw"; - ntru1087TestData.base64PrivateKeyPair = "ATd65ryBzz46hRpJRx8Noyr5F201qsW11dHbp8yx7a0x5BCtGRG9Vd-nFbDBuOqUlzV0mTUEhUNPNhl8umBLvyDfscg~ezIIfABWiDWt2V8PUvf5Lr~1~M3rx6mwbCYZEoqrGuVpkD4scAj9qFl6yydgIBz2HR9-yCxZ~6lnBbjoOjUcvg-nngFFU7Y0rKzJYPOZoZszzhxIdEcta99Ai3z5~iFjTfLUivdodp1PXU3~jOp6VHfjKjPuugkUytUoWdHMNdQp0B~YTrWYIx8WkBv8425xbTicAdMJEY6E~HDol5fQ2E4qaCjCoO2o33ffYut4oBm5r1D3Q07brn6tzFxzeJOcJ2cKcGSgTAxa8mv6fWFuwsCfq7GGVYM5MABd8N05v9TumlxIoco-HkzzUXJI928H93dpsVPBqdy26lqQSwPEqtPNdPeS~ZpWfGak9U0XUD95tJ5ggNa4pUegz8a8ZX4BxPu9f62Clf~C95tni3q73P~CQDdhHXhBdQFWbe4Tvy4BKJ48FtxJKJ56FKalxvAcKF61qr7oMxoXE4PKGl9vuurQ5T5cAZD1ViZ74oO9oc~19N9088Glmql26Ox0mHzkU94Pt1rrrpOpCLg5ZOvlL~WaL7xXaXgeL731qrRokRdswg-VGans8Ya6~kN2BofVoPUZV3IP4srT05yuQ6j-1ZKrmErcf2fB2-dvNb5jz9r9VNwBQtY57cuJ3uQCVjIu-5~E92uKhqHAfyVlHlSGmecyWJ66TRQgyT2W-uXhfDDsg8yQPJia~FTNtAQvtrrUVEBQiB5fAg9gej1I3nvLjX1O~SGpg-K5rWNWDFzSyjd83fFY96rLpBRKW0P~OHk2lByk9imEvbNEirSNCvFkrn2G8KT7u~ScdF7piBr8Z~ZudbknH-tAwxxuFZN9lZRDoQiJER1dhpYc-f2NFCJXg-agduSxna0drNI7ALTKyRB8ovlqP1BYf49Og01e2810IxvqqYPIX-Fkl2JZHIgw1iwM2GB2SfpjtTvnczeGN-bxDGX39PMb91rVi8H-FB6WDEpY5CjOpGRYzjhtau6lYQXQRwXW9w1LysOxx2HTvAgVewyfq6Qiyx5a2qzVWdxTuchdDGZd2Wep9syNc8fyjMj7pUvix1F76cllr-FHMApdrA~uSNKlC6jJjR-haC5DCr17Gw"; - testConfigurationSet.add(ntru1087TestData); - - for (TestConfiguration testData: testConfigurationSet) { - testData.publicKeys = testData.cryptoImpl.createPublicKeyPair(testData.base64PublicKeyPair); - testData.privateKeys = testData.cryptoImpl.createPrivateKeyPair(testData.base64PrivateKeyPair); - testData.signingKeys = new KeyPair(testData.publicKeys.signingKey, testData.privateKeys.signingKey); - testData.encryptionKeys = new KeyPair(testData.publicKeys.encryptionKey, testData.privateKeys.encryptionKey); - } - testMessages = new ArrayList(); testMessages.add("Test test 1234567890 %&$%/&§,--.:_ abcdef äöüß".getBytes()); Random rng = new Random(0); @@ -84,11 +54,13 @@ public class CryptoImplementationTest { rng.nextBytes(message); testMessages.add(message); } + + testIdentities = TestUtil.createTestIdentities(); } @Test public void testToByteArray() throws GeneralSecurityException { - for (TestConfiguration testData: testConfigurationSet) { + for (TestIdentity testData: testIdentities) { CryptoImplementation cryptoImpl = testData.cryptoImpl; // test public key pair @@ -110,7 +82,7 @@ public class CryptoImplementationTest { @Test public void encodeDecodeBase64() throws GeneralSecurityException { - for (TestConfiguration testData: testConfigurationSet) { + for (TestIdentity testData: testIdentities) { CryptoImplementation cryptoImpl = testData.cryptoImpl; // test a public key pair @@ -129,7 +101,7 @@ public class CryptoImplementationTest { @Test public void encryptAndDecrypt() throws GeneralSecurityException, InvalidCipherTextException { - for (TestConfiguration testData: testConfigurationSet) + for (TestIdentity testData: testIdentities) for (byte[] original: testMessages) { CryptoImplementation cryptoImpl = testData.cryptoImpl; KeyPair encryptionKeys = testData.encryptionKeys; @@ -140,24 +112,20 @@ public class CryptoImplementationTest { } @Test - public void signAndVerify() throws GeneralSecurityException { - for (TestConfiguration testData: testConfigurationSet) + public void signAndVerify() throws GeneralSecurityException, IOException { + for (TestIdentity testIdentity: testIdentities) { + KeyUpdateHandler keyUpdateHandler; + if (testIdentity.cryptoImpl instanceof NTRUEncrypt1087_GMSS512) + keyUpdateHandler = TestUtil.createVerifyingKeyUpdateHandler(testMessages.size()); // verify that KeyUpdateHandler is called once for each signed message + else + keyUpdateHandler = TestUtil.createDummyKeyUpdateHandler(); + for (byte[] message: testMessages) { - CryptoImplementation cryptoImpl = testData.cryptoImpl; - KeyPair signingKeys = testData.signingKeys; - byte[] signature = cryptoImpl.sign(message, signingKeys.getPublic(), signingKeys.getPrivate()); + CryptoImplementation cryptoImpl = testIdentity.cryptoImpl; + KeyPair signingKeys = testIdentity.signingKeys; + byte[] signature = cryptoImpl.sign(message, signingKeys.getPrivate(), keyUpdateHandler); assertTrue("Invalid signature for crypto implementation <" + cryptoImpl.getName() + ">", cryptoImpl.verify(message, signature, signingKeys.getPublic())); } - } - - /** Contains a CryptoImplementation and a set of keys */ - private class TestConfiguration { - CryptoImplementation cryptoImpl; - String base64PublicKeyPair; - String base64PrivateKeyPair; - PublicKeyPair publicKeys; - PrivateKeyPair privateKeys; - KeyPair encryptionKeys; - KeyPair signingKeys; + } } } \ No newline at end of file diff --git a/test/i2p/bote/email/EmailTest.java b/test/i2p/bote/email/EmailTest.java index 1c476af9..e40b02eb 100644 --- a/test/i2p/bote/email/EmailTest.java +++ b/test/i2p/bote/email/EmailTest.java @@ -24,13 +24,14 @@ package i2p.bote.email; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import i2p.bote.TestUtil; +import i2p.bote.crypto.KeyUpdateHandler; import i2p.bote.packet.I2PBotePacket; import i2p.bote.packet.dht.UnencryptedEmailPacket; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.security.GeneralSecurityException; import java.util.Collection; import java.util.HashMap; @@ -116,40 +117,26 @@ public class EmailTest { continue; // sign and verify signature - sign(email, identity); + email.sign(identity, TestUtil.createDummyKeyUpdateHandler()); assertTrue(email.isSignatureValid()); // write the email to a byte array, make a new email from the byte array, and verify the signature ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - for (UnencryptedEmailPacket packet: email.createEmailPackets(identity, null, I2PBotePacket.MAX_DATAGRAM_SIZE)) + KeyUpdateHandler keyUpdateHandler = TestUtil.createDummyKeyUpdateHandler(); + for (UnencryptedEmailPacket packet: email.createEmailPackets(identity, keyUpdateHandler, null, I2PBotePacket.MAX_DATAGRAM_SIZE)) outputStream.write(packet.getContent()); email = new Email(outputStream.toByteArray()); assertTrue(email.isSignatureValid()); } } - /** - * Calls the private method {link Email.sign(EmailIdentity)}. - * @param email - * @param identity - * @throws NoSuchMethodException - * @throws SecurityException - * @throws InvocationTargetException - * @throws IllegalAccessException - * @throws IllegalArgumentException - */ - private void sign(Email email, EmailIdentity identity) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { - Method signMethod = Email.class.getDeclaredMethod("sign", EmailIdentity.class); - signMethod.setAccessible(true); - signMethod.invoke(email, identity); - } - @Test public void testCreateEmailPackets() throws MessagingException, IOException, GeneralSecurityException, SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException { // convert an email to a byte array, convert back, and compare with the original email for (Email email: emails) { EmailIdentity identity = identities.get(email); - Collection packets = email.createEmailPackets(identity, null, I2PBotePacket.MAX_DATAGRAM_SIZE); + KeyUpdateHandler keyUpdateHandler = TestUtil.createDummyKeyUpdateHandler(); + Collection packets = email.createEmailPackets(identity, keyUpdateHandler, null, I2PBotePacket.MAX_DATAGRAM_SIZE); assertTrue("Expected more email packets. #packets = " + packets.size(), packets.size() > email.getText().length()/I2PBotePacket.MAX_DATAGRAM_SIZE/2); // the emails are somewhat compressible, but definitely not by 50% ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -169,10 +156,11 @@ public class EmailTest { Email newEmail; Collection packets; ByteArrayOutputStream outputStream; + KeyUpdateHandler keyUpdateHandler = TestUtil.createDummyKeyUpdateHandler(); // verify that all BCC addresses are removed when sending to a TO: address EmailIdentity identity2 = identities.get(emails[2]); - packets = emails[2].createEmailPackets(identity2, null, I2PBotePacket.MAX_DATAGRAM_SIZE); + packets = emails[2].createEmailPackets(identity2, keyUpdateHandler, null, I2PBotePacket.MAX_DATAGRAM_SIZE); outputStream = new ByteArrayOutputStream(); for (UnencryptedEmailPacket packet: packets) outputStream.write(packet.getContent()); @@ -181,7 +169,7 @@ public class EmailTest { assertEquals(3, newEmail.getAllRecipients().length); // verify that the recipient is not removed if it is a BCC: addresses - packets = emails[2].createEmailPackets(bccIdentity, bccEmailDestination, I2PBotePacket.MAX_DATAGRAM_SIZE); // use the plain email dest because that is what the Email class compares against + packets = emails[2].createEmailPackets(bccIdentity, keyUpdateHandler, bccEmailDestination, I2PBotePacket.MAX_DATAGRAM_SIZE); // use the plain email dest because that is what the Email class compares against outputStream = new ByteArrayOutputStream(); for (UnencryptedEmailPacket packet: packets) outputStream.write(packet.getContent()); @@ -199,7 +187,8 @@ public class EmailTest { Email newEmail = new Email(true); newEmail.setText(stringBuilder.toString()); - Collection packets = newEmail.createEmailPackets(bccIdentity, null, I2PBotePacket.MAX_DATAGRAM_SIZE); + KeyUpdateHandler keyUpdateHandler = TestUtil.createDummyKeyUpdateHandler(); + Collection packets = newEmail.createEmailPackets(bccIdentity, keyUpdateHandler, null, I2PBotePacket.MAX_DATAGRAM_SIZE); assertEquals("The email was not compressed into one email packet.", 1, packets.size()); } diff --git a/test/i2p/bote/email/IdentitiesTest.java b/test/i2p/bote/email/IdentitiesTest.java new file mode 100644 index 00000000..0881b402 --- /dev/null +++ b/test/i2p/bote/email/IdentitiesTest.java @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2009 HungryHobo@mail.i2p + * + * The GPG fingerprint for HungryHobo@mail.i2p is: + * 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12 + * + * This file is part of I2P-Bote. + * I2P-Bote 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 3 of the License, or + * (at your option) any later version. + * + * I2P-Bote 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 I2P-Bote. If not, see . + */ + +package i2p.bote.email; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import i2p.bote.TestUtil; +import i2p.bote.TestUtil.TestIdentity; +import i2p.bote.crypto.CryptoImplementation; +import i2p.bote.crypto.NTRUEncrypt1087_GMSS512; +import i2p.bote.fileencryption.PasswordHolder; + +import java.io.File; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Arrays; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class IdentitiesTest { + private File testDir; + private File identitiesFile; + private PasswordHolder passwordHolder; + private Identities identities; + + @Before + public void setUp() throws Exception { + File tmpDir = new File(System.getProperty("java.io.tmpdir")); + testDir = new File(tmpDir, "IdentitiesTest-" + System.currentTimeMillis()); + identitiesFile = new File(testDir, "identities"); + assertTrue("Can't create directory: " + testDir.getAbsolutePath(), testDir.mkdir()); + passwordHolder = TestUtil.createPasswordCache(tmpDir); + + identities = new Identities(identitiesFile, passwordHolder); + for (TestIdentity identity: TestUtil.createTestIdentities()) + identities.add(identity.identity); + } + + @After + public void tearDown() throws Exception { + assertTrue("Can't delete file: " + identitiesFile.getAbsolutePath(), identitiesFile.delete()); + assertTrue("Can't delete directory: " + testDir.getAbsolutePath(), testDir.delete()); + } + + /** Checks that the private signing key is updated on disk if the CryptoImplementation requires it */ + @Test + public void testUpdateKey() throws GeneralSecurityException { + byte[] message = "Hopfen und Malz, Gott erhalt's!".getBytes(); + + for (EmailIdentity identity: identities) { + PublicKey publicKey = identity.getPublicSigningKey(); + PrivateKey privateKey = identity.getPrivateSigningKey(); + + // make a copy of the old signing keys + byte[] encodedPublicKey = publicKey.getEncoded().clone(); + byte[] encodedPrivateKey = privateKey.getEncoded().clone(); + + CryptoImplementation cryptoImpl = identity.getCryptoImpl(); + cryptoImpl.sign(message, privateKey, identities); + + // read identities from file and compare keys before / after + boolean publicKeyChanged; + boolean privateKeyChanged; + if (!identitiesFile.exists()) + publicKeyChanged = privateKeyChanged = false; + else { + Identities newIdentities = new Identities(identitiesFile, passwordHolder); + PublicKey newPublicKey = newIdentities.get(identity).getPublicSigningKey(); + PrivateKey newPrivateKey = newIdentities.get(identity).getPrivateSigningKey(); + publicKeyChanged = !Arrays.equals(encodedPublicKey, newPublicKey.getEncoded()); + privateKeyChanged = !Arrays.equals(encodedPrivateKey, newPrivateKey.getEncoded()); + } + assertFalse(publicKeyChanged); + assertTrue(privateKeyChanged == (cryptoImpl instanceof NTRUEncrypt1087_GMSS512)); + } + } +} \ No newline at end of file diff --git a/test/i2p/bote/folder/IncompleteEmailFolderTest.java b/test/i2p/bote/folder/IncompleteEmailFolderTest.java index ac544838..66810c36 100644 --- a/test/i2p/bote/folder/IncompleteEmailFolderTest.java +++ b/test/i2p/bote/folder/IncompleteEmailFolderTest.java @@ -23,6 +23,7 @@ package i2p.bote.folder; import static org.junit.Assert.assertTrue; import i2p.bote.TestUtil; +import i2p.bote.crypto.KeyUpdateHandler; import i2p.bote.email.Email; import i2p.bote.email.EmailIdentity; import i2p.bote.fileencryption.PasswordCache; @@ -98,7 +99,8 @@ public class IncompleteEmailFolderTest { email.setText(mailContent); EmailIdentity identity = new EmailIdentity("DVkhqF6R9SHB5svViGtqRYZO7oI-0-omnIFtae29fNnNtTTH2j37Fr5fWp4t6rseTjiJ8gwg08DnbA4qP72aSQcDQPSErOELOMSU5BUTtsT8hnv1-DKdhIn~1qoIjxzIFHbxT3xnR3nFI7lKd6couscilzPBCjoFDUKb5ds2u23RO29K7~EKxU1O7Ltu6sT5etXkJkhAziOcuyfZyxJXqH1caYX5e2aWIhY3D2ESfy4nMK66r5KcDVQOPTzCkJq6d1FFOmnDGrlJjN~HgHmfUCtLbO~TLugWx9FCiDGfPkBb-3ODYTDaUR1zobOj1tiffV3Nm73PsYddRt84emLKzIRsC77JJpflw~h8UIRYJ29vJDf4VQ54BhZcelmN192sIrWr2nKN8n6PpSP4LI4RAuG2UvLytnDYzFM7O9WcnFP2-Qs3t1lD9aF72JVTYTpH5PZupnB1cglSsdRg8RmtRa41Fseyx8D3EdH~DCdpMGmfupaWp9~dKpFMleqk9scRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTjDxn3wEOjCjJ4APg~2IGpqWwy2Hw728aZ3eCC5l0MP913BLdIfSUiXPbs6sN9A2"); - Collection packets = email.createEmailPackets(identity, recipient, I2PBotePacket.MAX_DATAGRAM_SIZE); + KeyUpdateHandler keyUpdateHandler = TestUtil.createDummyKeyUpdateHandler(); + Collection packets = email.createEmailPackets(identity, keyUpdateHandler, recipient, I2PBotePacket.MAX_DATAGRAM_SIZE); assertTrue("Expected " + expectedNumPackets + " email packets, got " + packets.size(), packets.size() == expectedNumPackets); assertTrue("The inbox should be empty at this point!", inbox.getElements().size() == 0);