1 module hunt.security.x509.CRLExtensions;
2 
3 
4 import hunt.security.util.DerInputStream;
5 import hunt.security.util.DerOutputStream;
6 import hunt.security.util.DerValue;
7 import hunt.security.x509.CertAttrSet;
8 import hunt.security.x509.Extension;
9 
10 import hunt.stream.Common;
11 
12 import hunt.collection;
13 import hunt.Exceptions;
14 
15 /**
16  * This class defines the CRL Extensions.
17  * It is used for both CRL Extensions and CRL Entry Extensions,
18  * which are defined are follows:
19  * <pre>
20  * TBSCertList  ::=  SEQUENCE  {
21  *    version              Version OPTIONAL,   -- if present, must be v2
22  *    signature            AlgorithmIdentifier,
23  *    issuer               Name,
24  *    thisUpdate           Time,
25  *    nextUpdate           Time  OPTIONAL,
26  *    revokedCertificates  SEQUENCE OF SEQUENCE  {
27  *        userCertificate         CertificateSerialNumber,
28  *        revocationDate          Time,
29  *        crlEntryExtensions      Extensions OPTIONAL  -- if present, must be v2
30  *    }  OPTIONAL,
31  *    crlExtensions        [0] EXPLICIT Extensions OPTIONAL  -- if present, must be v2
32  * }
33  * </pre>
34  *
35  * @author Hemma Prafullchandra
36  */
37 class CRLExtensions {
38 
39     private Map!(string,Extension) map;
40     private bool unsupportedCritExt = false;
41 
42     /**
43      * Default constructor.
44      */
45     this() { 
46         map = new TreeMap!(string,Extension)();
47         // map = Collections.synchronizedMap(
48         //     new TreeMap!(string,Extension)());
49     }
50 
51     /**
52      * Create the object, decoding the values from the passed DER stream.
53      *
54      * @param in the DerInputStream to read the Extension from, i.e. the
55      *        sequence of extensions.
56      * @exception CRLException on decoding errors.
57      */
58     this(DerInputStream inputStream) {
59         this();
60         initilize(inputStream);
61     }
62 
63     // helper routine
64     private void initilize(DerInputStream derStrm) {
65         try {
66             DerInputStream str = derStrm;
67 
68             byte nextByte = cast(byte)derStrm.peekByte();
69             // check for context specific byte 0; skip it
70             if (((nextByte & 0x0c0) == 0x080) &&
71                 ((nextByte & 0x01f) == 0x000)) {
72                     implementationMissing();
73                 // DerValue val = str.getDerValue();
74                 // str = val.data;
75             }
76 
77             DerValue[] exts = str.getSequence(5);
78             for (int i = 0; i < exts.length; i++) {
79                 Extension ext = new Extension(exts[i]);
80                 parseExtension(ext);
81             }
82         } catch (IOException e) {
83             throw new CRLException("Parsing error: " ~ e.toString());
84         }
85     }
86 
87     // private static final Class[] PARAMS = {Boolean.class, Object.class};
88 
89     // Parse the encoded extension
90     private void parseExtension(Extension ext) {
91         implementationMissing();
92         // try {
93         //     Class<?> extClass = OIDMap.getClass(ext.getExtensionId());
94         //     if (extClass is null) {   // Unsupported extension
95         //         if (ext.isCritical())
96         //             unsupportedCritExt = true;
97         //         if (map.put(ext.getExtensionId().toString(), ext) !is null)
98         //             throw new CRLException("Duplicate extensions not allowed");
99         //         return;
100         //     }
101         //     Constructor<?> cons = extClass.getConstructor(PARAMS);
102         //     Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()),
103         //                                     ext.getExtensionValue()};
104         //     CertAttrSet<?> crlExt = (CertAttrSet<?>)cons.newInstance(passed);
105         //     if (map.put(crlExt.getName(), (Extension)crlExt) !is null) {
106         //         throw new CRLException("Duplicate extensions not allowed");
107         //     }
108         // } catch (InvocationTargetException invk) {
109         //     throw new CRLException(invk.getTargetException().getMessage());
110         // } catch (Exception e) {
111         //     throw new CRLException(e.toString());
112         // }
113     }
114 
115     /**
116      * Encode the extensions in DER form to the stream.
117      *
118      * @param stream the DerOutputStream to marshal the contents to.
119      * @param isExplicit the tag indicating whether this is an entry
120      * extension (false) or a CRL extension (true).
121      * @exception CRLException on encoding errors.
122      */
123     void encode(OutputStream stream, bool isExplicit)
124     {
125         implementationMissing();
126         // try {
127         //     DerOutputStream extOut = new DerOutputStream();
128         //     Collection<Extension> allExts = map.values();
129         //     Object[] objs = allExts.toArray();
130 
131         //     for (int i = 0; i < objs.length; i++) {
132         //         if (objs[i] instanceof CertAttrSet)
133         //             ((CertAttrSet)objs[i]).encode(extOut);
134         //         else if (objs[i] instanceof Extension)
135         //             ((Extension)objs[i]).encode(extOut);
136         //         else
137         //             throw new CRLException("Illegal extension object");
138         //     }
139 
140         //     DerOutputStream seq = new DerOutputStream();
141         //     seq.write(DerValue.tag_Sequence, extOut);
142 
143         //     DerOutputStream tmp = new DerOutputStream();
144         //     if (isExplicit)
145         //         tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT,
146         //                                      true, cast(byte)0), seq);
147         //     else
148         //         tmp = seq;
149 
150         //     stream.write(tmp.toByteArray());
151         // } catch (IOException e) {
152         //     throw new CRLException("Encoding error: " ~ e.toString());
153         // } catch (CertificateException e) {
154         //     throw new CRLException("Encoding error: " ~ e.toString());
155         // }
156     }
157 
158     /**
159      * Get the extension with this as.
160      *
161      * @param as the identifier string for the extension to retrieve.
162      */
163     Extension get(string as) {
164         // X509AttributeName attr = new X509AttributeName(as);
165         string name;
166         // string id = attr.getPrefix();
167         // if (id.equalsIgnoreCase(X509CertImpl.NAME)) { // fully qualified
168         //     int index = as.lastIndexOf(".");
169         //     name = as.substring(index + 1);
170         // } else
171         //     name = as;
172         implementationMissing();
173         return map.get(name);
174     }
175 
176     /**
177      * Set the extension value with this as.
178      *
179      * @param as the identifier string for the extension to set.
180      * @param obj the Object to set the extension identified by the
181      *        as.
182      */
183     void set(string as, Object obj) {
184         map.put(as, cast(Extension)obj);
185     }
186 
187     /**
188      * Delete the extension value with this as.
189      *
190      * @param as the identifier string for the extension to delete.
191      */
192     void remove(string as) {
193         map.remove(as);
194     }
195 
196     /**
197      * Return an enumeration of the extensions.
198      * @return an enumeration of the extensions in this CRL.
199      */
200     Enumeration!Extension getElements() {
201         return Collections.enumeration(map.values());
202     }
203 
204     /**
205      * Return a collection view of the extensions.
206      * @return a collection view of the extensions in this CRL.
207      */
208     Extension[] getAllExtensions() {
209         return map.values();
210     }
211 
212     /**
213      * Return true if a critical extension is found that is
214      * not supported, otherwise return false.
215      */
216     bool hasUnsupportedCriticalExtension() {
217         return unsupportedCritExt;
218     }
219 
220     /**
221      * Compares this CRLExtensions for equality with the specified
222      * object. If the <code>other</code> object is an
223      * <code>instanceof</code> <code>CRLExtensions</code>, then
224      * all the entries are compared with the entries from this.
225      *
226      * @param other the object to test for equality with this CRLExtensions.
227      * @return true iff all the entries match that of the Other,
228      * false otherwise.
229      */
230     override bool opEquals(Object other) {
231         if (this is other)
232             return true;
233         CRLExtensions crlExtensions = cast(CRLExtensions)other;
234         if (crlExtensions is null)
235             return false;
236         // Collection<Extension> otherC =
237         //                 ((CRLExtensions)other).getAllExtensions();
238         Extension[] objs = crlExtensions.getAllExtensions();
239 
240         size_t len = objs.length;
241         if (len != map.size())
242             return false;
243 
244         Extension otherExt, thisExt;
245         string key = null;
246         for (int i = 0; i < len; i++) {
247             implementationMissing();
248             // CertAttrSet!string certAttrSet = cast(CertAttrSet!string)objs[i];
249             // if (certAttrSet !is null)
250             //     key = certAttrSet.getName();
251             // otherExt = objs[i];
252             // if (key is null)
253             //     key = otherExt.getExtensionId().toString();
254             // thisExt = map.get(key);
255             // if (thisExt is null)
256             //     return false;
257             // if (! thisExt.opEquals(otherExt))
258             //     return false;
259         }
260         return true;
261     }
262 
263     /**
264      * Returns a hashcode value for this CRLExtensions.
265      *
266      * @return the hashcode value.
267      */
268     override size_t toHash() @trusted nothrow {
269         return map.toHash();
270     }
271 
272     /**
273      * Returns a string representation of this <tt>CRLExtensions</tt> object
274      * in the form of a set of entries, enclosed in braces and separated
275      * by the ASCII characters "<tt>,&nbsp;</tt>" (comma and space).
276      * <p>Overrides to <tt>toString</tt> method of <tt>Object</tt>.
277      *
278      * @return  a string representation of this CRLExtensions.
279      */
280     override string toString() {
281         return map.toString();
282     }
283 }