1 module hunt.security.util.DerOutputStream;
2 
3 import hunt.stream.ByteArrayOutputStream;
4 import hunt.security.util.DerEncoder;
5 import hunt.security.util.DerValue;
6 
7 import hunt.stream.Common;
8 import hunt.Exceptions;
9 
10 import std.datetime;
11 import std.bitmanip;
12 
13 /**
14  * Output stream marshaling DER-encoded data.  This is eventually provided
15  * in the form of a byte array; there is no advance limit on the size of
16  * that byte array.
17  *
18  * <P>At this time, this class supports only a subset of the types of
19  * DER data encodings which are defined.  That subset is sufficient for
20  * generating most X.509 certificates.
21  *
22  *
23  * @author David Brownell
24  * @author Amit Kapoor
25  * @author Hemma Prafullchandra
26  */
27 class DerOutputStream : ByteArrayOutputStream, DerEncoder {
28     /**
29      * Construct an DER output stream.
30      *
31      * @param size how large a buffer to preallocate.
32      */
33     this(int size) { super(size); }
34 
35     /**
36      * Construct an DER output stream.
37      */
38     this() { }
39 
40     alias write = ByteArrayOutputStream.write;
41 
42     /**
43      * Writes tagged, pre-marshaled data.  This calcuates and encodes
44      * the length, so that the output data is the standard triple of
45      * { tag, length, data } used by all DER values.
46      *
47      * @param tag the DER value tag for the data, such as
48      *          <em>DerValue.tag_Sequence</em>
49      * @param buf buffered data, which must be DER-encoded
50      */
51     void write(byte tag, byte[] buf) {
52         write(tag);
53         putLength(cast(int)buf.length);
54         write(buf, 0, cast(int)buf.length);
55     }
56 
57     /**
58      * Writes tagged data using buffer-to-buffer copy.  As above,
59      * this writes a standard DER record.  This is often used when
60      * efficiently encapsulating values in sequences.
61      *
62      * @param tag the DER value tag for the data, such as
63      *          <em>DerValue.tag_Sequence</em>
64      * @param ot buffered data
65      */
66     void write(byte tag, DerOutputStream ot) {
67         write(tag);
68         putLength(ot.count);
69         write(ot.buf, 0, ot.count);
70     }
71 
72     /**
73      * Writes implicitly tagged data using buffer-to-buffer copy.  As above,
74      * this writes a standard DER record.  This is often used when
75      * efficiently encapsulating implicitly tagged values.
76      *
77      * @param tag the DER value of the context-specific tag that replaces
78      * original tag of the value in the output, such as in
79      * <pre>
80      *          <em> <field> [N] IMPLICIT <type></em>
81      * </pre>
82      * For example, <em>FooLength [1] IMPLICIT INTEGER</em>, with value=4;
83      * would be encoded as "81 01 04"  whereas in explicit
84      * tagging it would be encoded as "A1 03 02 01 04".
85      * Notice that the tag is A1 and not 81, this is because with
86      * explicit tagging the form is always constructed.
87      * @param value original value being implicitly tagged
88      */
89     void writeImplicit(byte tag, DerOutputStream value)
90     {
91         write(tag);
92         write(value.buf, 1, value.count-1);
93     }
94 
95     /**
96      * Marshals pre-encoded DER value onto the output stream.
97      */
98     void putDerValue(DerValue val) {
99         // val.encode(this);
100         implementationMissing();
101     }
102 
103     /*
104      * PRIMITIVES -- these are "universal" ASN.1 simple types.
105      *
106      *  BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL
107      *  OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF)
108      *  PrintableString, T61String, IA5String, UTCTime
109      */
110 
111     /**
112      * Marshals a DER bool on the output stream.
113      */
114     void putBoolean(bool val) {
115         write(DerValue.tag_Boolean);
116         putLength(1);
117         if (val) {
118             write(0xff);
119         } else {
120             write(0);
121         }
122     }
123 
124     /**
125      * Marshals a DER enumerated on the output stream.
126      * @param i the enumerated value.
127      */
128     void putEnumerated(int i) {
129         write(DerValue.tag_Enumerated);
130         putIntegerContents(i);
131     }
132 
133     /**
134      * Marshals a DER integer on the output stream.
135      *
136      * @param i the integer in the form of a BigInt.
137      */
138     // void putInteger(BigInt i) {
139     //     write(DerValue.tag_Integer);
140     //     byte[]    buf = i.toByteArray(); // least number  of bytes
141     //     putLength(buf.length);
142     //     write(buf, 0, buf.length);
143     // }
144 
145     /**
146      * Marshals a DER integer on the output stream.
147      * @param i the integer in the form of an Integer.
148      */
149     // void putInteger(Integer i) {
150     //     putInteger(i.intValue());
151     // }
152 
153     /**
154      * Marshals a DER integer on the output stream.
155      * @param i the integer.
156      */
157     void putInteger(int i) {
158         write(DerValue.tag_Integer);
159         putIntegerContents(i);
160     }
161 
162     private void putIntegerContents(int i) {
163 
164         byte[] bytes = new byte[4];
165         int start = 0;
166 
167         // Obtain the four bytes of the int
168 
169         bytes[3] = cast(byte) (i & 0xff);
170         bytes[2] = cast(byte)((i & 0xff00) >>> 8);
171         bytes[1] = cast(byte)((i & 0xff0000) >>> 16);
172         bytes[0] = cast(byte)((i & 0xff000000) >>> 24);
173 
174         // Reduce them to the least number of bytes needed to
175         // represent this int
176 
177         if (bytes[0] == cast(byte)0xff) {
178 
179             // Eliminate redundant 0xff
180 
181             for (int j = 0; j < 3; j++) {
182                 if ((bytes[j] == cast(byte)0xff) &&
183                     ((bytes[j+1] & 0x80) == 0x80))
184                     start++;
185                 else
186                     break;
187              }
188          } else if (bytes[0] == 0x00) {
189 
190              // Eliminate redundant 0x00
191 
192             for (int j = 0; j < 3; j++) {
193                 if ((bytes[j] == 0x00) &&
194                     ((bytes[j+1] & 0x80) == 0))
195                     start++;
196                 else
197                     break;
198             }
199         }
200 
201         putLength(4 - start);
202         for (int k = start; k < 4; k++)
203             write(bytes[k]);
204     }
205 
206     /**
207      * Marshals a DER bit string on the output stream. The bit
208      * string must be byte-aligned.
209      *
210      * @param bits the bit string, MSB first
211      */
212     void putBitString(byte[] bits) {
213         write(DerValue.tag_BitString);
214         putLength(cast(int)bits.length + 1);
215         write(0);               // all of last octet is used
216         write(bits);
217     }
218 
219     /**
220      * Marshals a DER bit string on the output stream.
221      * The bit strings need not be byte-aligned.
222      *
223      * @param bits the bit string, MSB first
224      */
225     void putUnalignedBitString(BitArray ba) {
226         implementationMissing();
227         // byte[] bits = ba.toByteArray();
228 
229         // write(DerValue.tag_BitString);
230         // putLength(bits.length + 1);
231         // write(bits.length*8 - ba.length()); // excess bits in last octet
232         // write(bits);
233     }
234 
235     /**
236      * Marshals a truncated DER bit string on the output stream.
237      * The bit strings need not be byte-aligned.
238      *
239      * @param bits the bit string, MSB first
240      */
241     // void putTruncatedUnalignedBitString(BitArray ba) {
242     //     putUnalignedBitString(ba.truncate());
243     // }
244 
245     /**
246      * DER-encodes an ASN.1 OCTET STRING value on the output stream.
247      *
248      * @param octets the octet string
249      */
250     void putOctetString(byte[] octets) {
251         write(DerValue.tag_OctetString, octets);
252     }
253 
254     /**
255      * Marshals a DER "null" value on the output stream.  These are
256      * often used to indicate optional values which have been omitted.
257      */
258     void putNull() {
259         write(DerValue.tag_Null);
260         putLength(0);
261     }
262 
263     /**
264      * Marshals an object identifier (OID) on the output stream.
265      * Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct.
266      */
267     // void putOID(ObjectIdentifier oid) {
268     //     oid.encode(this);
269     // }
270 
271     /**
272      * Marshals a sequence on the output stream.  This supports both
273      * the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF"
274      * (one to N values) constructs.
275      */
276     // void putSequence(DerValue[] seq) {
277     //     DerOutputStream bytes = new DerOutputStream();
278     //     int i;
279 
280     //     for (i = 0; i < seq.length; i++)
281     //         seq[i].encode(bytes);
282 
283     //     write(DerValue.tag_Sequence, bytes);
284     // }
285 
286     /**
287      * Marshals the contents of a set on the output stream without
288      * ordering the elements.  Ok for BER encoding, but not for DER
289      * encoding.
290      *
291      * For DER encoding, use orderedPutSet() or orderedPutSetOf().
292      */
293     // void putSet(DerValue[] set) {
294     //     DerOutputStream bytes = new DerOutputStream();
295     //     int i;
296 
297     //     for (i = 0; i < set.length; i++)
298     //         set[i].encode(bytes);
299 
300     //     write(DerValue.tag_Set, bytes);
301     // }
302 
303     /**
304      * Marshals the contents of a set on the output stream.  Sets
305      * are semantically unordered, but DER requires that encodings of
306      * set elements be sorted into ascending lexicographical order
307      * before being output.  Hence sets with the same tags and
308      * elements have the same DER encoding.
309      *
310      * This method supports the ASN.1 "SET OF" construct, but not
311      * "SET", which uses a different order.
312      */
313     // void putOrderedSetOf(byte tag, DerEncoder[] set) {
314     //     putOrderedSet(tag, set, lexOrder);
315     // }
316 
317     /**
318      * Marshals the contents of a set on the output stream.  Sets
319      * are semantically unordered, but DER requires that encodings of
320      * set elements be sorted into ascending tag order
321      * before being output.  Hence sets with the same tags and
322      * elements have the same DER encoding.
323      *
324      * This method supports the ASN.1 "SET" construct, but not
325      * "SET OF", which uses a different order.
326      */
327     // void putOrderedSet(byte tag, DerEncoder[] set) {
328     //     putOrderedSet(tag, set, tagOrder);
329     // }
330 
331     /**
332      *  Lexicographical order comparison on byte arrays, for ordering
333      *  elements of a SET OF objects in DER encoding.
334      */
335     // private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder();
336 
337     /**
338      *  Tag order comparison on byte arrays, for ordering elements of
339      *  SET objects in DER encoding.
340      */
341     // private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder();
342 
343     /**
344      * Marshals a the contents of a set on the output stream with the
345      * encodings of its sorted in increasing order.
346      *
347      * @param order the order to use when sorting encodings of components.
348      */
349     // private void putOrderedSet(byte tag, DerEncoder[] set,
350     //                            Comparator<byte[]> order) {
351     //     DerOutputStream[] streams = new DerOutputStream[set.length];
352 
353     //     for (int i = 0; i < set.length; i++) {
354     //         streams[i] = new DerOutputStream();
355     //         set[i].derEncode(streams[i]);
356     //     }
357 
358     //     // order the element encodings
359     //     byte[][] bufs = new byte[streams.length][];
360     //     for (int i = 0; i < streams.length; i++) {
361     //         bufs[i] = streams[i].toByteArray();
362     //     }
363     //     Arrays.<byte[]>sort(bufs, order);
364 
365     //     DerOutputStream bytes = new DerOutputStream();
366     //     for (int i = 0; i < streams.length; i++) {
367     //         bytes.write(bufs[i]);
368     //     }
369     //     write(tag, bytes);
370 
371     // }
372 
373     /**
374      * Marshals a string as a DER encoded UTF8String.
375      */
376     void putUTF8String(string s) {
377         writeString(s, DerValue.tag_UTF8String, "UTF8");
378     }
379 
380     /**
381      * Marshals a string as a DER encoded PrintableString.
382      */
383     void putPrintableString(string s) {
384         writeString(s, DerValue.tag_PrintableString, "ASCII");
385     }
386 
387     /**
388      * Marshals a string as a DER encoded T61String.
389      */
390     void putT61String(string s) {
391         /*
392          * Works for characters that are defined in both ASCII and
393          * T61.
394          */
395         writeString(s, DerValue.tag_T61String, "ISO-8859-1");
396     }
397 
398     /**
399      * Marshals a string as a DER encoded IA5String.
400      */
401     void putIA5String(string s) {
402         writeString(s, DerValue.tag_IA5String, "ASCII");
403     }
404 
405     /**
406      * Marshals a string as a DER encoded BMPString.
407      */
408     void putBMPString(string s) {
409         writeString(s, DerValue.tag_BMPString, "UnicodeBigUnmarked");
410     }
411 
412     /**
413      * Marshals a string as a DER encoded GeneralString.
414      */
415     void putGeneralString(string s) {
416         writeString(s, DerValue.tag_GeneralString, "ASCII");
417     }
418 
419     /**
420      * Private helper routine for writing DER encoded string values.
421      * @param s the string to write
422      * @param stringTag one of the DER string tags that indicate which
423      * encoding should be used to write the string out.
424      * @param enc the name of the encoder that should be used corresponding
425      * to the above tag.
426      */
427     private void writeString(string s, byte stringTag, string enc)
428         {
429 implementationMissing();
430         // byte[] data = s.getBytes(enc);
431         // write(stringTag);
432         // putLength(data.length);
433         // write(data);
434     }
435 
436     /**
437      * Marshals a DER UTC time/date value.
438      *
439      * <P>YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time
440      * and with seconds (even if seconds=0) as per RFC 3280.
441      */
442     void putUTCTime(Date d) {
443         putTime(d, DerValue.tag_UtcTime);
444     }
445 
446     /**
447      * Marshals a DER Generalized Time/date value.
448      *
449      * <P>YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time
450      * and with seconds (even if seconds=0) as per RFC 3280.
451      */
452     void putGeneralizedTime(Date d) {
453         putTime(d, DerValue.tag_GeneralizedTime);
454     }
455 
456     /**
457      * Private helper routine for marshalling a DER UTC/Generalized
458      * time/date value. If the tag specified is not that for UTC Time
459      * then it defaults to Generalized Time.
460      * @param d the date to be marshalled
461      * @param tag the tag for UTC Time or Generalized Time
462      */
463     private void putTime(Date d, byte tag) {
464         implementationMissing();
465 
466         /*
467          * Format the date.
468          */
469 
470         // TimeZone tz = TimeZone.getTimeZone("GMT");
471         // string pattern = null;
472 
473         // if (tag == DerValue.tag_UtcTime) {
474         //     pattern = "yyMMddHHmmss'Z'";
475         // } else {
476         //     tag = DerValue.tag_GeneralizedTime;
477         //     pattern = "yyyyMMddHHmmss'Z'";
478         // }
479 
480         // SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.US);
481         // sdf.setTimeZone(tz);
482         // byte[] time = (sdf.format(d)).getBytes("ISO-8859-1");
483 
484         // /*
485         //  * Write the formatted date.
486         //  */
487 
488         // write(tag);
489         // putLength(cast(int)time.length);
490         // write(time);
491     }
492 
493     /**
494      * Put the encoding of the length in the stream.
495      *
496      * @params len the length of the attribute.
497      * @exception IOException on writing errors.
498      */
499     void putLength(int len) {
500         if (len < 128) {
501             write(cast(byte)len);
502 
503         } else if (len < (1 << 8)) {
504             write(cast(byte)0x081);
505             write(cast(byte)len);
506 
507         } else if (len < (1 << 16)) {
508             write(cast(byte)0x082);
509             write(cast(byte)(len >> 8));
510             write(cast(byte)len);
511 
512         } else if (len < (1 << 24)) {
513             write(cast(byte)0x083);
514             write(cast(byte)(len >> 16));
515             write(cast(byte)(len >> 8));
516             write(cast(byte)len);
517 
518         } else {
519             write(cast(byte)0x084);
520             write(cast(byte)(len >> 24));
521             write(cast(byte)(len >> 16));
522             write(cast(byte)(len >> 8));
523             write(cast(byte)len);
524         }
525     }
526 
527     /**
528      * Put the tag of the attribute in the stream.
529      *
530      * @params class the tag class type, one of UNIVERSAL, CONTEXT,
531      *                            APPLICATION or PRIVATE
532      * @params form if true, the value is constructed, otherwise it is
533      * primitive.
534      * @params val the tag value
535      */
536     void putTag(byte tagClass, bool form, byte val) {
537         byte tag = cast(byte)(tagClass | val);
538         if (form) {
539             tag |= cast(byte)0x20;
540         }
541         write(tag);
542     }
543 
544     /**
545      *  Write the current contents of this <code>DerOutputStream</code>
546      *  to an <code>OutputStream</code>.
547      *
548      *  @exception IOException on output error.
549      */
550     void derEncode(OutputStream ot) {
551         ot.write(toByteArray());
552     }
553 }