12fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// Copyright (C) 2013 Google Inc.
22fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//
32fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// Licensed under the Apache License, Version 2.0 (the "License");
42fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// you may not use this file except in compliance with the License.
52fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// You may obtain a copy of the License at
62fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//
72fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// http://www.apache.org/licenses/LICENSE-2.0
82fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//
92fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// Unless required by applicable law or agreed to in writing, software
102fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// distributed under the License is distributed on an "AS IS" BASIS,
112fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
122fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// See the License for the specific language governing permissions and
132fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// limitations under the License.
142fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//
152fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// ValidatingUtil wraps data with checksum and timestamp. Format:
162fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//
172fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//    timestamp=<timestamp>
182fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//    checksum=<checksum>
192fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//    <data>
202fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//
212fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// The timestamp is the time_t that was returned from time() function. The
222fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// timestamp does not need to be portable because it is written and read only by
232fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// ValidatingUtil. The value is somewhat human-readable: it is the number of
242fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// seconds since the epoch.
252fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org//
262fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// The checksum is the 32-character hexadecimal MD5 checksum of <data>. It is
272fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// meant to protect from random file changes on disk.
282fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
292fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include "validating_util.h"
302fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
312fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include <cassert>
322fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include <cstddef>
332fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include <cstdio>
342fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include <cstdlib>
352fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include <ctime>
362fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include <string>
372fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
382fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org#include "util/md5.h"
392fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
402fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgnamespace i18n {
412fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgnamespace addressinput {
422fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
432fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgnamespace {
442fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
452fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgconst char kTimestampPrefix[] = "timestamp=";
462fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgconst size_t kTimestampPrefixLength = sizeof kTimestampPrefix - 1;
472fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
482fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgconst char kChecksumPrefix[] = "checksum=";
492fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgconst size_t kChecksumPrefixLength = sizeof kChecksumPrefix - 1;
502fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
512fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgconst char kSeparator = '\n';
522fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
532fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// Places the header value into |header_value| parameter and erases the header
542fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// from |data|. Returns |true| if the header format is valid.
552fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgbool UnwrapHeader(const char* header_prefix,
562fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org                  size_t header_prefix_length,
572fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org                  std::string* data,
582fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org                  std::string* header_value) {
592fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  assert(header_prefix != NULL);
602fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  assert(data != NULL);
612fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  assert(header_value != NULL);
622fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
632fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  if (data->compare(
642fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org          0, header_prefix_length, header_prefix, header_prefix_length) != 0) {
652fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org    return false;
662fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  }
672fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
682fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  std::string::size_type separator_position =
692fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org      data->find(kSeparator, header_prefix_length);
702fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  if (separator_position == std::string::npos) {
712fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org    return false;
722fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  }
732fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
742fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  header_value->assign(
752fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org      *data, header_prefix_length, separator_position - header_prefix_length);
762fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  data->erase(0, separator_position + 1);
772fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
782fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  return true;
792fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org}
802fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
812fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org}  // namespace
822fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
832fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// static
84128ae075bbf50996c1068740746f27430f89136droubert@google.comvoid ValidatingUtil::Wrap(time_t timestamp, std::string* data) {
85128ae075bbf50996c1068740746f27430f89136droubert@google.com  assert(data != NULL);
862fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  char timestamp_string[2 + 3 * sizeof timestamp];
87945d96387a716d0d82b195fa69a5e9a701249517rouslan@chromium.org  int size =
88945d96387a716d0d82b195fa69a5e9a701249517rouslan@chromium.org      std::sprintf(timestamp_string, "%ld", static_cast<long>(timestamp));
898a5ea9e2e8b5642281fa679b70266b80a4bf039drouslan@chromium.org  assert(size > 0);
908a5ea9e2e8b5642281fa679b70266b80a4bf039drouslan@chromium.org  assert(size < sizeof timestamp_string);
918a5ea9e2e8b5642281fa679b70266b80a4bf039drouslan@chromium.org  (void)size;
922fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
93128ae075bbf50996c1068740746f27430f89136droubert@google.com  std::string header;
94128ae075bbf50996c1068740746f27430f89136droubert@google.com  header.append(kTimestampPrefix, kTimestampPrefixLength);
95128ae075bbf50996c1068740746f27430f89136droubert@google.com  header.append(timestamp_string);
96128ae075bbf50996c1068740746f27430f89136droubert@google.com  header.push_back(kSeparator);
972fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
98128ae075bbf50996c1068740746f27430f89136droubert@google.com  header.append(kChecksumPrefix, kChecksumPrefixLength);
99128ae075bbf50996c1068740746f27430f89136droubert@google.com  header.append(MD5String(*data));
100128ae075bbf50996c1068740746f27430f89136droubert@google.com  header.push_back(kSeparator);
1012fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
102128ae075bbf50996c1068740746f27430f89136droubert@google.com  data->reserve(header.size() + data->size());
103128ae075bbf50996c1068740746f27430f89136droubert@google.com  data->insert(0, header);
1042fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org}
1052fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
1062fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// static
1072fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgbool ValidatingUtil::UnwrapTimestamp(std::string* data, time_t now) {
1082fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  assert(data != NULL);
1092fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  if (now < 0) {
1102fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org    return false;
1112fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  }
1122fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
1132fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  std::string timestamp_string;
1142fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  if (!UnwrapHeader(
115000aa6dbb70273ccefa77a5d4cd1a400939a2666roubert@google.com          kTimestampPrefix, kTimestampPrefixLength, data, &timestamp_string)) {
1162fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org    return false;
1172fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  }
1182fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
1192fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  time_t timestamp = atol(timestamp_string.c_str());
1202fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  if (timestamp < 0) {
1212fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org    return false;
1222fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  }
1232fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
1242fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  // One month contains:
1252fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  //    30 days *
1262fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  //    24 hours per day *
1272fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  //    60 minutes per hour *
1282fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  //    60 seconds per minute.
1292fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  static const double kOneMonthInSeconds = 30.0 * 24.0 * 60.0 * 60.0;
1302fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  double age_in_seconds = difftime(now, timestamp);
1312fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  return !(age_in_seconds < 0.0) && age_in_seconds < kOneMonthInSeconds;
1322fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org}
1332fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
1342fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org// static
1352fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.orgbool ValidatingUtil::UnwrapChecksum(std::string* data) {
1362fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  assert(data != NULL);
1372fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  std::string checksum;
1382fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  if (!UnwrapHeader(kChecksumPrefix, kChecksumPrefixLength, data, &checksum)) {
1392fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org    return false;
1402fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  }
1412fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org  return checksum == MD5String(*data);
1422fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org}
1432fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org
1442fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org}  // namespace addressinput
1452fefd83dd2c65ee585a2aa05e4856eb4d0e93f0brouslan@chromium.org}  // namespace i18n
146