Based on Nekogram. Key additions: - Rebrand to FoxiGram (app name, APK name, applicationId com.foxigram.app) - Embedded Xray (VLESS+Reality) proxy client via JNI libxray.so - Bundled hidden one-tap proxies (LTE + WiFi), read-only in UI - Auto-restore proxy on restart, rebind to active network (LTE/WiFi) - Server credentials externalized to git-ignored XrayServers.java (+ template) - libxray Go source included; compiled .so, keystore, google-services.json ignored
357 lines
12 KiB
C++
357 lines
12 KiB
C++
// Copyright 2023 The BoringSSL 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
|
|
//
|
|
// 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 <openssl/pki/verify.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <chrono>
|
|
#include <optional>
|
|
#include <string_view>
|
|
|
|
#include <openssl/base.h>
|
|
#include <openssl/bytestring.h>
|
|
#include <openssl/pool.h>
|
|
|
|
#include <openssl/pki/signature_verify_cache.h>
|
|
|
|
#include "cert_errors.h"
|
|
#include "cert_issuer_source_static.h"
|
|
#include "certificate_policies.h"
|
|
#include "common_cert_errors.h"
|
|
#include "encode_values.h"
|
|
#include "input.h"
|
|
#include "parse_certificate.h"
|
|
#include "parse_values.h"
|
|
#include "parsed_certificate.h"
|
|
#include "path_builder.h"
|
|
#include "simple_path_builder_delegate.h"
|
|
#include "trust_store.h"
|
|
#include "trust_store_in_memory.h"
|
|
#include "verify_certificate_chain.h"
|
|
|
|
BSSL_NAMESPACE_BEGIN
|
|
|
|
namespace {
|
|
|
|
std::optional<std::shared_ptr<const ParsedCertificate>>
|
|
InternalParseCertificate(Span<const uint8_t> cert, std::string *out_diagnostic) {
|
|
ParseCertificateOptions default_options{};
|
|
// We follow Chromium in setting |allow_invalid_serial_numbers| in order to
|
|
// not choke on 21-byte serial numbers, which are common.
|
|
//
|
|
// The reason for the discrepancy is that unsigned numbers with the high bit
|
|
// otherwise set get an extra 0 byte in front to keep them positive. So if you
|
|
// do:
|
|
// var num [20]byte
|
|
// fillWithRandom(num[:])
|
|
// serialNumber := new(big.Int).SetBytes(num[:])
|
|
// encodeASN1Integer(serialNumber)
|
|
//
|
|
// Then half of your serial numbers will be encoded with 21 bytes. (And
|
|
// 1/512th will have 19 bytes instead of 20.)
|
|
default_options.allow_invalid_serial_numbers = true;
|
|
|
|
UniquePtr<CRYPTO_BUFFER> buffer(
|
|
CRYPTO_BUFFER_new(cert.data(), cert.size(), nullptr));
|
|
CertErrors errors;
|
|
std::shared_ptr<const ParsedCertificate> parsed_cert(
|
|
ParsedCertificate::Create(std::move(buffer), default_options, &errors));
|
|
if (!parsed_cert) {
|
|
*out_diagnostic = errors.ToDebugString();
|
|
return {};
|
|
}
|
|
return parsed_cert;
|
|
}
|
|
} // namespace
|
|
|
|
|
|
CertPool::CertPool() {}
|
|
|
|
CertificateVerifyOptions::CertificateVerifyOptions() {}
|
|
|
|
static std::unique_ptr<VerifyTrustStore> WrapTrustStore(
|
|
std::unique_ptr<TrustStoreInMemory> trust_store) {
|
|
std::unique_ptr<VerifyTrustStore> ret(new VerifyTrustStore);
|
|
ret->trust_store = std::move(trust_store);
|
|
return ret;
|
|
}
|
|
|
|
VerifyTrustStore::~VerifyTrustStore() {}
|
|
|
|
std::unique_ptr<VerifyTrustStore> VerifyTrustStore::FromDER(
|
|
std::string_view der_certs, std::string *out_diagnostic) {
|
|
auto store = std::make_unique<TrustStoreInMemory>();
|
|
CBS cbs = StringAsBytes(der_certs);
|
|
|
|
for (size_t cert_num = 1; CBS_len(&cbs) != 0; cert_num++) {
|
|
CBS cert;
|
|
if (!CBS_get_asn1_element(&cbs, &cert, CBS_ASN1_SEQUENCE)) {
|
|
*out_diagnostic = "failed to get ASN.1 SEQUENCE from input at cert " +
|
|
std::to_string(cert_num);
|
|
return {};
|
|
}
|
|
|
|
auto parsed_cert = InternalParseCertificate(cert, out_diagnostic);
|
|
if (!parsed_cert.has_value()) {
|
|
return {};
|
|
}
|
|
store->AddTrustAnchor(parsed_cert.value());
|
|
}
|
|
|
|
return WrapTrustStore(std::move(store));
|
|
}
|
|
|
|
std::unique_ptr<VerifyTrustStore> VerifyTrustStore::FromDER(
|
|
const std::vector<std::string_view> &der_roots,
|
|
std::string *out_diagnostic) {
|
|
auto store = std::make_unique<TrustStoreInMemory>();
|
|
|
|
for (const std::string_view &cert : der_roots) {
|
|
auto parsed_cert = InternalParseCertificate(StringAsBytes(cert), out_diagnostic);
|
|
if (!parsed_cert.has_value()) {
|
|
return {};
|
|
}
|
|
store->AddTrustAnchor(parsed_cert.value());
|
|
}
|
|
|
|
return WrapTrustStore(std::move(store));
|
|
}
|
|
|
|
CertPool::~CertPool() {}
|
|
|
|
|
|
std::unique_ptr<CertPool> CertPool::FromCerts(
|
|
const std::vector<std::string_view> &der_certs,
|
|
std::string *out_diagnostic) {
|
|
auto pool = std::make_unique<CertPool>();
|
|
pool->impl_ = std::make_unique<CertIssuerSourceStatic>();
|
|
|
|
for (const std::string_view &cert : der_certs) {
|
|
auto parsed_cert =
|
|
InternalParseCertificate(StringAsBytes(cert), out_diagnostic);
|
|
if (!parsed_cert.has_value()) {
|
|
return {};
|
|
}
|
|
pool->impl_->AddCert(std::move(parsed_cert.value()));
|
|
}
|
|
|
|
return pool;
|
|
}
|
|
|
|
CertificateVerifyStatus::CertificateVerifyStatus() {}
|
|
|
|
size_t CertificateVerifyStatus::IterationCount() const {
|
|
return iteration_count_;
|
|
}
|
|
|
|
size_t CertificateVerifyStatus::MaxDepthSeen() const { return max_depth_seen_; }
|
|
|
|
// PathBuilderDelegateImpl implements a deadline and allows for the
|
|
// use of a SignatureVerifyCache if an implementation is provided.
|
|
class PathBuilderDelegateImpl : public SimplePathBuilderDelegate {
|
|
public:
|
|
PathBuilderDelegateImpl(
|
|
size_t min_rsa_modulus_length_bits, DigestPolicy digest_policy,
|
|
std::chrono::time_point<std::chrono::steady_clock> deadline,
|
|
SignatureVerifyCache *cache)
|
|
: SimplePathBuilderDelegate(min_rsa_modulus_length_bits, digest_policy),
|
|
deadline_(deadline),
|
|
cache_(cache) {}
|
|
|
|
bool IsDeadlineExpired() override {
|
|
return (std::chrono::steady_clock::now() > deadline_);
|
|
}
|
|
|
|
SignatureVerifyCache *GetVerifyCache() override { return cache_; }
|
|
|
|
private:
|
|
const std::chrono::time_point<std::chrono::steady_clock> deadline_;
|
|
SignatureVerifyCache *cache_;
|
|
};
|
|
|
|
std::optional<std::vector<std::vector<std::string>>> CertificateVerifyInternal(
|
|
const CertificateVerifyOptions &opts, VerifyError *out_error,
|
|
CertificateVerifyStatus *out_status, bool all_paths) {
|
|
VerifyError dummy;
|
|
if (!out_error) {
|
|
out_error = &dummy;
|
|
}
|
|
if (out_status != nullptr) {
|
|
out_status->iteration_count_ = 0;
|
|
out_status->max_depth_seen_ = 0;
|
|
}
|
|
|
|
std::string diagnostic;
|
|
std::optional<std::shared_ptr<const ParsedCertificate>> maybe_leaf =
|
|
InternalParseCertificate(StringAsBytes(opts.leaf_cert), &diagnostic);
|
|
|
|
if (!maybe_leaf.has_value()) {
|
|
*out_error = {VerifyError::StatusCode::CERTIFICATE_INVALID, 0, diagnostic};
|
|
return {};
|
|
}
|
|
std::shared_ptr<const ParsedCertificate> leaf_cert = maybe_leaf.value();
|
|
|
|
int64_t now;
|
|
if (opts.time.has_value()) {
|
|
now = opts.time.value();
|
|
} else {
|
|
now = time(NULL);
|
|
}
|
|
|
|
der::GeneralizedTime verification_time;
|
|
if (!der::EncodePosixTimeAsGeneralizedTime(now, &verification_time)) {
|
|
*out_error = {VerifyError::StatusCode::VERIFICATION_FAILURE, -1,
|
|
"\nCould not encode verification time\n"};
|
|
return {};
|
|
}
|
|
|
|
TrustStore *trust_store = nullptr;
|
|
if (opts.trust_store) {
|
|
trust_store = opts.trust_store->trust_store.get();
|
|
}
|
|
|
|
auto digest_policy = SimplePathBuilderDelegate::DigestPolicy::kStrong;
|
|
// TODO(b/111551631): remove this
|
|
if (opts.insecurely_allow_sha1) {
|
|
digest_policy = SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1;
|
|
}
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock> deadline =
|
|
std::chrono::time_point<std::chrono::steady_clock>::max();
|
|
if (opts.deadline.has_value()) {
|
|
deadline = opts.deadline.value();
|
|
}
|
|
|
|
PathBuilderDelegateImpl path_builder_delegate(
|
|
opts.min_rsa_modulus_length, digest_policy, deadline,
|
|
opts.signature_verify_cache);
|
|
|
|
KeyPurpose key_purpose = KeyPurpose::SERVER_AUTH;
|
|
switch (opts.key_purpose) {
|
|
case CertificateVerifyOptions::KeyPurpose::ANY_EKU:
|
|
key_purpose = KeyPurpose::ANY_EKU;
|
|
break;
|
|
case CertificateVerifyOptions::KeyPurpose::SERVER_AUTH:
|
|
key_purpose = KeyPurpose::SERVER_AUTH;
|
|
break;
|
|
case CertificateVerifyOptions::KeyPurpose::CLIENT_AUTH:
|
|
key_purpose = KeyPurpose::CLIENT_AUTH;
|
|
break;
|
|
case CertificateVerifyOptions::KeyPurpose::SERVER_AUTH_STRICT:
|
|
key_purpose = KeyPurpose::SERVER_AUTH_STRICT;
|
|
break;
|
|
case CertificateVerifyOptions::KeyPurpose::CLIENT_AUTH_STRICT:
|
|
key_purpose = KeyPurpose::CLIENT_AUTH_STRICT;
|
|
break;
|
|
case CertificateVerifyOptions::KeyPurpose::SERVER_AUTH_STRICT_LEAF:
|
|
key_purpose = KeyPurpose::SERVER_AUTH_STRICT_LEAF;
|
|
break;
|
|
case CertificateVerifyOptions::KeyPurpose::CLIENT_AUTH_STRICT_LEAF:
|
|
key_purpose = KeyPurpose::CLIENT_AUTH_STRICT_LEAF;
|
|
break;
|
|
case CertificateVerifyOptions::KeyPurpose::RCS_MLS_CLIENT_AUTH:
|
|
key_purpose = KeyPurpose::RCS_MLS_CLIENT_AUTH;
|
|
break;
|
|
}
|
|
CertPathBuilder path_builder(leaf_cert, trust_store, &path_builder_delegate,
|
|
verification_time, key_purpose,
|
|
InitialExplicitPolicy::kFalse,
|
|
/* user_initial_policy_set= */
|
|
{der::Input(kAnyPolicyOid)},
|
|
InitialPolicyMappingInhibit::kFalse,
|
|
InitialAnyPolicyInhibit::kFalse);
|
|
|
|
CertIssuerSourceStatic intermediates;
|
|
for (const std::string_view &cert : opts.intermediates) {
|
|
std::string diag_string;
|
|
std::optional<std::shared_ptr<const ParsedCertificate>> parsed =
|
|
InternalParseCertificate(StringAsBytes(cert), &diag_string);
|
|
if (!parsed.has_value()) {
|
|
if (path_builder_delegate.IsDebugLogEnabled()) {
|
|
path_builder_delegate.DebugLog("skipping bad intermediate: " +
|
|
diag_string);
|
|
}
|
|
continue;
|
|
}
|
|
intermediates.AddCert(std::move(parsed.value()));
|
|
}
|
|
path_builder.AddCertIssuerSource(&intermediates);
|
|
|
|
if (opts.extra_intermediates != nullptr) {
|
|
path_builder.AddCertIssuerSource(opts.extra_intermediates->impl_.get());
|
|
}
|
|
|
|
if (opts.max_iteration_count > 0) {
|
|
path_builder.SetIterationLimit(opts.max_iteration_count);
|
|
}
|
|
|
|
if (opts.max_path_building_depth > 0) {
|
|
path_builder.SetDepthLimit(opts.max_path_building_depth);
|
|
}
|
|
|
|
path_builder.SetExploreAllPaths(all_paths);
|
|
|
|
CertPathBuilder::Result result = path_builder.Run();
|
|
|
|
if (out_status != nullptr) {
|
|
out_status->iteration_count_ = result.iteration_count;
|
|
out_status->max_depth_seen_ = result.max_depth_seen;
|
|
}
|
|
|
|
*out_error = result.GetBestPathVerifyError();
|
|
|
|
if (result.HasValidPath()) {
|
|
std::vector<std::vector<std::string>> ret;
|
|
if (!all_paths) {
|
|
auto best_path = result.GetBestValidPath();
|
|
ret.push_back(std::vector<std::string>());
|
|
for (size_t i = 0; i < best_path->certs.size(); i++) {
|
|
ret[0].emplace_back(BytesAsStringView(best_path->certs[i]->der_cert()));
|
|
}
|
|
return ret;
|
|
}
|
|
for (const auto &path : result.paths) {
|
|
if (!path->IsValid()) {
|
|
continue;
|
|
}
|
|
std::vector<std::string> ret_path;
|
|
for (const auto &cert : path->certs) {
|
|
ret_path.emplace_back(BytesAsStringView(cert->der_cert()));
|
|
}
|
|
ret.push_back(ret_path);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
std::optional<std::vector<std::string>> CertificateVerify(
|
|
const CertificateVerifyOptions &opts, VerifyError *out_error,
|
|
CertificateVerifyStatus *out_status) {
|
|
auto single_path = CertificateVerifyInternal(opts, out_error, out_status,
|
|
/*all_paths=*/false);
|
|
if (!single_path.has_value()) {
|
|
return {};
|
|
}
|
|
return single_path.value()[0];
|
|
}
|
|
|
|
std::optional<std::vector<std::vector<std::string>>> CertificateVerifyAllPaths(
|
|
const CertificateVerifyOptions &opts) {
|
|
return CertificateVerifyInternal(opts, nullptr, nullptr, /*all_paths=*/true);
|
|
}
|
|
|
|
BSSL_NAMESPACE_END
|