// Copyright 2017 Google Inc. // // 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. // /////////////////////////////////////////////////////////////////////////////// #ifndef TINK_UTIL_TEST_UTIL_H_ #define TINK_UTIL_TEST_UTIL_H_ #include #include #include #include #include #include "absl/base/thread_annotations.h" #include "absl/status/status.h" #include "absl/strings/cord.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "tink/aead.h" #include "tink/aead/cord_aead.h" #include "tink/deterministic_aead.h" #include "tink/hybrid_decrypt.h" #include "tink/hybrid_encrypt.h" #include "tink/input_stream.h" #include "tink/keyset_handle.h" #include "tink/kms_client.h" #include "tink/mac.h" #include "tink/output_stream.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" #include "tink/random_access_stream.h" #include "tink/streaming_aead.h" #include "tink/subtle/common_enums.h" #include "tink/subtle/mac/stateful_mac.h" #include "tink/util/buffer.h" #include "tink/util/constants.h" #include "tink/util/protobuf_helper.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/common.pb.h" #include "proto/ecdsa.pb.h" #include "proto/ecies_aead_hkdf.pb.h" #include "proto/ed25519.pb.h" #include "proto/tink.pb.h" namespace crypto { namespace tink { namespace test { // Various utilities for testing. /////////////////////////////////////////////////////////////////////////////// // Reads the test file specified by `filename`, and returns its contents. std::string ReadTestFile(absl::string_view filename); // Converts a hexadecimal string into a string of bytes. // Returns a status if the size of the input is odd or if the input contains // characters that are not hexadecimal. crypto::tink::util::StatusOr HexDecode(absl::string_view hex); // Converts a hexadecimal string into a string of bytes. // Dies if the input is not a valid hexadecimal string. std::string HexDecodeOrDie(absl::string_view hex); // Converts a string of bytes into a hexadecimal string. std::string HexEncode(absl::string_view bytes); // Returns a temporary directory suitable for temporary testing files. std::string TmpDir(); // Adds the given 'keyData' with specified status, key_id, and // output_prefix_type to the keyset. void AddKeyData(const google::crypto::tink::KeyData& key_data, uint32_t key_id, google::crypto::tink::OutputPrefixType output_prefix, google::crypto::tink::KeyStatusType key_status, google::crypto::tink::Keyset* keyset); // Adds the given 'key' with specified parameters and output_prefix_type=TINK // to the specified 'keyset'. void AddTinkKey(const std::string& key_type, uint32_t key_id, const portable_proto::MessageLite& key, google::crypto::tink::KeyStatusType key_status, google::crypto::tink::KeyData::KeyMaterialType material_type, google::crypto::tink::Keyset* keyset); // Adds the given 'key' with specified parameters and output_prefix_type=LEGACY // to the specified 'keyset'. void AddLegacyKey(const std::string& key_type, uint32_t key_id, const portable_proto::MessageLite& key, google::crypto::tink::KeyStatusType key_status, google::crypto::tink::KeyData::KeyMaterialType material_type, google::crypto::tink::Keyset* keyset); // Adds the given 'key' with specified parameters and output_prefix_type=RAW // to the specified 'keyset'. void AddRawKey(const std::string& key_type, uint32_t key_id, const portable_proto::MessageLite& key, google::crypto::tink::KeyStatusType key_status, google::crypto::tink::KeyData::KeyMaterialType material_type, google::crypto::tink::Keyset* keyset); // Generates a fresh test key for ECIES-AEAD-HKDF for the given curve, // using AesGcm with the specified key size as AEAD, and HKDF with 'hash_type'. google::crypto::tink::EciesAeadHkdfPrivateKey GetEciesAesGcmHkdfTestKey( subtle::EllipticCurveType curve_type, subtle::EcPointFormat ec_point_format, subtle::HashType hash_type, uint32_t aes_gcm_key_size); // Generates a fresh test key for ECIES-AEAD-HKDF for the given curve, // using AesGcm with the specified key size as AEAD, and HKDF with 'hash_type'. google::crypto::tink::EciesAeadHkdfPrivateKey GetEciesAesGcmHkdfTestKey( google::crypto::tink::EllipticCurveType curve_type, google::crypto::tink::EcPointFormat ec_point_format, google::crypto::tink::HashType hash_type, uint32_t aes_gcm_key_size); // Generates a fresh test key for ECIES-AEAD-HKDF for the given curve, // using XChaCha20Poly1305 as AEAD, and HKDF with 'hash_type'. google::crypto::tink::EciesAeadHkdfPrivateKey GetEciesXChaCha20Poly1305HkdfTestKey( google::crypto::tink::EllipticCurveType curve_type, google::crypto::tink::EcPointFormat ec_point_format, google::crypto::tink::HashType hash_type); // Generates a fresh test key for ECIES-AEAD-HKDF for the given curve, // using AesCtrHmac with the specified AEAD params, and HKDF with 'hash_type'. google::crypto::tink::EciesAeadHkdfPrivateKey GetEciesAesCtrHmacHkdfTestKey( google::crypto::tink::EllipticCurveType curve_type, google::crypto::tink::EcPointFormat ec_point_format, google::crypto::tink::HashType hash_type, uint32_t aes_ctr_key_size, uint32_t aes_ctr_iv_size, google::crypto::tink::HashType hmac_hash_type, uint32_t hmac_tag_size, uint32_t hmac_key_size); // Generates a fresh test key for ECIES-AEAD-HKDF for the given curve, // using AesSiv as the determinisitic AEAD, and HKDF with 'hash_type'. google::crypto::tink::EciesAeadHkdfPrivateKey GetEciesAesSivHkdfTestKey( google::crypto::tink::EllipticCurveType curve_type, google::crypto::tink::EcPointFormat ec_point_format, google::crypto::tink::HashType hash_type); // Generates a fresh test key for EC DSA for the given 'curve_type', 'hash_type' // and 'encoding'. google::crypto::tink::EcdsaPrivateKey GetEcdsaTestPrivateKey( subtle::EllipticCurveType curve_type, subtle::HashType hash_type, subtle::EcdsaSignatureEncoding encoding); // Generates a fresh test key for EC DSA for the given 'curve_type', 'hash_type' // and 'encoding'. google::crypto::tink::EcdsaPrivateKey GetEcdsaTestPrivateKey( google::crypto::tink::EllipticCurveType curve_type, google::crypto::tink::HashType hash_type, google::crypto::tink::EcdsaSignatureEncoding encoding); // TODO(ambrosin): Remove because it is unused. // Generates a fresh test key for ED25519. google::crypto::tink::Ed25519PrivateKey GetEd25519TestPrivateKey(); // Embeds the given Proto into a KeyData proto. template google::crypto::tink::KeyData AsKeyData( const Proto& proto, google::crypto::tink::KeyData::KeyMaterialType key_material_type) { google::crypto::tink::KeyData result; result.set_value(proto.SerializeAsString()); result.set_type_url(absl::StrCat(kTypeGoogleapisCom, proto.GetTypeName())); result.set_key_material_type(key_material_type); return result; } // Uses a z test on the given byte string, expecting all bits to be uniformly // set with probability 1/2. Returns non ok status if the z test fails by more // than 10 standard deviations. // // With less statistics jargon: This counts the number of bits set and expects // the number to be roughly half of the length of the string. The law of large // numbers suggests that we can assume that the longer the string is, the more // accurate that estimate becomes for a random string. This test is useful to // detect things like strings that are entirely zero. // // Note: By itself, this is a very weak test for randomness. util::Status ZTestUniformString(absl::string_view bytes); // Tests that the crosscorrelation of two strings of equal length points to // independent and uniformly distributed strings. Returns non ok status if the z // test fails by more than 10 standard deviations. // // With less statistics jargon: This xors two strings and then performs the // ZTestUniformString on the result. If the two strings are independent and // uniformly distributed, the xor'ed string is as well. A cross correlation test // will find whether two strings overlap more or less than it would be expected. // // Note: Having a correlation of zero is only a necessary but not sufficient // condition for independence. util::Status ZTestCrosscorrelationUniformStrings(absl::string_view bytes1, absl::string_view bytes2); // Tests that the autocorrelation of a string points to the bits being // independent and uniformly distributed. Rotates the string in a cyclic // fashion. Returns non ok status if the z test fails by more than 10 standard // deviations. // // With less statistics jargon: This rotates the string bit by bit and performs // ZTestCrosscorrelationUniformStrings on each of the rotated strings and the // original. This will find self similarity of the input string, especially // periodic self similarity. For example, it is a decent test to find English // text (needs about 180 characters with the current settings). // // Note: Having a correlation of zero is only a necessary but not sufficient // condition for independence. util::Status ZTestAutocorrelationUniformString(absl::string_view bytes); // A dummy implementation of Aead-interface. // An instance of DummyAead can be identified by a name specified // as a parameter of the constructor. class DummyAead : public Aead { public: explicit DummyAead(absl::string_view aead_name) : aead_name_(aead_name) {} // Computes a dummy ciphertext, which is concatenation of provided 'plaintext' // with the name of this DummyAead. crypto::tink::util::StatusOr Encrypt( absl::string_view plaintext, absl::string_view associated_data) const override { return absl::StrCat(aead_name_.size(), ":", associated_data.size(), ":", aead_name_, associated_data, plaintext); } crypto::tink::util::StatusOr Decrypt( absl::string_view ciphertext, absl::string_view associated_data) const override { std::string prefix = absl::StrCat(aead_name_.size(), ":", associated_data.size(), ":", aead_name_, associated_data); if (!absl::StartsWith(ciphertext, prefix)) { return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, "Dummy operation failed."); } ciphertext.remove_prefix(prefix.size()); return std::string(ciphertext); } private: std::string aead_name_; }; // A dummy implementation of CordAead-interface. // An instance of DummyCordAead can be identified by a name specified // as a parameter of the constructor. class DummyCordAead : public CordAead { public: explicit DummyCordAead(absl::string_view aead_name) : aead_(aead_name) {} // Computes a dummy ciphertext, which is concatenation of provided 'plaintext' // with the name of this DummyCordAead. crypto::tink::util::StatusOr Encrypt( absl::Cord plaintext, absl::Cord associated_data) const override { auto ciphertext = aead_.Encrypt(plaintext.Flatten(), associated_data.Flatten()); if (!ciphertext.ok()) return ciphertext.status(); absl::Cord ciphertext_cord; ciphertext_cord.Append(ciphertext.value()); return ciphertext_cord; } crypto::tink::util::StatusOr Decrypt( absl::Cord ciphertext, absl::Cord associated_data) const override { auto plaintext = aead_.Decrypt(ciphertext.Flatten(), associated_data.Flatten()); if (!plaintext.ok()) return plaintext.status(); absl::Cord plaintext_cord; plaintext_cord.Append(plaintext.value()); return plaintext_cord; } private: DummyAead aead_; }; // A dummy implementation of DeterministicAead-interface. // An instance of DummyDeterministicAead can be identified by a name specified // as a parameter of the constructor. // The implementation is the same as DummyAead. class DummyDeterministicAead : public DeterministicAead { public: explicit DummyDeterministicAead(absl::string_view daead_name) : aead_(daead_name) {} crypto::tink::util::StatusOr EncryptDeterministically( absl::string_view plaintext, absl::string_view associated_data) const override { return aead_.Encrypt(plaintext, associated_data); } crypto::tink::util::StatusOr DecryptDeterministically( absl::string_view ciphertext, absl::string_view associated_data) const override { return aead_.Decrypt(ciphertext, associated_data); } private: DummyAead aead_; }; // A dummy implementation of StreamingAead-interface. An instance of // DummyStreamingAead can be identified by a name specified as a parameter of // the constructor. This name concatenated with 'associated_data' for a // specific stream yields a header of an encrypted stream produced/consumed // by DummyStreamingAead. class DummyStreamingAead : public StreamingAead { public: explicit DummyStreamingAead(absl::string_view streaming_aead_name) : streaming_aead_name_(streaming_aead_name) {} crypto::tink::util::StatusOr> NewEncryptingStream( std::unique_ptr ciphertext_destination, absl::string_view associated_data) const override { return {absl::make_unique( std::move(ciphertext_destination), absl::StrCat(streaming_aead_name_, associated_data))}; } crypto::tink::util::StatusOr> NewDecryptingStream( std::unique_ptr ciphertext_source, absl::string_view associated_data) const override { return {absl::make_unique( std::move(ciphertext_source), absl::StrCat(streaming_aead_name_, associated_data))}; } crypto::tink::util::StatusOr< std::unique_ptr> NewDecryptingRandomAccessStream( std::unique_ptr ciphertext_source, absl::string_view associated_data) const override { return {absl::make_unique( std::move(ciphertext_source), absl::StrCat(streaming_aead_name_, associated_data))}; } // Upon first call to Next() writes to 'ct_dest' the specifed 'header', // and subsequently forwards all methods calls to the corresponding // methods of 'cd_dest'. class DummyEncryptingStream : public crypto::tink::OutputStream { public: DummyEncryptingStream(std::unique_ptr ct_dest, absl::string_view header) : ct_dest_(std::move(ct_dest)), header_(header), after_init_(false), status_(util::OkStatus()) {} crypto::tink::util::StatusOr Next(void** data) override { if (!after_init_) { // Try to initialize. after_init_ = true; auto next_result = ct_dest_->Next(data); if (!next_result.ok()) { status_ = next_result.status(); return status_; } if (next_result.value() < header_.size()) { status_ = util::Status(absl::StatusCode::kInternal, "Buffer too small"); } else { memcpy(*data, header_.data(), static_cast(header_.size())); ct_dest_->BackUp(next_result.value() - header_.size()); } } if (!status_.ok()) return status_; return ct_dest_->Next(data); } void BackUp(int count) override { if (after_init_ && status_.ok()) { ct_dest_->BackUp(count); } } int64_t Position() const override { if (after_init_ && status_.ok()) { return ct_dest_->Position() - header_.size(); } else { return 0; } } util::Status Close() override { if (!after_init_) { // Call Next() to write the header to ct_dest_. void* buf; auto next_result = Next(&buf); if (next_result.ok()) { BackUp(next_result.value()); } else { status_ = next_result.status(); return status_; } } return ct_dest_->Close(); } private: std::unique_ptr ct_dest_; std::string header_; bool after_init_; util::Status status_; }; // class DummyEncryptingStream // Upon first call to Next() tries to read from 'ct_source' a header // that is expected to be equal to 'expected_header'. If this // header matching succeeds, all subsequent method calls are forwarded // to the corresponding methods of 'cd_source'. class DummyDecryptingStream : public crypto::tink::InputStream { public: DummyDecryptingStream(std::unique_ptr ct_source, absl::string_view expected_header) : ct_source_(std::move(ct_source)), exp_header_(expected_header), after_init_(false), status_(util::OkStatus()) {} crypto::tink::util::StatusOr Next(const void** data) override { if (!after_init_) { // Try to initialize. after_init_ = true; auto next_result = ct_source_->Next(data); if (!next_result.ok()) { status_ = next_result.status(); if (status_.code() == absl::StatusCode::kOutOfRange) { status_ = util::Status(absl::StatusCode::kInvalidArgument, "Could not read header"); } return status_; } if (next_result.value() < exp_header_.size()) { status_ = util::Status(absl::StatusCode::kInternal, "Buffer too small"); } else if (memcmp((*data), exp_header_.data(), static_cast(exp_header_.size()))) { status_ = util::Status(absl::StatusCode::kInvalidArgument, "Corrupted header"); } if (status_.ok()) { ct_source_->BackUp(next_result.value() - exp_header_.size()); } } if (!status_.ok()) return status_; return ct_source_->Next(data); } void BackUp(int count) override { if (after_init_ && status_.ok()) { ct_source_->BackUp(count); } } int64_t Position() const override { if (after_init_ && status_.ok()) { return ct_source_->Position() - exp_header_.size(); } else { return 0; } } private: std::unique_ptr ct_source_; std::string exp_header_; bool after_init_; util::Status status_; }; // class DummyDecryptingStream // Upon first call to PRead() tries to read from `ct_source` a header // that is expected to be equal to `expected_header`. If this // header matching succeeds, all subsequent method calls are forwarded // to `ct_source->PRead`. class DummyDecryptingRandomAccessStream : public crypto::tink::RandomAccessStream { public: DummyDecryptingRandomAccessStream( std::unique_ptr ct_source, absl::string_view expected_header) : ct_source_(std::move(ct_source)), exp_header_(expected_header) {} crypto::tink::util::Status PRead( int64_t position, int count, crypto::tink::util::Buffer* dest_buffer) override { util::Status status = CheckHeader(); if (!status.ok()) { return status; } status = dest_buffer->set_size(0); if (!status.ok()) return status; return ct_source_->PRead(position + exp_header_.size(), count, dest_buffer); } util::StatusOr size() override { util::Status status = CheckHeader(); if (!status.ok()) { return status; } auto ct_size_result = ct_source_->size(); if (!ct_size_result.ok()) return ct_size_result.status(); auto pt_size = ct_size_result.value() - exp_header_.size(); if (pt_size >= 0) return pt_size; return util::Status(absl::StatusCode::kUnavailable, "size not available"); } private: util::Status CheckHeader() ABSL_LOCKS_EXCLUDED(header_check_status_mutex_) { absl::MutexLock lock(&header_check_status_mutex_); if (header_check_status_.code() != absl::StatusCode::kUnavailable) { return header_check_status_; } auto buf = std::move(util::Buffer::New(exp_header_.size()).value()); header_check_status_ = ct_source_->PRead(0, exp_header_.size(), buf.get()); if (!header_check_status_.ok() && header_check_status_.code() != absl::StatusCode::kOutOfRange) { return header_check_status_; } // EOF or Ok indicate a valid read has happened. header_check_status_ = util::OkStatus(); // Invalid header. if (buf->size() < exp_header_.size()) { header_check_status_ = util::Status(absl::StatusCode::kInvalidArgument, "Could not read header"); } else if (memcmp(buf->get_mem_block(), exp_header_.data(), static_cast(exp_header_.size()))) { header_check_status_ = util::Status(absl::StatusCode::kInvalidArgument, "Corrupted header"); } return header_check_status_; } std::unique_ptr ct_source_; std::string exp_header_; mutable absl::Mutex header_check_status_mutex_; util::Status header_check_status_ ABSL_GUARDED_BY(header_check_status_mutex_) = util::Status(absl::StatusCode::kUnavailable, "Uninitialized"); }; // class DummyDecryptingRandomAccessStream private: std::string streaming_aead_name_; }; // class DummyStreamingAead // A dummy implementation of HybridEncrypt-interface. // An instance of DummyHybridEncrypt can be identified by a name specified // as a parameter of the constructor. class DummyHybridEncrypt : public HybridEncrypt { public: explicit DummyHybridEncrypt(absl::string_view hybrid_name) : dummy_aead_(absl::StrCat("DummyHybrid:", hybrid_name)) {} // Computes a dummy ciphertext, which is concatenation of provided 'plaintext' // with the name of this DummyHybridEncrypt. crypto::tink::util::StatusOr Encrypt( absl::string_view plaintext, absl::string_view context_info) const override { return dummy_aead_.Encrypt(plaintext, context_info); } private: DummyAead dummy_aead_; }; // A dummy implementation of HybridDecrypt-interface. // An instance of DummyHybridDecrypt can be identified by a name specified // as a parameter of the constructor. class DummyHybridDecrypt : public HybridDecrypt { public: explicit DummyHybridDecrypt(absl::string_view hybrid_name) : dummy_aead_(absl::StrCat("DummyHybrid:", hybrid_name)) {} // Decrypts a dummy ciphertext, which should be a concatenation // of a plaintext with the name of this DummyHybridDecrypt. crypto::tink::util::StatusOr Decrypt( absl::string_view ciphertext, absl::string_view context_info) const override { return dummy_aead_.Decrypt(ciphertext, context_info); } private: DummyAead dummy_aead_; }; // A dummy implementation of PublicKeySign-interface. // An instance of DummyPublicKeySign can be identified by a name specified // as a parameter of the constructor. class DummyPublicKeySign : public PublicKeySign { public: explicit DummyPublicKeySign(absl::string_view signature_name) : dummy_aead_(absl::StrCat("DummySign:", signature_name)) {} // Computes a dummy signature, which is a concatenation of 'data' // with the name of this DummyPublicKeySign. crypto::tink::util::StatusOr Sign( absl::string_view data) const override { return dummy_aead_.Encrypt("", data); } private: DummyAead dummy_aead_; }; // A dummy implementation of PublicKeyVerify-interface. // An instance of DummyPublicKeyVerify can be identified by a name specified // as a parameter of the constructor. class DummyPublicKeyVerify : public PublicKeyVerify { public: explicit DummyPublicKeyVerify(absl::string_view signature_name) : dummy_aead_(absl::StrCat("DummySign:", signature_name)) {} // Verifies a dummy signature, should be a concatenation of the name // of this DummyPublicKeyVerify with the provided 'data'. crypto::tink::util::Status Verify(absl::string_view signature, absl::string_view data) const override { return dummy_aead_.Decrypt(signature, data).status(); } private: DummyAead dummy_aead_; }; // A dummy implementation of Mac-interface. // An instance of DummyMac can be identified by a name specified // as a parameter of the constructor. class DummyMac : public Mac { public: explicit DummyMac(const std::string& mac_name) : dummy_aead_(absl::StrCat("DummyMac:", mac_name)) {} // Computes a dummy MAC, which is concatenation of provided 'data' // with the name of this DummyMac. crypto::tink::util::StatusOr ComputeMac( absl::string_view data) const override { return dummy_aead_.Encrypt("", data); } crypto::tink::util::Status VerifyMac(absl::string_view mac, absl::string_view data) const override { return dummy_aead_.Decrypt(mac, data).status(); } private: DummyAead dummy_aead_; }; // A dummy implementation of Stateful Mac interface. // An instance of DummyStatefulMac can be identified by a name specified // as a parameter of the constructor. // Over the same inputs, the DummyStatefulMac and DummyMac should give the same // output; DummyStatefulMac builds and internal_state_ and calls DummyMac. class DummyStatefulMac : public subtle::StatefulMac { public: explicit DummyStatefulMac(const std::string& mac_name) : dummy_aead_(absl::StrCat("DummyMac:", mac_name)), buffer_("") {} util::Status Update(absl::string_view data) override { absl::StrAppend(&buffer_, data); return util::OkStatus(); } util::StatusOr Finalize() override { return dummy_aead_.Encrypt("", buffer_); } private: DummyAead dummy_aead_; std::string buffer_; }; // A dummy implementation of KeysetWriter-interface. class DummyKeysetWriter : public KeysetWriter { public: static crypto::tink::util::StatusOr> New( std::unique_ptr destination_stream) { std::unique_ptr writer( new DummyKeysetWriter(std::move(destination_stream))); return std::move(writer); } crypto::tink::util::Status Write( const google::crypto::tink::Keyset& keyset) override { return crypto::tink::util::OkStatus(); } crypto::tink::util::Status Write( const google::crypto::tink::EncryptedKeyset& encrypted_keyset) override { return crypto::tink::util::OkStatus(); } private: explicit DummyKeysetWriter(std::unique_ptr destination_stream) : destination_stream_(std::move(destination_stream)) {} std::unique_ptr destination_stream_; }; // A dummy implementation of KmsClient-interface. class DummyKmsClient : public KmsClient { public: DummyKmsClient(absl::string_view uri_prefix, absl::string_view key_uri) : uri_prefix_(uri_prefix), key_uri_(key_uri) {} bool DoesSupport(absl::string_view key_uri) const override { if (key_uri.empty()) return false; if (key_uri_.empty()) return absl::StartsWith(key_uri, uri_prefix_); return key_uri == key_uri_; } crypto::tink::util::StatusOr> GetAead( absl::string_view key_uri) const override { if (!DoesSupport(key_uri)) return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, "key_uri not supported"); return {absl::make_unique(key_uri)}; } ~DummyKmsClient() override = default; private: std::string uri_prefix_; std::string key_uri_; }; } // namespace test } // namespace tink } // namespace crypto #endif // TINK_UTIL_TEST_UTIL_H_