1 /**
2 * PEM Encoding/Decoding
3 * 
4 * Copyright:
5 * (C) 1999-2007 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 
12 module botan.codec.pem;
13 import botan.filters.filters;
14 import botan.utils.parsing;
15 import botan.filters.data_src;
16 import botan.filters.b64_filt;
17 import botan.utils.types;
18 import std.array : Appender;
19 import botan.constants;
20 
21 struct PEM
22 {
23 
24     /**
25     * Encode some binary data in PEM format
26     */
27     static string encode(const(ubyte)* der, size_t length, in string label, size_t width = 64)
28     {
29         immutable(string) PEM_HEADER = "-----BEGIN " ~ label ~ "-----\n";
30         immutable(string) PEM_TRAILER = "-----END " ~ label ~ "-----\n";
31         
32         Pipe pipe = Pipe(new Base64Encoder(true, width));
33         pipe.processMsg(der, length);
34         return (PEM_HEADER ~ pipe.toString() ~ PEM_TRAILER);
35     }
36 
37     /**
38     * Encode some binary data in PEM format
39     */
40     static string encode(ALLOC)(auto const ref Vector!(ubyte, ALLOC) data, 
41                                     in string label, size_t line_width = 64)
42     {
43         return encode(data.ptr, data.length, label, line_width);
44     }
45 
46     /**
47     * Encode some binary data in PEM format
48     */
49     static string encode(ALLOC)(auto const ref RefCounted!(Vector!(ubyte, ALLOC), ALLOC) data, 
50                                     in string label, size_t line_width = 64)
51     {
52         return encode(data.ptr, data.length, label, line_width);
53     }
54 
55     /**
56     * Decode PEM data
57     * Params:
58     *  pem = a datasource containing PEM encoded data
59     *  label = is set to the PEM label found for later inspection
60     */
61     /*
62     * Decode PEM down to raw BER/DER
63     */
64     static SecureVector!ubyte decode(DataSource source, ref string label)
65     {
66         //logTrace("PEM decode");
67         Appender!string label_buf;
68         __gshared immutable size_t RANDOM_CHAR_LIMIT = 8;
69         
70         const string PEM_HEADER1 = "-----BEGIN ";
71         const string PEM_HEADER2 = "-----";
72         size_t position = 0;
73         
74         while (position != PEM_HEADER1.length)
75         {
76             ubyte b;
77             if (!source.readByte(b))
78                 throw new DecodingError("PEM: No PEM header found");
79             if (b == PEM_HEADER1[position])
80                 ++position;
81             else if (position >= RANDOM_CHAR_LIMIT)
82                 throw new DecodingError("PEM: Malformed PEM header");
83             else
84                 position = 0;
85         }
86         position = 0;
87         while (position != PEM_HEADER2.length)
88         {
89             ubyte b;
90             if (!source.readByte(b))
91                 throw new DecodingError("PEM: No PEM header found");
92             if (b == PEM_HEADER2[position])
93                 ++position;
94             else if (position)
95                 throw new DecodingError("PEM: Malformed PEM header");
96             
97             if (position == 0)
98                 label_buf ~= cast(char) b;
99         }
100         label = label_buf.data;
101 
102         Pipe base64 = Pipe(new Base64Decoder);
103         base64.startMsg();
104         const string PEM_TRAILER = "-----END " ~ label ~ "-----";
105         position = 0;
106         while (position != PEM_TRAILER.length)
107         {
108             ubyte b;
109             if (!source.readByte(b))
110                 throw new DecodingError("PEM: No PEM trailer found");
111             if (b == PEM_TRAILER[position])
112                 ++position;
113             else if (position)
114                 throw new DecodingError("PEM: Malformed PEM trailer");
115             
116             if (position == 0)
117                 base64.write(b);
118         }
119         base64.endMsg();
120         return base64.readAll();
121     }
122 
123     /**
124     * Decode PEM data
125     * Params:
126     *  pem = a string containing PEM encoded data
127     *  label = is set to the PEM label found for later inspection
128     */
129     static SecureVector!ubyte decode(in string pem, ref string label)
130     {
131         auto src = DataSourceMemory(pem);
132         return decode(cast(DataSource)src, label);
133     }
134     /**
135     * Decode PEM data
136     * Params:
137     *  pem = a datasource containing PEM encoded data
138     *  label = is what we expect the label to be
139     */
140     static SecureVector!ubyte decodeCheckLabel(DataSource source, in string label_want)
141     {
142         string label_got;
143         SecureVector!ubyte ber = decode(source, label_got);
144         if (label_got != label_want)
145             throw new DecodingError("PEM: Label mismatch, wanted " ~ label_want ~ ", got " ~ label_got);
146         return ber;
147     }
148 
149     /**
150     * Decode PEM data
151     * Params:
152     *  pem = a string containing PEM encoded data
153     *  label = is what we expect the label to be
154     */
155     static SecureVector!ubyte decodeCheckLabel(in string pem,
156                                                in string label_want)
157     {
158         auto src = DataSourceMemory(pem);
159         return decodeCheckLabel(cast(DataSource) src, label_want);
160     }
161 
162     /**
163     * Heuristic test for PEM data.
164     * Search for a PEM signature
165     */
166     static bool matches(DataSource source, in string extra = "", size_t search_range = 4096)
167     {
168         const string PEM_HEADER = "-----BEGIN " ~ extra;
169         
170         SecureVector!ubyte search_buf = SecureVector!ubyte(search_range);
171         size_t got = source.peek(search_buf.ptr, search_buf.length, 0);
172         
173         if (got < PEM_HEADER.length)
174             return false;
175         
176         size_t index = 0;
177         
178         foreach (size_t j; 0 .. got)
179         {
180             if (search_buf[j] == PEM_HEADER[index])
181                 ++index;
182             else
183                 index = 0;
184             if (index == PEM_HEADER.length)
185                 return true;
186         }
187         return false;
188     }
189 
190 }