1 // Copyright 2011 The Chromium Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef BSSL_PKI_PEM_H_
16 #define BSSL_PKI_PEM_H_
17 
18 #include <stddef.h>
19 
20 #include <optional>
21 #include <string>
22 #include <string_view>
23 #include <vector>
24 
25 #include <openssl/base.h>
26 #include <openssl/span.h>
27 
28 BSSL_NAMESPACE_BEGIN
29 
30 // PEMTokenizer is a utility class for the parsing of data encapsulated
31 // using RFC 1421, Privacy Enhancement for Internet Electronic Mail. It
32 // does not implement the full specification, most notably it does not
33 // support the Encapsulated Header Portion described in Section 4.4.
34 class OPENSSL_EXPORT PEMTokenizer {
35  public:
36   // Create a new PEMTokenizer that iterates through |str| searching for
37   // instances of PEM encoded blocks that are of the |allowed_block_types|.
38   // |str| must remain valid for the duration of the PEMTokenizer.
39   PEMTokenizer(std::string_view str,
40                const std::vector<std::string> &allowed_block_types);
41   PEMTokenizer(std::string_view str,
42                bssl::Span<const std::string_view> allowed_block_types);
43 
44   PEMTokenizer(const PEMTokenizer &) = delete;
45   PEMTokenizer &operator=(const PEMTokenizer &) = delete;
46 
47   ~PEMTokenizer();
48 
49   // Attempts to decode the next PEM block in the string. Returns false if no
50   // PEM blocks can be decoded. The decoded PEM block will be available via
51   // data().
52   bool GetNext();
53 
54   // Returns the PEM block type (eg: CERTIFICATE) of the last successfully
55   // decoded PEM block.
56   // GetNext() must have returned true before calling this method.
block_type()57   const std::string &block_type() const { return block_type_; }
58 
59   // Returns the raw, Base64-decoded data of the last successfully decoded
60   // PEM block.
61   // GetNext() must have returned true before calling this method.
data()62   const std::string &data() const { return data_; }
63 
64  private:
65   void Init(std::string_view str,
66             bssl::Span<const std::string_view> allowed_block_types);
67 
68   // A simple cache of the allowed PEM header and footer for a given PEM
69   // block type, so that it is only computed once.
70   struct PEMType;
71 
72   // The string to search, which must remain valid for as long as this class
73   // is around.
74   std::string_view str_;
75 
76   // The current position within |str_| that searching should begin from,
77   // or std::string_view::npos if iteration is complete
78   std::string_view::size_type pos_;
79 
80   // The type of data that was encoded, as indicated in the PEM
81   // Pre-Encapsulation Boundary (eg: CERTIFICATE, PKCS7, or
82   // PRIVACY-ENHANCED MESSAGE).
83   std::string block_type_;
84 
85   // The types of PEM blocks that are allowed. PEM blocks that are not of
86   // one of these types will be skipped.
87   std::vector<PEMType> block_types_;
88 
89   // The raw (Base64-decoded) data of the last successfully decoded block.
90   std::string data_;
91 };
92 
93 // PEMToken represents a single PEM token. Headers are not stored or supported.
94 struct PEMToken {
95   std::string type;
96   std::string data;
97 };
98 
99 // PEMDecode decodes |data| into a sequence of PEM tokens. Only tokens whose
100 // type appears in |allowed_types| are included in the resulting vector. The
101 // resulting vector may be empty if either there are no valid PEM tokens in the
102 // input, or there are valid tokens but none of them match any of the allowed
103 // types.
104 OPENSSL_EXPORT std::vector<PEMToken> PEMDecode(
105     std::string_view data, bssl::Span<const std::string_view> allowed_types);
106 
107 // PEMDecodeSingle decodes |data| into a single PEM token, which must be of the
108 // specified |allowed_type|, and returns that token's base64-decoded body.
109 // Returns nullopt if there is not exactly one token of the allowed type in the
110 // input for any reason.
111 OPENSSL_EXPORT std::optional<std::string> PEMDecodeSingle(
112     std::string_view data, std::string_view allowed_type);
113 
114 // PEMEncode encodes |data| in the encapsulated message format described in RFC
115 // 1421, with |type| as the PEM block type (eg: CERTIFICATE).
116 OPENSSL_EXPORT std::string PEMEncode(std::string_view data,
117                                      const std::string &type);
118 
119 BSSL_NAMESPACE_END
120 
121 #endif  // BSSL_PKI_PEM_H_
122