// Copyright 2020 Google LLC
//
// 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
//
// https://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 "third_party/private_membership/src/internal/rlwe_id_utils.h"
#include <string>
#include <utility>
#include "third_party/private_membership/src/internal/crypto_utils.h"
#include "third_party/private_membership/src/private_membership.pb.h"
#include "third_party/private_membership/src/internal/constants.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "third_party/shell-encryption/src/status_macros.h"
namespace private_membership {
namespace rlwe {
::rlwe::StatusOr<std::string> ComputeBucketStoredEncryptedId(
const RlwePlaintextId& id, const EncryptedBucketsParameters& params,
::private_join_and_compute::ECCommutativeCipher* ec_cipher, ::private_join_and_compute::Context* ctx) {
if (ec_cipher == nullptr || ctx == nullptr) {
return absl::InvalidArgumentError(
"ECCipher and Context should both be non-null.");
}
std::string full_id = rlwe::HashRlwePlaintextId(id);
auto encrypted_id = ec_cipher->Encrypt(full_id);
if (!encrypted_id.ok()) {
return absl::InternalError(encrypted_id.status().message());
}
return ComputeBucketStoredEncryptedId(std::move(encrypted_id).value(), params,
ctx);
}
::rlwe::StatusOr<std::string> ComputeBucketStoredEncryptedId(
const absl::string_view encrypted_id,
const EncryptedBucketsParameters& params, ::private_join_and_compute::Context* ctx) {
if (ctx == nullptr) {
return absl::InvalidArgumentError("Context should be non-null.");
}
const std::string hashed_encrypted_id = HashEncryptedId(encrypted_id, ctx);
switch (params.sensitive_id_hash_type()) {
case ENCRYPTED_BUCKET_HASH_TYPE_UNDEFINED: {
return absl::InvalidArgumentError(
"Sensitive id hash type must be defined.");
}
case ENCRYPTED_BUCKET_TEST_HASH_TYPE:
case SHA256_NON_SENSITIVE_AND_SENSITIVE_ID: {
// Find the first byte that is not used by the bucket ID.
int start_byte = (params.encrypted_bucket_id_length() - 1) / 8 + 1;
// Ensure that the total of the bytes stored in the bucket plus the length
// of the bucket ID is at least kBucketStoredEncryptedIdByteLength. Since
// the ID within a bucket needs to be represented at byte-level
// granularity, pad up the remainder of a byte if needed.
int byte_length = kStoredEncryptedIdByteLength -
(params.encrypted_bucket_id_length() / 8);
if (byte_length < 0) {
byte_length = 0;
}
std::string stored_encrypted_id(hashed_encrypted_id, start_byte,
byte_length);
return stored_encrypted_id;
}
default:
return absl::InvalidArgumentError(absl::StrCat(
"Unknown sensitive id hash type: ", params.sensitive_id_hash_type()));
}
}
std::string HashRlwePlaintextId(const RlwePlaintextId& id) {
// For backward compatibility. We can show that if non_sensitive_id length is
// 0, it is okay to concatenate without delimiters to make it injective.
if (id.non_sensitive_id().empty()) {
return absl::StrCat(0, id.sensitive_id().length(), id.sensitive_id());
}
static constexpr char delimiter[] = "/";
// Add delimiters in between to make the function injective.
return absl::StrCat(id.non_sensitive_id().length(), delimiter,
id.non_sensitive_id(), delimiter,
id.sensitive_id().length(), delimiter, id.sensitive_id());
}
::rlwe::StatusOr<std::string> HashNonsensitiveIdWithSalt(
absl::string_view nsid, HashType hash_type, ::private_join_and_compute::Context* ctx) {
switch (hash_type) {
case HashType::TEST_HASH_TYPE:
case HashType::SHA256: {
if (ctx == nullptr) {
return absl::InvalidArgumentError("Context must be non-null.");
}
// Randomly generated salt forcing adversaries to re-compute rainbow
// tables.
static constexpr unsigned char kHashedBucketIdSalt[] = {
0xD6, 0x50, 0x82, 0x81, 0x82, 0xD9, 0x99, 0x11, 0x61, 0xE6, 0x7D,
0xB2, 0x91, 0x72, 0xE4, 0x05, 0x3E, 0x4A, 0xE8, 0x54, 0x0D, 0xFF,
0xB7, 0x8F, 0x61, 0x08, 0x0D, 0x96, 0x4D, 0x8F, 0x58, 0xFE};
return ctx->Sha256String(absl::StrCat(
std::string(reinterpret_cast<const char*>(kHashedBucketIdSalt), 32),
nsid));
break;
}
default:
return absl::InvalidArgumentError("Invalid hash type.");
}
}
} // namespace rlwe
} // namespace private_membership