You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by dr...@apache.org on 2015/01/22 01:56:55 UTC
[31/50] [abbrv] directory-kerberos git commit: Many changes with
newname
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/README.md
----------------------------------------------------------------------
diff --git a/kerby-asn1/README.md b/kerby-asn1/README.md
new file mode 100644
index 0000000..eb35929
--- /dev/null
+++ b/kerby-asn1/README.md
@@ -0,0 +1,301 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+haox-asn1
+=========
+
+### A ASN1 parser with easy and simple API
+
+```
+// encoding
+Asn1Integer aValue = new Asn1Integer(8899);
+byte[] encoded = aValue.encode();
+
+// decoding
+byte[] contentToDecode = ...
+Asn1Integer decodedValue = new Asn1Integer();
+decodedValue.decode(contentToDecode);
+Integer value = decodedValue.getValue();
+```
+
+### Data-driven ASN1 encoding/decoding framework and parser
+
+With the following definition from Kerberos protocol
+```
+ AuthorizationData ::= SEQUENCE OF SEQUENCE {
+ ad-type [0] Int32,
+ ad-data [1] OCTET STRING
+ }
+ ```
+
+You can model AuthzDataEntry as follows
+```java
+public class AuthzDataEntry extends Asn1SequenceType {
+ static int AD_TYPE = 0;
+ static int AD_DATA = 1;
+
+ public AuthzDataEntry() {
+ super(new Asn1FieldInfo[] {
+ new Asn1FieldInfo(AD_TYPE, Asn1Integer.class),
+ new Asn1FieldInfo(AD_DATA, Asn1OctetString.class)
+ });
+ }
+
+ public int getAuthzType() {
+ Integer value = getFieldAsInteger(AD_TYPE);
+ return value;
+ }
+
+ public byte[] getAuthzData() {
+ return getFieldAsOctetBytes(AD_DATA);
+ }
+}
+```
+
+And then define AuthorizationData simply
+```java
+public class AuthorizationData extends Asn1SequenceOf<AuthzDataEntry> {
+
+}
+```
+
+Then you can process with above definitions, encode and decode, without caring about the details.
+
+Think about how to implement the following more complex and pratical sample from [ITU-T Rec. X.680 ISO/IEC 8824-1](http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf):
+```
+A.1 ASN.1 description of the record structure
+The structure of the hypothetical personnel record is formally described below using ASN.1 specified in
+ITU-T Rec. X.680 | ISO/IEC 8824-1 for defining types.
+
+PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
+ Name Name,
+ title [0] VisibleString,
+ number EmployeeNumber,
+ dateOfHire [1] Date,
+ nameOfSpouse [2] Name,
+ children [3] IMPLICIT
+ SEQUENCE OF ChildInformation DEFAULT {}
+}
+
+ChildInformation ::= SET {
+ name Name,
+ dateOfBirth [0] Date
+}
+
+Name ::= [APPLICATION 1] IMPLICIT SEQUENCE {
+ givenName VisibleString,
+ initial VisibleString,
+ familyName VisibleString
+}
+
+EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER
+Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD
+```
+Similarly as above, we can have (from the unit test codes):
+```java
+public class PersonnelRecord extends TaggingSet {
+ private static int NAME = 0;
+ private static int TITLE = 1;
+ private static int NUMBER = 2;
+ private static int DATEOFHIRE= 3;
+ private static int NAMEOFSPOUSE = 4;
+ private static int CHILDREN = 5;
+
+ static Asn1FieldInfo[] fieldInfos = new Asn1FieldInfo[] {
+ new Asn1FieldInfo(NAME, -1, Name.class),
+ new Asn1FieldInfo(TITLE, 0, Asn1VisibleString.class),
+ new Asn1FieldInfo(NUMBER, -1, EmployeeNumber.class),
+ new Asn1FieldInfo(DATEOFHIRE, 1, Date.class),
+ new Asn1FieldInfo(NAMEOFSPOUSE, 2, Name.class),
+ new Asn1FieldInfo(CHILDREN, 3, Children.class, true)
+ };
+
+ public PersonnelRecord() {
+ super(0, fieldInfos, true);
+ setEncodingOption(EncodingOption.IMPLICIT);
+ }
+
+ public void setName(Name name) {
+ setFieldAs(NAME, name);
+ }
+
+ public Name getName() {
+ return getFieldAs(NAME, Name.class);
+ }
+
+ public void setTitle(String title) {
+ setFieldAs(TITLE, new Asn1VisibleString(title));
+ }
+
+ public String getTitle() {
+ return getFieldAsString(TITLE);
+ }
+
+ public void setEmployeeNumber(EmployeeNumber employeeNumber) {
+ setFieldAs(NUMBER, employeeNumber);
+ }
+
+ public EmployeeNumber getEmployeeNumber() {
+ return getFieldAs(NUMBER, EmployeeNumber.class);
+ }
+
+ public void setDateOfHire(Date dateOfHire) {
+ setFieldAs(DATEOFHIRE, dateOfHire);
+ }
+
+ public Date getDateOfHire() {
+ return getFieldAs(DATEOFHIRE, Date.class);
+ }
+
+ public void setNameOfSpouse(Name spouse) {
+ setFieldAs(NAMEOFSPOUSE, spouse);
+ }
+
+ public Name getNameOfSpouse() {
+ return getFieldAs(NAMEOFSPOUSE, Name.class);
+ }
+
+ public void setChildren(Children children) {
+ setFieldAs(CHILDREN, children);
+ }
+
+ public Children getChildren() {
+ return getFieldAs(CHILDREN, Children.class);
+ }
+
+ public static class Children extends Asn1SequenceOf<ChildInformation> {
+ public Children(ChildInformation ... children) {
+ super();
+ for (ChildInformation child : children) {
+ addElement(child);
+ }
+ }
+
+ public Children() {
+ super();
+ }
+ }
+
+ public static class ChildInformation extends Asn1SetType {
+ private static int NAME = 0;
+ private static int DATEOFBIRTH = 1;
+
+ static Asn1FieldInfo[] tags = new Asn1FieldInfo[] {
+ new Asn1FieldInfo(NAME, -1, Name.class),
+ new Asn1FieldInfo(DATEOFBIRTH, 0, Date.class)
+ };
+
+ public ChildInformation() {
+ super(tags);
+ }
+
+ public void setName(Name name) {
+ setFieldAs(NAME, name);
+ }
+
+ public Name getName() {
+ return getFieldAs(NAME, Name.class);
+ }
+
+ public void setDateOfBirth(Date date) {
+ setFieldAs(DATEOFBIRTH, date);
+ }
+
+ public Date getDateOfBirth() {
+ return getFieldAs(DATEOFBIRTH, Date.class);
+ }
+ }
+
+ public static class Name extends TaggingSequence {
+ private static int GIVENNAME = 0;
+ private static int INITIAL = 1;
+ private static int FAMILYNAME = 2;
+
+ static Asn1FieldInfo[] tags = new Asn1FieldInfo[] {
+ new Asn1FieldInfo(GIVENNAME, -1, Asn1VisibleString.class),
+ new Asn1FieldInfo(INITIAL, -1, Asn1VisibleString.class),
+ new Asn1FieldInfo(FAMILYNAME, -1, Asn1VisibleString.class)
+ };
+
+ public Name() {
+ super(1, tags, true);
+ setEncodingOption(EncodingOption.IMPLICIT);
+ }
+
+ public Name(String givenName, String initial, String familyName) {
+ this();
+ setGivenName(givenName);
+ setInitial(initial);
+ setFamilyName(familyName);
+ }
+
+ public void setGivenName(String givenName) {
+ setFieldAs(GIVENNAME, new Asn1VisibleString(givenName));
+ }
+
+ public String getGivenName() {
+ return getFieldAsString(GIVENNAME);
+ }
+
+ public void setInitial(String initial) {
+ setFieldAs(INITIAL, new Asn1VisibleString(initial));
+ }
+
+ public String getInitial() {
+ return getFieldAsString(INITIAL);
+ }
+
+ public void setFamilyName(String familyName) {
+ setFieldAs(FAMILYNAME, new Asn1VisibleString(familyName));
+ }
+
+ public String getFamilyName() {
+ return getFieldAsString(FAMILYNAME);
+ }
+ }
+
+ public static class EmployeeNumber extends Asn1Tagging<Asn1Integer> {
+ public EmployeeNumber(Integer value) {
+ super(2, new Asn1Integer(value), true);
+ setEncodingOption(EncodingOption.IMPLICIT);
+ }
+ public EmployeeNumber() {
+ this(null);
+ }
+ }
+
+ public static class Date extends Asn1Tagging<Asn1VisibleString> {
+ public Date(String value) {
+ super(3, new Asn1VisibleString(value), true);
+ setEncodingOption(EncodingOption.IMPLICIT);
+ }
+ public Date() {
+ this(null);
+ }
+ }
+}
+```
+### Notes
+* 90% tests coverage for DER encoding
+* For BER & CER encoding, to be fully supported
+* No extra dependency
+
+### License
+Apache V2 License
+
+
+
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/pom.xml
----------------------------------------------------------------------
diff --git a/kerby-asn1/pom.xml b/kerby-asn1/pom.xml
new file mode 100644
index 0000000..b667443
--- /dev/null
+++ b/kerby-asn1/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <parent>
+ <groupId>org.apache.kerby</groupId>
+ <artifactId>kerby-all</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>kerby-asn1</artifactId>
+ <name>Kerby ASN1 Project</name>
+ <description>Kerby ASN1 Project</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dump.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dump.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dump.java
new file mode 100644
index 0000000..cfd9c5d
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dump.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+import org.apache.kerby.asn1.type.Asn1Item;
+import org.apache.kerby.asn1.type.Asn1Simple;
+import org.apache.kerby.asn1.type.Asn1Type;
+
+import java.io.IOException;
+
+public class Asn1Dump {
+
+ public static void dump(byte[] content) throws IOException {
+ String dumped = dumpAsString(content);
+ System.out.println(dumped);
+ }
+
+ public static String dumpAsString(byte[] content) throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ Asn1InputBuffer buffer = new Asn1InputBuffer(content);
+ Asn1Type value;
+ while (true) {
+ value = buffer.read();
+ if (value == null) break;
+ dump(value, sb);
+ }
+
+ return sb.toString();
+ }
+
+ public static String dumpAsString(Asn1Type value) {
+ StringBuilder sb = new StringBuilder();
+ dump(value, sb);
+ return sb.toString();
+ }
+
+ private static void dump(Asn1Type value, StringBuilder buffer) {
+ if (value instanceof Asn1Simple) {
+ buffer.append(((Asn1Simple) value).getValue().toString());
+ } else if (value instanceof Asn1Item) {
+ dump((Asn1Item) value, buffer);
+ }
+ }
+
+ private static void dump(Asn1Item value, StringBuilder buffer) {
+ if (value.isFullyDecoded()) {
+ dump(value.getValue(), buffer);
+ } else {
+ buffer.append("Asn1Item");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Factory.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Factory.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Factory.java
new file mode 100644
index 0000000..2762476
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Factory.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+import org.apache.kerby.asn1.type.Asn1Collection;
+import org.apache.kerby.asn1.type.Asn1Simple;
+import org.apache.kerby.asn1.type.Asn1Type;
+
+public class Asn1Factory {
+
+ public static Asn1Type create(int tagNo) {
+ UniversalTag tagNoEnum = UniversalTag.fromValue(tagNo);
+ if (tagNoEnum != UniversalTag.UNKNOWN) {
+ return create(tagNoEnum);
+ }
+ throw new IllegalArgumentException("Unexpected tag " + tagNo);
+ }
+
+ public static Asn1Type create(UniversalTag tagNo) {
+ if (Asn1Simple.isSimple(tagNo)) {
+ return Asn1Simple.createSimple(tagNo);
+ } else if (Asn1Collection.isCollection(tagNo)) {
+ return Asn1Collection.createCollection(tagNo);
+ }
+ throw new IllegalArgumentException("Unexpected tag " + tagNo);
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1InputBuffer.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1InputBuffer.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1InputBuffer.java
new file mode 100644
index 0000000..934b0c1
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1InputBuffer.java
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+import org.apache.kerby.asn1.type.AbstractAsn1Type;
+import org.apache.kerby.asn1.type.Asn1Item;
+import org.apache.kerby.asn1.type.Asn1Type;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Asn1 decoder
+ */
+public class Asn1InputBuffer {
+ private final LimitedByteBuffer limitedBuffer;
+
+ public Asn1InputBuffer(byte[] bytes) {
+ this(new LimitedByteBuffer(bytes));
+ }
+
+ public Asn1InputBuffer(ByteBuffer byteBuffer) {
+ this(new LimitedByteBuffer(byteBuffer));
+ }
+
+ public Asn1InputBuffer(LimitedByteBuffer limitedByteBuffer) {
+ this.limitedBuffer = limitedByteBuffer;
+ }
+
+ public Asn1Type read() throws IOException {
+ if (! limitedBuffer.available()) {
+ return null;
+ }
+ Asn1Item one = AbstractAsn1Type.decodeOne(limitedBuffer);
+ if (one.isSimple()) {
+ one.decodeValueAsSimple();
+ } else if (one.isCollection()) {
+ one.decodeValueAsCollection();
+ }
+ if (one.isFullyDecoded()) {
+ return one.getValue();
+ }
+ return one;
+ }
+
+ public void readBytes(byte[] bytes) throws IOException {
+ limitedBuffer.readBytes(bytes);
+ }
+
+ public byte[] readAllLeftBytes() throws IOException {
+ return limitedBuffer.readAllLeftBytes();
+ }
+
+ public void skipNext() throws IOException {
+ if (limitedBuffer.available()) {
+ AbstractAsn1Type.skipOne(limitedBuffer);
+ }
+ }
+
+ public void skipBytes(int len) throws IOException {
+ if (limitedBuffer.available()) {
+ limitedBuffer.skip(len);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1OutputBuffer.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1OutputBuffer.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1OutputBuffer.java
new file mode 100644
index 0000000..42f8ead
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1OutputBuffer.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+import org.apache.kerby.asn1.type.AbstractAsn1Type;
+import org.apache.kerby.asn1.type.Asn1Type;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Asn1 encoder
+ */
+public class Asn1OutputBuffer {
+ private List<Asn1Type> objects;
+
+ public Asn1OutputBuffer() {
+ this.objects = new ArrayList<Asn1Type>(3);
+ }
+
+ public void write(Asn1Type value) {
+ objects.add(value);
+ }
+
+ public void write(Asn1Type value, EncodingOption option) {
+ value.setEncodingOption(option);
+ objects.add(value);
+ }
+
+ public ByteBuffer getOutput() {
+ int len = encodingLength();
+ ByteBuffer byteBuffer = ByteBuffer.allocate(len);
+ encode(byteBuffer);
+ return byteBuffer;
+ }
+
+ private int encodingLength() {
+ int allLen = 0;
+ for (Asn1Type item : objects) {
+ if (item != null) {
+ allLen += ((AbstractAsn1Type) item).encodingLength();
+ }
+ }
+ return allLen;
+ }
+
+ private void encode(ByteBuffer buffer) {
+ for (Asn1Type item : objects) {
+ if (item != null) {
+ item.encode(buffer);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/EncodingOption.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/EncodingOption.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/EncodingOption.java
new file mode 100644
index 0000000..c15f5ce
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/EncodingOption.java
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+public enum EncodingOption
+{
+ UNKNOWN(-1),
+ PRIMITIVE(1),
+ CONSTRUCTED(2),
+ CONSTRUCTED_DEFLEN(3),
+ CONSTRUCTED_INDEFLEN(4),
+ IMPLICIT(5),
+ EXPLICIT(6),
+ BER(7),
+ DER(8),
+ CER(9);
+
+ private int value;
+
+ private EncodingOption(int value) {
+ this.value = value;
+ }
+
+ public static int CONSTRUCTED_FLAG = 0x20;
+
+ public static boolean isConstructed(int tag) {
+ return (tag & CONSTRUCTED_FLAG) != 0;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public boolean isPrimitive() {
+ return this == PRIMITIVE;
+ }
+
+ public boolean isConstructed() {
+ return this == CONSTRUCTED || this == CONSTRUCTED_DEFLEN || this == CONSTRUCTED_INDEFLEN;
+ }
+
+ public boolean isImplicit() {
+ return this == IMPLICIT;
+ }
+
+ public boolean isExplicit() {
+ return this == EXPLICIT;
+ }
+
+ public boolean isDer() {
+ return this == DER;
+ }
+
+ public boolean isCer() {
+ return this == CER;
+ }
+
+ public static EncodingOption fromValue(int value) {
+ for (EncodingOption e : values()) {
+ if (e.getValue() == value) {
+ return (EncodingOption) e;
+ }
+ }
+
+ return UNKNOWN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/LimitedByteBuffer.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/LimitedByteBuffer.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/LimitedByteBuffer.java
new file mode 100644
index 0000000..ed5332b
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/LimitedByteBuffer.java
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class LimitedByteBuffer {
+ private final ByteBuffer byteBuffer;
+ private final int limit;
+ private int startOffset;
+
+ public LimitedByteBuffer(byte[] bytes) {
+ this.byteBuffer = ByteBuffer.wrap(bytes);
+ this.limit = bytes.length;
+ this.startOffset = 0;
+ }
+
+ public LimitedByteBuffer(ByteBuffer byteBuffer) {
+ this(byteBuffer, byteBuffer.limit());
+ }
+
+ public LimitedByteBuffer(ByteBuffer byteBuffer, int limit) {
+ this.byteBuffer = byteBuffer;
+ this.limit = limit;
+ this.startOffset = byteBuffer.position();
+ }
+
+ public LimitedByteBuffer(LimitedByteBuffer other, int limit) {
+ if (limit > other.hasLeft()) {
+ throw new IllegalArgumentException("limit is too large, out of bound");
+ }
+ this.byteBuffer = other.byteBuffer.duplicate();
+ this.limit = limit;
+ this.startOffset = byteBuffer.position();
+ }
+
+ public boolean available() {
+ return byteBuffer.hasRemaining() &&
+ byteBuffer.position() - startOffset < limit;
+ }
+
+ public long hasRead() {
+ return byteBuffer.position() - startOffset;
+ }
+ public long hasLeft() {
+ return limit - hasRead();
+ }
+
+ public byte readByte() throws IOException {
+ if (!available()) {
+ throw new IOException("Buffer EOF");
+ }
+ return byteBuffer.get();
+ }
+
+ public byte[] readAllLeftBytes() throws IOException {
+ return readBytes((int) hasLeft());
+ }
+
+ public void skip(int len) throws IOException {
+ checkLen(len);
+ int newPos = byteBuffer.position() + len;
+ byteBuffer.position(newPos);
+ }
+
+ public byte[] readBytes(int len) throws IOException {
+ checkLen(len);
+
+ byte[] bytes = new byte[len];
+ if (len > 0) {
+ byteBuffer.get(bytes);
+ }
+ return bytes;
+ }
+
+ private void checkLen(int len) throws IOException {
+ if (len < 0) {
+ throw new IllegalArgumentException("Bad argument len: " + len);
+ }
+ if (len > 0) {
+ if (!available()) {
+ throw new IOException("Buffer EOF");
+ }
+ if (hasLeft() < len) {
+ throw new IOException("Out of Buffer");
+ }
+ }
+ }
+
+ public void readBytes(byte[] bytes) throws IOException {
+ if (bytes == null) {
+ throw new IllegalArgumentException("Bad argument bytes: null");
+ }
+ if (!available()) {
+ throw new IOException("Buffer EOF");
+ }
+ if (hasLeft() < bytes.length) {
+ throw new IOException("Out of Buffer");
+ }
+
+ byteBuffer.get(bytes);
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/TagClass.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/TagClass.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/TagClass.java
new file mode 100644
index 0000000..430630f
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/TagClass.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+public enum TagClass {
+ UNKNOWN(-1),
+ UNIVERSAL(0x00),
+ APPLICATION(0x40),
+ CONTEXT_SPECIFIC(0x80),
+ PRIVATE(0xC0);
+
+ private int value;
+
+ private TagClass(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public boolean isUniversal() {
+ return this == UNIVERSAL;
+ }
+
+ public boolean isAppSpecific() {
+ return this == APPLICATION;
+ }
+
+ public boolean isContextSpecific() {
+ return this == CONTEXT_SPECIFIC;
+ }
+
+ public boolean isTagged() {
+ return this == APPLICATION || this == CONTEXT_SPECIFIC;
+ }
+
+ public static TagClass fromValue(int value) {
+ // Optimized by Emmanuel
+ switch (value) {
+ case 0x00:
+ return TagClass.UNIVERSAL;
+ case 0x40:
+ return TagClass.APPLICATION;
+ case 0x80:
+ return TagClass.CONTEXT_SPECIFIC;
+ case 0xC0:
+ return TagClass.PRIVATE;
+ default:
+ return TagClass.UNKNOWN;
+ }
+ }
+
+ public static TagClass fromTagFlags(int tag) {
+ return fromValue(tag & 0xC0);
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/TaggingOption.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/TaggingOption.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/TaggingOption.java
new file mode 100644
index 0000000..534977b
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/TaggingOption.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+public class TaggingOption
+{
+ private int tagNo;
+ private boolean isImplicit;
+ private boolean isAppSpecific;
+
+ public static TaggingOption newImplicitAppSpecific(int tagNo) {
+ return new TaggingOption(tagNo, true, true);
+ }
+
+ public static TaggingOption newExplicitAppSpecific(int tagNo) {
+ return new TaggingOption(tagNo, false, true);
+ }
+
+ public static TaggingOption newImplicitContextSpecific(int tagNo) {
+ return new TaggingOption(tagNo, true, false);
+ }
+
+ public static TaggingOption newExplicitContextSpecific(int tagNo) {
+ return new TaggingOption(tagNo, false, false);
+ }
+
+ private TaggingOption(int tagNo, boolean isImplicit, boolean isAppSpecific) {
+ this.tagNo = tagNo;
+ this.isImplicit = isImplicit;
+ this.isAppSpecific = isAppSpecific;
+ }
+
+ public int tagFlags(boolean isTaggedConstructed) {
+ boolean isConstructed = isImplicit ? isTaggedConstructed : true;
+ TagClass tagClass = isAppSpecific ? TagClass.APPLICATION : TagClass.CONTEXT_SPECIFIC;
+ int flags = tagClass.getValue() | (isConstructed ? EncodingOption.CONSTRUCTED_FLAG : 0x00);
+ return flags;
+ }
+
+ public int getTagNo() {
+ return tagNo;
+ }
+
+ public boolean isAppSpecific() {
+ return isAppSpecific;
+ }
+
+ public boolean isImplicit() {
+ return isImplicit;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/UniversalTag.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/UniversalTag.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/UniversalTag.java
new file mode 100644
index 0000000..0eb5c78
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/UniversalTag.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1;
+
+// Optimized by Emmanuel
+public enum UniversalTag {
+ UNKNOWN (-1),
+ CHOICE (-2), // Only for internal using
+ BOOLEAN (0x01),
+ INTEGER (0x02),
+ BIT_STRING (0x03),
+ OCTET_STRING (0x04),
+ NULL (0x05),
+ OBJECT_IDENTIFIER (0x06),
+ OBJECT_DESCRIPTOR (0x07), // Added for completness
+ EXTERNAL (0x08),
+ REAL (0x09),
+ ENUMERATED (0x0a),
+ EMBEDDED_PDV (0x0b), // Added for completness
+ UTF8_STRING (0x0c),
+ RELATIVE_OID (0x0d), // Added for completness
+ RESERVED_14 (0x0e), // Added for completness
+ RESERVED_15 (0x0f), // Added for completness
+ SEQUENCE (0x10),
+ SEQUENCE_OF (0x10),
+ SET (0x11),
+ SET_OF (0x11),
+ NUMERIC_STRING (0x12),
+ PRINTABLE_STRING (0x13),
+ T61_STRING (0x14),
+ VIDEOTEX_STRING (0x15),
+ IA5_STRING (0x16),
+ UTC_TIME (0x17),
+ GENERALIZED_TIME (0x18),
+ GRAPHIC_STRING (0x19),
+ VISIBLE_STRING (0x1a),
+ GENERAL_STRING (0x1b),
+ UNIVERSAL_STRING (0x1c),
+ CHARACTER_STRING (0x1d), // Added for completness
+ BMP_STRING (0x1e),
+ RESERVED_31 (0x1f); // Added for completness
+
+ private int value;
+
+ private UniversalTag(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static UniversalTag fromValue(int value) {
+ switch (value) {
+ case 0x01 : return BOOLEAN;
+ case 0x02 : return INTEGER;
+ case 0x03 : return BIT_STRING;
+ case 0x04 : return OCTET_STRING;
+ case 0x05 : return NULL;
+ case 0x06 : return OBJECT_IDENTIFIER;
+ case 0x07 : return OBJECT_DESCRIPTOR;
+ case 0x08 : return EXTERNAL;
+ case 0x09 : return REAL;
+ case 0x0A : return ENUMERATED;
+ case 0x0B : return EMBEDDED_PDV;
+ case 0x0C : return UTF8_STRING;
+ case 0x0D : return RELATIVE_OID;
+ case 0x0E : return RESERVED_14;
+ case 0x0F : return RESERVED_15;
+ case 0x10 : return SEQUENCE;
+ case 0x11 : return SET;
+ case 0x12 : return NUMERIC_STRING;
+ case 0x13 : return PRINTABLE_STRING;
+ case 0x14 : return T61_STRING;
+ case 0x15 : return VIDEOTEX_STRING;
+ case 0x16 : return IA5_STRING;
+ case 0x17 : return UTC_TIME;
+ case 0x18 : return GENERALIZED_TIME;
+ case 0x19 : return GRAPHIC_STRING;
+ case 0x1A : return VISIBLE_STRING;
+ case 0x1B : return GENERAL_STRING;
+ case 0x1C : return UNIVERSAL_STRING;
+ case 0x1D : return CHARACTER_STRING;
+ case 0x1E : return BMP_STRING;
+ case 0x1F : return RESERVED_31;
+ default : return UNKNOWN;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/AbstractAsn1Type.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/AbstractAsn1Type.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/AbstractAsn1Type.java
new file mode 100644
index 0000000..3aefed7
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/AbstractAsn1Type.java
@@ -0,0 +1,420 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.EncodingOption;
+import org.apache.kerby.asn1.LimitedByteBuffer;
+import org.apache.kerby.asn1.TagClass;
+import org.apache.kerby.asn1.TaggingOption;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public abstract class AbstractAsn1Type<T> implements Asn1Type {
+ private TagClass tagClass = TagClass.UNKNOWN;
+ private int tagNo = -1;
+ private int tagFlags = -1;
+ protected EncodingOption encodingOption = EncodingOption.UNKNOWN;
+ private int encodingLen = -1;
+ private T value;
+
+ public AbstractAsn1Type(TagClass tagClass, int tagNo) {
+ this(tagClass, tagNo, null);
+ }
+
+ public AbstractAsn1Type(int tagFlags, int tagNo) {
+ this(tagFlags, tagNo, null);
+ }
+
+ public AbstractAsn1Type(int tagFlags, int tagNo, T value) {
+ this(TagClass.fromTagFlags(tagFlags), tagNo, value);
+ setTagFlags(tagFlags);
+ }
+
+ public AbstractAsn1Type(TagClass tagClass, int tagNo, T value) {
+ this.tagClass = tagClass;
+ this.tagNo = tagNo;
+ this.value = value;
+ }
+
+ public void setEncodingOption(EncodingOption encodingOption) {
+ this.encodingOption = encodingOption;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public void setValue(T value) {
+ this.value = value;
+ }
+
+ protected TagClass tagClass() {
+ return tagClass;
+ }
+
+ @Override
+ public int tagNo() {
+ return tagNo;
+ }
+
+ protected void setTagFlags(int tagFlags) {
+ this.tagFlags = tagFlags & 0xe0;
+ }
+
+ protected void setTagNo(int tagNo) {
+ this.tagNo = tagNo;
+ }
+
+ @Override
+ public byte[] encode() {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(encodingLength());
+ encode(byteBuffer);
+ byteBuffer.flip();
+ return byteBuffer.array();
+ }
+
+ @Override
+ public void encode(ByteBuffer buffer) {
+ encodeTag(buffer, tagFlags(), tagNo());
+ encodeLength(buffer, encodingBodyLength());
+ encodeBody(buffer);
+ }
+
+ protected void encodeBody(ByteBuffer buffer) { }
+
+ @Override
+ public void decode(byte[] content) throws IOException {
+ decode(new LimitedByteBuffer(content));
+ }
+
+ @Override
+ public void decode(ByteBuffer content) throws IOException {
+ decode(new LimitedByteBuffer(content));
+ }
+
+ @Override
+ public int tagFlags() {
+ if (tagFlags == -1) {
+ int flags = tagClass.getValue();
+ if (isConstructed()) flags |= EncodingOption.CONSTRUCTED_FLAG;
+ return flags;
+ }
+ return tagFlags;
+ }
+
+ @Override
+ public int encodingLength() {
+ if (encodingLen == -1) {
+ int bodyLen = encodingBodyLength();
+ encodingLen = lengthOfTagLength(tagNo()) + lengthOfBodyLength(bodyLen) + bodyLen;
+ }
+ return encodingLen;
+ }
+
+ public boolean isConstructed() {
+ if (tagFlags != -1) {
+ return (tagFlags & EncodingOption.CONSTRUCTED_FLAG) != 0;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isUniversal() {
+ return tagClass.isUniversal();
+ }
+
+ public boolean isAppSpecific() {
+ return tagClass.isAppSpecific();
+ }
+
+ public boolean isContextSpecific() {
+ return tagClass.isContextSpecific();
+ }
+
+ public boolean isTagged() {
+ return tagClass.isTagged();
+ }
+
+ public boolean isSimple() {
+ return isUniversal() && Asn1Simple.isSimple(tagNo);
+ }
+
+ public boolean isCollection() {
+ return isUniversal() && Asn1Collection.isCollection(tagNo);
+ }
+
+ protected abstract int encodingBodyLength();
+
+ protected void decode(LimitedByteBuffer content) throws IOException {
+ int tag = readTag(content);
+ int tagNo = readTagNo(content, tag);
+ int length = readLength(content);
+
+ decode(tag, tagNo, new LimitedByteBuffer(content, length));
+ }
+
+ public void decode(int tagFlags, int tagNo, LimitedByteBuffer content) throws IOException {
+ if (this.tagClass != TagClass.UNKNOWN && this.tagClass != TagClass.fromTagFlags(tagFlags)) {
+ throw new IOException("Unexpected tagFlags " + tagFlags + ", expecting " + this.tagClass);
+ }
+ if (this.tagNo != -1 && this.tagNo != tagNo) {
+ throw new IOException("Unexpected tagNo " + tagNo + ", expecting " + this.tagNo);
+ }
+
+ this.tagClass = TagClass.fromTagFlags(tagFlags);
+ this.tagFlags = tagFlags;
+ this.tagNo = tagNo;
+
+ decodeBody(content);
+ }
+
+ protected abstract void decodeBody(LimitedByteBuffer content) throws IOException;
+
+ protected int taggedEncodingLength(TaggingOption taggingOption) {
+ int taggingTagNo = taggingOption.getTagNo();
+ int taggingBodyLen = taggingOption.isImplicit() ? encodingBodyLength() : encodingLength();
+ int taggingEncodingLen = lengthOfTagLength(taggingTagNo) + lengthOfBodyLength(taggingBodyLen) + taggingBodyLen;
+ return taggingEncodingLen;
+ }
+
+ public byte[] taggedEncode(TaggingOption taggingOption) {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(taggedEncodingLength(taggingOption));
+ taggedEncode(byteBuffer, taggingOption);
+ byteBuffer.flip();
+ return byteBuffer.array();
+ }
+
+ @Override
+ public void taggedEncode(ByteBuffer buffer, TaggingOption taggingOption) {
+ int taggingTagFlags = taggingOption.tagFlags(isConstructed());
+ encodeTag(buffer, taggingTagFlags, taggingOption.getTagNo());
+ int taggingBodyLen = taggingOption.isImplicit() ? encodingBodyLength() : encodingLength();
+ encodeLength(buffer, taggingBodyLen);
+ if (taggingOption.isImplicit()) {
+ encodeBody(buffer);
+ } else {
+ encode(buffer);
+ }
+ }
+
+ public void taggedDecode(byte[] content, TaggingOption taggingOption) throws IOException {
+ taggedDecode(ByteBuffer.wrap(content), taggingOption);
+ }
+
+ @Override
+ public void taggedDecode(ByteBuffer content, TaggingOption taggingOption) throws IOException {
+ LimitedByteBuffer limitedBuffer = new LimitedByteBuffer(content);
+ taggedDecode(limitedBuffer, taggingOption);
+ }
+
+ protected void taggedDecode(LimitedByteBuffer content, TaggingOption taggingOption) throws IOException {
+ int taggingTag = readTag(content);
+ int taggingTagNo = readTagNo(content, taggingTag);
+ int taggingLength = readLength(content);
+ LimitedByteBuffer newContent = new LimitedByteBuffer(content, taggingLength);
+
+ int tagFlags = taggingTag & 0xe0;
+ taggedDecode(tagFlags, taggingTagNo, newContent, taggingOption);
+ }
+
+ protected void taggedDecode(int taggingTagFlags, int taggingTagNo, LimitedByteBuffer content, TaggingOption taggingOption) throws IOException {
+ int expectedTaggingTagFlags = taggingOption.tagFlags(isConstructed());
+ if (expectedTaggingTagFlags != taggingTagFlags) {
+ throw new IOException("Unexpected tag flags" + taggingTagFlags + ", expecting " + expectedTaggingTagFlags);
+ }
+ if (taggingOption.getTagNo() != taggingTagNo) {
+ throw new IOException("Unexpected tagNo " + taggingTagNo + ", expecting " + taggingOption.getTagNo());
+ }
+
+ if (taggingOption.isImplicit()) {
+ decodeBody(content);
+ } else {
+ decode(content);
+ }
+ }
+
+ public static Asn1Item decodeOne(LimitedByteBuffer content) throws IOException {
+ int tag = readTag(content);
+ int tagNo = readTagNo(content, tag);
+ boolean isConstructed = EncodingOption.isConstructed(tag);
+ int length = readLength(content);
+ if (length < 0) {
+ throw new IOException("Unexpected length");
+ }
+ LimitedByteBuffer valueContent = new LimitedByteBuffer(content, length);
+ content.skip(length);
+
+ Asn1Item result = new Asn1Item(tag, tagNo, valueContent);
+ if (result.isSimple()) {
+ result.decodeValueAsSimple();
+ }
+ return result;
+ }
+
+ public static void skipOne(LimitedByteBuffer content) throws IOException {
+ int tag = readTag(content);
+ int tagNo = readTagNo(content, tag);
+ int length = readLength(content);
+ if (length < 0) {
+ throw new IOException("Unexpected length");
+ }
+ content.skip(length);
+ }
+
+ public static int lengthOfBodyLength(int bodyLength) {
+ int length = 1;
+
+ if (bodyLength > 127) {
+ int payload = bodyLength;
+ while (payload != 0) {
+ payload >>= 8;
+ length++;
+ }
+ }
+
+ return length;
+ }
+
+ public static int lengthOfTagLength(int tagNo) {
+ int length = 1;
+
+ if (tagNo >= 31) {
+ if (tagNo < 128) {
+ length++;
+ } else {
+ length++;
+
+ do {
+ tagNo >>= 7;
+ length++;
+ } while (tagNo > 127);
+ }
+ }
+
+ return length;
+ }
+
+ public static void encodeTag(ByteBuffer buffer, int flags, int tagNo) {
+ if (tagNo < 31) {
+ buffer.put((byte) (flags | tagNo));
+ } else {
+ buffer.put((byte) (flags | 0x1f));
+ if (tagNo < 128) {
+ buffer.put((byte) tagNo);
+ } else {
+ byte[] tmpBytes = new byte[5]; // 5 * 7 > 32
+ int iPut = tmpBytes.length;
+
+ tmpBytes[--iPut] = (byte)(tagNo & 0x7f);
+ do {
+ tagNo >>= 7;
+ tmpBytes[--iPut] = (byte)(tagNo & 0x7f | 0x80);
+ } while (tagNo > 127);
+
+ buffer.put(tmpBytes, iPut, tmpBytes.length - iPut);
+ }
+ }
+ }
+
+ public static void encodeLength(ByteBuffer buffer, int bodyLength) {
+ if (bodyLength < 128) {
+ buffer.put((byte) bodyLength);
+ } else {
+ int length = 0;
+ int payload = bodyLength;
+
+ while (payload != 0) {
+ payload >>= 8;
+ length++;
+ }
+
+ buffer.put((byte) (length | 0x80));
+
+ payload = bodyLength;
+ for (int i = length - 1; i >= 0; i--) {
+ buffer.put((byte) (payload >> (i * 8)));
+ }
+ }
+ }
+
+ public static int readTag(LimitedByteBuffer buffer) throws IOException {
+ int tag = buffer.readByte() & 0xff;
+ if (tag == 0) {
+ throw new IOException("Bad tag 0 found");
+ }
+ return tag;
+ }
+
+ public static int readTagNo(LimitedByteBuffer buffer, int tag) throws IOException {
+ int tagNo = tag & 0x1f;
+
+ if (tagNo == 0x1f) {
+ tagNo = 0;
+
+ int b = buffer.readByte() & 0xff;
+ if ((b & 0x7f) == 0) {
+ throw new IOException("Invalid high tag number found");
+ }
+
+ while ((b >= 0) && ((b & 0x80) != 0)) {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = buffer.readByte();
+ }
+
+ tagNo |= (b & 0x7f);
+ }
+
+ return tagNo;
+ }
+
+ public static int readLength(LimitedByteBuffer buffer) throws IOException {
+ int bodyLength = buffer.readByte() & 0xff;
+ if (bodyLength < 0) {
+ throw new EOFException("Unexpected EOF");
+ }
+
+ if (bodyLength > 127) {
+ int length = bodyLength & 0x7f;
+ if (length > 4) {
+ throw new IOException("Bad bodyLength of more than 4 bytes: " + length);
+ }
+
+ bodyLength = 0;
+ int tmp;
+ for (int i = 0; i < length; i++) {
+ tmp = buffer.readByte() & 0xff;
+ bodyLength = (bodyLength << 8) + tmp;
+ }
+
+ if (bodyLength < 0) {
+ throw new IOException("Invalid bodyLength " + bodyLength);
+ }
+ if (bodyLength > buffer.hasLeft()) {
+ throw new IOException("Corrupt stream - less data "
+ + buffer.hasLeft() + " than expected " + bodyLength);
+ }
+ }
+
+ return bodyLength;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java
new file mode 100644
index 0000000..1474851
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.LimitedByteBuffer;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class Asn1Any extends AbstractAsn1Type<Asn1Type> {
+
+ public Asn1Any(Asn1Type anyValue) {
+ super(anyValue.tagFlags(), anyValue.tagNo(), anyValue);
+ }
+
+ @Override
+ protected int encodingBodyLength() {
+ return ((AbstractAsn1Type) getValue()).encodingBodyLength();
+ }
+
+ @Override
+ protected void encodeBody(ByteBuffer buffer) {
+ ((AbstractAsn1Type) getValue()).encodeBody(buffer);
+ }
+
+ @Override
+ protected void decodeBody(LimitedByteBuffer content) throws IOException {
+ ((AbstractAsn1Type) getValue()).decodeBody(content);
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BigInteger.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BigInteger.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BigInteger.java
new file mode 100644
index 0000000..f880840
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BigInteger.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.UniversalTag;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public class Asn1BigInteger extends Asn1Simple<BigInteger>
+{
+ public Asn1BigInteger() {
+ this(null);
+ }
+
+ public Asn1BigInteger(long value) {
+ this(BigInteger.valueOf(value));
+ }
+
+ public Asn1BigInteger(BigInteger value) {
+ super(UniversalTag.INTEGER, value);
+ }
+
+ protected void toBytes() {
+ setBytes(getValue().toByteArray());
+ }
+
+ protected void toValue() throws IOException {
+ setValue(new BigInteger(getBytes()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BitString.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BitString.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BitString.java
new file mode 100644
index 0000000..1aa0232
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BitString.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.UniversalTag;
+
+import java.io.IOException;
+
+public class Asn1BitString extends Asn1Simple<byte[]>
+{
+ private int padding;
+
+ public Asn1BitString() {
+ this(null);
+ }
+
+ public Asn1BitString(byte[] value) {
+ this(value, 0);
+ }
+
+ public Asn1BitString(byte[] value, int padding) {
+ super(UniversalTag.BIT_STRING, value);
+ this.padding = padding;
+ }
+
+ public void setPadding(int padding) {
+ this.padding = padding;
+ }
+
+ public int getPadding() {
+ return padding;
+ }
+
+ @Override
+ protected int encodingBodyLength() {
+ return getValue().length + 1;
+ }
+
+ @Override
+ protected void toBytes() {
+ byte[] bytes = new byte[encodingBodyLength()];
+ bytes[0] = (byte)padding;
+ System.arraycopy(getValue(), 0, bytes, 1, bytes.length - 1);
+ setBytes(bytes);
+ }
+
+ @Override
+ protected void toValue() throws IOException {
+ byte[] bytes = getBytes();
+ if (bytes.length < 1) {
+ throw new IOException("Bad stream, zero bytes found for bitstring");
+ }
+ int paddingBits = bytes[0];
+ validatePaddingBits(paddingBits);
+ setPadding(paddingBits);
+
+ byte[] newBytes = new byte[bytes.length - 1];
+ if (bytes.length > 1) {
+ System.arraycopy(bytes, 1, newBytes, 0, bytes.length - 1);
+ }
+ setValue(newBytes);
+ }
+
+ private void validatePaddingBits(int paddingBits) throws IOException {
+ if (paddingBits < 0 || paddingBits > 7) {
+ throw new IOException("Bad padding number: " + paddingBits + ", should be in [0, 7]");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java
new file mode 100644
index 0000000..125b9a2
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.EncodingOption;
+import org.apache.kerby.asn1.LimitedByteBuffer;
+import org.apache.kerby.asn1.UniversalTag;
+
+import java.io.IOException;
+
+public class Asn1BmpString extends Asn1Simple<String>
+{
+ public Asn1BmpString() {
+ super(null);
+ }
+
+ public Asn1BmpString(String value) {
+ super(UniversalTag.BMP_STRING, value);
+ }
+
+ @Override
+ protected int encodingBodyLength() {
+ return getValue().length() * 2;
+ }
+
+ protected void toBytes(EncodingOption encodingOption) {
+ String strValue = getValue();
+ int len = strValue.length();
+ byte[] bytes = new byte[len * 2];
+ char c;
+ for (int i = 0; i != len; i++) {
+ c = strValue.charAt(i);
+ bytes[2 * i] = (byte)(c >> 8);
+ bytes[2 * i + 1] = (byte)c;
+ }
+ setBytes(bytes);
+ }
+
+ protected void toValue() throws IOException {
+ byte[] bytes = getBytes();
+ char[] chars = new char[bytes.length / 2];
+ for (int i = 0; i != chars.length; i++) {
+ chars[i] = (char)((bytes[2 * i] << 8) | (bytes[2 * i + 1] & 0xff));
+ }
+ setValue(new String(chars));
+ }
+
+ @Override
+ protected void decodeBody(LimitedByteBuffer content) throws IOException {
+ if (content.hasLeft() % 2 != 0) {
+ throw new IOException("Bad stream, BMP string expecting multiple of 2 bytes");
+ }
+ super.decodeBody(content);
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java
new file mode 100644
index 0000000..38e0d38
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.LimitedByteBuffer;
+import org.apache.kerby.asn1.UniversalTag;
+
+import java.io.IOException;
+
+public class Asn1Boolean extends Asn1Simple<Boolean>
+{
+ private static final byte[] TRUE_BYTE = new byte[] { (byte)0xff };
+ private static final byte[] FALSE_BYTE = new byte[] { (byte)0x00 };
+
+ public static final Asn1Boolean TRUE = new Asn1Boolean(true);
+ public static final Asn1Boolean FALSE = new Asn1Boolean(false);
+
+ public Asn1Boolean() {
+ this(null);
+ }
+
+ public Asn1Boolean(Boolean value) {
+ super(UniversalTag.BOOLEAN, value);
+ }
+
+ @Override
+ protected int encodingBodyLength() {
+ return 1;
+ }
+
+ @Override
+ protected void decodeBody(LimitedByteBuffer content) throws IOException {
+ if (content.hasLeft() != 1) {
+ throw new IOException("More than 1 byte found for Boolean");
+ }
+ super.decodeBody(content);
+ }
+
+ @Override
+ protected void toBytes() {
+ setBytes(getValue() ? TRUE_BYTE : FALSE_BYTE);
+ }
+
+ protected void toValue() throws IOException {
+ byte[] bytes = getBytes();
+ if (bytes[0] == 0) {
+ setValue(false);
+ } else if (bytes[0] == 0xff) {
+ setValue(true);
+ } else {
+ setValue(true);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java
new file mode 100644
index 0000000..59d4ba5
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java
@@ -0,0 +1,173 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.*;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class Asn1Choice extends AbstractAsn1Type<Asn1Type> {
+
+ private Asn1FieldInfo[] fieldInfos;
+ private Asn1Type[] fields;
+
+ public Asn1Choice(Asn1FieldInfo[] fieldInfos) {
+ super(TagClass.UNIVERSAL, UniversalTag.CHOICE.getValue());
+ setValue(this);
+ this.fieldInfos = fieldInfos;
+ this.fields = new Asn1Type[fieldInfos.length];
+ setEncodingOption(EncodingOption.CONSTRUCTED);
+ }
+
+ @Override
+ public boolean isConstructed() {
+ return true;
+ }
+
+ @Override
+ protected int encodingBodyLength() {
+ AbstractAsn1Type field;
+ TaggingOption taggingOption;
+ for (int i = 0; i < fields.length; ++i) {
+ field = (AbstractAsn1Type) fields[i];
+ if (field != null) {
+ if (fieldInfos[i].isTagged()) {
+ taggingOption = fieldInfos[i].getTaggingOption();
+ return field.taggedEncodingLength(taggingOption);
+ } else {
+ return field.encodingLength();
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ protected void encodeBody(ByteBuffer buffer) {
+ Asn1Type field;
+ TaggingOption taggingOption;
+ for (int i = 0; i < fields.length; ++i) {
+ field = fields[i];
+ if (field != null) {
+ if (fieldInfos[i].isTagged()) {
+ taggingOption = fieldInfos[i].getTaggingOption();
+ field.taggedEncode(buffer, taggingOption);
+ } else {
+ field.encode(buffer);
+ }
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void decode(LimitedByteBuffer content) throws IOException {
+ int foundPos = -1;
+ Asn1Item item = decodeOne(content);
+ for (int i = 0; i < fieldInfos.length; ++i) {
+ if (item.isContextSpecific()) {
+ if (fieldInfos[i].getTagNo() == item.tagNo()) {
+ foundPos = i;
+ break;
+ }
+ } else {
+ initField(i);
+ if (fields[i].tagFlags() == item.tagFlags() &&
+ fields[i].tagNo() == item.tagNo()) {
+ foundPos = i;
+ break;
+ } else {
+ fields[i] = null;
+ }
+ }
+ }
+ if (foundPos == -1) {
+ throw new RuntimeException("Unexpected item with (tagFlags, tagNo): ("
+ + item.tagFlags() + ", " + item.tagNo() + ")");
+ }
+
+ if (! item.isFullyDecoded()) {
+ AbstractAsn1Type fieldValue = (AbstractAsn1Type) fields[foundPos];
+ if (item.isContextSpecific()) {
+ item.decodeValueWith(fieldValue, fieldInfos[foundPos].getTaggingOption());
+ } else {
+ item.decodeValueWith(fieldValue);
+ }
+ }
+ fields[foundPos] = item.getValue();
+ }
+
+ protected void decodeBody(LimitedByteBuffer content) throws IOException {
+ // Not used
+ }
+
+ private void initField(int idx) {
+ try {
+ fields[idx] = fieldInfos[idx].getType().newInstance();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Bad field info specified at index of " + idx, e);
+ }
+ }
+
+ protected <T extends Asn1Type> T getFieldAs(int index, Class<T> t) {
+ Asn1Type value = fields[index];
+ if (value == null) return null;
+ return (T) value;
+ }
+
+ protected void setFieldAs(int index, Asn1Type value) {
+ fields[index] = value;
+ }
+
+ protected String getFieldAsString(int index) {
+ Asn1Type value = fields[index];
+ if (value == null) return null;
+
+ if (value instanceof Asn1String) {
+ return ((Asn1String) value).getValue();
+ }
+
+ throw new RuntimeException("The targeted field type isn't of string");
+ }
+
+ protected byte[] getFieldAsOctets(int index) {
+ Asn1OctetString value = getFieldAs(index, Asn1OctetString.class);
+ if (value != null) return value.getValue();
+ return null;
+ }
+
+ protected void setFieldAsOctets(int index, byte[] bytes) {
+ Asn1OctetString value = new Asn1OctetString(bytes);
+ setFieldAs(index, value);
+ }
+
+ protected Integer getFieldAsInteger(int index) {
+ Asn1Integer value = getFieldAs(index, Asn1Integer.class);
+ if (value != null) {
+ return value.getValue();
+ }
+ return null;
+ }
+
+ protected void setFieldAsInt(int index, int value) {
+ setFieldAs(index, new Asn1Integer(value));
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java
new file mode 100644
index 0000000..58dc6a3
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.EncodingOption;
+import org.apache.kerby.asn1.LimitedByteBuffer;
+import org.apache.kerby.asn1.TagClass;
+import org.apache.kerby.asn1.UniversalTag;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Asn1Collection extends AbstractAsn1Type<List<Asn1Item>>
+{
+ public Asn1Collection(TagClass tagClass, int tagNo) {
+ super(tagClass, tagNo);
+ setValue(new ArrayList<Asn1Item>());
+ setEncodingOption(EncodingOption.CONSTRUCTED);
+ }
+
+ @Override
+ public boolean isConstructed() {
+ return true;
+ }
+
+ public void addItem(Asn1Type value) {
+ if (value instanceof Asn1Item) {
+ getValue().add((Asn1Item) value);
+ } else {
+ getValue().add(new Asn1Item(value));
+ }
+ }
+
+ public void clear() {
+ getValue().clear();
+ }
+
+ @Override
+ protected int encodingBodyLength() {
+ List<Asn1Item> valueItems = getValue();
+ int allLen = 0;
+ for (Asn1Item item : valueItems) {
+ if (item != null) {
+ allLen += item.encodingLength();
+ }
+ }
+ return allLen;
+ }
+
+ @Override
+ protected void encodeBody(ByteBuffer buffer) {
+ List<Asn1Item> valueItems = getValue();
+ for (Asn1Item item : valueItems) {
+ if (item != null) {
+ item.encode(buffer);
+ }
+ }
+ }
+
+ @Override
+ protected void decodeBody(LimitedByteBuffer content) throws IOException {
+ while (content.available()) {
+ Asn1Type aValue = decodeOne(content);
+ if (aValue != null) {
+ if (aValue instanceof Asn1Item) {
+ addItem((Asn1Item) aValue);
+ } else {
+ addItem(aValue);
+ }
+ } else {
+ throw new RuntimeException("Unexpected running into here");
+ }
+ }
+ }
+
+ public static boolean isCollection(int tagNo) {
+ return isCollection(UniversalTag.fromValue(tagNo));
+ }
+
+ public static boolean isCollection(UniversalTag tagNo) {
+ switch (tagNo) {
+ case SEQUENCE:
+ case SEQUENCE_OF:
+ case SET:
+ case SET_OF:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static Asn1Type createCollection(int tagNo) {
+ if (! isCollection(tagNo)) {
+ throw new IllegalArgumentException("Not collection type, tag: " + tagNo);
+ }
+ return createCollection(UniversalTag.fromValue(tagNo));
+ }
+
+ public static Asn1Type createCollection(UniversalTag tagNo) {
+ if (! isCollection(tagNo)) {
+ throw new IllegalArgumentException("Not collection type, tag: " + tagNo);
+ }
+
+ switch (tagNo) {
+ case SEQUENCE:
+ return new Asn1Sequence();
+ case SEQUENCE_OF:
+ return new Asn1Sequence();
+ case SET:
+ return new Asn1Set();
+ case SET_OF:
+ return new Asn1Set();
+ default:
+ throw new IllegalArgumentException("Unexpected tag " + tagNo.getValue());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java
new file mode 100644
index 0000000..5a8709f
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.TagClass;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class Asn1CollectionOf<T extends Asn1Type> extends Asn1Collection
+{
+ public Asn1CollectionOf(TagClass tagClass, int tagNo) {
+ super(tagClass, tagNo);
+ }
+
+ public List<T> getElements() {
+ List<Asn1Item> items = getValue();
+ int nElements = items != null ? items.size() : 0;
+ List<T> results = new ArrayList<T>(nElements);
+ if (nElements > 0) {
+ for (Asn1Item item : items) {
+ if (!item.isFullyDecoded()) {
+ try {
+ item.decodeValueAs(getElementType());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ results.add((T) item.getValue());
+ }
+ }
+ return results;
+ }
+
+ public void setElements(List<T> elements) {
+ super.clear();
+
+ for (T ele : elements) {
+ addElement(ele);
+ }
+ }
+
+ public void addElements(T ... elements) {
+ for (T ele : elements) {
+ addElement(ele);
+ }
+ }
+
+ public void addElement(T element) {
+ super.addItem(element);
+ }
+
+ @Override
+ public void addItem(Asn1Type value) {
+ Class<T> eleType = getElementType();
+ if (value instanceof Asn1Item) {
+ super.addItem(value);
+ } else if (! eleType.isInstance(value)) {
+ throw new RuntimeException("Unexpected element type " + value.getClass().getCanonicalName());
+ } else {
+ addElement((T) value);
+ }
+ }
+
+ protected Class<T> getElementType() {
+ Class<T> elementType = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ return elementType;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/7d9261af/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java
new file mode 100644
index 0000000..b506c2f
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.asn1.type;
+
+import org.apache.kerby.asn1.EncodingOption;
+import org.apache.kerby.asn1.LimitedByteBuffer;
+import org.apache.kerby.asn1.TagClass;
+import org.apache.kerby.asn1.TaggingOption;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * For collection type that may consist of tagged fields
+ */
+public abstract class Asn1CollectionType extends AbstractAsn1Type<Asn1CollectionType> {
+ private Asn1FieldInfo[] fieldInfos;
+ private Asn1Type[] fields;
+
+ public Asn1CollectionType(int universalTagNo, Asn1FieldInfo[] fieldInfos) {
+ super(TagClass.UNIVERSAL, universalTagNo);
+ setValue(this);
+ this.fieldInfos = fieldInfos;
+ this.fields = new Asn1Type[fieldInfos.length];
+ setEncodingOption(EncodingOption.CONSTRUCTED);
+ }
+
+ @Override
+ public boolean isConstructed() {
+ return true;
+ }
+
+ @Override
+ protected int encodingBodyLength() {
+ int allLen = 0;
+ AbstractAsn1Type field;
+ TaggingOption taggingOption;
+ for (int i = 0; i < fields.length; ++i) {
+ field = (AbstractAsn1Type) fields[i];
+ if (field != null) {
+ if (fieldInfos[i].isTagged()) {
+ taggingOption = fieldInfos[i].getTaggingOption();
+ allLen += field.taggedEncodingLength(taggingOption);
+ } else {
+ allLen += field.encodingLength();
+ }
+ }
+ }
+ return allLen;
+ }
+
+ @Override
+ protected void encodeBody(ByteBuffer buffer) {
+ Asn1Type field;
+ TaggingOption taggingOption;
+ for (int i = 0; i < fields.length; ++i) {
+ field = fields[i];
+ if (field != null) {
+ if (fieldInfos[i].isTagged()) {
+ taggingOption = fieldInfos[i].getTaggingOption();
+ field.taggedEncode(buffer, taggingOption);
+ } else {
+ field.encode(buffer);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void decodeBody(LimitedByteBuffer content) throws IOException {
+ initFields();
+
+ Asn1Collection coll = createCollection();
+ coll.decode(tagFlags(), tagNo(), content);
+
+ int lastPos = -1, foundPos = -1;
+ for (Asn1Item item : coll.getValue()) {
+ foundPos = -1;
+ for (int i = lastPos + 1; i < fieldInfos.length; ++i) {
+ if (item.isContextSpecific()) {
+ if(fieldInfos[i].getTagNo() == item.tagNo()) {
+ foundPos = i;
+ break;
+ }
+ } else if (fields[i].tagFlags() == item.tagFlags() &&
+ fields[i].tagNo() == item.tagNo()) {
+ foundPos = i;
+ break;
+ }
+ }
+ if (foundPos == -1) {
+ throw new RuntimeException("Unexpected item with (tagFlags, tagNo): ("
+ + item.tagFlags() + ", " + item.tagNo() + ")");
+ }
+
+ if (! item.isFullyDecoded()) {
+ AbstractAsn1Type fieldValue = (AbstractAsn1Type) fields[foundPos];
+ if (item.isContextSpecific()) {
+ item.decodeValueWith(fieldValue, fieldInfos[foundPos].getTaggingOption());
+ } else {
+ item.decodeValueWith(fieldValue);
+ }
+ }
+ fields[foundPos] = item.getValue();
+ lastPos = foundPos;
+ }
+ }
+
+ private void initFields() {
+ for (int i = 0; i < fieldInfos.length; ++i) {
+ try {
+ fields[i] = fieldInfos[i].getType().newInstance();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Bad field info specified at index of " + i, e);
+ }
+ }
+ }
+
+ protected abstract Asn1Collection createCollection();
+
+ protected <T extends Asn1Type> T getFieldAs(int index, Class<T> t) {
+ Asn1Type value = fields[index];
+ if (value == null) return null;
+ return (T) value;
+ }
+
+ protected void setFieldAs(int index, Asn1Type value) {
+ fields[index] = value;
+ }
+
+ protected String getFieldAsString(int index) {
+ Asn1Type value = fields[index];
+ if (value == null) return null;
+
+ if (value instanceof Asn1String) {
+ return ((Asn1String) value).getValue();
+ }
+
+ throw new RuntimeException("The targeted field type isn't of string");
+ }
+
+ protected byte[] getFieldAsOctets(int index) {
+ Asn1OctetString value = getFieldAs(index, Asn1OctetString.class);
+ if (value != null) return value.getValue();
+ return null;
+ }
+
+ protected void setFieldAsOctets(int index, byte[] bytes) {
+ Asn1OctetString value = new Asn1OctetString(bytes);
+ setFieldAs(index, value);
+ }
+
+ protected Integer getFieldAsInteger(int index) {
+ Asn1Integer value = getFieldAs(index, Asn1Integer.class);
+ if (value != null) {
+ return value.getValue();
+ }
+ return null;
+ }
+
+ protected void setFieldAsInt(int index, int value) {
+ setFieldAs(index, new Asn1Integer(value));
+ }
+
+ protected Asn1Type getFieldAsAny(int index) {
+ Asn1Any any = getFieldAs(index, Asn1Any.class);
+ if (any != null) {
+ return any.getValue();
+ }
+ return null;
+ }
+
+ protected void setFieldAsAny(int index, Asn1Type value) {
+ if (value != null) {
+ setFieldAs(index, new Asn1Any(value));
+ }
+ }
+}