1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * @author Stepan M. Mishura 20 * @version $Revision$ 21 */ 22 23 package org.apache.harmony.security.asn1; 24 25 import java.io.IOException; 26 import java.io.InputStream; 27 28 /** 29 * Decodes ASN.1 types encoded with DER (X.690) 30 * 31 * @see <a href="http://asn1.elibel.tm.fr/en/standards/index.htm">ASN.1</a> 32 */ 33 public final class DerInputStream extends BerInputStream { 34 35 /** mask for verifying unused bits for ASN.1 bitstring */ 36 private static final byte[] UNUSED_BITS_MASK = new byte[] { 0x01, 0x03, 37 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; 38 39 public DerInputStream(byte[] encoded) throws IOException { 40 super(encoded, 0, encoded.length); 41 } 42 43 public DerInputStream(byte[] encoded, int offset, int encodingLen) throws IOException { 44 super(encoded, offset, encodingLen); 45 } 46 47 public DerInputStream(InputStream in) throws IOException { 48 super(in); 49 } 50 51 public int next() throws IOException { 52 int tag = super.next(); 53 54 if (length == INDEFINIT_LENGTH) { 55 throw new ASN1Exception("DER: only definite length encoding MUST be used"); 56 } 57 58 // FIXME add check: length encoding uses minimum number of octets 59 60 return tag; 61 } 62 63 public void readBitString() throws IOException { 64 if (tag == ASN1Constants.TAG_C_BITSTRING) { 65 throw new ASN1Exception("ASN.1 bitstring: constructed identifier at [" + tagOffset 66 + "]. Not valid for DER."); 67 } 68 69 super.readBitString(); 70 71 //check: unused bits values - MUST be 0 72 if (length > 1 73 && buffer[contentOffset] != 0 74 && (buffer[offset - 1] & UNUSED_BITS_MASK[buffer[contentOffset] - 1]) != 0) { 75 throw new ASN1Exception("ASN.1 bitstring: wrong content at [" + contentOffset 76 + "]. DER requires zero unused bits in final octet."); 77 } 78 } 79 80 public void readBoolean() throws IOException { 81 super.readBoolean(); 82 83 // check encoded content 84 if (buffer[contentOffset] != 0 && buffer[contentOffset] != (byte) 0xFF) { 85 throw new ASN1Exception("ASN.1 boolean: wrong content at [" + contentOffset 86 + "]. DER allows only 0x00 or 0xFF values"); 87 } 88 } 89 90 public void readOctetString() throws IOException { 91 if (tag == ASN1Constants.TAG_C_OCTETSTRING) { 92 throw new ASN1Exception("ASN.1 octetstring: constructed identifier at [" + tagOffset 93 + "]. Not valid for DER."); 94 } 95 super.readOctetString(); 96 } 97 98 public void readSequence(ASN1Sequence sequence) throws IOException { 99 // 100 // According to ASN.1 DER spec. sequence MUST not include 101 // any encoding which value is equal to its default value 102 // 103 // Verification of this assertion is not implemented 104 // 105 super.readSequence(sequence); 106 } 107 108 public void readSetOf(ASN1SetOf setOf) throws IOException { 109 // 110 // According to ASN.1 DER spec. set of MUST appear in 111 // ascending order (short component are padded for comparison) 112 // 113 // Verification of this assertion is not implemented 114 // 115 super.readSetOf(setOf); 116 } 117 118 public void readString(ASN1StringType type) throws IOException { 119 if (tag == type.constrId) { 120 throw new ASN1Exception("ASN.1 string: constructed identifier at [" + tagOffset 121 + "]. Not valid for DER."); 122 } 123 super.readString(type); 124 } 125 126 public void readUTCTime() throws IOException { 127 if (tag == ASN1Constants.TAG_C_UTCTIME) { 128 // It is a string type and it can be encoded as primitive or constructed. 129 throw new ASN1Exception("ASN.1 UTCTime: constructed identifier at [" + tagOffset 130 + "]. Not valid for DER."); 131 } 132 133 // check format: DER uses YYMMDDHHMMSS'Z' only 134 if (length != ASN1UTCTime.UTC_HMS) { 135 throw new ASN1Exception("ASN.1 UTCTime: wrong format for DER, identifier at [" 136 + tagOffset + "]"); 137 } 138 139 super.readUTCTime(); 140 } 141 142 public void readGeneralizedTime() throws IOException { 143 if (tag == ASN1Constants.TAG_C_GENERALIZEDTIME) { 144 // It is a string type and it can be encoded as primitive or constructed. 145 throw new ASN1Exception("ASN.1 GeneralizedTime: constructed identifier at [" 146 + tagOffset + "]. Not valid for DER."); 147 } 148 149 super.readGeneralizedTime(); 150 } 151 } 152