summaryrefslogtreecommitdiffstats
path: root/components/proximity_auth/cryptauth/fake_secure_message_delegate.cc
blob: d05361c2d1ae50e65bbfcae06519a9974759d82b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2015 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 "components/proximity_auth/cryptauth/fake_secure_message_delegate.h"

#include <algorithm>

#include "base/callback.h"
#include "base/md5.h"
#include "base/strings/string_util.h"

namespace proximity_auth {

namespace {

const char kKeyPrefix[] = "fake_key_";
const char kPrivateKeyPrefix[] = "private_";

// Encrypts the |plaintext| with the |key| using the |encryption_scheme| and
// returns the ciphertext.
std::string Encrypt(const std::string& plaintext,
                    const std::string& key,
                    const securemessage::EncScheme& encryption_scheme) {
  if (encryption_scheme == securemessage::NONE)
    return plaintext;

  // Encrypt with a Vigenere cipher.
  std::string ciphertext;
  ciphertext.resize(plaintext.size());
  for (size_t i = 0; i < plaintext.size(); ++i) {
    unsigned char plaintext_char = plaintext[i];
    unsigned char key_char = key[i % key.size()];
    ciphertext[i] = plaintext_char + key_char;
  }
  return ciphertext;
}

// Decrypts the |ciphertext| with the |key| using the |encryption_scheme| and
// returns the plaintext.
std::string Decrypt(const std::string& ciphertext,
                    const std::string& key,
                    const securemessage::EncScheme& encryption_scheme) {
  if (encryption_scheme == securemessage::NONE)
    return ciphertext;

  // Decrypt with a Vigenere cipher.
  std::string plaintext;
  plaintext.resize(ciphertext.size());
  for (size_t i = 0; i < ciphertext.size(); ++i) {
    unsigned char ciphertext_char = ciphertext[i];
    unsigned char key_char = key[i % key.size()];
    plaintext[i] = ciphertext_char - key_char;
  }
  return plaintext;
}

// Signs the |payload| and |associated_data| with the |key| using the
// |signature_scheme| and returns the signature.
std::string Sign(const std::string& payload,
                 const std::string& associated_data,
                 const std::string& key) {
  return base::MD5String(payload + "|" + associated_data + "|" + key);
}

// Verifies that the |signature| for the the |payload| and |associated_data| is
// valid for the given the |key| and |signature_scheme|.
bool Verify(const std::string& signature,
            const std::string& payload,
            const std::string& associated_data,
            const std::string& key,
            const securemessage::SigScheme& signature_scheme) {
  std::string signing_key;

  if (signature_scheme == securemessage::HMAC_SHA256) {
    signing_key = key;
  } else {
    std::string prefix = kPrivateKeyPrefix;
    bool is_private_key = base::StartsWithASCII(key, prefix, true);
    signing_key = is_private_key ? key.substr(prefix.size()) : prefix + key;
  }

  std::string expected_signature = Sign(payload, associated_data, signing_key);
  return signature == expected_signature;
}

}  // namespace

FakeSecureMessageDelegate::FakeSecureMessageDelegate()
    : next_public_key_(std::string(kKeyPrefix) + "0") {
}

FakeSecureMessageDelegate::~FakeSecureMessageDelegate() {
}

void FakeSecureMessageDelegate::GenerateKeyPair(
    const GenerateKeyPairCallback& callback) {
  std::string public_key = next_public_key_;

  // The private key is simply the public key prepended with "private_".
  std::string private_key(kPrivateKeyPrefix + public_key);

  next_public_key_ = std::string(kKeyPrefix) + base::MD5String(public_key);

  callback.Run(public_key, private_key);
}

void FakeSecureMessageDelegate::DeriveKey(const std::string& private_key,
                                          const std::string& public_key,
                                          const DeriveKeyCallback& callback) {
  // To ensure that the same symmetric key is derived for DeriveKey(private1,
  // public2) and DeriveKey(private2, public1), we remove the prefix from the
  // private key so it is equal to its corresponding public key.
  std::string prefix(kPrivateKeyPrefix);
  std::string normalized_private_key =
      base::StartsWithASCII(private_key, prefix, true)
          ? private_key.substr(prefix.size())
          : private_key;

  std::vector<std::string> keys;
  keys.push_back(normalized_private_key);
  keys.push_back(public_key);
  std::sort(keys.begin(), keys.end());
  callback.Run(base::MD5String(keys[0] + "|" + keys[1]));
}

void FakeSecureMessageDelegate::CreateSecureMessage(
    const std::string& payload,
    const std::string& key,
    const CreateOptions& create_options,
    const CreateSecureMessageCallback& callback) {
  securemessage::Header header;
  header.set_signature_scheme(create_options.signature_scheme);
  header.set_encryption_scheme(create_options.encryption_scheme);
  if (!create_options.public_metadata.empty())
    header.set_public_metadata(create_options.public_metadata);
  if (!create_options.verification_key_id.empty())
    header.set_verification_key_id(create_options.verification_key_id);
  if (!create_options.decryption_key_id.empty())
    header.set_decryption_key_id(create_options.decryption_key_id);

  securemessage::HeaderAndBody header_and_body;
  header_and_body.mutable_header()->CopyFrom(header);
  header_and_body.set_body(
      Encrypt(payload, key, create_options.encryption_scheme));
  std::string serialized_header_and_body;
  header_and_body.SerializeToString(&serialized_header_and_body);

  securemessage::SecureMessage secure_message;
  secure_message.set_header_and_body(serialized_header_and_body);
  secure_message.set_signature(
      Sign(payload, create_options.associated_data, key));

  std::string serialized_secure_message;
  secure_message.SerializeToString(&serialized_secure_message);
  callback.Run(serialized_secure_message);
}

void FakeSecureMessageDelegate::UnwrapSecureMessage(
    const std::string& serialized_message,
    const std::string& key,
    const UnwrapOptions& unwrap_options,
    const UnwrapSecureMessageCallback& callback) {
  securemessage::SecureMessage secure_message;
  if (!secure_message.ParseFromString(serialized_message)) {
    LOG(ERROR) << "Failed to parse SecureMessage.";
    callback.Run(false, std::string(), securemessage::Header());
    return;
  }

  securemessage::HeaderAndBody header_and_body;
  if (!header_and_body.ParseFromString(secure_message.header_and_body())) {
    LOG(ERROR) << "Failed to parse secure message HeaderAndBody.";
    callback.Run(false, std::string(), securemessage::Header());
    return;
  }

  const securemessage::Header& header = header_and_body.header();
  std::string payload =
      Decrypt(header_and_body.body(), key, unwrap_options.encryption_scheme);

  bool verified = Verify(secure_message.signature(), payload,
                         unwrap_options.associated_data, key,
                         unwrap_options.signature_scheme);
  if (verified) {
    callback.Run(true, payload, header);
  } else {
    callback.Run(false, std::string(), securemessage::Header());
  }
}

std::string FakeSecureMessageDelegate::GetPrivateKeyForPublicKey(
    const std::string& public_key) {
  return kPrivateKeyPrefix + public_key;
}

}  // namespace proximity_auth