blob: a245b10013ce2468db678e9bc23521ab7c66b945 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "algorithms/forculus/forculus_decrypter.h"
#include <functional>
#include <vector>
#include "algorithms/forculus/polynomial_computations.h"
#include "util/crypto_util/cipher.h"
namespace cobalt {
namespace forculus {
using crypto::SymmetricCipher;
ForculusDecrypter::ForculusDecrypter(uint32_t threshold,
std::string ciphertext) :
threshold_(threshold), num_seen_(0), ciphertext_(std::move(ciphertext)) {}
ForculusDecrypter::Status ForculusDecrypter::AddObservation(
const ForculusObservation& obs) {
if (obs.ciphertext() != ciphertext_) {
return kWrongCiphertext;
}
// Keep a copy of y so we can check it its the same as a previously added
// point.
FieldElement y(obs.point_y());
auto result = points_.insert(std::make_pair(FieldElement(obs.point_x()), y));
auto key_value_pair = result.first;
auto success = result.second;
if (!success) {
if (key_value_pair->second != y) {
return kInconsistentPoints;
}
}
num_seen_++;
return kOK;
}
uint32_t ForculusDecrypter::size() {
return points_.size();
}
ForculusDecrypter::Status ForculusDecrypter::Decrypt(
std::string *plain_text_out) {
if (size() < threshold_) {
return kNotEnoughPoints;
}
// Put pointers to the first |threshold_| x and y values into vectors.
std::vector<const FieldElement*> x_values(threshold_);
std::vector<const FieldElement*> y_values(threshold_);
uint32_t point_index = 0;
for (const auto& point : points_) {
if (point_index >= threshold_) {
break;
}
x_values[point_index] = &point.first;
y_values[point_index++] = &point.second;
}
// The decryption key we need is the constant term of the unique polynomial of
// degree (threshold_ - 1) that passes through the points given by the
// x_values and y_values. We can find this using interpolation.
FieldElement c0 = InterpolateConstant(x_values, y_values);
// Now we have the key, decrypt.
SymmetricCipher cipher;
cipher.set_key(c0.KeyBytes());
std::vector<byte> recoverd_text;
// Our encryption scheme uses a zero nonce. (Note that C++11 initializes
// the entire array to 0 with this syntax.)
static const byte kZeroNonce[SymmetricCipher::NONCE_SIZE] = {0};
if (!cipher.Decrypt(kZeroNonce,
reinterpret_cast<const byte*>(ciphertext_.data()),
ciphertext_.size(), &recoverd_text)) {
// TODO(pseudorandom, rudominer) One reason that decryption might fail
// is a ballot suppression attack. An adversary may intentionally flood
// us with bad (x, y) values in order to keep us from decrypting a
// ciphertext. Because we use authenticated encryption, the result of
// invalid (x, y) values will be failure to decrypt. One way to combat
// this attack might be to try different sets of points of size
// |threshold| iteratively until decryption succeeds.
return kDecryptionFailed;
}
plain_text_out->assign(recoverd_text.begin(), recoverd_text.end());
return kOK;
}
} // namespace forculus
} // namespace cobalt