- 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 @@
-
+ * 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:
+ * + *+ * + * This type can be created as follows based on the classes in this package: + *+ * PrivateKeyInfo ::= SEQUENCE ( + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] IMPLICIT Attributes OPTIONAL + * } + * Version ::= INTEGER + * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + * PrivateKey ::= OCTET STRING + * Attributes ::= SET OF Attribute + *+ * + *
+ *
+ *
+ * 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
+ *
+ * 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
+ *
+ *
+ * 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
+ *
+ * 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
+ *
+ * This class enforces as an invariant that inner types have the same tagging as
+ * the type itself. For instance:
+ *
+ * This method may return
+ * If an exception is thrown by the {@link Resolver Resolver} upon resolving
+ * the inner type of this type then
+ *
+ * If no inner type can be resolved then
+ *
+ * This permission is a simple named permission. The names distinguished are:
+ *
+ *
+ * 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
+ *
+ * New instances are created by invoking the
+ *
+ * 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
+ *
+ * New instances are created by invoking the
+ *
+ * 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
+ *
+ * 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
+ *
+ * @param o
+ * The constraint to set.
+ */
+ public void setConstraint(Constraint o);
+
+ /**
+ * Returns the {@link Constraint Constraint} of this type or
+ *
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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 (
+ *
+ * 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
+ *
+ * The number is expected to be
+ *
+ * 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
+ *
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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
+ *
+ *
+ * @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
+ *
+ * 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
+ *
+ * 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:
+ *
+ *
+ * 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
+ *
+ *
+ * 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
+ *
+ * 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:
+ *
+ *
+ *
+ *
+ * The OID must not be
+ *
+ * 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:
+ *
+ *
+ *
+ * For this class to work properly, providers need to define the following
+ * algorithm aliases for the {@link java.security.AlgorithmParameters
+ * AlgorithmParameters} implementations they provide:
+ *
+ *
+ * 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
+ *
+ * @param alg
+ * The name of the algorithm.
+ * @param params
+ * The AlgorithmParameters.
+ * @throws NullPointerException
+ * if
+ *
+ * Such providers need to specify the following aliases for this to work:
+ *
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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.
+ * 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:
+ * (A|B) = 0 IF gcd(A,B) > 1
+ *
+ * @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:
+ * 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
+ *
+ *
+ * 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;i
+ *
+ * This implementation is based on the quicksort algorithm as described in
+ *
+ * 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:
+ *
+ *
+ * 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:
+ *
+ *
+ * @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:
+ * The key pair generator can be initialized with an integer value as well. For
+ * this purpose call
+ * 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)
+ *
+ * @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
+ *
+ * A given
+ *
+ * @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
+ *
+ * The ASN.1 definition of this structure is
+ *
+ *
+ *
+ * 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
+ *
+ * The GMSSSignature can be used as follows:
+ *
+ * Signature generation:
+ *
+ * 1. generate KeySpec from encoded GMSS private key:
+ * Signature verification:
+ *
+ * 1. generate KeySpec from encoded GMSS public key:
+ *
+ * @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
+ *
+ * @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
+ *
+ * @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<{@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 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
+ * where
+ * [UNIVERSAL x] IMPLICIT OCTET STRING
+ *
x is the tag.
+ * 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.
+ * 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.
+ *
+ *
+ *
will cause the tagging method of
+ * ASN1OpenType ot;
+ * ASN1Integer n;
+ * n = new Integer("42");
+ * n.setExplicit(true);
+ * ot = new OpenType(new FooResolver());
+ * ot.setExplicit(false);
+ * ot.setInnerType(n);
+ *
+ *
+ * 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.
+ * 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.
+ * false is returned in
+ * order to provoke a decoding error.
+ * 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
+ *
+ *
+ * @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).
+ * OIDRegistry.add
+ * OIDRegistry.remove
+ * 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.
+ * Resolver
+ * instance set in this instance.
+ * 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.
+ * Resolver
+ * instance set in this instance.
+ *
+ * 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.
+ * 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.
+ * 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.
+ * 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.
+ *
+ * false.
+ * false is returned and
+ * true else. If {@link #skip_ skip_} is true
+ * then nothing is read and true is returned.
+ * 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.
+ * 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.
+ * 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?
+ * 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.
+ * 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.
+ * null if no
+ * such type was found. Strings in the given Map are replaced
+ * by the resolved classes.
+ * 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.
+ * PrivateKeyInfo as defined in PKCS#8.
+ * The ASN.1 definition of this structure is
+ *
+ *
+ *
+ *
+ * @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
+ * 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
+ *
+ *
+ *
+ * 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.
+ * 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.
+ * 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.
+ *
+ *
+ *
+ * @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
+ *
+ *
+ * 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:
+ *
+ *
+ * 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 null is returned.
+ * 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:
+ *
+ *
+ * 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.
+ * 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:
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * 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.
+ * 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.
+ *
+ * AlgorithmIdentifier ::= SEQUENCE{
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ *
+ *
+ *
+ * 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.
+ * 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}.
+ * 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.
+ *
+ *
+ * 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.
+ * 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.
+ * 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:
+ *
+ *
+ * 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".
+ * 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.
+ * 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.
+ * 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.
+ *
+ * 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.
+ *
+ *
+ *
+ *
+ * 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
+ *
+ * (-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 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.
+ *
+ * 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)
+ * 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:
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * 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();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.
+ *
+ *
+ * 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.
+ * keySize as input. It provides a simple use of the GMSS for
+ * testing demands.
+ * 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.
+ * 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.
+ *
+ * GMSSPrivateKey ::= SEQUENCE {
+ *
+ *
+ * @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 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 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.
+ *
+ * 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;
+ * 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());
+ *
+ * @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.
+ *
+ * return gmssSig.verify(signature);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
+ * 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
+ * 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
- * 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("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("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("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
* The Email Identities can be written to, and read from, a password-encrypted file.
*/
-public class Identities implements IterableIdentities object.
+ * Constructs a new empty Identities object. The identitiesFile
+ * is lazy-loaded.
* @param identitiesFile
* @param passwordHolder
*/
@@ -160,10 +162,10 @@ public class Identities implements IterableKeyUpdateHandler 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 ListCryptoImplementations */
public class CryptoImplementationTest {
- private ListCryptoImplementation 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