1// Copyright (C) 2013 Google Inc. 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// http://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// ValidatingUtil wraps data with checksum and timestamp. Format: 16// 17// timestamp=<timestamp> 18// checksum=<checksum> 19// <data> 20// 21// The timestamp is the time_t that was returned from time() function. The 22// timestamp does not need to be portable because it is written and read only by 23// ValidatingUtil. The value is somewhat human-readable: it is the number of 24// seconds since the epoch. 25// 26// The checksum is the 32-character hexadecimal MD5 checksum of <data>. It is 27// meant to protect from random file changes on disk. 28 29#include "validating_util.h" 30 31#include <cassert> 32#include <cstddef> 33#include <cstdio> 34#include <cstdlib> 35#include <ctime> 36#include <string> 37 38#include "util/md5.h" 39 40namespace i18n { 41namespace addressinput { 42 43namespace { 44 45const char kTimestampPrefix[] = "timestamp="; 46const size_t kTimestampPrefixLength = sizeof kTimestampPrefix - 1; 47 48const char kChecksumPrefix[] = "checksum="; 49const size_t kChecksumPrefixLength = sizeof kChecksumPrefix - 1; 50 51const char kSeparator = '\n'; 52 53// Places the header value into |header_value| parameter and erases the header 54// from |data|. Returns |true| if the header format is valid. 55bool UnwrapHeader(const char* header_prefix, 56 size_t header_prefix_length, 57 std::string* data, 58 std::string* header_value) { 59 assert(header_prefix != NULL); 60 assert(data != NULL); 61 assert(header_value != NULL); 62 63 if (data->compare( 64 0, header_prefix_length, header_prefix, header_prefix_length) != 0) { 65 return false; 66 } 67 68 std::string::size_type separator_position = 69 data->find(kSeparator, header_prefix_length); 70 if (separator_position == std::string::npos) { 71 return false; 72 } 73 74 header_value->assign( 75 *data, header_prefix_length, separator_position - header_prefix_length); 76 data->erase(0, separator_position + 1); 77 78 return true; 79} 80 81} // namespace 82 83// static 84void ValidatingUtil::Wrap(time_t timestamp, std::string* data) { 85 assert(data != NULL); 86 char timestamp_string[2 + 3 * sizeof timestamp]; 87 int size = 88 std::sprintf(timestamp_string, "%ld", static_cast<long>(timestamp)); 89 assert(size > 0); 90 assert(size < sizeof timestamp_string); 91 (void)size; 92 93 std::string header; 94 header.append(kTimestampPrefix, kTimestampPrefixLength); 95 header.append(timestamp_string); 96 header.push_back(kSeparator); 97 98 header.append(kChecksumPrefix, kChecksumPrefixLength); 99 header.append(MD5String(*data)); 100 header.push_back(kSeparator); 101 102 data->reserve(header.size() + data->size()); 103 data->insert(0, header); 104} 105 106// static 107bool ValidatingUtil::UnwrapTimestamp(std::string* data, time_t now) { 108 assert(data != NULL); 109 if (now < 0) { 110 return false; 111 } 112 113 std::string timestamp_string; 114 if (!UnwrapHeader( 115 kTimestampPrefix, kTimestampPrefixLength, data, ×tamp_string)) { 116 return false; 117 } 118 119 time_t timestamp = atol(timestamp_string.c_str()); 120 if (timestamp < 0) { 121 return false; 122 } 123 124 // One month contains: 125 // 30 days * 126 // 24 hours per day * 127 // 60 minutes per hour * 128 // 60 seconds per minute. 129 static const double kOneMonthInSeconds = 30.0 * 24.0 * 60.0 * 60.0; 130 double age_in_seconds = difftime(now, timestamp); 131 return !(age_in_seconds < 0.0) && age_in_seconds < kOneMonthInSeconds; 132} 133 134// static 135bool ValidatingUtil::UnwrapChecksum(std::string* data) { 136 assert(data != NULL); 137 std::string checksum; 138 if (!UnwrapHeader(kChecksumPrefix, kChecksumPrefixLength, data, &checksum)) { 139 return false; 140 } 141 return checksum == MD5String(*data); 142} 143 144} // namespace addressinput 145} // namespace i18n 146