1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/cert/pem_tokenizer.h"
7 #include "base/base64.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
13 const char kPEMSearchBlock
[] = "-----BEGIN ";
14 const char kPEMBeginBlock
[] = "-----BEGIN %s-----";
15 const char kPEMEndBlock
[] = "-----END %s-----";
21 using base::StringPiece
;
23 struct PEMTokenizer::PEMType
{
29 PEMTokenizer::PEMTokenizer(
30 const StringPiece
& str
,
31 const std::vector
<std::string
>& allowed_block_types
) {
32 Init(str
, allowed_block_types
);
35 PEMTokenizer::~PEMTokenizer() {
38 bool PEMTokenizer::GetNext() {
39 while (pos_
!= StringPiece::npos
) {
40 // Scan for the beginning of the next PEM encoded block.
41 pos_
= str_
.find(kPEMSearchBlock
, pos_
);
42 if (pos_
== StringPiece::npos
)
43 return false; // No more PEM blocks
45 std::vector
<PEMType
>::const_iterator it
;
46 // Check to see if it is of an acceptable block type.
47 for (it
= block_types_
.begin(); it
!= block_types_
.end(); ++it
) {
48 if (!str_
.substr(pos_
).starts_with(it
->header
))
51 // Look for a footer matching the header. If none is found, then all
52 // data following this point is invalid and should not be parsed.
53 StringPiece::size_type footer_pos
= str_
.find(it
->footer
, pos_
);
54 if (footer_pos
== StringPiece::npos
) {
55 pos_
= StringPiece::npos
;
59 // Chop off the header and footer and parse the data in between.
60 StringPiece::size_type data_begin
= pos_
+ it
->header
.size();
61 pos_
= footer_pos
+ it
->footer
.size();
62 block_type_
= it
->type
;
64 StringPiece encoded
= str_
.substr(data_begin
,
65 footer_pos
- data_begin
);
66 if (!base::Base64Decode(CollapseWhitespaceASCII(encoded
.as_string(),
68 // The most likely cause for a decode failure is a datatype that
69 // includes PEM headers, which are not supported.
76 // If the block did not match any acceptable type, move past it and
77 // continue the search. Otherwise, |pos_| has been updated to the most
78 // appropriate search position to continue searching from and should not
80 if (it
== block_types_
.end())
81 pos_
+= sizeof(kPEMSearchBlock
);
87 void PEMTokenizer::Init(
88 const StringPiece
& str
,
89 const std::vector
<std::string
>& allowed_block_types
) {
93 // Construct PEM header/footer strings for all the accepted types, to
94 // reduce parsing later.
95 for (std::vector
<std::string
>::const_iterator it
=
96 allowed_block_types
.begin(); it
!= allowed_block_types
.end(); ++it
) {
98 allowed_type
.type
= *it
;
99 allowed_type
.header
= base::StringPrintf(kPEMBeginBlock
, it
->c_str());
100 allowed_type
.footer
= base::StringPrintf(kPEMEndBlock
, it
->c_str());
101 block_types_
.push_back(allowed_type
);