Home | History | Annotate | Download | only in cert
      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.
      4 
      5 #include "net/cert/pem_tokenizer.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/strings/stringprintf.h"
     10 
     11 namespace {
     12 
     13 const char kPEMSearchBlock[] = "-----BEGIN ";
     14 const char kPEMBeginBlock[] = "-----BEGIN %s-----";
     15 const char kPEMEndBlock[] = "-----END %s-----";
     16 
     17 }  // namespace
     18 
     19 namespace net {
     20 
     21 using base::StringPiece;
     22 
     23 struct PEMTokenizer::PEMType {
     24   std::string type;
     25   std::string header;
     26   std::string footer;
     27 };
     28 
     29 PEMTokenizer::PEMTokenizer(
     30     const StringPiece& str,
     31     const std::vector<std::string>& allowed_block_types) {
     32   Init(str, allowed_block_types);
     33 }
     34 
     35 PEMTokenizer::~PEMTokenizer() {
     36 }
     37 
     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
     44 
     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))
     49         continue;
     50 
     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;
     56         return false;
     57       }
     58 
     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;
     63 
     64       StringPiece encoded = str_.substr(data_begin,
     65                                         footer_pos - data_begin);
     66       if (!base::Base64Decode(CollapseWhitespaceASCII(encoded.as_string(),
     67                                                       true), &data_)) {
     68         // The most likely cause for a decode failure is a datatype that
     69         // includes PEM headers, which are not supported.
     70         break;
     71       }
     72 
     73       return true;
     74     }
     75 
     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
     79     // be adjusted.
     80     if (it == block_types_.end())
     81       pos_ += sizeof(kPEMSearchBlock);
     82   }
     83 
     84   return false;
     85 }
     86 
     87 void PEMTokenizer::Init(
     88     const StringPiece& str,
     89     const std::vector<std::string>& allowed_block_types) {
     90   str_ = str;
     91   pos_ = 0;
     92 
     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) {
     97     PEMType allowed_type;
     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);
    102   }
    103 }
    104 
    105 }  // namespace net
    106