|  | // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/base/pem_tokenizer.h" | 
|  |  | 
|  | #include "base/base64.h" | 
|  | #include "base/string_util.h" | 
|  | #include "base/stringprintf.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kPEMSearchBlock[] = "-----BEGIN "; | 
|  | const char kPEMBeginBlock[] = "-----BEGIN %s-----"; | 
|  | const char kPEMEndBlock[] = "-----END %s-----"; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | using base::StringPiece; | 
|  |  | 
|  | struct PEMTokenizer::PEMType { | 
|  | std::string type; | 
|  | std::string header; | 
|  | std::string footer; | 
|  | }; | 
|  |  | 
|  | PEMTokenizer::PEMTokenizer( | 
|  | const StringPiece& str, | 
|  | const std::vector<std::string>& allowed_block_types) { | 
|  | Init(str, allowed_block_types); | 
|  | } | 
|  |  | 
|  | PEMTokenizer::~PEMTokenizer() { | 
|  | } | 
|  |  | 
|  | bool PEMTokenizer::GetNext() { | 
|  | while (pos_ != StringPiece::npos) { | 
|  | // Scan for the beginning of the next PEM encoded block. | 
|  | pos_ = str_.find(kPEMSearchBlock, pos_); | 
|  | if (pos_ == StringPiece::npos) | 
|  | return false;  // No more PEM blocks | 
|  |  | 
|  | std::vector<PEMType>::const_iterator it; | 
|  | // Check to see if it is of an acceptable block type. | 
|  | for (it = block_types_.begin(); it != block_types_.end(); ++it) { | 
|  | if (!str_.substr(pos_).starts_with(it->header)) | 
|  | continue; | 
|  |  | 
|  | // Look for a footer matching the header. If none is found, then all | 
|  | // data following this point is invalid and should not be parsed. | 
|  | StringPiece::size_type footer_pos = str_.find(it->footer, pos_); | 
|  | if (footer_pos == StringPiece::npos) { | 
|  | pos_ = StringPiece::npos; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Chop off the header and footer and parse the data in between. | 
|  | StringPiece::size_type data_begin = pos_ + it->header.size(); | 
|  | pos_ = footer_pos + it->footer.size(); | 
|  | block_type_ = it->type; | 
|  |  | 
|  | StringPiece encoded = str_.substr(data_begin, | 
|  | footer_pos - data_begin); | 
|  | if (!base::Base64Decode(CollapseWhitespaceASCII(encoded.as_string(), | 
|  | true), &data_)) { | 
|  | // The most likely cause for a decode failure is a datatype that | 
|  | // includes PEM headers, which are not supported. | 
|  | break; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // If the block did not match any acceptable type, move past it and | 
|  | // continue the search. Otherwise, |pos_| has been updated to the most | 
|  | // appropriate search position to continue searching from and should not | 
|  | // be adjusted. | 
|  | if (it == block_types_.end()) | 
|  | pos_ += sizeof(kPEMSearchBlock); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void PEMTokenizer::Init( | 
|  | const StringPiece& str, | 
|  | const std::vector<std::string>& allowed_block_types) { | 
|  | str_ = str; | 
|  | pos_ = 0; | 
|  |  | 
|  | // Construct PEM header/footer strings for all the accepted types, to | 
|  | // reduce parsing later. | 
|  | for (std::vector<std::string>::const_iterator it = | 
|  | allowed_block_types.begin(); it != allowed_block_types.end(); ++it) { | 
|  | PEMType allowed_type; | 
|  | allowed_type.type = *it; | 
|  | allowed_type.header = base::StringPrintf(kPEMBeginBlock, it->c_str()); | 
|  | allowed_type.footer = base::StringPrintf(kPEMEndBlock, it->c_str()); | 
|  | block_types_.push_back(allowed_type); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace net |