зеркало из https://github.com/microsoft/CCF.git
Add issuer and subject to COSE signatures (#6637)
This commit is contained in:
Родитель
999fa17089
Коммит
31ceb7b93c
|
@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
### Added
|
||||
|
||||
- Added a `ccf::any_cert_auth_policy` (C++), or `any_cert` (JS/TS), implementing TLS client certificate authentication, but without checking for the presence of the certificate in the governance user or member tables. This enables applications wanting to do so to perform user management in application space, using application tables (#6608).
|
||||
- Added OpenAPI support for `std::unordered_set`.
|
||||
- Added OpenAPI support for `std::unordered_set` (#6634).
|
||||
- Added ["cose_signatures"](https://microsoft.github.io/CCF/main/operations/configuration.html#command-start-cose-signatures) entry in the configuration, which allows setting "issuer" and "subject" at network start or recovery time (#6637).
|
||||
|
||||
## [6.0.0-dev5]
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ protected-headers = {
|
|||
}
|
||||
|
||||
cwt-map = {
|
||||
&(iss: 1) => tstr, ; "issuer", string
|
||||
&(sub: 2) => tstr, ; "subject", string
|
||||
&(iat: 6) => int ; "issued at", number of seconds since the epoch
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ protected-headers = {
|
|||
}
|
||||
|
||||
cwt-map = {
|
||||
&(iss: 1) => tstr, ; "issuer", string
|
||||
&(sub: 2) => tstr, ; "subject", string
|
||||
&(iat: 6) => int ; "issued at", number of seconds since the epoch
|
||||
}
|
||||
|
||||
|
|
|
@ -296,6 +296,19 @@
|
|||
"default": "CN=CCF Service",
|
||||
"description": "Subject name to include in service certificate. Can only be set once on service start."
|
||||
},
|
||||
"cose_signatures": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"issuer": {
|
||||
"type": "string",
|
||||
"description": "Issuer, set in CWT_Claims of COSE ledger signatures. Can only be set once on service start."
|
||||
},
|
||||
"subject": {
|
||||
"type": "string",
|
||||
"description": "Subject, set in CWT_Claims of COSE ledger signatures. Can only be set once on service start."
|
||||
}
|
||||
}
|
||||
},
|
||||
"members": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
struct COSESignaturesConfig
|
||||
{
|
||||
std::string issuer;
|
||||
std::string subject;
|
||||
std::string issuer = "";
|
||||
std::string subject = "";
|
||||
|
||||
bool operator==(const COSESignaturesConfig& other) const = default;
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "ccf/crypto/curve.h"
|
||||
#include "ccf/ds/unit_strings.h"
|
||||
#include "ccf/node/cose_signatures_config.h"
|
||||
#include "ccf/pal/attestation_sev_snp_endorsements.h"
|
||||
#include "ccf/service/consensus_config.h"
|
||||
#include "ccf/service/node_info_network.h"
|
||||
|
@ -86,6 +87,7 @@ struct StartupConfig : CCFConfig
|
|||
// Only if starting or recovering
|
||||
size_t initial_service_certificate_validity_days = 1;
|
||||
std::string service_subject_name = "CN=CCF Service";
|
||||
COSESignaturesConfig cose_signatures;
|
||||
|
||||
nlohmann::json service_data = nullptr;
|
||||
|
||||
|
|
|
@ -243,6 +243,7 @@ def verify_receipt(
|
|||
raise ValueError("Signature verification failed")
|
||||
if claim_digest != leaf[2]:
|
||||
raise ValueError(f"Claim digest mismatch: {leaf[2]!r} != {claim_digest!r}")
|
||||
return receipt.phdr
|
||||
|
||||
|
||||
_SIGN_DESCRIPTION = """Create and sign a COSE Sign1 message for CCF governance
|
||||
|
|
|
@ -51,7 +51,11 @@
|
|||
"maximum_node_certificate_validity_days": 365
|
||||
},
|
||||
"initial_service_certificate_validity_days": 1,
|
||||
"service_subject_name": "CN=A Sample CCF Service"
|
||||
"service_subject_name": "CN=A Sample CCF Service",
|
||||
"cose_signatures": {
|
||||
"issuer": "service.example.com",
|
||||
"subject": "ledger.signature"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ledger": {
|
||||
|
|
|
@ -117,6 +117,7 @@ DECLARE_JSON_REQUIRED_FIELDS(
|
|||
snapshot_tx_interval,
|
||||
initial_service_certificate_validity_days,
|
||||
service_subject_name,
|
||||
cose_signatures,
|
||||
service_data,
|
||||
node_data,
|
||||
start,
|
||||
|
|
|
@ -30,6 +30,19 @@ namespace ccf::crypto
|
|||
* omitted.
|
||||
*/
|
||||
static constexpr int64_t COSE_PHEADER_KEY_IAT = 6;
|
||||
// Standardised: issuer CWT claim.
|
||||
// https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
|
||||
/* The "iss" (issuer) claim identifies the principal that issued the CWT.
|
||||
* The "iss" value is a case-sensitive string containing a StringOrURI value.
|
||||
*/
|
||||
static constexpr int64_t COSE_PHEADER_KEY_ISS = 1;
|
||||
// Standardised: subject CWT claim.
|
||||
// https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
|
||||
/* The "sub" (subject) claim identifies the principal that is the subject of
|
||||
* the CWT. The claims in a CWT are normally statements about the subject.
|
||||
* The "sub" value is a case-sensitive string containing a StringOrURI value.
|
||||
*/
|
||||
static constexpr int64_t COSE_PHEADER_KEY_SUB = 2;
|
||||
// CCF headers nested map key.
|
||||
static const std::string COSE_PHEADER_KEY_CCF = "ccf.v1";
|
||||
// CCF-specific: last signed TxID.
|
||||
|
|
|
@ -144,6 +144,7 @@ namespace host
|
|||
ccf::ServiceConfiguration service_configuration;
|
||||
size_t initial_service_certificate_validity_days = 1;
|
||||
std::string service_subject_name = "CN=CCF Service";
|
||||
COSESignaturesConfig cose_signatures;
|
||||
|
||||
bool operator==(const Start&) const = default;
|
||||
};
|
||||
|
@ -209,7 +210,8 @@ namespace host
|
|||
CCHostConfig::Command::Start,
|
||||
service_configuration,
|
||||
initial_service_certificate_validity_days,
|
||||
service_subject_name);
|
||||
service_subject_name,
|
||||
cose_signatures);
|
||||
|
||||
DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(CCHostConfig::Command::Join);
|
||||
DECLARE_JSON_REQUIRED_FIELDS(CCHostConfig::Command::Join, target_rpc_address);
|
||||
|
|
|
@ -640,6 +640,7 @@ int main(int argc, char** argv)
|
|||
config.command.start.initial_service_certificate_validity_days;
|
||||
startup_config.service_subject_name =
|
||||
config.command.start.service_subject_name;
|
||||
startup_config.cose_signatures = config.command.start.cose_signatures;
|
||||
LOG_INFO_FMT(
|
||||
"Creating new node: new network (with {} initial member(s) and {} "
|
||||
"member(s) required for recovery)",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "ccf/kv/get_name.h"
|
||||
#include "ccf/kv/hooks.h"
|
||||
#include "ccf/kv/version.h"
|
||||
#include "ccf/node/cose_signatures_config.h"
|
||||
#include "ccf/tx_id.h"
|
||||
#include "crypto/openssl/key_pair.h"
|
||||
#include "enclave/consensus_type.h"
|
||||
|
@ -424,8 +425,9 @@ namespace ccf::kv
|
|||
virtual std::vector<uint8_t> serialise_tree(size_t to) = 0;
|
||||
virtual void set_endorsed_certificate(const ccf::crypto::Pem& cert) = 0;
|
||||
virtual void start_signature_emit_timer() = 0;
|
||||
virtual void set_service_kp(
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL>) = 0;
|
||||
virtual void set_service_signing_identity(
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> keypair,
|
||||
const COSESignaturesConfig& cose_signatures) = 0;
|
||||
};
|
||||
|
||||
class Consensus : public ConfigurableConsensus
|
||||
|
|
|
@ -196,8 +196,9 @@ namespace ccf
|
|||
|
||||
void start_signature_emit_timer() override {}
|
||||
|
||||
void set_service_kp(
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> service_kp_) override
|
||||
void set_service_signing_identity(
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> service_kp_,
|
||||
const COSESignaturesConfig& cose_signatures) override
|
||||
{
|
||||
std::ignore = std::move(service_kp_);
|
||||
}
|
||||
|
@ -322,6 +323,7 @@ namespace ccf
|
|||
ccf::crypto::KeyPair& node_kp;
|
||||
ccf::crypto::KeyPair_OpenSSL& service_kp;
|
||||
ccf::crypto::Pem& endorsed_cert;
|
||||
const COSESignaturesConfig& cose_signatures_config;
|
||||
|
||||
public:
|
||||
MerkleTreeHistoryPendingTx(
|
||||
|
@ -331,14 +333,16 @@ namespace ccf
|
|||
const NodeId& id_,
|
||||
ccf::crypto::KeyPair& node_kp_,
|
||||
ccf::crypto::KeyPair_OpenSSL& service_kp_,
|
||||
ccf::crypto::Pem& endorsed_cert_) :
|
||||
ccf::crypto::Pem& endorsed_cert_,
|
||||
const COSESignaturesConfig& cose_signatures_config_) :
|
||||
txid(txid_),
|
||||
store(store_),
|
||||
history(history_),
|
||||
id(id_),
|
||||
node_kp(node_kp_),
|
||||
service_kp(service_kp_),
|
||||
endorsed_cert(endorsed_cert_)
|
||||
endorsed_cert(endorsed_cert_),
|
||||
cose_signatures_config(cose_signatures_config_)
|
||||
{}
|
||||
|
||||
ccf::kv::PendingTxInfo call() override
|
||||
|
@ -392,8 +396,16 @@ namespace ccf
|
|||
std::make_shared<ccf::crypto::COSEParametersMap>(
|
||||
std::make_shared<ccf::crypto::COSEMapIntKey>(
|
||||
ccf::crypto::COSE_PHEADER_KEY_CWT),
|
||||
ccf::crypto::COSEHeadersArray{ccf::crypto::cose_params_int_int(
|
||||
ccf::crypto::COSE_PHEADER_KEY_IAT, time_since_epoch)}));
|
||||
ccf::crypto::COSEHeadersArray{
|
||||
ccf::crypto::cose_params_int_int(
|
||||
ccf::crypto::COSE_PHEADER_KEY_IAT, time_since_epoch),
|
||||
ccf::crypto::cose_params_int_string(
|
||||
ccf::crypto::COSE_PHEADER_KEY_ISS,
|
||||
cose_signatures_config.issuer),
|
||||
ccf::crypto::cose_params_int_string(
|
||||
ccf::crypto::COSE_PHEADER_KEY_SUB,
|
||||
cose_signatures_config.subject),
|
||||
}));
|
||||
|
||||
const auto pheaders = {
|
||||
// Key digest
|
||||
|
@ -568,6 +580,7 @@ namespace ccf
|
|||
ccf::kv::Term term_of_next_version;
|
||||
|
||||
std::optional<ccf::crypto::Pem> endorsed_cert = std::nullopt;
|
||||
COSESignaturesConfig cose_signatures_config;
|
||||
|
||||
public:
|
||||
HashedTxHistory(
|
||||
|
@ -589,10 +602,16 @@ namespace ccf
|
|||
}
|
||||
}
|
||||
|
||||
void set_service_kp(
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> service_kp_) override
|
||||
void set_service_signing_identity(
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> service_kp_,
|
||||
const COSESignaturesConfig& cose_signatures_config_) override
|
||||
{
|
||||
service_kp = std::move(service_kp_);
|
||||
cose_signatures_config = cose_signatures_config_;
|
||||
LOG_INFO_FMT(
|
||||
"Setting service signing identity to iss: {} sub: {}",
|
||||
cose_signatures_config.issuer,
|
||||
cose_signatures_config.subject);
|
||||
}
|
||||
|
||||
void start_signature_emit_timer() override
|
||||
|
@ -860,7 +879,14 @@ namespace ccf
|
|||
store.commit(
|
||||
txid,
|
||||
std::make_unique<MerkleTreeHistoryPendingTx<T>>(
|
||||
txid, store, *this, id, node_kp, *service_kp, endorsed_cert.value()),
|
||||
txid,
|
||||
store,
|
||||
*this,
|
||||
id,
|
||||
node_kp,
|
||||
*service_kp,
|
||||
endorsed_cert.value(),
|
||||
cose_signatures_config),
|
||||
true);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "ccf/crypto/curve.h"
|
||||
#include "ccf/node/cose_signatures_config.h"
|
||||
#include "crypto/certs.h"
|
||||
#include "crypto/openssl/key_pair.h"
|
||||
|
||||
|
@ -24,6 +25,7 @@ namespace ccf
|
|||
ccf::crypto::Pem cert;
|
||||
std::optional<IdentityType> type = IdentityType::REPLICATED;
|
||||
std::string subject_name = "CN=CCF Service";
|
||||
COSESignaturesConfig cose_signatures_config;
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> kp{};
|
||||
|
||||
std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> get_key_pair()
|
||||
|
@ -39,12 +41,16 @@ namespace ccf
|
|||
bool operator==(const NetworkIdentity& other) const
|
||||
{
|
||||
return cert == other.cert && priv_key == other.priv_key &&
|
||||
type == other.type && subject_name == other.subject_name;
|
||||
type == other.type && subject_name == other.subject_name &&
|
||||
cose_signatures_config == other.cose_signatures_config;
|
||||
}
|
||||
|
||||
NetworkIdentity(const std::string& subject_name_) :
|
||||
NetworkIdentity(
|
||||
const std::string& subject_name_,
|
||||
const COSESignaturesConfig& cose_signatures_config_) :
|
||||
type(IdentityType::REPLICATED),
|
||||
subject_name(subject_name_)
|
||||
subject_name(subject_name_),
|
||||
cose_signatures_config(cose_signatures_config_)
|
||||
{}
|
||||
NetworkIdentity() = default;
|
||||
|
||||
|
@ -68,8 +74,9 @@ namespace ccf
|
|||
const std::string& subject_name_,
|
||||
ccf::crypto::CurveID curve_id,
|
||||
const std::string& valid_from,
|
||||
size_t validity_period_days) :
|
||||
NetworkIdentity(subject_name_)
|
||||
size_t validity_period_days,
|
||||
const COSESignaturesConfig& cose_signatures_config_) :
|
||||
NetworkIdentity(subject_name_, cose_signatures_config_)
|
||||
{
|
||||
auto identity_key_pair =
|
||||
std::make_shared<ccf::crypto::KeyPair_OpenSSL>(curve_id);
|
||||
|
@ -84,7 +91,7 @@ namespace ccf
|
|||
}
|
||||
|
||||
ReplicatedNetworkIdentity(const NetworkIdentity& other) :
|
||||
NetworkIdentity(other.subject_name)
|
||||
NetworkIdentity(other.subject_name, other.cose_signatures_config)
|
||||
{
|
||||
if (type != other.type)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "ccf/crypto/verifier.h"
|
||||
#include "ccf/ds/logger.h"
|
||||
#include "ccf/js/core/context.h"
|
||||
#include "ccf/node/cose_signatures_config.h"
|
||||
#include "ccf/pal/attestation.h"
|
||||
#include "ccf/pal/locking.h"
|
||||
#include "ccf/pal/platform.h"
|
||||
|
@ -501,11 +502,13 @@ namespace ccf
|
|||
config.service_subject_name,
|
||||
curve_id,
|
||||
config.startup_host_time,
|
||||
config.initial_service_certificate_validity_days);
|
||||
config.initial_service_certificate_validity_days,
|
||||
config.cose_signatures);
|
||||
|
||||
network.ledger_secrets->init();
|
||||
|
||||
history->set_service_kp(network.identity->get_key_pair());
|
||||
history->set_service_signing_identity(
|
||||
network.identity->get_key_pair(), config.cose_signatures);
|
||||
|
||||
setup_consensus(
|
||||
ServiceStatus::OPENING,
|
||||
|
@ -540,9 +543,11 @@ namespace ccf
|
|||
ccf::crypto::get_subject_name(previous_service_identity_cert),
|
||||
curve_id,
|
||||
config.startup_host_time,
|
||||
config.initial_service_certificate_validity_days);
|
||||
config.initial_service_certificate_validity_days,
|
||||
config.cose_signatures);
|
||||
|
||||
history->set_service_kp(network.identity->get_key_pair());
|
||||
history->set_service_signing_identity(
|
||||
network.identity->get_key_pair(), config.cose_signatures);
|
||||
|
||||
LOG_INFO_FMT("Created recovery node {}", self);
|
||||
return {self_signed_node_cert, network.identity->cert};
|
||||
|
@ -654,12 +659,20 @@ namespace ccf
|
|||
// Set network secrets, node id and become part of network.
|
||||
if (resp.node_status == NodeStatus::TRUSTED)
|
||||
{
|
||||
if (!resp.network_info.has_value())
|
||||
{
|
||||
throw std::logic_error("Expected network info in join response");
|
||||
}
|
||||
|
||||
network.identity = std::make_unique<ReplicatedNetworkIdentity>(
|
||||
resp.network_info->identity);
|
||||
network.ledger_secrets->init_from_map(
|
||||
std::move(resp.network_info->ledger_secrets));
|
||||
|
||||
history->set_service_kp(network.identity->get_key_pair());
|
||||
history->set_service_signing_identity(
|
||||
network.identity->get_key_pair(),
|
||||
resp.network_info->cose_signatures_config.value_or(
|
||||
COSESignaturesConfig{}));
|
||||
|
||||
ccf::crypto::Pem n2n_channels_cert;
|
||||
if (!resp.network_info->endorsed_certificate.has_value())
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "ccf/ds/json_schema.h"
|
||||
#include "ccf/node/cose_signatures_config.h"
|
||||
#include "ccf/node_startup_state.h"
|
||||
#include "ccf/pal/mem.h"
|
||||
#include "ccf/service/node_info_network.h"
|
||||
|
@ -14,7 +15,6 @@
|
|||
#include "enclave/interface.h"
|
||||
#include "node/identity.h"
|
||||
#include "node/ledger_secrets.h"
|
||||
#include "node/rpc/cose_signatures_config.h"
|
||||
#include "node/uvm_endorsements.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
|
|
@ -123,7 +123,8 @@ std::unique_ptr<ccf::NetworkIdentity> make_test_network_ident()
|
|||
"CN=CCF test network",
|
||||
ccf::crypto::service_identity_curve_choice,
|
||||
valid_from,
|
||||
2);
|
||||
2,
|
||||
COSESignaturesConfig{});
|
||||
}
|
||||
|
||||
void init_network(NetworkState& network)
|
||||
|
|
|
@ -62,7 +62,7 @@ TestState create_and_init_state(bool initialise_ledger_rekey = true)
|
|||
auto h =
|
||||
std::make_shared<ccf::MerkleTxHistory>(*ts.kv_store, node_id, *ts.node_kp);
|
||||
h->set_endorsed_certificate({});
|
||||
h->set_service_kp(ts.service_kp);
|
||||
h->set_service_signing_identity(ts.service_kp, COSESignaturesConfig{});
|
||||
ts.kv_store->set_history(h);
|
||||
ts.kv_store->initialise_term(2);
|
||||
|
||||
|
|
|
@ -87,7 +87,8 @@ TEST_CASE("Check signature verification")
|
|||
std::make_shared<ccf::MerkleTxHistory>(
|
||||
primary_store, ccf::kv::test::PrimaryNodeId, *node_kp);
|
||||
primary_history->set_endorsed_certificate(self_signed);
|
||||
primary_history->set_service_kp(service_kp);
|
||||
primary_history->set_service_signing_identity(
|
||||
service_kp, COSESignaturesConfig{});
|
||||
primary_store.set_history(primary_history);
|
||||
primary_store.initialise_term(store_term);
|
||||
|
||||
|
@ -97,7 +98,8 @@ TEST_CASE("Check signature verification")
|
|||
std::make_shared<ccf::MerkleTxHistory>(
|
||||
backup_store, ccf::kv::test::FirstBackupNodeId, *node_kp);
|
||||
backup_history->set_endorsed_certificate(self_signed);
|
||||
backup_history->set_service_kp(service_kp);
|
||||
backup_history->set_service_signing_identity(
|
||||
service_kp, COSESignaturesConfig{});
|
||||
backup_store.set_history(backup_history);
|
||||
backup_store.initialise_term(store_term);
|
||||
|
||||
|
@ -163,7 +165,8 @@ TEST_CASE("Check signing works across rollback")
|
|||
std::make_shared<ccf::MerkleTxHistory>(
|
||||
primary_store, ccf::kv::test::PrimaryNodeId, *node_kp);
|
||||
primary_history->set_endorsed_certificate(self_signed);
|
||||
primary_history->set_service_kp(service_kp);
|
||||
primary_history->set_service_signing_identity(
|
||||
service_kp, COSESignaturesConfig{});
|
||||
primary_store.set_history(primary_history);
|
||||
primary_store.initialise_term(store_term);
|
||||
|
||||
|
@ -172,7 +175,8 @@ TEST_CASE("Check signing works across rollback")
|
|||
std::make_shared<ccf::MerkleTxHistory>(
|
||||
backup_store, ccf::kv::test::FirstBackupNodeId, *node_kp);
|
||||
backup_history->set_endorsed_certificate(self_signed);
|
||||
backup_history->set_service_kp(service_kp);
|
||||
backup_history->set_service_signing_identity(
|
||||
service_kp, COSESignaturesConfig{});
|
||||
backup_store.set_history(backup_history);
|
||||
backup_store.set_encryptor(encryptor);
|
||||
backup_store.initialise_term(store_term);
|
||||
|
|
|
@ -34,7 +34,8 @@ TEST_CASE("Snapshot with merkle tree" * doctest::test_suite("snapshot"))
|
|||
auto source_history = std::make_shared<ccf::MerkleTxHistory>(
|
||||
source_store, source_node_id, *source_node_kp);
|
||||
source_history->set_endorsed_certificate({});
|
||||
source_history->set_service_kp(service_kp);
|
||||
source_history->set_service_signing_identity(
|
||||
service_kp, COSESignaturesConfig{});
|
||||
source_store.set_history(source_history);
|
||||
source_store.initialise_term(2);
|
||||
|
||||
|
@ -102,7 +103,8 @@ TEST_CASE("Snapshot with merkle tree" * doctest::test_suite("snapshot"))
|
|||
auto target_history = std::make_shared<ccf::MerkleTxHistory>(
|
||||
target_store, ccf::kv::test::PrimaryNodeId, *target_node_kp);
|
||||
target_history->set_endorsed_certificate({});
|
||||
target_history->set_service_kp(service_kp);
|
||||
target_history->set_service_signing_identity(
|
||||
service_kp, COSESignaturesConfig{});
|
||||
target_store.set_history(target_history);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,12 @@
|
|||
"maximum_service_certificate_validity_days": {{ maximum_service_certificate_validity_days }}
|
||||
},
|
||||
"initial_service_certificate_validity_days": {{ initial_service_cert_validity_days }},
|
||||
"service_subject_name": {{ service_subject_name|tojson }}
|
||||
"service_subject_name": {{ service_subject_name|tojson }},
|
||||
"cose_signatures":
|
||||
{
|
||||
"issuer": {{ cose_signatures_issuer|tojson }},
|
||||
"subject": {{ cose_signatures_subject|tojson }}
|
||||
}
|
||||
},
|
||||
"join":
|
||||
{
|
||||
|
|
|
@ -1053,7 +1053,11 @@ def test_cose_receipt_schema(network, args):
|
|||
|
||||
if r.status_code == http.HTTPStatus.OK:
|
||||
cbor_proof = r.body.data()
|
||||
ccf.cose.verify_receipt(cbor_proof, service_key, b"\0" * 32)
|
||||
receipt_phdr = ccf.cose.verify_receipt(
|
||||
cbor_proof, service_key, b"\0" * 32
|
||||
)
|
||||
assert receipt_phdr[15][1] == "service.example.com"
|
||||
assert receipt_phdr[15][2] == "ledger.signature"
|
||||
cbor_proof_filename = os.path.join(
|
||||
network.common_dir, f"receipt_{txid}.cose"
|
||||
)
|
||||
|
|
|
@ -8,6 +8,8 @@ import infra.logging_app as app
|
|||
import infra.e2e_args
|
||||
import infra.network
|
||||
import ccf.ledger
|
||||
from ccf.tx_id import TxID
|
||||
import base64
|
||||
import suite.test_requirements as reqs
|
||||
import infra.crypto
|
||||
import ipaddress
|
||||
|
@ -19,9 +21,11 @@ import json
|
|||
import subprocess
|
||||
import time
|
||||
import http
|
||||
import copy
|
||||
import infra.snp as snp
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from pycose.messages import Sign1Message
|
||||
|
||||
from loguru import logger as LOG
|
||||
|
||||
|
@ -589,6 +593,64 @@ def run_service_subject_name_check(args):
|
|||
assert cert.subject.rfc4514_string() == "CN=This test service", cert
|
||||
|
||||
|
||||
def run_cose_signatures_config_check(args):
|
||||
nargs = copy.deepcopy(args)
|
||||
nargs.nodes = infra.e2e_args.max_nodes(nargs, f=0)
|
||||
|
||||
with infra.network.network(
|
||||
nargs.nodes,
|
||||
nargs.binary_dir,
|
||||
nargs.debug_nodes,
|
||||
nargs.perf_nodes,
|
||||
pdb=nargs.pdb,
|
||||
) as network:
|
||||
network.start_and_open(
|
||||
nargs,
|
||||
cose_signatures_issuer="test.issuer.example.com",
|
||||
cose_signatures_subject="test.subject",
|
||||
)
|
||||
|
||||
for node in network.get_joined_nodes():
|
||||
with node.client("user0") as client:
|
||||
r = client.get("/commit")
|
||||
assert r.status_code == http.HTTPStatus.OK
|
||||
txid = TxID.from_str(r.body.json()["transaction_id"])
|
||||
max_retries = 10
|
||||
for _ in range(max_retries):
|
||||
response = client.get(
|
||||
"/log/public/cose_signature",
|
||||
headers={
|
||||
infra.clients.CCF_TX_ID_HEADER: f"{txid.view}.{txid.seqno}"
|
||||
},
|
||||
)
|
||||
|
||||
if response.status_code == http.HTTPStatus.OK:
|
||||
signature = response.body.json()["cose_signature"]
|
||||
signature = base64.b64decode(signature)
|
||||
signature_filename = os.path.join(
|
||||
network.common_dir, f"cose_signature_{txid}.cose"
|
||||
)
|
||||
with open(signature_filename, "wb") as f:
|
||||
f.write(signature)
|
||||
sig = Sign1Message.decode(signature)
|
||||
assert sig.phdr[15][1] == "test.issuer.example.com"
|
||||
assert sig.phdr[15][2] == "test.subject"
|
||||
LOG.debug(
|
||||
"Checked COSE signature schema for issuer and subject"
|
||||
)
|
||||
break
|
||||
elif response.status_code == http.HTTPStatus.ACCEPTED:
|
||||
LOG.debug(f"Transaction {txid} accepted, retrying")
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
LOG.error(f"Failed to get COSE signature for txid {txid}")
|
||||
break
|
||||
else:
|
||||
assert (
|
||||
False
|
||||
), f"Failed to get receipt for txid {txid} after {max_retries} retries"
|
||||
|
||||
|
||||
def run(args):
|
||||
run_max_uncommitted_tx_count(args)
|
||||
run_file_operations(args)
|
||||
|
@ -599,3 +661,4 @@ def run(args):
|
|||
run_preopen_readiness_check(args)
|
||||
run_sighup_check(args)
|
||||
run_service_subject_name_check(args)
|
||||
run_cose_signatures_config_check(args)
|
||||
|
|
|
@ -341,6 +341,8 @@ class CCFRemote(object):
|
|||
snp_uvm_endorsements_file=None,
|
||||
service_subject_name="CN=CCF Test Service",
|
||||
historical_cache_soft_limit=None,
|
||||
cose_signatures_issuer="service.example.com",
|
||||
cose_signatures_subject="ledger.signature",
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
|
@ -536,6 +538,8 @@ class CCFRemote(object):
|
|||
snp_uvm_endorsements_file=snp_uvm_endorsements_file,
|
||||
service_subject_name=service_subject_name,
|
||||
historical_cache_soft_limit=historical_cache_soft_limit,
|
||||
cose_signatures_issuer=cose_signatures_issuer,
|
||||
cose_signatures_subject=cose_signatures_subject,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче