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 }