diff --git a/boringssl.BUILD b/boringssl.BUILD new file mode 100644 index 00000000000..054b9ecbd9d --- /dev/null +++ b/boringssl.BUILD @@ -0,0 +1,410 @@ +package(default_visibility = ["//tensorflow:__subpackages__"]) + +licenses(["restricted"]) # OpenSSL license, partly BSD-like + +# See https://boringssl.googlesource.com/boringssl/+/master/INCORPORATING.md +# on how to re-generate the list of source files. + +crypto_headers = [ + "include/openssl/aead.h", + "include/openssl/aes.h", + "include/openssl/arm_arch.h", + "include/openssl/asn1.h", + "include/openssl/asn1_mac.h", + "include/openssl/asn1t.h", + "include/openssl/base.h", + "include/openssl/base64.h", + "include/openssl/bio.h", + "include/openssl/blowfish.h", + "include/openssl/bn.h", + "include/openssl/buf.h", + "include/openssl/buffer.h", + "include/openssl/bytestring.h", + "include/openssl/cast.h", + "include/openssl/chacha.h", + "include/openssl/cipher.h", + "include/openssl/cmac.h", + "include/openssl/conf.h", + "include/openssl/cpu.h", + "include/openssl/crypto.h", + "include/openssl/curve25519.h", + "include/openssl/des.h", + "include/openssl/dh.h", + "include/openssl/digest.h", + "include/openssl/dsa.h", + "include/openssl/ec.h", + "include/openssl/ec_key.h", + "include/openssl/ecdh.h", + "include/openssl/ecdsa.h", + "include/openssl/engine.h", + "include/openssl/err.h", + "include/openssl/evp.h", + "include/openssl/ex_data.h", + "include/openssl/hkdf.h", + "include/openssl/hmac.h", + "include/openssl/lhash.h", + "include/openssl/lhash_macros.h", + "include/openssl/md4.h", + "include/openssl/md5.h", + "include/openssl/mem.h", + "include/openssl/newhope.h", + "include/openssl/nid.h", + "include/openssl/obj.h", + "include/openssl/obj_mac.h", + "include/openssl/objects.h", + "include/openssl/opensslconf.h", + "include/openssl/opensslv.h", + "include/openssl/ossl_typ.h", + "include/openssl/pem.h", + "include/openssl/pkcs12.h", + "include/openssl/pkcs7.h", + "include/openssl/pkcs8.h", + "include/openssl/poly1305.h", + "include/openssl/pqueue.h", + "include/openssl/rand.h", + "include/openssl/rc4.h", + "include/openssl/ripemd.h", + "include/openssl/rsa.h", + "include/openssl/safestack.h", + "include/openssl/sha.h", + "include/openssl/srtp.h", + "include/openssl/stack.h", + "include/openssl/stack_macros.h", + "include/openssl/thread.h", + "include/openssl/time_support.h", + "include/openssl/type_check.h", + "include/openssl/x509.h", + "include/openssl/x509_vfy.h", + "include/openssl/x509v3.h", +] + +crypto_internal_headers = [ + "crypto/aes/internal.h", + "crypto/asn1/asn1_locl.h", + "crypto/bio/internal.h", + "crypto/bn/internal.h", + "crypto/bn/rsaz_exp.h", + "crypto/bytestring/internal.h", + "crypto/cipher/internal.h", + "crypto/conf/conf_def.h", + "crypto/conf/internal.h", + "crypto/curve25519/internal.h", + "crypto/des/internal.h", + "crypto/dh/internal.h", + "crypto/digest/internal.h", + "crypto/digest/md32_common.h", + "crypto/ec/internal.h", + "crypto/ec/p256-x86_64-table.h", + "crypto/evp/internal.h", + "crypto/internal.h", + "crypto/modes/internal.h", + "crypto/newhope/internal.h", + "crypto/obj/obj_dat.h", + "crypto/obj/obj_xref.h", + "crypto/pkcs8/internal.h", + "crypto/poly1305/internal.h", + "crypto/rand/internal.h", + "crypto/rsa/internal.h", + "crypto/test/scoped_types.h", + "crypto/test/test_util.h", + "crypto/x509/charmap.h", + "crypto/x509/internal.h", + "crypto/x509/vpm_int.h", + "crypto/x509v3/ext_dat.h", + "crypto/x509v3/pcy_int.h", +] + +crypto_sources = [ + ":err_data_c", + "crypto/aes/aes.c", + "crypto/aes/mode_wrappers.c", + "crypto/asn1/a_bitstr.c", + "crypto/asn1/a_bool.c", + "crypto/asn1/a_bytes.c", + "crypto/asn1/a_d2i_fp.c", + "crypto/asn1/a_dup.c", + "crypto/asn1/a_enum.c", + "crypto/asn1/a_gentm.c", + "crypto/asn1/a_i2d_fp.c", + "crypto/asn1/a_int.c", + "crypto/asn1/a_mbstr.c", + "crypto/asn1/a_object.c", + "crypto/asn1/a_octet.c", + "crypto/asn1/a_print.c", + "crypto/asn1/a_strnid.c", + "crypto/asn1/a_time.c", + "crypto/asn1/a_type.c", + "crypto/asn1/a_utctm.c", + "crypto/asn1/a_utf8.c", + "crypto/asn1/asn1_lib.c", + "crypto/asn1/asn1_par.c", + "crypto/asn1/asn_pack.c", + "crypto/asn1/bio_asn1.c", + "crypto/asn1/bio_ndef.c", + "crypto/asn1/f_enum.c", + "crypto/asn1/f_int.c", + "crypto/asn1/f_string.c", + "crypto/asn1/t_bitst.c", + "crypto/asn1/tasn_dec.c", + "crypto/asn1/tasn_enc.c", + "crypto/asn1/tasn_fre.c", + "crypto/asn1/tasn_new.c", + "crypto/asn1/tasn_prn.c", + "crypto/asn1/tasn_typ.c", + "crypto/asn1/tasn_utl.c", + "crypto/asn1/x_bignum.c", + "crypto/asn1/x_long.c", + "crypto/base64/base64.c", + "crypto/bio/bio.c", + "crypto/bio/bio_mem.c", + "crypto/bio/buffer.c", + "crypto/bio/connect.c", + "crypto/bio/fd.c", + "crypto/bio/file.c", + "crypto/bio/hexdump.c", + "crypto/bio/pair.c", + "crypto/bio/printf.c", + "crypto/bio/socket.c", + "crypto/bio/socket_helper.c", + "crypto/bn/add.c", + "crypto/bn/asm/x86_64-gcc.c", + "crypto/bn/bn.c", + "crypto/bn/bn_asn1.c", + "crypto/bn/cmp.c", + "crypto/bn/convert.c", + "crypto/bn/ctx.c", + "crypto/bn/div.c", + "crypto/bn/exponentiation.c", + "crypto/bn/gcd.c", + "crypto/bn/generic.c", + "crypto/bn/kronecker.c", + "crypto/bn/montgomery.c", + "crypto/bn/mul.c", + "crypto/bn/prime.c", + "crypto/bn/random.c", + "crypto/bn/rsaz_exp.c", + "crypto/bn/shift.c", + "crypto/bn/sqrt.c", + "crypto/buf/buf.c", + "crypto/bytestring/asn1_compat.c", + "crypto/bytestring/ber.c", + "crypto/bytestring/cbb.c", + "crypto/bytestring/cbs.c", + "crypto/chacha/chacha.c", + "crypto/cipher/aead.c", + "crypto/cipher/cipher.c", + "crypto/cipher/derive_key.c", + "crypto/cipher/e_aes.c", + "crypto/cipher/e_chacha20poly1305.c", + "crypto/cipher/e_des.c", + "crypto/cipher/e_null.c", + "crypto/cipher/e_rc2.c", + "crypto/cipher/e_rc4.c", + "crypto/cipher/e_ssl3.c", + "crypto/cipher/e_tls.c", + "crypto/cipher/tls_cbc.c", + "crypto/cmac/cmac.c", + "crypto/conf/conf.c", + "crypto/cpu-aarch64-linux.c", + "crypto/cpu-arm-linux.c", + "crypto/cpu-arm.c", + "crypto/cpu-intel.c", + "crypto/crypto.c", + "crypto/curve25519/curve25519.c", + "crypto/curve25519/spake25519.c", + "crypto/curve25519/x25519-x86_64.c", + "crypto/des/des.c", + "crypto/dh/check.c", + "crypto/dh/dh.c", + "crypto/dh/dh_asn1.c", + "crypto/dh/params.c", + "crypto/digest/digest.c", + "crypto/digest/digests.c", + "crypto/dsa/dsa.c", + "crypto/dsa/dsa_asn1.c", + "crypto/ec/ec.c", + "crypto/ec/ec_asn1.c", + "crypto/ec/ec_key.c", + "crypto/ec/ec_montgomery.c", + "crypto/ec/oct.c", + "crypto/ec/p224-64.c", + "crypto/ec/p256-64.c", + "crypto/ec/p256-x86_64.c", + "crypto/ec/simple.c", + "crypto/ec/util-64.c", + "crypto/ec/wnaf.c", + "crypto/ecdh/ecdh.c", + "crypto/ecdsa/ecdsa.c", + "crypto/ecdsa/ecdsa_asn1.c", + "crypto/engine/engine.c", + "crypto/err/err.c", + "crypto/evp/digestsign.c", + "crypto/evp/evp.c", + "crypto/evp/evp_asn1.c", + "crypto/evp/evp_ctx.c", + "crypto/evp/p_dsa_asn1.c", + "crypto/evp/p_ec.c", + "crypto/evp/p_ec_asn1.c", + "crypto/evp/p_rsa.c", + "crypto/evp/p_rsa_asn1.c", + "crypto/evp/pbkdf.c", + "crypto/evp/print.c", + "crypto/evp/sign.c", + "crypto/ex_data.c", + "crypto/hkdf/hkdf.c", + "crypto/hmac/hmac.c", + "crypto/lhash/lhash.c", + "crypto/md4/md4.c", + "crypto/md5/md5.c", + "crypto/mem.c", + "crypto/modes/cbc.c", + "crypto/modes/cfb.c", + "crypto/modes/ctr.c", + "crypto/modes/gcm.c", + "crypto/modes/ofb.c", + "crypto/newhope/error_correction.c", + "crypto/newhope/newhope.c", + "crypto/newhope/ntt.c", + "crypto/newhope/poly.c", + "crypto/newhope/precomp.c", + "crypto/newhope/reduce.c", + "crypto/obj/obj.c", + "crypto/obj/obj_xref.c", + "crypto/pem/pem_all.c", + "crypto/pem/pem_info.c", + "crypto/pem/pem_lib.c", + "crypto/pem/pem_oth.c", + "crypto/pem/pem_pk8.c", + "crypto/pem/pem_pkey.c", + "crypto/pem/pem_x509.c", + "crypto/pem/pem_xaux.c", + "crypto/pkcs8/p5_pbe.c", + "crypto/pkcs8/p5_pbev2.c", + "crypto/pkcs8/p8_pkey.c", + "crypto/pkcs8/pkcs8.c", + "crypto/poly1305/poly1305.c", + "crypto/poly1305/poly1305_arm.c", + "crypto/poly1305/poly1305_vec.c", + "crypto/rand/deterministic.c", + "crypto/rand/rand.c", + "crypto/rand/urandom.c", + "crypto/rand/windows.c", + "crypto/rc4/rc4.c", + "crypto/refcount_c11.c", + "crypto/refcount_lock.c", + "crypto/rsa/blinding.c", + "crypto/rsa/padding.c", + "crypto/rsa/rsa.c", + "crypto/rsa/rsa_asn1.c", + "crypto/rsa/rsa_impl.c", + "crypto/sha/sha1.c", + "crypto/sha/sha256.c", + "crypto/sha/sha512.c", + "crypto/stack/stack.c", + "crypto/thread.c", + "crypto/thread_none.c", + "crypto/thread_pthread.c", + "crypto/thread_win.c", + "crypto/time_support.c", + "crypto/x509/a_digest.c", + "crypto/x509/a_sign.c", + "crypto/x509/a_strex.c", + "crypto/x509/a_verify.c", + "crypto/x509/algorithm.c", + "crypto/x509/asn1_gen.c", + "crypto/x509/by_dir.c", + "crypto/x509/by_file.c", + "crypto/x509/i2d_pr.c", + "crypto/x509/pkcs7.c", + "crypto/x509/rsa_pss.c", + "crypto/x509/t_crl.c", + "crypto/x509/t_req.c", + "crypto/x509/t_x509.c", + "crypto/x509/t_x509a.c", + "crypto/x509/x509.c", + "crypto/x509/x509_att.c", + "crypto/x509/x509_cmp.c", + "crypto/x509/x509_d2.c", + "crypto/x509/x509_def.c", + "crypto/x509/x509_ext.c", + "crypto/x509/x509_lu.c", + "crypto/x509/x509_obj.c", + "crypto/x509/x509_r2x.c", + "crypto/x509/x509_req.c", + "crypto/x509/x509_set.c", + "crypto/x509/x509_trs.c", + "crypto/x509/x509_txt.c", + "crypto/x509/x509_v3.c", + "crypto/x509/x509_vfy.c", + "crypto/x509/x509_vpm.c", + "crypto/x509/x509cset.c", + "crypto/x509/x509name.c", + "crypto/x509/x509rset.c", + "crypto/x509/x509spki.c", + "crypto/x509/x509type.c", + "crypto/x509/x_algor.c", + "crypto/x509/x_all.c", + "crypto/x509/x_attrib.c", + "crypto/x509/x_crl.c", + "crypto/x509/x_exten.c", + "crypto/x509/x_info.c", + "crypto/x509/x_name.c", + "crypto/x509/x_pkey.c", + "crypto/x509/x_pubkey.c", + "crypto/x509/x_req.c", + "crypto/x509/x_sig.c", + "crypto/x509/x_spki.c", + "crypto/x509/x_val.c", + "crypto/x509/x_x509.c", + "crypto/x509/x_x509a.c", + "crypto/x509v3/pcy_cache.c", + "crypto/x509v3/pcy_data.c", + "crypto/x509v3/pcy_lib.c", + "crypto/x509v3/pcy_map.c", + "crypto/x509v3/pcy_node.c", + "crypto/x509v3/pcy_tree.c", + "crypto/x509v3/v3_akey.c", + "crypto/x509v3/v3_akeya.c", + "crypto/x509v3/v3_alt.c", + "crypto/x509v3/v3_bcons.c", + "crypto/x509v3/v3_bitst.c", + "crypto/x509v3/v3_conf.c", + "crypto/x509v3/v3_cpols.c", + "crypto/x509v3/v3_crld.c", + "crypto/x509v3/v3_enum.c", + "crypto/x509v3/v3_extku.c", + "crypto/x509v3/v3_genn.c", + "crypto/x509v3/v3_ia5.c", + "crypto/x509v3/v3_info.c", + "crypto/x509v3/v3_int.c", + "crypto/x509v3/v3_lib.c", + "crypto/x509v3/v3_ncons.c", + "crypto/x509v3/v3_pci.c", + "crypto/x509v3/v3_pcia.c", + "crypto/x509v3/v3_pcons.c", + "crypto/x509v3/v3_pku.c", + "crypto/x509v3/v3_pmaps.c", + "crypto/x509v3/v3_prn.c", + "crypto/x509v3/v3_purp.c", + "crypto/x509v3/v3_skey.c", + "crypto/x509v3/v3_sxnet.c", + "crypto/x509v3/v3_utl.c", +] + +# A trick to take the generated err_data.c from another package. +genrule( + name = "err_data_c", + srcs = ["@//third_party/boringssl:err_data_c"], + outs = ["err_data.c"], + cmd = "cp $< $@", +) + +cc_library( + name = "crypto", + srcs = crypto_internal_headers + crypto_sources, + hdrs = crypto_headers, + # To avoid linking platform-specific ASM files. + defines = ["OPENSSL_NO_ASM"], + includes = ["include"], + visibility = ["//visibility:public"], +) diff --git a/tensorflow/core/platform/cloud/BUILD b/tensorflow/core/platform/cloud/BUILD index 95da1373869..a47e772dfd2 100644 --- a/tensorflow/core/platform/cloud/BUILD +++ b/tensorflow/core/platform/cloud/BUILD @@ -36,6 +36,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ "@jsoncpp_git//:jsoncpp", + ":google_auth_provider", ":http_request", "//tensorflow/core:framework_headers_lib", "//tensorflow/core:lib_internal", @@ -57,11 +58,74 @@ cc_library( ], ) +cc_library( + name = "http_request_fake", + testonly = 1, + hdrs = [ + "http_request_fake.h", + ], + deps = [ + ":http_request", + "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", + "//tensorflow/core:test", + ], +) + +cc_library( + name = "google_auth_provider", + srcs = [ + "google_auth_provider.cc", + ], + hdrs = [ + "auth_provider.h", + "google_auth_provider.h", + ], + deps = [ + "@jsoncpp_git//:jsoncpp", + ":base64", + ":http_request", + ":oauth_client", + "//tensorflow/core:lib", + ], +) + +cc_library( + name = "oauth_client", + srcs = [ + "oauth_client.cc", + ], + hdrs = [ + "oauth_client.h", + ], + deps = [ + "@boringssl_git//:crypto", + "@jsoncpp_git//:jsoncpp", + ":base64", + ":http_request", + "//tensorflow/core:lib", + ], +) + +cc_library( + name = "base64", + srcs = [ + "base64.cc", + ], + hdrs = [ + "base64.h", + ], + deps = [ + "//tensorflow/core:lib", + ], +) + tf_cc_test( name = "gcs_file_system_test", size = "small", deps = [ ":gcs_file_system", + ":http_request_fake", "//tensorflow/core:test", "//tensorflow/core:test_main", ], @@ -77,3 +141,53 @@ tf_cc_test( "//tensorflow/core:test_main", ], ) + +tf_cc_test( + name = "oauth_client_test", + size = "small", + data = [ + "testdata/service_account_credentials.json", + "testdata/service_account_public_key.txt", + ], + deps = [ + "@boringssl_git//:crypto", + ":base64", + ":http_request_fake", + ":oauth_client", + "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) + +tf_cc_test( + name = "base64_test", + size = "small", + deps = [ + ":base64", + "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) + +tf_cc_test( + name = "google_auth_provider_test", + size = "small", + data = [ + "testdata/application_default_credentials.json", + "testdata/service_account_credentials.json", + ], + deps = [ + ":base64", + ":google_auth_provider", + ":http_request_fake", + ":oauth_client", + "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) diff --git a/tensorflow/core/platform/cloud/auth_provider.h b/tensorflow/core/platform/cloud/auth_provider.h new file mode 100644 index 00000000000..88636cba4ea --- /dev/null +++ b/tensorflow/core/platform/cloud/auth_provider.h @@ -0,0 +1,52 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 TENSORFLOW_CORE_PLATFORM_AUTH_PROVIDER_H_ +#define TENSORFLOW_CORE_PLATFORM_AUTH_PROVIDER_H_ + +#include +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/core/status.h" + +namespace tensorflow { + +/// Interface for a provider of authentication bearer tokens. +class AuthProvider { + public: + virtual ~AuthProvider() {} + + /// Returns the short-term authentication bearer token. + virtual Status GetToken(string* t) = 0; + + static Status GetToken(AuthProvider* provider, string* token) { + if (!provider) { + return errors::Internal("Auth provider is required."); + } + return provider->GetToken(token); + } +}; + +/// No-op auth provider, which will only work for public objects. +class EmptyAuthProvider : public AuthProvider { + public: + Status GetToken(string* token) override { + *token = ""; + return Status::OK(); + } +}; + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_PLATFORM_AUTH_PROVIDER_H_ diff --git a/tensorflow/core/platform/cloud/base64.cc b/tensorflow/core/platform/cloud/base64.cc new file mode 100644 index 00000000000..c19f04217d6 --- /dev/null +++ b/tensorflow/core/platform/cloud/base64.cc @@ -0,0 +1,221 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 "tensorflow/core/platform/cloud/base64.h" +#include +#include "tensorflow/core/lib/core/errors.h" + +namespace tensorflow { + +namespace { + +constexpr signed char kBase64Bytes[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0x3E, -1, -1, -1, 0x3F, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1, -1, + -1, 0x7F, -1, -1, -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, -1, -1, -1, -1, -1, + -1, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, -1, -1, -1, -1, -1}; + +constexpr char kBase64UrlSafeChars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +constexpr char kPadChar = '='; +constexpr char kPadByte = 0x7F; +constexpr int kMultilineLineLen = 76; +constexpr int kMultilineNumBlocks = kMultilineLineLen / 4; + +Status Base64Encode(StringPiece source, bool multiline, bool with_padding, + string *encoded) { + if (!encoded) { + return errors::FailedPrecondition("'encoded' cannot be nullptr."); + } + size_t data_size = source.size(); + const char *data = source.data(); + const char *base64_chars = kBase64UrlSafeChars; + const size_t result_projected_size = + 4 * ((data_size + 3) / 3) + + 2 * (multiline ? (data_size / (3 * kMultilineNumBlocks)) : 0) + 1; + size_t num_blocks = 0; + size_t i = 0; + std::unique_ptr result(new char[result_projected_size]); + char *current = result.get(); + + /* Encode each block. */ + while (data_size >= 3) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = + base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; + *current++ = + base64_chars[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)]; + *current++ = base64_chars[data[i + 2] & 0x3F]; + + data_size -= 3; + i += 3; + if (multiline && (++num_blocks == kMultilineNumBlocks)) { + *current++ = '\r'; + *current++ = '\n'; + num_blocks = 0; + } + } + + /* Take care of the tail. */ + if (data_size == 2) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = + base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; + *current++ = base64_chars[(data[i + 1] & 0x0F) << 2]; + if (with_padding) { + *current++ = kPadChar; + } + } else if (data_size == 1) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = base64_chars[(data[i] & 0x03) << 4]; + if (with_padding) { + *current++ = kPadChar; + *current++ = kPadChar; + } + } + + if (current < result.get() || + current >= result.get() + result_projected_size) { + return errors::Internal("Unexpected encoding bug."); + } + *current++ = '\0'; + *encoded = result.get(); + return Status::OK(); +} + +void DecodeOneChar(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + const uint32_t packed = ((uint32_t)codes[0] << 2) | ((uint32_t)codes[1] >> 4); + result[(*result_offset)++] = (unsigned char)packed; +} + +void DecodeTwoChars(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + const uint32_t packed = ((uint32_t)codes[0] << 10) | + ((uint32_t)codes[1] << 4) | ((uint32_t)codes[2] >> 2); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); +} + +Status DecodeGroup(const unsigned char *codes, size_t num_codes, + unsigned char *result, size_t *result_offset) { + if (num_codes > 4) { + return errors::FailedPrecondition("Expected 4 or fewer codes."); + } + + /* Short end groups that may not have padding. */ + if (num_codes == 1) { + return errors::FailedPrecondition( + "Invalid group. Must be at least 2 bytes."); + } + if (num_codes == 2) { + DecodeOneChar(codes, result, result_offset); + return Status::OK(); + } + if (num_codes == 3) { + DecodeTwoChars(codes, result, result_offset); + return Status::OK(); + } + + /* Regular 4 byte groups with padding or not. */ + if (num_codes != 4) { + return errors::FailedPrecondition("Expected exactly 4 codes."); + } + if (codes[0] == kPadByte || codes[1] == kPadByte) { + return errors::FailedPrecondition("Invalid padding detected."); + } + if (codes[2] == kPadByte) { + if (codes[3] == kPadByte) { + DecodeOneChar(codes, result, result_offset); + } else { + return errors::FailedPrecondition("Invalid padding detected."); + } + } else if (codes[3] == kPadByte) { + DecodeTwoChars(codes, result, result_offset); + } else { + /* No padding. */ + const uint32_t packed = ((uint32_t)codes[0] << 18) | + ((uint32_t)codes[1] << 12) | + ((uint32_t)codes[2] << 6) | codes[3]; + result[(*result_offset)++] = (unsigned char)(packed >> 16); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); + } + return Status::OK(); +} + +} // namespace + +Status Base64Encode(StringPiece source, string *encoded) { + return Base64Encode(source, false, false, encoded); +} + +Status Base64Decode(StringPiece data, string *decoded) { + if (!decoded) { + return errors::FailedPrecondition("'decoded' cannot be nullptr."); + } + std::unique_ptr result(new unsigned char[data.size()]); + unsigned char *current = result.get(); + size_t result_size = 0; + unsigned char codes[4]; + size_t num_codes = 0; + + const char *b64 = data.data(); + size_t b64_len = data.size(); + while (b64_len--) { + unsigned char c = (unsigned char)(*b64++); + signed char code; + if (c >= sizeof(kBase64Bytes)) continue; + if (c == '+' || c == '/') { + return errors::FailedPrecondition( + strings::StrCat("Invalid character for url safe base64 ", c)); + } + if (c == '-') { + c = '+'; + } else if (c == '_') { + c = '/'; + } + code = kBase64Bytes[c]; + if (code == -1) { + if (c != '\r' && c != '\n') { + return errors::FailedPrecondition( + strings::StrCat("Invalid character ", c)); + } + } else { + codes[num_codes++] = (unsigned char)code; + if (num_codes == 4) { + TF_RETURN_IF_ERROR( + DecodeGroup(codes, num_codes, current, &result_size)); + num_codes = 0; + } + } + } + + if (num_codes != 0) { + TF_RETURN_IF_ERROR(DecodeGroup(codes, num_codes, current, &result_size)); + } + *decoded = string(reinterpret_cast(result.get()), result_size); + return Status::OK(); +} + +} // namespace tensorflow diff --git a/tensorflow/core/platform/cloud/base64.h b/tensorflow/core/platform/cloud/base64.h new file mode 100644 index 00000000000..4e495a243f2 --- /dev/null +++ b/tensorflow/core/platform/cloud/base64.h @@ -0,0 +1,36 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 TENSORFLOW_CORE_PLATFORM_B64_H_ +#define TENSORFLOW_CORE_PLATFORM_B64_H_ + +#include +#include "tensorflow/core/lib/core/status.h" + +namespace tensorflow { + +/// \brief Converts data into base64 encoding. +/// +/// See https://en.wikipedia.org/wiki/Base64 +Status Base64Encode(StringPiece data, string* encoded); + +/// \brief Converts data from base64 encoding. +/// +/// See https://en.wikipedia.org/wiki/Base64 +Status Base64Decode(StringPiece data, string* decoded); + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_PLATFORM_B64_H_ diff --git a/tensorflow/core/platform/cloud/base64_test.cc b/tensorflow/core/platform/cloud/base64_test.cc new file mode 100644 index 00000000000..65fd738a9b4 --- /dev/null +++ b/tensorflow/core/platform/cloud/base64_test.cc @@ -0,0 +1,33 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 "tensorflow/core/platform/cloud/base64.h" +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { + +TEST(Base64, EncodeDecode) { + const string original = "a simple test message!"; + string encoded; + TF_EXPECT_OK(Base64Encode(original, &encoded)); + EXPECT_EQ("YSBzaW1wbGUgdGVzdCBtZXNzYWdlIQ", encoded); + + string decoded; + TF_EXPECT_OK(Base64Decode(encoded, &decoded)); + EXPECT_EQ(original, decoded); +} + +} // namespace tensorflow diff --git a/tensorflow/core/platform/cloud/gcs_file_system.cc b/tensorflow/core/platform/cloud/gcs_file_system.cc index ba9418f06f4..76cb80afb32 100644 --- a/tensorflow/core/platform/cloud/gcs_file_system.cc +++ b/tensorflow/core/platform/cloud/gcs_file_system.cc @@ -28,6 +28,7 @@ limitations under the License. #include "tensorflow/core/lib/strings/numbers.h" #include "tensorflow/core/lib/strings/scanner.h" #include "tensorflow/core/lib/strings/str_util.h" +#include "tensorflow/core/platform/cloud/google_auth_provider.h" #include "tensorflow/core/platform/env.h" #include "tensorflow/core/platform/protobuf.h" #include "tensorflow/core/platform/regexp.h" @@ -56,22 +57,6 @@ Status GetTmpFilename(string* filename) { return Status::OK(); } -/// No-op auth provider, which will only work for public objects. -class EmptyAuthProvider : public AuthProvider { - public: - Status GetToken(string* token) const override { - *token = ""; - return Status::OK(); - } -}; - -Status GetAuthToken(const AuthProvider* provider, string* token) { - if (!provider) { - return errors::Internal("Auth provider is required."); - } - return provider->GetToken(token); -} - /// \brief Splits a GCS path to a bucket and an object. /// /// For example, "gs://bucket-name/path/to/file.txt" gets split into @@ -109,7 +94,7 @@ class GcsRandomAccessFile : public RandomAccessFile { Status Read(uint64 offset, size_t n, StringPiece* result, char* scratch) const override { string auth_token; - TF_RETURN_IF_ERROR(GetAuthToken(auth_provider_, &auth_token)); + TF_RETURN_IF_ERROR(AuthProvider::GetToken(auth_provider_, &auth_token)); std::unique_ptr request(http_request_factory_->Create()); TF_RETURN_IF_ERROR(request->Init()); @@ -198,7 +183,7 @@ class GcsWritableFile : public WritableFile { TF_RETURN_IF_ERROR(CheckWritable()); outfile_.flush(); string auth_token; - TF_RETURN_IF_ERROR(GetAuthToken(auth_provider_, &auth_token)); + TF_RETURN_IF_ERROR(AuthProvider::GetToken(auth_provider_, &auth_token)); std::unique_ptr request(http_request_factory_->Create()); TF_RETURN_IF_ERROR(request->Init()); @@ -242,7 +227,7 @@ class GcsReadOnlyMemoryRegion : public ReadOnlyMemoryRegion { } // namespace GcsFileSystem::GcsFileSystem() - : auth_provider_(new EmptyAuthProvider()), + : auth_provider_(new GoogleAuthProvider()), http_request_factory_(new HttpRequest::Factory()) {} GcsFileSystem::GcsFileSystem( @@ -334,7 +319,7 @@ bool GcsFileSystem::FileExists(const string& fname) { } string auth_token; - if (!GetAuthToken(auth_provider_.get(), &auth_token).ok()) { + if (!AuthProvider::GetToken(auth_provider_.get(), &auth_token).ok()) { LOG(ERROR) << "Could not get an auth token."; return false; } @@ -363,7 +348,7 @@ Status GcsFileSystem::GetChildren(const string& dirname, TF_RETURN_IF_ERROR(ParseGcsPath(sanitized_dirname, &bucket, &object_prefix)); string auth_token; - TF_RETURN_IF_ERROR(GetAuthToken(auth_provider_.get(), &auth_token)); + TF_RETURN_IF_ERROR(AuthProvider::GetToken(auth_provider_.get(), &auth_token)); std::unique_ptr scratch(new char[kBufferSize]); StringPiece response_piece; @@ -417,7 +402,7 @@ Status GcsFileSystem::DeleteFile(const string& fname) { TF_RETURN_IF_ERROR(ParseGcsPath(fname, &bucket, &object)); string auth_token; - TF_RETURN_IF_ERROR(GetAuthToken(auth_provider_.get(), &auth_token)); + TF_RETURN_IF_ERROR(AuthProvider::GetToken(auth_provider_.get(), &auth_token)); std::unique_ptr request(http_request_factory_->Create()); TF_RETURN_IF_ERROR(request->Init()); @@ -452,7 +437,7 @@ Status GcsFileSystem::GetFileSize(const string& fname, uint64* file_size) { TF_RETURN_IF_ERROR(ParseGcsPath(fname, &bucket, &object_prefix)); string auth_token; - TF_RETURN_IF_ERROR(GetAuthToken(auth_provider_.get(), &auth_token)); + TF_RETURN_IF_ERROR(AuthProvider::GetToken(auth_provider_.get(), &auth_token)); std::unique_ptr scratch(new char[kBufferSize]); StringPiece response_piece; @@ -496,7 +481,7 @@ Status GcsFileSystem::RenameFile(const string& src, const string& target) { TF_RETURN_IF_ERROR(ParseGcsPath(target, &target_bucket, &target_object)); string auth_token; - TF_RETURN_IF_ERROR(GetAuthToken(auth_provider_.get(), &auth_token)); + TF_RETURN_IF_ERROR(AuthProvider::GetToken(auth_provider_.get(), &auth_token)); std::unique_ptr request(http_request_factory_->Create()); TF_RETURN_IF_ERROR(request->Init()); diff --git a/tensorflow/core/platform/cloud/gcs_file_system.h b/tensorflow/core/platform/cloud/gcs_file_system.h index 47c22173de5..9e71455cb6a 100644 --- a/tensorflow/core/platform/cloud/gcs_file_system.h +++ b/tensorflow/core/platform/cloud/gcs_file_system.h @@ -19,18 +19,12 @@ limitations under the License. #include #include #include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/platform/cloud/auth_provider.h" #include "tensorflow/core/platform/cloud/http_request.h" #include "tensorflow/core/platform/file_system.h" namespace tensorflow { -/// Interface for a provider of HTTP auth bearer tokens. -class AuthProvider { - public: - virtual ~AuthProvider() {} - virtual Status GetToken(string* t) const = 0; -}; - /// Google Cloud Storage implementation of a file system. class GcsFileSystem : public FileSystem { public: diff --git a/tensorflow/core/platform/cloud/gcs_file_system_test.cc b/tensorflow/core/platform/cloud/gcs_file_system_test.cc index 151aebd87c5..4505000b615 100644 --- a/tensorflow/core/platform/cloud/gcs_file_system_test.cc +++ b/tensorflow/core/platform/cloud/gcs_file_system_test.cc @@ -16,101 +16,15 @@ limitations under the License. #include "tensorflow/core/platform/cloud/gcs_file_system.h" #include #include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/platform/cloud/http_request_fake.h" #include "tensorflow/core/platform/test.h" namespace tensorflow { namespace { -class FakeHttpRequest : public HttpRequest { - public: - FakeHttpRequest(const string& request, const string& response) - : FakeHttpRequest(request, response, Status::OK()) {} - - FakeHttpRequest(const string& request, const string& response, - Status response_status) - : expected_request_(request), - response_(response), - response_status_(response_status) {} - - Status Init() override { return Status::OK(); } - Status SetUri(const string& uri) override { - actual_request_ += "Uri: " + uri + "\n"; - return Status::OK(); - } - Status SetRange(uint64 start, uint64 end) override { - actual_request_ += strings::StrCat("Range: ", start, "-", end, "\n"); - return Status::OK(); - } - Status AddAuthBearerHeader(const string& auth_token) override { - actual_request_ += "Auth Token: " + auth_token + "\n"; - return Status::OK(); - } - Status SetDeleteRequest() override { - actual_request_ += "Delete: yes\n"; - return Status::OK(); - } - Status SetPostRequest(const string& body_filepath) override { - std::ifstream stream(body_filepath); - string content((std::istreambuf_iterator(stream)), - std::istreambuf_iterator()); - actual_request_ += "Post body: " + content + "\n"; - return Status::OK(); - } - Status SetPostRequest() override { - actual_request_ += "Post: yes\n"; - return Status::OK(); - } - Status SetResultBuffer(char* scratch, size_t size, - StringPiece* result) override { - scratch_ = scratch; - size_ = size; - result_ = result; - return Status::OK(); - } - Status Send() override { - EXPECT_EQ(expected_request_, actual_request_) << "Unexpected HTTP request."; - if (scratch_ && result_) { - auto actual_size = std::min(response_.size(), size_); - memcpy(scratch_, response_.c_str(), actual_size); - *result_ = StringPiece(scratch_, actual_size); - } - return response_status_; - } - - private: - char* scratch_ = nullptr; - size_t size_ = 0; - StringPiece* result_ = nullptr; - string expected_request_; - string actual_request_; - string response_; - Status response_status_; -}; - -class FakeHttpRequestFactory : public HttpRequest::Factory { - public: - FakeHttpRequestFactory(const std::vector* requests) - : requests_(requests) {} - - ~FakeHttpRequestFactory() { - EXPECT_EQ(current_index_, requests_->size()) - << "Not all expected requests were made."; - } - - HttpRequest* Create() override { - EXPECT_LT(current_index_, requests_->size()) - << "Too many calls of HttpRequest factory."; - return (*requests_)[current_index_++]; - } - - private: - const std::vector* requests_; - int current_index_ = 0; -}; - class FakeAuthProvider : public AuthProvider { public: - Status GetToken(string* token) const override { + Status GetToken(string* token) override { *token = "fake_token"; return Status::OK(); } diff --git a/tensorflow/core/platform/cloud/google_auth_provider.cc b/tensorflow/core/platform/cloud/google_auth_provider.cc new file mode 100644 index 00000000000..50b3a9eb1d4 --- /dev/null +++ b/tensorflow/core/platform/cloud/google_auth_provider.cc @@ -0,0 +1,190 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 "tensorflow/core/platform/cloud/google_auth_provider.h" +#include +#include +#include +#include +#include "include/json/json.h" +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/io/path.h" +#include "tensorflow/core/platform/cloud/base64.h" +#include "tensorflow/core/platform/cloud/http_request.h" +#include "tensorflow/core/platform/env.h" + +namespace tensorflow { + +namespace { + +// The environment variable pointing to the file with local +// Application Default Credentials. +constexpr char kGoogleApplicationCredentials[] = + "GOOGLE_APPLICATION_CREDENTIALS"; + +// The environment variable which can override '~/.config/gcloud' if set. +constexpr char kCloudSdkConfig[] = "CLOUDSDK_CONFIG"; + +// The default path to the gcloud config folder, relative to the home folder. +constexpr char kGCloudConfigFolder[] = ".config/gcloud/"; + +// The name of the well-known credentials JSON file in the gcloud config folder. +constexpr char kWellKnownCredentialsFile[] = + "application_default_credentials.json"; + +// The minimum time delta between now and the token expiration time +// for the token to be re-used. +constexpr int kExpirationTimeMarginSec = 10; + +// The URL to retrieve the auth bearer token via OAuth with a refresh token. +constexpr char kOAuthV3Url[] = "https://www.googleapis.com/oauth2/v3/token"; + +// The URL to retrieve the auth bearer token via OAuth with a private key. +constexpr char kOAuthV4Url[] = "https://www.googleapis.com/oauth2/v4/token"; + +// The URL to retrieve the auth bearer token when running in Google Compute +// Engine. +constexpr char kGceTokenUrl[] = + "http://metadata/computeMetadata/v1/instance/service-accounts/default/" + "token"; + +// The authentication token scope to request. +constexpr char kOAuthScope[] = "https://www.googleapis.com/auth/cloud-platform"; + +/// Returns whether the given path points to a readable file. +bool IsFile(const string& filename) { + std::ifstream fstream(filename.c_str()); + return fstream.good(); +} + +/// Returns the credentials file name from the env variable. +Status GetEnvironmentVariableFileName(string* filename) { + if (!filename) { + return errors::FailedPrecondition("'filename' cannot be nullptr."); + } + const char* result = std::getenv(kGoogleApplicationCredentials); + if (!result || !IsFile(result)) { + return errors::NotFound(strings::StrCat("$", kGoogleApplicationCredentials, + " is not set or corrupt.")); + } + *filename = result; + return Status::OK(); +} + +/// Returns the well known file produced by command 'gcloud auth login'. +Status GetWellKnownFileName(string* filename) { + if (!filename) { + return errors::FailedPrecondition("'filename' cannot be nullptr."); + } + string config_dir; + const char* config_dir_override = std::getenv(kCloudSdkConfig); + if (config_dir_override) { + config_dir = config_dir_override; + } else { + // Determine the home dir path. + const char* home_dir = std::getenv("HOME"); + if (!home_dir) { + return errors::FailedPrecondition("Could not read $HOME."); + } + config_dir = io::JoinPath(home_dir, kGCloudConfigFolder); + } + auto result = io::JoinPath(config_dir, kWellKnownCredentialsFile); + if (!IsFile(result)) { + return errors::NotFound( + "Could not find the credentials file in the standard gcloud location."); + } + *filename = result; + return Status::OK(); +} + +} // namespace + +GoogleAuthProvider::GoogleAuthProvider() + : GoogleAuthProvider( + std::unique_ptr(new OAuthClient()), + std::unique_ptr(new HttpRequest::Factory()), + Env::Default()) {} + +GoogleAuthProvider::GoogleAuthProvider( + std::unique_ptr oauth_client, + std::unique_ptr http_request_factory, Env* env) + : oauth_client_(std::move(oauth_client)), + http_request_factory_(std::move(http_request_factory)), + env_(env) {} + +Status GoogleAuthProvider::GetToken(string* t) { + const uint64 now_sec = env_->NowSeconds(); + + if (!current_token_.empty() && + now_sec + kExpirationTimeMarginSec < expiration_timestamp_sec_) { + *t = current_token_; + return Status::OK(); + } + if (GetTokenFromFiles().ok() || GetTokenFromGce().ok()) { + *t = current_token_; + return Status::OK(); + } + return errors::FailedPrecondition( + "All attempts to get a Google authentication bearer token failed."); +} + +Status GoogleAuthProvider::GetTokenFromFiles() { + string credentials_filename; + if (!GetEnvironmentVariableFileName(&credentials_filename).ok() && + !GetWellKnownFileName(&credentials_filename).ok()) { + return errors::NotFound("Could not locate the credentials file."); + } + + Json::Value json; + Json::Reader reader; + std::ifstream credentials_fstream(credentials_filename); + if (!reader.parse(credentials_fstream, json)) { + return errors::FailedPrecondition( + "Couldn't parse the JSON credentials file."); + } + if (json.isMember("refresh_token")) { + TF_RETURN_IF_ERROR(oauth_client_->GetTokenFromRefreshTokenJson( + json, kOAuthV3Url, ¤t_token_, &expiration_timestamp_sec_)); + } else if (json.isMember("private_key")) { + TF_RETURN_IF_ERROR(oauth_client_->GetTokenFromServiceAccountJson( + json, kOAuthV4Url, kOAuthScope, ¤t_token_, + &expiration_timestamp_sec_)); + } else { + return errors::FailedPrecondition( + "Unexpected content of the JSON credentials file."); + } + return Status::OK(); +} + +Status GoogleAuthProvider::GetTokenFromGce() { + std::unique_ptr request(http_request_factory_->Create()); + std::unique_ptr response_buffer( + new char[OAuthClient::kResponseBufferSize]); + const uint64 request_timestamp_sec = env_->NowSeconds(); + StringPiece response; + TF_RETURN_IF_ERROR(request->Init()); + TF_RETURN_IF_ERROR(request->SetUri(kGceTokenUrl)); + TF_RETURN_IF_ERROR(request->AddHeader("Metadata-Flavor", "Google")); + TF_RETURN_IF_ERROR(request->SetResultBuffer( + response_buffer.get(), OAuthClient::kResponseBufferSize, &response)); + TF_RETURN_IF_ERROR(request->Send()); + + TF_RETURN_IF_ERROR(oauth_client_->ParseOAuthResponse( + response, request_timestamp_sec, ¤t_token_, + &expiration_timestamp_sec_)); + return Status::OK(); +} + +} // namespace tensorflow diff --git a/tensorflow/core/platform/cloud/google_auth_provider.h b/tensorflow/core/platform/cloud/google_auth_provider.h new file mode 100644 index 00000000000..0a1f755c8dd --- /dev/null +++ b/tensorflow/core/platform/cloud/google_auth_provider.h @@ -0,0 +1,57 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 TENSORFLOW_CORE_PLATFORM_GOOGLE_AUTH_PROVIDER_H_ +#define TENSORFLOW_CORE_PLATFORM_GOOGLE_AUTH_PROVIDER_H_ + +#include +#include "tensorflow/core/platform/cloud/auth_provider.h" +#include "tensorflow/core/platform/cloud/oauth_client.h" + +namespace tensorflow { + +/// Implementation based on Google Application Default Credentials. +class GoogleAuthProvider : public AuthProvider { + public: + GoogleAuthProvider(); + explicit GoogleAuthProvider( + std::unique_ptr oauth_client, + std::unique_ptr http_request_factory, Env* env); + virtual ~GoogleAuthProvider() {} + + /// Returns the short-term authentication bearer token. + Status GetToken(string* token) override; + + private: + /// \brief Gets the bearer token from files. + /// + /// Tries the file from $GOOGLE_APPLICATION_CREDENTIALS and the + /// standard gcloud tool's location. + Status GetTokenFromFiles(); + + /// Gets the bearer token from Google Compute Engine environment. + Status GetTokenFromGce(); + + std::unique_ptr oauth_client_; + std::unique_ptr http_request_factory_; + Env* env_; + string current_token_; + uint64 expiration_timestamp_sec_ = 0; + TF_DISALLOW_COPY_AND_ASSIGN(GoogleAuthProvider); +}; + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_PLATFORM_GOOGLE_AUTH_PROVIDER_H_ diff --git a/tensorflow/core/platform/cloud/google_auth_provider_test.cc b/tensorflow/core/platform/cloud/google_auth_provider_test.cc new file mode 100644 index 00000000000..54a6030b749 --- /dev/null +++ b/tensorflow/core/platform/cloud/google_auth_provider_test.cc @@ -0,0 +1,200 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 "tensorflow/core/platform/cloud/google_auth_provider.h" +#include +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/lib/io/path.h" +#include "tensorflow/core/platform/cloud/http_request_fake.h" +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { + +namespace { + +constexpr char kTestData[] = "core/platform/cloud/testdata/"; + +class FakeEnv : public EnvWrapper { + public: + FakeEnv() : EnvWrapper(Env::Default()) {} + + uint64 NowSeconds() override { return now; } + uint64 now = 10000; +}; + +class FakeOAuthClient : public OAuthClient { + public: + Status GetTokenFromServiceAccountJson( + Json::Value json, StringPiece oauth_server_uri, StringPiece scope, + string* token, uint64* expiration_timestamp_sec) override { + provided_credentials_json = json; + *token = return_token; + *expiration_timestamp_sec = return_expiration_timestamp; + return Status::OK(); + } + + /// Retrieves a bearer token using a refresh token. + Status GetTokenFromRefreshTokenJson( + Json::Value json, StringPiece oauth_server_uri, string* token, + uint64* expiration_timestamp_sec) override { + provided_credentials_json = json; + *token = return_token; + *expiration_timestamp_sec = return_expiration_timestamp; + return Status::OK(); + } + + string return_token; + uint64 return_expiration_timestamp; + Json::Value provided_credentials_json; +}; + +} // namespace + +TEST(GoogleAuthProvider, EnvironmentVariable_Caching) { + setenv("GOOGLE_APPLICATION_CREDENTIALS", + io::JoinPath( + io::JoinPath(testing::TensorFlowSrcRoot(), kTestData).c_str(), + "service_account_credentials.json") + .c_str(), + 1); + setenv("CLOUDSDK_CONFIG", + io::JoinPath(testing::TensorFlowSrcRoot(), kTestData).c_str(), + 1); // Will not be used. + + auto oauth_client = new FakeOAuthClient; + std::vector requests; + + FakeEnv env; + GoogleAuthProvider provider(std::unique_ptr(oauth_client), + std::unique_ptr( + new FakeHttpRequestFactory(&requests)), + &env); + oauth_client->return_token = "fake-token"; + oauth_client->return_expiration_timestamp = env.NowSeconds() + 3600; + + string token; + TF_EXPECT_OK(provider.GetToken(&token)); + EXPECT_EQ("fake-token", token); + EXPECT_EQ("fake_key_id", + oauth_client->provided_credentials_json.get("private_key_id", "") + .asString()); + + // Check that the token is re-used if not expired. + oauth_client->return_token = "new-fake-token"; + env.now += 3000; + TF_EXPECT_OK(provider.GetToken(&token)); + EXPECT_EQ("fake-token", token); + + // Check that the token is re-generated when almost expired. + env.now += 598; // 2 seconds before expiration + TF_EXPECT_OK(provider.GetToken(&token)); + EXPECT_EQ("new-fake-token", token); +} + +TEST(GoogleAuthProvider, GCloudRefreshToken) { + setenv("GOOGLE_APPLICATION_CREDENTIALS", "", 1); + setenv("CLOUDSDK_CONFIG", + io::JoinPath(testing::TensorFlowSrcRoot(), kTestData).c_str(), 1); + + auto oauth_client = new FakeOAuthClient; + std::vector requests; + + FakeEnv env; + GoogleAuthProvider provider(std::unique_ptr(oauth_client), + std::unique_ptr( + new FakeHttpRequestFactory(&requests)), + &env); + oauth_client->return_token = "fake-token"; + oauth_client->return_expiration_timestamp = env.NowSeconds() + 3600; + + string token; + TF_EXPECT_OK(provider.GetToken(&token)); + EXPECT_EQ("fake-token", token); + EXPECT_EQ("fake-refresh-token", + oauth_client->provided_credentials_json.get("refresh_token", "") + .asString()); +} + +TEST(GoogleAuthProvider, RunningOnGCE) { + setenv("GOOGLE_APPLICATION_CREDENTIALS", "", 1); + setenv("CLOUDSDK_CONFIG", "", 1); + + auto oauth_client = new FakeOAuthClient; + std::vector requests( + {new FakeHttpRequest( + "Uri: http://metadata/computeMetadata/v1/instance/service-accounts" + "/default/token\n" + "Header Metadata-Flavor: Google\n", + R"( + { + "access_token":"fake-gce-token", + "expires_in": 3920, + "token_type":"Bearer" + })"), + new FakeHttpRequest( + "Uri: http://metadata/computeMetadata/v1/instance/service-accounts" + "/default/token\n" + "Header Metadata-Flavor: Google\n", + R"( + { + "access_token":"new-fake-gce-token", + "expires_in": 3920, + "token_type":"Bearer" + })")}); + + FakeEnv env; + GoogleAuthProvider provider(std::unique_ptr(oauth_client), + std::unique_ptr( + new FakeHttpRequestFactory(&requests)), + &env); + + string token; + TF_EXPECT_OK(provider.GetToken(&token)); + EXPECT_EQ("fake-gce-token", token); + + // Check that the token is re-used if not expired. + env.now += 3700; + TF_EXPECT_OK(provider.GetToken(&token)); + EXPECT_EQ("fake-gce-token", token); + + // Check that the token is re-generated when almost expired. + env.now += 598; // 2 seconds before expiration + TF_EXPECT_OK(provider.GetToken(&token)); + EXPECT_EQ("new-fake-gce-token", token); +} + +TEST(GoogleAuthProvider, NothingAvailable) { + setenv("GOOGLE_APPLICATION_CREDENTIALS", "", 1); + setenv("CLOUDSDK_CONFIG", "", 1); + + auto oauth_client = new FakeOAuthClient; + + std::vector requests({new FakeHttpRequest( + "Uri: http://metadata/computeMetadata/v1/instance/service-accounts" + "/default/token\n" + "Header Metadata-Flavor: Google\n", + "", errors::NotFound("404"))}); + + FakeEnv env; + GoogleAuthProvider provider(std::unique_ptr(oauth_client), + std::unique_ptr( + new FakeHttpRequestFactory(&requests)), + &env); + + string token; + EXPECT_FALSE(provider.GetToken(&token).ok()); +} + +} // namespace tensorflow diff --git a/tensorflow/core/platform/cloud/http_request.cc b/tensorflow/core/platform/cloud/http_request.cc index 38f132e723a..5e9d96ba865 100644 --- a/tensorflow/core/platform/cloud/http_request.cc +++ b/tensorflow/core/platform/cloud/http_request.cc @@ -24,6 +24,7 @@ limitations under the License. #include "tensorflow/core/lib/strings/scanner.h" #include "tensorflow/core/platform/protobuf.h" #include "tensorflow/core/platform/types.h" +#include "tensorflow/core/public/version.h" namespace tensorflow { @@ -200,8 +201,11 @@ HttpRequest::~HttpRequest() { } Status HttpRequest::Init() { + if (is_initialized_) { + return errors::FailedPrecondition("Already initialized."); + } if (!libcurl_) { - return errors::Internal("libcurl proxy cannot be nullptr."); + return errors::FailedPrecondition("libcurl proxy cannot be nullptr."); } TF_RETURN_IF_ERROR(libcurl_->MaybeLoadDll()); curl_ = libcurl_->curl_easy_init(); @@ -211,6 +215,9 @@ Status HttpRequest::Init() { libcurl_->curl_easy_setopt(curl_, CURLOPT_VERBOSE, kVerboseOutput); libcurl_->curl_easy_setopt(curl_, CURLOPT_CAPATH, kCertsPath); + libcurl_->curl_easy_setopt( + curl_, CURLOPT_USERAGENT, + strings::StrCat("TensorFlow/", TF_VERSION_STRING).c_str()); // If response buffer is not set, libcurl will print results to stdout, // so we always set it. @@ -240,13 +247,19 @@ Status HttpRequest::SetRange(uint64 start, uint64 end) { return Status::OK(); } +Status HttpRequest::AddHeader(const string& name, const string& value) { + TF_RETURN_IF_ERROR(CheckInitialized()); + TF_RETURN_IF_ERROR(CheckNotSent()); + curl_headers_ = libcurl_->curl_slist_append( + curl_headers_, strings::StrCat(name, ": ", value).c_str()); + return Status::OK(); +} + Status HttpRequest::AddAuthBearerHeader(const string& auth_token) { TF_RETURN_IF_ERROR(CheckInitialized()); TF_RETURN_IF_ERROR(CheckNotSent()); if (!auth_token.empty()) { - curl_headers_ = libcurl_->curl_slist_append( - curl_headers_, - strings::StrCat("Authorization: Bearer ", auth_token).c_str()); + return AddHeader("Authorization", strings::StrCat("Bearer ", auth_token)); } return Status::OK(); } @@ -285,6 +298,22 @@ Status HttpRequest::SetPostRequest(const string& body_filepath) { return Status::OK(); } +Status HttpRequest::SetPostRequest(const char* buffer, size_t size) { + TF_RETURN_IF_ERROR(CheckInitialized()); + TF_RETURN_IF_ERROR(CheckNotSent()); + TF_RETURN_IF_ERROR(CheckMethodNotSet()); + is_method_set_ = true; + curl_headers_ = libcurl_->curl_slist_append( + curl_headers_, strings::StrCat("Content-Length: ", size).c_str()); + libcurl_->curl_easy_setopt(curl_, CURLOPT_POST, 1); + libcurl_->curl_easy_setopt(curl_, CURLOPT_READDATA, + reinterpret_cast(this)); + libcurl_->curl_easy_setopt(curl_, CURLOPT_READFUNCTION, + &HttpRequest::ReadCallback); + post_body_buffer_ = StringPiece(buffer, size); + return Status::OK(); +} + Status HttpRequest::SetPostRequest() { TF_RETURN_IF_ERROR(CheckInitialized()); TF_RETURN_IF_ERROR(CheckNotSent()); @@ -337,6 +366,19 @@ size_t HttpRequest::WriteCallback(const void* ptr, size_t size, size_t nmemb, return bytes_to_copy; } +size_t HttpRequest::ReadCallback(void* ptr, size_t size, size_t nmemb, + FILE* this_object) { + CHECK(ptr); + auto that = reinterpret_cast(this_object); + CHECK(that->post_body_read_ <= that->post_body_buffer_.size()); + const size_t bytes_to_copy = std::min( + size * nmemb, that->post_body_buffer_.size() - that->post_body_read_); + memcpy(ptr, that->post_body_buffer_.data() + that->post_body_read_, + bytes_to_copy); + that->post_body_read_ += bytes_to_copy; + return bytes_to_copy; +} + Status HttpRequest::Send() { TF_RETURN_IF_ERROR(CheckInitialized()); TF_RETURN_IF_ERROR(CheckNotSent()); diff --git a/tensorflow/core/platform/cloud/http_request.h b/tensorflow/core/platform/cloud/http_request.h index 19aed67e6a4..2e0f92bc13c 100644 --- a/tensorflow/core/platform/cloud/http_request.h +++ b/tensorflow/core/platform/cloud/http_request.h @@ -16,7 +16,6 @@ limitations under the License. #ifndef TENSORFLOW_CORE_PLATFORM_HTTP_REQUEST_H_ #define TENSORFLOW_CORE_PLATFORM_HTTP_REQUEST_H_ -#include #include #include #include @@ -64,6 +63,9 @@ class HttpRequest { /// (note that the right border is included). virtual Status SetRange(uint64 start, uint64 end); + /// Sets a request header. + virtual Status AddHeader(const string& name, const string& value); + /// Sets the 'Authorization' header to the value of 'Bearer ' + auth_token. virtual Status AddAuthBearerHeader(const string& auth_token); @@ -75,6 +77,11 @@ class HttpRequest { /// The request body will be taken from the specified file. virtual Status SetPostRequest(const string& body_filepath); + /// \brief Makes the request a POST request. + /// + /// The request body will be taken from the specified buffer. + virtual Status SetPostRequest(const char* buffer, size_t size); + /// Makes the request a POST request. virtual Status SetPostRequest(); @@ -91,15 +98,23 @@ class HttpRequest { virtual Status Send(); private: - /// A callback in the form which can be accepted by libcurl. + /// A write callback in the form which can be accepted by libcurl. static size_t WriteCallback(const void* ptr, size_t size, size_t nmemb, void* userdata); + /// A read callback in the form which can be accepted by libcurl. + static size_t ReadCallback(void* ptr, size_t size, size_t nmemb, + FILE* userdata); Status CheckInitialized() const; Status CheckMethodNotSet() const; Status CheckNotSent() const; std::unique_ptr libcurl_; + FILE* post_body_ = nullptr; + + StringPiece post_body_buffer_; + size_t post_body_read_ = 0; + char* response_buffer_ = nullptr; size_t response_buffer_size_ = 0; size_t response_buffer_written_ = 0; diff --git a/tensorflow/core/platform/cloud/http_request_fake.h b/tensorflow/core/platform/cloud/http_request_fake.h new file mode 100644 index 00000000000..b091e5d6302 --- /dev/null +++ b/tensorflow/core/platform/cloud/http_request_fake.h @@ -0,0 +1,166 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 TENSORFLOW_CORE_PLATFORM_HTTP_REQUEST_FAKE_H_ +#define TENSORFLOW_CORE_PLATFORM_HTTP_REQUEST_FAKE_H_ + +#include +#include +#include +#include +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/lib/core/stringpiece.h" +#include "tensorflow/core/platform/cloud/http_request.h" +#include "tensorflow/core/platform/macros.h" +#include "tensorflow/core/platform/protobuf.h" +#include "tensorflow/core/platform/test.h" +#include "tensorflow/core/platform/types.h" + +namespace tensorflow { + +/// Fake HttpRequest for testing. +class FakeHttpRequest : public HttpRequest { + public: + /// Return the response for the given request. + FakeHttpRequest(const string& request, const string& response) + : FakeHttpRequest(request, response, Status::OK(), nullptr) {} + + /// \brief Return the response for the request and capture the POST body. + /// + /// Post body is not expected to be a part of the 'request' parameter. + FakeHttpRequest(const string& request, const string& response, + string* captured_post_body) + : FakeHttpRequest(request, response, Status::OK(), captured_post_body) {} + + /// \brief Return the response and the status for the given request. + FakeHttpRequest(const string& request, const string& response, + Status response_status) + : FakeHttpRequest(request, response, response_status, nullptr) {} + + /// \brief Return the response and the status for the given request + /// and capture the POST body. + /// + /// Post body is not expected to be a part of the 'request' parameter. + FakeHttpRequest(const string& request, const string& response, + Status response_status, string* captured_post_body) + : expected_request_(request), + response_(response), + response_status_(response_status), + captured_post_body_(captured_post_body) {} + + Status Init() override { return Status::OK(); } + Status SetUri(const string& uri) override { + actual_request_ += "Uri: " + uri + "\n"; + return Status::OK(); + } + Status SetRange(uint64 start, uint64 end) override { + actual_request_ += strings::StrCat("Range: ", start, "-", end, "\n"); + return Status::OK(); + } + Status AddHeader(const string& name, const string& value) override { + actual_request_ += "Header " + name + ": " + value + "\n"; + return Status::OK(); + } + Status AddAuthBearerHeader(const string& auth_token) override { + actual_request_ += "Auth Token: " + auth_token + "\n"; + return Status::OK(); + } + Status SetDeleteRequest() override { + actual_request_ += "Delete: yes\n"; + return Status::OK(); + } + Status SetPostRequest(const string& body_filepath) override { + std::ifstream stream(body_filepath); + string content((std::istreambuf_iterator(stream)), + std::istreambuf_iterator()); + if (captured_post_body_) { + *captured_post_body_ = content; + } else { + actual_request_ += "Post body: " + content + "\n"; + } + return Status::OK(); + } + Status SetPostRequest(const char* buffer, size_t size) override { + if (captured_post_body_) { + *captured_post_body_ = string(buffer, size); + } else { + actual_request_ += + strings::StrCat("Post body: ", StringPiece(buffer, size), "\n"); + } + return Status::OK(); + } + Status SetPostRequest() override { + if (captured_post_body_) { + *captured_post_body_ = ""; + } else { + actual_request_ += "Post: yes\n"; + } + return Status::OK(); + } + Status SetResultBuffer(char* scratch, size_t size, + StringPiece* result) override { + scratch_ = scratch; + size_ = size; + result_ = result; + return Status::OK(); + } + Status Send() override { + EXPECT_EQ(expected_request_, actual_request_) << "Unexpected HTTP request."; + if (scratch_ && result_) { + auto actual_size = std::min(response_.size(), size_); + memcpy(scratch_, response_.c_str(), actual_size); + *result_ = StringPiece(scratch_, actual_size); + } + return response_status_; + } + + private: + char* scratch_ = nullptr; + size_t size_ = 0; + StringPiece* result_ = nullptr; + string expected_request_; + string actual_request_; + string response_; + Status response_status_; + string* captured_post_body_ = nullptr; +}; + +/// Fake HttpRequest factory for testing. +class FakeHttpRequestFactory : public HttpRequest::Factory { + public: + FakeHttpRequestFactory(const std::vector* requests) + : requests_(requests) {} + + ~FakeHttpRequestFactory() { + EXPECT_EQ(current_index_, requests_->size()) + << "Not all expected requests were made."; + } + + HttpRequest* Create() override { + EXPECT_LT(current_index_, requests_->size()) + << "Too many calls of HttpRequest factory."; + return (*requests_)[current_index_++]; + } + + private: + const std::vector* requests_; + int current_index_ = 0; +}; + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_PLATFORM_HTTP_REQUEST_FAKE_H_ diff --git a/tensorflow/core/platform/cloud/http_request_test.cc b/tensorflow/core/platform/cloud/http_request_test.cc index 247514c9da4..e7bfcd4cb4b 100644 --- a/tensorflow/core/platform/cloud/http_request_test.cc +++ b/tensorflow/core/platform/cloud/http_request_test.cc @@ -81,7 +81,7 @@ class FakeLibCurl : public LibCurl { CURLcode curl_easy_setopt(CURL* curl, CURLoption option, size_t (*param)(void*, size_t, size_t, FILE*)) override { - EXPECT_EQ(param, &fread) << "Expected the standard fread() function."; + read_callback = param; return CURLE_OK; } CURLcode curl_easy_setopt(CURL* curl, CURLoption option, @@ -98,11 +98,11 @@ class FakeLibCurl : public LibCurl { } CURLcode curl_easy_perform(CURL* curl) override { if (read_data) { - char buffer[100]; + char buffer[3]; int bytes_read; posted_content = ""; do { - bytes_read = fread(buffer, 1, 100, read_data); + bytes_read = read_callback(buffer, 1, sizeof(buffer), read_data); posted_content = strings::StrCat(posted_content, StringPiece(buffer, bytes_read)); } while (bytes_read > 0); @@ -158,11 +158,13 @@ class FakeLibCurl : public LibCurl { bool is_initialized = false; bool is_cleaned_up = false; std::vector* headers = nullptr; - FILE* read_data = nullptr; bool is_post = false; void* write_data = nullptr; size_t (*write_callback)(const void* ptr, size_t size, size_t nmemb, void* userdata) = nullptr; + FILE* read_data = nullptr; + size_t (*read_callback)(void* ptr, size_t size, size_t nmemb, + FILE* userdata) = &fread; // Outcome of performing the request. string posted_content; }; @@ -193,7 +195,7 @@ TEST(HttpRequestTest, GetRequest) { EXPECT_FALSE(libcurl->is_post); } -TEST(HttpRequestTest, PostRequest_WithBody) { +TEST(HttpRequestTest, PostRequest_WithBody_FromFile) { FakeLibCurl* libcurl = new FakeLibCurl("", 200); HttpRequest http_request((std::unique_ptr(libcurl))); TF_EXPECT_OK(http_request.Init()); @@ -221,6 +223,29 @@ TEST(HttpRequestTest, PostRequest_WithBody) { std::remove(content_filename.c_str()); } +TEST(HttpRequestTest, PostRequest_WithBody_FromMemory) { + FakeLibCurl* libcurl = new FakeLibCurl("", 200); + HttpRequest http_request((std::unique_ptr(libcurl))); + TF_EXPECT_OK(http_request.Init()); + + string content = "post body content"; + + TF_EXPECT_OK(http_request.SetUri("http://www.testuri.com")); + TF_EXPECT_OK(http_request.AddAuthBearerHeader("fake-bearer")); + TF_EXPECT_OK(http_request.SetPostRequest(content.c_str(), content.size())); + TF_EXPECT_OK(http_request.Send()); + + // Check interactions with libcurl. + EXPECT_TRUE(libcurl->is_initialized); + EXPECT_EQ("http://www.testuri.com", libcurl->url); + EXPECT_EQ("", libcurl->custom_request); + EXPECT_EQ(2, libcurl->headers->size()); + EXPECT_EQ("Authorization: Bearer fake-bearer", (*libcurl->headers)[0]); + EXPECT_EQ("Content-Length: 17", (*libcurl->headers)[1]); + EXPECT_TRUE(libcurl->is_post); + EXPECT_EQ("post body content", libcurl->posted_content); +} + TEST(HttpRequestTest, PostRequest_WithoutBody) { FakeLibCurl* libcurl = new FakeLibCurl("", 200); HttpRequest http_request((std::unique_ptr(libcurl))); diff --git a/tensorflow/core/platform/cloud/oauth_client.cc b/tensorflow/core/platform/cloud/oauth_client.cc new file mode 100644 index 00000000000..629ac11f8d8 --- /dev/null +++ b/tensorflow/core/platform/cloud/oauth_client.cc @@ -0,0 +1,288 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 "tensorflow/core/platform/cloud/oauth_client.h" +#include +#include +#include +#include +#include +#include +#include +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/platform/cloud/base64.h" +#include "tensorflow/core/platform/cloud/http_request.h" +#include "tensorflow/core/platform/env.h" + +namespace tensorflow { + +namespace { + +// The requested lifetime of a auth bearer token. +constexpr int kRequestedTokenLifetimeSec = 3600; + +// The crypto algorithm to be used with OAuth. +constexpr char kCryptoAlgorithm[] = "RS256"; + +// The token type for the OAuth request. +constexpr char kJwtType[] = "JWT"; + +// The grant type for the OAuth request. Already URL-encoded for convenience. +constexpr char kGrantType[] = + "urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer"; + +Status ReadJsonValue(Json::Value json, const string& name, Json::Value* value) { + if (!value) { + return errors::FailedPrecondition("'value' cannot be nullptr."); + } + *value = json.get(name, Json::Value::null); + if (*value == Json::Value::null) { + return errors::FailedPrecondition( + strings::StrCat("Couldn't read a JSON value '", name, "'.")); + } + return Status::OK(); +} + +Status ReadJsonString(Json::Value json, const string& name, string* value) { + Json::Value json_value; + TF_RETURN_IF_ERROR(ReadJsonValue(json, name, &json_value)); + if (!json_value.isString()) { + return errors::FailedPrecondition( + strings::StrCat("JSON value '", name, "' is not string.")); + } + *value = json_value.asString(); + return Status::OK(); +} + +Status ReadJsonInt(Json::Value json, const string& name, int64* value) { + Json::Value json_value; + TF_RETURN_IF_ERROR(ReadJsonValue(json, name, &json_value)); + if (!json_value.isIntegral()) { + return errors::FailedPrecondition( + strings::StrCat("JSON value '", name, "' is not integer.")); + } + *value = json_value.asInt64(); + return Status::OK(); +} + +Status CreateSignature(RSA* private_key, StringPiece to_sign, + string* signature) { + if (!private_key || !signature) { + return errors::FailedPrecondition( + "'private_key' and 'signature' cannot be nullptr."); + } + + const auto md = EVP_sha256(); + if (!md) { + return errors::Internal("Could not get a sha256 encryptor."); + } + std::unique_ptr> md_ctx( + EVP_MD_CTX_create(), [](EVP_MD_CTX* ptr) { EVP_MD_CTX_destroy(ptr); }); + if (!md_ctx.get()) { + return errors::Internal("Could not create MD_CTX."); + } + + std::unique_ptr> key( + EVP_PKEY_new(), [](EVP_PKEY* ptr) { EVP_PKEY_free(ptr); }); + EVP_PKEY_set1_RSA(key.get(), private_key); + + if (EVP_DigestSignInit(md_ctx.get(), NULL, md, NULL, key.get()) != 1) { + return errors::Internal("DigestInit failed."); + } + if (EVP_DigestSignUpdate(md_ctx.get(), to_sign.data(), to_sign.size()) != 1) { + return errors::Internal("DigestUpdate failed."); + } + size_t sig_len = 0; + if (EVP_DigestSignFinal(md_ctx.get(), NULL, &sig_len) != 1) { + return errors::Internal("DigestFinal (get signature length) failed."); + } + std::unique_ptr sig(new unsigned char[sig_len]); + if (EVP_DigestSignFinal(md_ctx.get(), sig.get(), &sig_len) != 1) { + return errors::Internal("DigestFinal (signature compute) failed."); + } + EVP_MD_CTX_cleanup(md_ctx.get()); + return Base64Encode(StringPiece(reinterpret_cast(sig.get()), sig_len), + signature); +} + +/// Encodes a claim for a JSON web token (JWT) to make an OAuth request. +Status EncodeJwtClaim(StringPiece client_email, StringPiece scope, + StringPiece audience, uint64 request_timestamp_sec, + string* encoded) { + // Step 1: create the JSON with the claim. + Json::Value root; + root["iss"] = Json::Value(client_email.begin(), client_email.end()); + root["scope"] = Json::Value(scope.begin(), scope.end()); + root["aud"] = Json::Value(audience.begin(), audience.end()); + + const auto expiration_timestamp_sec = + request_timestamp_sec + kRequestedTokenLifetimeSec; + + root["iat"] = request_timestamp_sec; + root["exp"] = expiration_timestamp_sec; + + // Step 2: represent the JSON as a string. + string claim = root.toStyledString(); + + // Step 3: encode the string as base64. + return Base64Encode(claim, encoded); +} + +/// Encodes a header for a JSON web token (JWT) to make an OAuth request. +Status EncodeJwtHeader(StringPiece key_id, string* encoded) { + // Step 1: create the JSON with the header. + Json::Value root; + root["alg"] = kCryptoAlgorithm; + root["typ"] = kJwtType; + root["kid"] = Json::Value(key_id.begin(), key_id.end()); + + // Step 2: represent the JSON as a string. + const string header = root.toStyledString(); + + // Step 3: encode the string as base64. + return Base64Encode(header, encoded); +} + +} // namespace + +OAuthClient::OAuthClient() + : OAuthClient( + std::unique_ptr(new HttpRequest::Factory()), + Env::Default()) {} + +OAuthClient::OAuthClient( + std::unique_ptr http_request_factory, Env* env) + : http_request_factory_(std::move(http_request_factory)), env_(env) {} + +Status OAuthClient::GetTokenFromServiceAccountJson( + Json::Value json, StringPiece oauth_server_uri, StringPiece scope, + string* token, uint64* expiration_timestamp_sec) { + if (!token || !expiration_timestamp_sec) { + return errors::FailedPrecondition( + "'token' and 'expiration_timestamp_sec' cannot be nullptr."); + } + string private_key_serialized, private_key_id, client_id, client_email; + TF_RETURN_IF_ERROR( + ReadJsonString(json, "private_key", &private_key_serialized)); + TF_RETURN_IF_ERROR(ReadJsonString(json, "private_key_id", &private_key_id)); + TF_RETURN_IF_ERROR(ReadJsonString(json, "client_id", &client_id)); + TF_RETURN_IF_ERROR(ReadJsonString(json, "client_email", &client_email)); + + std::unique_ptr> bio( + BIO_new(BIO_s_mem()), [](BIO* ptr) { BIO_free_all(ptr); }); + if (BIO_puts(bio.get(), private_key_serialized.c_str()) != + static_cast(private_key_serialized.size())) { + return errors::Internal("Could not load the private key."); + } + std::unique_ptr> private_key( + PEM_read_bio_RSAPrivateKey(bio.get(), nullptr, nullptr, nullptr), + [](RSA* ptr) { RSA_free(ptr); }); + if (!private_key.get()) { + return errors::Internal("Could not deserialize the private key."); + } + + const uint64 request_timestamp_sec = env_->NowSeconds(); + + string encoded_claim, encoded_header; + TF_RETURN_IF_ERROR(EncodeJwtHeader(private_key_id, &encoded_header)); + TF_RETURN_IF_ERROR(EncodeJwtClaim(client_email, scope, oauth_server_uri, + request_timestamp_sec, &encoded_claim)); + const string to_sign = encoded_header + "." + encoded_claim; + string signature; + TF_RETURN_IF_ERROR(CreateSignature(private_key.get(), to_sign, &signature)); + const string jwt = to_sign + "." + signature; + const string request_body = + strings::StrCat("grant_type=", kGrantType, "&assertion=", jwt); + + // Send the request to the Google OAuth 2.0 server to get the token. + std::unique_ptr request(http_request_factory_->Create()); + std::unique_ptr response_buffer(new char[kResponseBufferSize]); + StringPiece response; + TF_RETURN_IF_ERROR(request->Init()); + TF_RETURN_IF_ERROR(request->SetUri(oauth_server_uri.ToString())); + TF_RETURN_IF_ERROR( + request->SetPostRequest(request_body.c_str(), request_body.size())); + TF_RETURN_IF_ERROR(request->SetResultBuffer(response_buffer.get(), + kResponseBufferSize, &response)); + TF_RETURN_IF_ERROR(request->Send()); + + TF_RETURN_IF_ERROR(ParseOAuthResponse(response, request_timestamp_sec, token, + expiration_timestamp_sec)); + return Status::OK(); +} + +Status OAuthClient::GetTokenFromRefreshTokenJson( + Json::Value json, StringPiece oauth_server_uri, string* token, + uint64* expiration_timestamp_sec) { + if (!token || !expiration_timestamp_sec) { + return errors::FailedPrecondition( + "'token' and 'expiration_timestamp_sec' cannot be nullptr."); + } + string client_id, client_secret, refresh_token; + TF_RETURN_IF_ERROR(ReadJsonString(json, "client_id", &client_id)); + TF_RETURN_IF_ERROR(ReadJsonString(json, "client_secret", &client_secret)); + TF_RETURN_IF_ERROR(ReadJsonString(json, "refresh_token", &refresh_token)); + + const auto request_body = strings::StrCat( + "client_id=", client_id, "&client_secret=", client_secret, + "&refresh_token=", refresh_token, "&grant_type=refresh_token"); + + const uint64 request_timestamp_sec = env_->NowSeconds(); + + std::unique_ptr request(http_request_factory_->Create()); + std::unique_ptr response_buffer(new char[kResponseBufferSize]); + StringPiece response; + TF_RETURN_IF_ERROR(request->Init()); + TF_RETURN_IF_ERROR(request->SetUri(oauth_server_uri.ToString())); + TF_RETURN_IF_ERROR( + request->SetPostRequest(request_body.c_str(), request_body.size())); + TF_RETURN_IF_ERROR(request->SetResultBuffer(response_buffer.get(), + kResponseBufferSize, &response)); + TF_RETURN_IF_ERROR(request->Send()); + + TF_RETURN_IF_ERROR(ParseOAuthResponse(response, request_timestamp_sec, token, + expiration_timestamp_sec)); + return Status::OK(); +} + +Status OAuthClient::ParseOAuthResponse(StringPiece response, + uint64 request_timestamp_sec, + string* token, + uint64* expiration_timestamp_sec) { + if (!token || !expiration_timestamp_sec) { + return errors::FailedPrecondition( + "'token' and 'expiration_timestamp_sec' cannot be nullptr."); + } + Json::Value root; + Json::Reader reader; + if (!reader.parse(response.begin(), response.end(), root)) { + return errors::Internal("Couldn't parse JSON response from OAuth server."); + } + + string token_type; + TF_RETURN_IF_ERROR(ReadJsonString(root, "token_type", &token_type)); + if (token_type != "Bearer") { + return errors::FailedPrecondition("Unexpected Oauth token type: " + + token_type); + } + int64 expires_in; + TF_RETURN_IF_ERROR(ReadJsonInt(root, "expires_in", &expires_in)); + *expiration_timestamp_sec = request_timestamp_sec + expires_in; + TF_RETURN_IF_ERROR(ReadJsonString(root, "access_token", token)); + + return Status::OK(); +} + +} // namespace tensorflow diff --git a/tensorflow/core/platform/cloud/oauth_client.h b/tensorflow/core/platform/cloud/oauth_client.h new file mode 100644 index 00000000000..dadd400cb1d --- /dev/null +++ b/tensorflow/core/platform/cloud/oauth_client.h @@ -0,0 +1,65 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 THIRD_PARTY_TENSORFLOW_CORE_PLATFORM_CLOUD_OAUTH_CLIENT_H_ +#define THIRD_PARTY_TENSORFLOW_CORE_PLATFORM_CLOUD_OAUTH_CLIENT_H_ + +#include +#include "include/json/json.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/platform/cloud/http_request.h" +#include "tensorflow/core/platform/env.h" + +namespace tensorflow { + +/// OAuth 2.0 client. +class OAuthClient { + public: + OAuthClient(); + explicit OAuthClient( + std::unique_ptr http_request_factory, Env* env); + virtual ~OAuthClient() {} + + /// \brief Retrieves a bearer token using a private key. + /// + /// Retrieves the authentication bearer token using a JSON file + /// with the client's private key. + virtual Status GetTokenFromServiceAccountJson( + Json::Value json, StringPiece oauth_server_uri, StringPiece scope, + string* token, uint64* expiration_timestamp_sec); + + /// Retrieves a bearer token using a refresh token. + virtual Status GetTokenFromRefreshTokenJson(Json::Value json, + StringPiece oauth_server_uri, + string* token, + uint64* expiration_timestamp_sec); + + /// Parses the JSON response with the token from an OAuth 2.0 server. + virtual Status ParseOAuthResponse(StringPiece response, + uint64 request_timestamp_sec, string* token, + uint64* expiration_timestamp_sec); + + /// The max size of the JSON response from an OAuth 2.0 server, in bytes. + static constexpr size_t kResponseBufferSize = 1000; + + private: + std::unique_ptr http_request_factory_; + Env* env_; + TF_DISALLOW_COPY_AND_ASSIGN(OAuthClient); +}; + +} // namespace tensorflow + +#endif // THIRD_PARTY_TENSORFLOW_CORE_PLATFORM_CLOUD_OAUTH_CLIENT_H_ diff --git a/tensorflow/core/platform/cloud/oauth_client_test.cc b/tensorflow/core/platform/cloud/oauth_client_test.cc new file mode 100644 index 00000000000..1bfc956c1c6 --- /dev/null +++ b/tensorflow/core/platform/cloud/oauth_client_test.cc @@ -0,0 +1,203 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + +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 "tensorflow/core/platform/cloud/oauth_client.h" +#include +#include +#include +#include +#include "tensorflow/core/lib/core/status_test_util.h" +#include "tensorflow/core/lib/io/path.h" +#include "tensorflow/core/lib/strings/scanner.h" +#include "tensorflow/core/platform/cloud/base64.h" +#include "tensorflow/core/platform/cloud/http_request_fake.h" +#include "tensorflow/core/platform/env.h" +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { +namespace { + +constexpr char kTestData[] = "core/platform/cloud/testdata/"; + +constexpr char kTokenJson[] = R"( + { + "access_token":"1/fFAGRNJru1FTz70BzhT3Zg", + "expires_in":3920, + "token_type":"Bearer" + })"; + +class FakeEnv : public EnvWrapper { + public: + FakeEnv() : EnvWrapper(Env::Default()) {} + + uint64 NowSeconds() override { return now; } + uint64 now = 10000; +}; + +} // namespace + +TEST(OAuthClientTest, ParseOAuthResponse) { + const uint64 request_timestamp = 100; + string token; + uint64 expiration_timestamp; + TF_EXPECT_OK(OAuthClient().ParseOAuthResponse(kTokenJson, request_timestamp, + &token, &expiration_timestamp)); + EXPECT_EQ("1/fFAGRNJru1FTz70BzhT3Zg", token); + EXPECT_EQ(4020, expiration_timestamp); +} + +TEST(OAuthClientTest, GetTokenFromRefreshTokenJson) { + const string credentials_json = R"( + { + "client_id": "test_client_id", + "client_secret": "test_client_secret", + "refresh_token": "test_refresh_token", + "type": "authorized_user" + })"; + Json::Value json; + Json::Reader reader; + ASSERT_TRUE(reader.parse(credentials_json, json)); + + std::vector requests({new FakeHttpRequest( + "Uri: https://www.googleapis.com/oauth2/v3/token\n" + "Post body: client_id=test_client_id&" + "client_secret=test_client_secret&" + "refresh_token=test_refresh_token&grant_type=refresh_token\n", + kTokenJson)}); + FakeEnv env; + OAuthClient client(std::unique_ptr( + new FakeHttpRequestFactory(&requests)), + &env); + string token; + uint64 expiration_timestamp; + TF_EXPECT_OK(client.GetTokenFromRefreshTokenJson( + json, "https://www.googleapis.com/oauth2/v3/token", &token, + &expiration_timestamp)); + EXPECT_EQ("1/fFAGRNJru1FTz70BzhT3Zg", token); + EXPECT_EQ(13920, expiration_timestamp); +} + +TEST(OAuthClientTest, GetTokenFromServiceAccountJson) { + std::ifstream credentials( + io::JoinPath(io::JoinPath(testing::TensorFlowSrcRoot(), kTestData), + "service_account_credentials.json")); + ASSERT_TRUE(credentials.is_open()); + Json::Value json; + Json::Reader reader; + ASSERT_TRUE(reader.parse(credentials, json)); + + string post_body; + std::vector requests( + {new FakeHttpRequest("Uri: https://www.googleapis.com/oauth2/v3/token\n", + kTokenJson, &post_body)}); + FakeEnv env; + OAuthClient client(std::unique_ptr( + new FakeHttpRequestFactory(&requests)), + &env); + string token; + uint64 expiration_timestamp; + TF_EXPECT_OK(client.GetTokenFromServiceAccountJson( + json, "https://www.googleapis.com/oauth2/v3/token", + "https://test-token-scope.com", &token, &expiration_timestamp)); + EXPECT_EQ("1/fFAGRNJru1FTz70BzhT3Zg", token); + EXPECT_EQ(13920, expiration_timestamp); + + // Now look at the JWT claim that was sent to the OAuth server. + StringPiece grant_type, assertion; + ASSERT_TRUE(strings::Scanner(post_body) + .OneLiteral("grant_type=") + .RestartCapture() + .ScanEscapedUntil('&') + .StopCapture() + .OneLiteral("&assertion=") + .GetResult(&assertion, &grant_type)); + EXPECT_EQ("urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer", + grant_type.ToString()); + + int last_dot = assertion.ToString().find_last_of("."); + string header_dot_claim = assertion.ToString().substr(0, last_dot); + string signature_encoded = assertion.ToString().substr(last_dot + 1); + + // Check that 'signature' signs 'header_dot_claim'. + + // Read the serialized public key. + std::ifstream public_key_stream( + io::JoinPath(io::JoinPath(testing::TensorFlowSrcRoot(), kTestData), + "service_account_public_key.txt")); + string public_key_serialized( + (std::istreambuf_iterator(public_key_stream)), + (std::istreambuf_iterator())); + + // Deserialize the public key. + auto bio = BIO_new(BIO_s_mem()); + RSA* public_key = nullptr; + EXPECT_EQ(public_key_serialized.size(), + BIO_puts(bio, public_key_serialized.c_str())); + public_key = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr); + EXPECT_TRUE(public_key) << "Could not load the public key from testdata."; + + // Deserialize the signature. + string signature; + TF_EXPECT_OK(Base64Decode(signature_encoded, &signature)); + + // Actually cryptographically verify the signature. + const auto md = EVP_sha256(); + auto md_ctx = EVP_MD_CTX_create(); + auto key = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(key, public_key); + ASSERT_EQ(1, EVP_DigestVerifyInit(md_ctx, nullptr, md, nullptr, key)); + ASSERT_EQ(1, EVP_DigestVerifyUpdate(md_ctx, header_dot_claim.c_str(), + header_dot_claim.size())); + ASSERT_EQ( + 1, + EVP_DigestVerifyFinal( + md_ctx, const_cast( + reinterpret_cast(signature.data())), + signature.size())); + EVP_MD_CTX_cleanup(md_ctx); + + // Free all the crypto-related resources. + EVP_PKEY_free(key); + EVP_MD_CTX_destroy(md_ctx); + RSA_free(public_key); + BIO_free_all(bio); + + // Now check the content of the header and the claim. + int dot = header_dot_claim.find_last_of("."); + string header_encoded = header_dot_claim.substr(0, dot); + string claim_encoded = header_dot_claim.substr(dot + 1); + + string header, claim; + TF_EXPECT_OK(Base64Decode(header_encoded, &header)); + TF_EXPECT_OK(Base64Decode(claim_encoded, &claim)); + + Json::Value header_json, claim_json; + EXPECT_TRUE(reader.parse(header, header_json)); + EXPECT_EQ("RS256", header_json.get("alg", Json::Value::null).asString()); + EXPECT_EQ("JWT", header_json.get("typ", Json::Value::null).asString()); + EXPECT_EQ("fake_key_id", + header_json.get("kid", Json::Value::null).asString()); + + EXPECT_TRUE(reader.parse(claim, claim_json)); + EXPECT_EQ("fake-test-project.iam.gserviceaccount.com", + claim_json.get("iss", Json::Value::null).asString()); + EXPECT_EQ("https://test-token-scope.com", + claim_json.get("scope", Json::Value::null).asString()); + EXPECT_EQ("https://www.googleapis.com/oauth2/v3/token", + claim_json.get("aud", Json::Value::null).asString()); + EXPECT_EQ(10000, claim_json.get("iat", Json::Value::null).asInt64()); + EXPECT_EQ(13600, claim_json.get("exp", Json::Value::null).asInt64()); +} +} // namespace tensorflow diff --git a/tensorflow/core/platform/cloud/testdata/application_default_credentials.json b/tensorflow/core/platform/cloud/testdata/application_default_credentials.json new file mode 100644 index 00000000000..e8bcb20fadb --- /dev/null +++ b/tensorflow/core/platform/cloud/testdata/application_default_credentials.json @@ -0,0 +1,6 @@ +{ + "client_id": "fake-client-id.apps.googleusercontent.com", + "client_secret": "fake-client-secret", + "refresh_token": "fake-refresh-token", + "type": "authorized_user" +} diff --git a/tensorflow/core/platform/cloud/testdata/service_account_credentials.json b/tensorflow/core/platform/cloud/testdata/service_account_credentials.json new file mode 100644 index 00000000000..f0edd96e228 --- /dev/null +++ b/tensorflow/core/platform/cloud/testdata/service_account_credentials.json @@ -0,0 +1,12 @@ +{ + "type": "service_account", + "project_id": "fake_project_id", + "private_key_id": "fake_key_id", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwrEZE6PWQYAy68mWPMuC6KAD02Sb9Pv/FHWpGKe8MxxdDiz/\nspb2KIrWxxZolStHgDXAOoElbAv4GbRLJiivEl8k0gSP9YpIE56nSxfXxRIDH25N\nI3fhRIs5hSG+/p3lLV5NsdNrm1CYHnEbTY7Ow7gpyxl0n+6q+ngguZTOGtBIMqVS\n4KIJlzTlJgeqvLFbtLP6uFc4OuGL6UZ+s4I7zSJVPBRxrFA+mOhBEPz/QjANBHBd\nIEhgh5VlmX/oRUK+D3zR/MnRTYtD8skiZSFMUix1eWvKw/1wX0mieH1rUQbpIYdJ\nTgFhROKuAJWVU7c+T6JHZwm8DqXaVz6oCJPlzwIDAQABAoIBAGHQVAb4A0b5P5wS\ntXZp0KVK72EfZPNaP7dpvcDzVKxhDad3mCeDjLyltG5lpbl7+vpBBwjdpY15Hfbc\nC/1p5ztVrcwOGr2D8d5ZkTc7DV6nRAZghkTRj82+HPH0GF8XuPJoNKSo0aFAhoyU\nyuDWZK8UMXsmmN9ZK3GXNOnIBxyUs703ueIgNkH9zlT2x0wmEs4toZKiPVZhLUrc\nG1zLfuf1onhB5xq7u0sYZCiJrvaVvzNrKune1IrBM+FK/dc3k0vF9NEvwCYxWuTj\nGwO2wU3U945Scj9718pxhMMxZpsPZfMZHrYcdMvjpPaKFhJjxb16kT4gvSdm015j\nLgpM1xECgYEA35/KW4npUPoltBZ2Gi/YPmGVfpyXz6ToOw9ENawiGdNrOQG1Pw+v\nPBV0+yvcp1AvlL46lp87xQrl0dYHwwsQ7eRqpeyG6PCXRN7pJXP9Dac6Tq07lu2g\nriltHcuw8WYLv0gjrNr8IaCN04VS30d8MayXgHuvR3+NHkBdryuKFgsCgYEA3uD7\nmNukdNxJBQhgOO8lCbLXdEjgFFDBuh/9GvpqaeILP4MIwpWj9tA9Hjw5JlK3qpHL\nvLsJinKMmaswX43Hzf8OAAhTkSC/TfIJwZTGuBPoDH4UnMD+83SAk8DDgWTUvz/6\n1ilR4zm3kus6ZxTA1zp3P5UFD2etbv+cmGkjHc0CgYBkpw1z6j0j/5Oc3UdHPiW8\n3jtlg6IpCfalLpfq+JFYwnpObGBiA/NBvf6rVvC4NjVUY9MHHKDQbblHm2he98ok\n6Vy/VhjbG/9aNmMGQpCx5oUuCHb71fUuruK4OIhp/x5meFfmY6J8mEF95VKJwSk7\nSo3efM1GBzlDVoFUaOp8RQKBgQDWBQ0Ul7WwUef8YTKk+V+DlKy4CVLDr1iYNieC\nRHzy+BD9CALdd3xfgU9vPT1Tw5KCxEX0EVb0D1NcLLrixu7arNTwyw4UCnIpkwYz\nUX4RPWxSsq9wZxNrDLB7MVuLYRu6GuHvzPXJUJ8rAZ6vZYpYIthnwd1+EXzFXcct\nw6fo8QKBgQClY0EmhGIoDHNPjPOGzl2hmZCm5FKPx9i2SOOVYuSMdPT3qTYOp4/Q\nUp1oqkbd1ZWxMlbuRljpwbUHRcj85O5bkmWylINjpA1hFqxcxtj1r9xRmeO9Qcqa\n89jOblkbSoVDE5CFHD0Cv4bFw09z/l6Ih9DOW4AlB5UN+byEUPsIdw==\n-----END RSA PRIVATE KEY-----", + "client_email": "fake-test-project.iam.gserviceaccount.com", + "client_id": "fake_client_id", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://accounts.google.com/o/oauth2/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/fake-test-project.iam.gserviceaccount.com" +} \ No newline at end of file diff --git a/tensorflow/core/platform/cloud/testdata/service_account_public_key.txt b/tensorflow/core/platform/cloud/testdata/service_account_public_key.txt new file mode 100644 index 00000000000..25bd17d6891 --- /dev/null +++ b/tensorflow/core/platform/cloud/testdata/service_account_public_key.txt @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwrEZE6PWQYAy68mWPMuC +6KAD02Sb9Pv/FHWpGKe8MxxdDiz/spb2KIrWxxZolStHgDXAOoElbAv4GbRLJiiv +El8k0gSP9YpIE56nSxfXxRIDH25NI3fhRIs5hSG+/p3lLV5NsdNrm1CYHnEbTY7O +w7gpyxl0n+6q+ngguZTOGtBIMqVS4KIJlzTlJgeqvLFbtLP6uFc4OuGL6UZ+s4I7 +zSJVPBRxrFA+mOhBEPz/QjANBHBdIEhgh5VlmX/oRUK+D3zR/MnRTYtD8skiZSFM +Uix1eWvKw/1wX0mieH1rUQbpIYdJTgFhROKuAJWVU7c+T6JHZwm8DqXaVz6oCJPl +zwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/tensorflow/core/platform/env.h b/tensorflow/core/platform/env.h index dd1beb32465..4e355426004 100644 --- a/tensorflow/core/platform/env.h +++ b/tensorflow/core/platform/env.h @@ -162,6 +162,10 @@ class Env { /// time. Only useful for computing deltas of time. virtual uint64 NowMicros() = 0; + /// \brief Returns the number of seconds since some fixed point in + /// time. Only useful for computing deltas of time. + virtual uint64 NowSeconds() { return NowMicros() / 1000000L; } + /// Sleeps/delays the thread for the prescribed number of micro-seconds. virtual void SleepForMicroseconds(int micros) = 0; diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl index 40b2017c754..d99cb5b5e3d 100644 --- a/tensorflow/workspace.bzl +++ b/tensorflow/workspace.bzl @@ -120,3 +120,10 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""): name = "jsoncpp", actual = "@jsoncpp_git//:jsoncpp", ) + + native.new_git_repository( + name = "boringssl_git", + commit = "e72df93461c6d9d2b5698f10e16d3ab82f5adde3", + remote = "https://boringssl.googlesource.com/boringssl", + build_file = path_prefix + "boringssl.BUILD", + ) diff --git a/third_party/boringssl/BUILD b/third_party/boringssl/BUILD new file mode 100644 index 00000000000..3211d7ae977 --- /dev/null +++ b/third_party/boringssl/BUILD @@ -0,0 +1,13 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["restricted"]) # OpenSSL license, partly BSD-like + +# See https://boringssl.googlesource.com/boringssl/+/master/INCORPORATING.md +# on how to re-generate err_data.c. + +filegroup( + name = "err_data_c", + srcs = [ + "err_data.c", + ], +) diff --git a/third_party/boringssl/err_data.c b/third_party/boringssl/err_data.c new file mode 100644 index 00000000000..2d5fed6c1f5 --- /dev/null +++ b/third_party/boringssl/err_data.c @@ -0,0 +1,1236 @@ +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + + /* This file was generated by err_data_generate.go. */ + +#include +#include +#include + + +OPENSSL_COMPILE_ASSERT(ERR_LIB_NONE == 1, library_values_changed_1); +OPENSSL_COMPILE_ASSERT(ERR_LIB_SYS == 2, library_values_changed_2); +OPENSSL_COMPILE_ASSERT(ERR_LIB_BN == 3, library_values_changed_3); +OPENSSL_COMPILE_ASSERT(ERR_LIB_RSA == 4, library_values_changed_4); +OPENSSL_COMPILE_ASSERT(ERR_LIB_DH == 5, library_values_changed_5); +OPENSSL_COMPILE_ASSERT(ERR_LIB_EVP == 6, library_values_changed_6); +OPENSSL_COMPILE_ASSERT(ERR_LIB_BUF == 7, library_values_changed_7); +OPENSSL_COMPILE_ASSERT(ERR_LIB_OBJ == 8, library_values_changed_8); +OPENSSL_COMPILE_ASSERT(ERR_LIB_PEM == 9, library_values_changed_9); +OPENSSL_COMPILE_ASSERT(ERR_LIB_DSA == 10, library_values_changed_10); +OPENSSL_COMPILE_ASSERT(ERR_LIB_X509 == 11, library_values_changed_11); +OPENSSL_COMPILE_ASSERT(ERR_LIB_ASN1 == 12, library_values_changed_12); +OPENSSL_COMPILE_ASSERT(ERR_LIB_CONF == 13, library_values_changed_13); +OPENSSL_COMPILE_ASSERT(ERR_LIB_CRYPTO == 14, library_values_changed_14); +OPENSSL_COMPILE_ASSERT(ERR_LIB_EC == 15, library_values_changed_15); +OPENSSL_COMPILE_ASSERT(ERR_LIB_SSL == 16, library_values_changed_16); +OPENSSL_COMPILE_ASSERT(ERR_LIB_BIO == 17, library_values_changed_17); +OPENSSL_COMPILE_ASSERT(ERR_LIB_PKCS7 == 18, library_values_changed_18); +OPENSSL_COMPILE_ASSERT(ERR_LIB_PKCS8 == 19, library_values_changed_19); +OPENSSL_COMPILE_ASSERT(ERR_LIB_X509V3 == 20, library_values_changed_20); +OPENSSL_COMPILE_ASSERT(ERR_LIB_RAND == 21, library_values_changed_21); +OPENSSL_COMPILE_ASSERT(ERR_LIB_ENGINE == 22, library_values_changed_22); +OPENSSL_COMPILE_ASSERT(ERR_LIB_OCSP == 23, library_values_changed_23); +OPENSSL_COMPILE_ASSERT(ERR_LIB_UI == 24, library_values_changed_24); +OPENSSL_COMPILE_ASSERT(ERR_LIB_COMP == 25, library_values_changed_25); +OPENSSL_COMPILE_ASSERT(ERR_LIB_ECDSA == 26, library_values_changed_26); +OPENSSL_COMPILE_ASSERT(ERR_LIB_ECDH == 27, library_values_changed_27); +OPENSSL_COMPILE_ASSERT(ERR_LIB_HMAC == 28, library_values_changed_28); +OPENSSL_COMPILE_ASSERT(ERR_LIB_DIGEST == 29, library_values_changed_29); +OPENSSL_COMPILE_ASSERT(ERR_LIB_CIPHER == 30, library_values_changed_30); +OPENSSL_COMPILE_ASSERT(ERR_LIB_HKDF == 31, library_values_changed_31); +OPENSSL_COMPILE_ASSERT(ERR_LIB_USER == 32, library_values_changed_32); +OPENSSL_COMPILE_ASSERT(ERR_NUM_LIBS == 33, library_values_changed_num); + +const uint32_t kOpenSSLReasonValues[] = { + 0xc320838, + 0xc328852, + 0xc330861, + 0xc338871, + 0xc340880, + 0xc348899, + 0xc3508a5, + 0xc3588c2, + 0xc3608d4, + 0xc3688e2, + 0xc3708f2, + 0xc3788ff, + 0xc38090f, + 0xc38891a, + 0xc390930, + 0xc39893f, + 0xc3a0953, + 0xc3a8845, + 0xc3b00ea, + 0x10320845, + 0x1032939a, + 0x103313a6, + 0x103393bf, + 0x103413d2, + 0x10348e7a, + 0x10350c19, + 0x103593e5, + 0x103613fa, + 0x1036940d, + 0x1037142c, + 0x10379445, + 0x1038145a, + 0x10389478, + 0x10391487, + 0x103994a3, + 0x103a14be, + 0x103a94cd, + 0x103b14e9, + 0x103b9504, + 0x103c151b, + 0x103c80ea, + 0x103d152c, + 0x103d9540, + 0x103e155f, + 0x103e956e, + 0x103f1585, + 0x103f9598, + 0x10400bea, + 0x104095ab, + 0x104115c9, + 0x104195dc, + 0x104215f6, + 0x10429606, + 0x1043161a, + 0x10439630, + 0x10441648, + 0x1044965d, + 0x10451671, + 0x10459683, + 0x104605fb, + 0x1046893f, + 0x10471698, + 0x104796af, + 0x104816c4, + 0x104896d2, + 0x14320bcd, + 0x14328bdb, + 0x14330bea, + 0x14338bfc, + 0x18320083, + 0x18328ed0, + 0x183300ac, + 0x18338ee6, + 0x18340efa, + 0x183480ea, + 0x18350f0f, + 0x18358f27, + 0x18360f3c, + 0x18368f50, + 0x18370f74, + 0x18378f8a, + 0x18380f9e, + 0x18388fae, + 0x18390a57, + 0x18398fbe, + 0x183a0fd3, + 0x183a8fe7, + 0x183b0c25, + 0x183b8ff4, + 0x183c1006, + 0x183c9011, + 0x183d1021, + 0x183d9032, + 0x183e1043, + 0x183e9055, + 0x183f107e, + 0x183f9097, + 0x184010af, + 0x184086d3, + 0x203210d6, + 0x243210e2, + 0x24328985, + 0x243310f4, + 0x24339101, + 0x2434110e, + 0x24349120, + 0x2435112f, + 0x2435914c, + 0x24361159, + 0x24369167, + 0x24371175, + 0x24379183, + 0x2438118c, + 0x24389199, + 0x243911ac, + 0x28320c0d, + 0x28328c25, + 0x28330bea, + 0x28338c38, + 0x28340c19, + 0x283480ac, + 0x283500ea, + 0x2c32274a, + 0x2c32a758, + 0x2c33276a, + 0x2c33a77c, + 0x2c342790, + 0x2c34a7a2, + 0x2c3527bd, + 0x2c35a7cf, + 0x2c3627e2, + 0x2c36832d, + 0x2c3727ef, + 0x2c37a801, + 0x2c382814, + 0x2c38a82b, + 0x2c392839, + 0x2c39a849, + 0x2c3a285b, + 0x2c3aa86f, + 0x2c3b2880, + 0x2c3ba89f, + 0x2c3c28b3, + 0x2c3ca8c9, + 0x2c3d28e2, + 0x2c3da8ff, + 0x2c3e2910, + 0x2c3ea91e, + 0x2c3f2936, + 0x2c3fa94e, + 0x2c40295b, + 0x2c4090d6, + 0x2c41296c, + 0x2c41a97f, + 0x2c4210af, + 0x2c42a990, + 0x2c430720, + 0x2c43a891, + 0x30320000, + 0x30328015, + 0x3033001f, + 0x30338038, + 0x3034004a, + 0x30348064, + 0x3035006b, + 0x30358083, + 0x30360094, + 0x303680ac, + 0x303700b9, + 0x303780c8, + 0x303800ea, + 0x303880f7, + 0x3039010a, + 0x30398125, + 0x303a013a, + 0x303a814e, + 0x303b0162, + 0x303b8173, + 0x303c018c, + 0x303c81a9, + 0x303d01b7, + 0x303d81cb, + 0x303e01db, + 0x303e81f4, + 0x303f0204, + 0x303f8217, + 0x30400226, + 0x30408232, + 0x30410247, + 0x30418257, + 0x3042026e, + 0x3042827b, + 0x3043028e, + 0x3043829d, + 0x304402b2, + 0x304482d3, + 0x304502e6, + 0x304582f9, + 0x30460312, + 0x3046832d, + 0x3047034a, + 0x30478363, + 0x30480371, + 0x30488382, + 0x30490391, + 0x304983a9, + 0x304a03bb, + 0x304a83cf, + 0x304b03ee, + 0x304b8401, + 0x304c040c, + 0x304c841d, + 0x304d0429, + 0x304d843f, + 0x304e044d, + 0x304e8463, + 0x304f0475, + 0x304f8487, + 0x3050049a, + 0x305084ad, + 0x305104be, + 0x305184ce, + 0x305204e6, + 0x305284fb, + 0x30530513, + 0x30538527, + 0x3054053f, + 0x30548558, + 0x30550571, + 0x3055858e, + 0x30560599, + 0x305685b1, + 0x305705c1, + 0x305785d2, + 0x305805e5, + 0x305885fb, + 0x30590604, + 0x30598619, + 0x305a062c, + 0x305a863b, + 0x305b065b, + 0x305b866a, + 0x305c068b, + 0x305c86a7, + 0x305d06b3, + 0x305d86d3, + 0x305e06ef, + 0x305e8700, + 0x305f0716, + 0x305f8720, + 0x34320b47, + 0x34328b5b, + 0x34330b78, + 0x34338b8b, + 0x34340b9a, + 0x34348bb7, + 0x3c320083, + 0x3c328c62, + 0x3c330c7b, + 0x3c338c96, + 0x3c340cb3, + 0x3c348cdd, + 0x3c350cf8, + 0x3c358d0d, + 0x3c360d26, + 0x3c368d3e, + 0x3c370d4f, + 0x3c378d5d, + 0x3c380d6a, + 0x3c388d7e, + 0x3c390c25, + 0x3c398d92, + 0x3c3a0da6, + 0x3c3a88ff, + 0x3c3b0db6, + 0x3c3b8dd1, + 0x3c3c0de3, + 0x3c3c8df9, + 0x3c3d0e03, + 0x3c3d8e17, + 0x3c3e0e25, + 0x3c3e8e4a, + 0x3c3f0c4e, + 0x3c3f8e33, + 0x3c4000ac, + 0x3c4080ea, + 0x3c410cce, + 0x403216e9, + 0x403296ff, + 0x4033172d, + 0x40339737, + 0x4034174e, + 0x4034976c, + 0x4035177c, + 0x4035978e, + 0x4036179b, + 0x403697a7, + 0x403717bc, + 0x403797ce, + 0x403817d9, + 0x403897eb, + 0x40390e7a, + 0x403997fb, + 0x403a180e, + 0x403a982f, + 0x403b1840, + 0x403b9850, + 0x403c0064, + 0x403c8083, + 0x403d185c, + 0x403d9872, + 0x403e1881, + 0x403e9894, + 0x403f18ae, + 0x403f98bc, + 0x404018d1, + 0x404098e5, + 0x40411902, + 0x4041991d, + 0x40421936, + 0x40429949, + 0x4043195d, + 0x40439975, + 0x4044198c, + 0x404480ac, + 0x404519a1, + 0x404599b3, + 0x404619d7, + 0x404699f7, + 0x40471a05, + 0x40479a19, + 0x40481a2e, + 0x40489a47, + 0x40491a5e, + 0x40499a78, + 0x404a1a8f, + 0x404a9aad, + 0x404b1ac5, + 0x404b9adc, + 0x404c1af2, + 0x404c9b04, + 0x404d1b25, + 0x404d9b47, + 0x404e1b5b, + 0x404e9b68, + 0x404f1b7f, + 0x404f9b8f, + 0x40501b9f, + 0x40509bb3, + 0x40511bce, + 0x40519bde, + 0x40521bf5, + 0x40529c07, + 0x40531c1f, + 0x40539c32, + 0x40541c47, + 0x40549c6a, + 0x40551c78, + 0x40559c95, + 0x40561ca2, + 0x40569cbb, + 0x40571cd3, + 0x40579ce6, + 0x40581cfb, + 0x40589d0d, + 0x40591d1d, + 0x40599d36, + 0x405a1d4a, + 0x405a9d5a, + 0x405b1d72, + 0x405b9d83, + 0x405c1d96, + 0x405c9da7, + 0x405d1db4, + 0x405d9dcb, + 0x405e1deb, + 0x405e8a95, + 0x405f1e0c, + 0x405f9e19, + 0x40601e27, + 0x40609e49, + 0x40611e71, + 0x40619e86, + 0x40621e9d, + 0x40629eae, + 0x40631ebf, + 0x40639ed4, + 0x40641eeb, + 0x40649efc, + 0x40651f17, + 0x40659f2e, + 0x40661f46, + 0x40669f70, + 0x40671f9b, + 0x40679fbc, + 0x40681fcf, + 0x40689ff0, + 0x40692022, + 0x4069a050, + 0x406a2071, + 0x406aa091, + 0x406b2219, + 0x406ba23c, + 0x406c2252, + 0x406ca47e, + 0x406d24ad, + 0x406da4d5, + 0x406e24ee, + 0x406ea506, + 0x406f2525, + 0x406fa53a, + 0x4070254d, + 0x4070a56a, + 0x40710800, + 0x4071a57c, + 0x4072258f, + 0x4072a5a8, + 0x407325c0, + 0x4073935c, + 0x407425d4, + 0x4074a5ee, + 0x407525ff, + 0x4075a613, + 0x40762621, + 0x40769199, + 0x40772646, + 0x4077a668, + 0x40782683, + 0x4078a698, + 0x407926af, + 0x4079a6c5, + 0x407a26d1, + 0x407aa6e4, + 0x407b26f9, + 0x407ba70b, + 0x407c2720, + 0x407ca729, + 0x407d200b, + 0x41f42144, + 0x41f921d6, + 0x41fe20c9, + 0x41fea2a5, + 0x41ff2396, + 0x4203215d, + 0x4208217f, + 0x4208a1bb, + 0x420920ad, + 0x4209a1f5, + 0x420a2104, + 0x420aa0e4, + 0x420b2124, + 0x420ba19d, + 0x420c23b2, + 0x420ca272, + 0x420d228c, + 0x420da2c3, + 0x421222dd, + 0x42172379, + 0x4217a31f, + 0x421c2341, + 0x421f22fc, + 0x422123c9, + 0x4226235c, + 0x422b2462, + 0x422ba42b, + 0x422c244a, + 0x422ca405, + 0x422d23e4, + 0x4432072b, + 0x4432873a, + 0x44330746, + 0x44338754, + 0x44340767, + 0x44348778, + 0x4435077f, + 0x44358789, + 0x4436079c, + 0x443687b2, + 0x443707c4, + 0x443787d1, + 0x443807e0, + 0x443887e8, + 0x44390800, + 0x4439880e, + 0x443a0821, + 0x4c3211c3, + 0x4c3291d3, + 0x4c3311e6, + 0x4c339206, + 0x4c3400ac, + 0x4c3480ea, + 0x4c351212, + 0x4c359220, + 0x4c36123c, + 0x4c36924f, + 0x4c37125e, + 0x4c37926c, + 0x4c381281, + 0x4c38928d, + 0x4c3912ad, + 0x4c3992d7, + 0x4c3a12f0, + 0x4c3a9309, + 0x4c3b05fb, + 0x4c3b9322, + 0x4c3c1334, + 0x4c3c9343, + 0x4c3d135c, + 0x4c3d936b, + 0x4c3e1378, + 0x503229a2, + 0x5032a9b1, + 0x503329bc, + 0x5033a9cc, + 0x503429e5, + 0x5034a9ff, + 0x50352a0d, + 0x5035aa23, + 0x50362a35, + 0x5036aa4b, + 0x50372a64, + 0x5037aa77, + 0x50382a8f, + 0x5038aaa0, + 0x50392ab5, + 0x5039aac9, + 0x503a2ae9, + 0x503aaaff, + 0x503b2b17, + 0x503bab29, + 0x503c2b45, + 0x503cab5c, + 0x503d2b75, + 0x503dab8b, + 0x503e2b98, + 0x503eabae, + 0x503f2bc0, + 0x503f8382, + 0x50402bd3, + 0x5040abe3, + 0x50412bfd, + 0x5041ac0c, + 0x50422c26, + 0x5042ac43, + 0x50432c53, + 0x5043ac63, + 0x50442c72, + 0x5044843f, + 0x50452c86, + 0x5045aca4, + 0x50462cb7, + 0x5046accd, + 0x50472cdf, + 0x5047acf4, + 0x50482d1a, + 0x5048ad28, + 0x50492d3b, + 0x5049ad50, + 0x504a2d66, + 0x504aad76, + 0x504b2d96, + 0x504bada9, + 0x504c2dcc, + 0x504cadfa, + 0x504d2e0c, + 0x504dae29, + 0x504e2e44, + 0x504eae60, + 0x504f2e72, + 0x504fae89, + 0x50502e98, + 0x505086ef, + 0x50512eab, + 0x58320eb8, + 0x68320e7a, + 0x68328c25, + 0x68330c38, + 0x68338e88, + 0x68340e98, + 0x683480ea, + 0x6c320e56, + 0x6c328bfc, + 0x6c330e61, + 0x74320a0b, + 0x78320970, + 0x78328985, + 0x78330991, + 0x78338083, + 0x783409a0, + 0x783489b5, + 0x783509d4, + 0x783589f6, + 0x78360a0b, + 0x78368a21, + 0x78370a31, + 0x78378a44, + 0x78380a57, + 0x78388a69, + 0x78390a76, + 0x78398a95, + 0x783a0aaa, + 0x783a8ab8, + 0x783b0ac2, + 0x783b8ad6, + 0x783c0aed, + 0x783c8b02, + 0x783d0b19, + 0x783d8b2e, + 0x783e0a84, + 0x7c3210c5, +}; + +const size_t kOpenSSLReasonValuesLen = sizeof(kOpenSSLReasonValues) / sizeof(kOpenSSLReasonValues[0]); + +const char kOpenSSLReasonStringData[] = + "ASN1_LENGTH_MISMATCH\0" + "AUX_ERROR\0" + "BAD_GET_ASN1_OBJECT_CALL\0" + "BAD_OBJECT_HEADER\0" + "BMPSTRING_IS_WRONG_LENGTH\0" + "BN_LIB\0" + "BOOLEAN_IS_WRONG_LENGTH\0" + "BUFFER_TOO_SMALL\0" + "CONTEXT_NOT_INITIALISED\0" + "DECODE_ERROR\0" + "DEPTH_EXCEEDED\0" + "DIGEST_AND_KEY_TYPE_NOT_SUPPORTED\0" + "ENCODE_ERROR\0" + "ERROR_GETTING_TIME\0" + "EXPECTING_AN_ASN1_SEQUENCE\0" + "EXPECTING_AN_INTEGER\0" + "EXPECTING_AN_OBJECT\0" + "EXPECTING_A_BOOLEAN\0" + "EXPECTING_A_TIME\0" + "EXPLICIT_LENGTH_MISMATCH\0" + "EXPLICIT_TAG_NOT_CONSTRUCTED\0" + "FIELD_MISSING\0" + "FIRST_NUM_TOO_LARGE\0" + "HEADER_TOO_LONG\0" + "ILLEGAL_BITSTRING_FORMAT\0" + "ILLEGAL_BOOLEAN\0" + "ILLEGAL_CHARACTERS\0" + "ILLEGAL_FORMAT\0" + "ILLEGAL_HEX\0" + "ILLEGAL_IMPLICIT_TAG\0" + "ILLEGAL_INTEGER\0" + "ILLEGAL_NESTED_TAGGING\0" + "ILLEGAL_NULL\0" + "ILLEGAL_NULL_VALUE\0" + "ILLEGAL_OBJECT\0" + "ILLEGAL_OPTIONAL_ANY\0" + "ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE\0" + "ILLEGAL_TAGGED_ANY\0" + "ILLEGAL_TIME_VALUE\0" + "INTEGER_NOT_ASCII_FORMAT\0" + "INTEGER_TOO_LARGE_FOR_LONG\0" + "INVALID_BIT_STRING_BITS_LEFT\0" + "INVALID_BMPSTRING_LENGTH\0" + "INVALID_DIGIT\0" + "INVALID_MODIFIER\0" + "INVALID_NUMBER\0" + "INVALID_OBJECT_ENCODING\0" + "INVALID_SEPARATOR\0" + "INVALID_TIME_FORMAT\0" + "INVALID_UNIVERSALSTRING_LENGTH\0" + "INVALID_UTF8STRING\0" + "LIST_ERROR\0" + "MISSING_ASN1_EOS\0" + "MISSING_EOC\0" + "MISSING_SECOND_NUMBER\0" + "MISSING_VALUE\0" + "MSTRING_NOT_UNIVERSAL\0" + "MSTRING_WRONG_TAG\0" + "NESTED_ASN1_ERROR\0" + "NESTED_ASN1_STRING\0" + "NON_HEX_CHARACTERS\0" + "NOT_ASCII_FORMAT\0" + "NOT_ENOUGH_DATA\0" + "NO_MATCHING_CHOICE_TYPE\0" + "NULL_IS_WRONG_LENGTH\0" + "OBJECT_NOT_ASCII_FORMAT\0" + "ODD_NUMBER_OF_CHARS\0" + "SECOND_NUMBER_TOO_LARGE\0" + "SEQUENCE_LENGTH_MISMATCH\0" + "SEQUENCE_NOT_CONSTRUCTED\0" + "SEQUENCE_OR_SET_NEEDS_CONFIG\0" + "SHORT_LINE\0" + "STREAMING_NOT_SUPPORTED\0" + "STRING_TOO_LONG\0" + "STRING_TOO_SHORT\0" + "TAG_VALUE_TOO_HIGH\0" + "TIME_NOT_ASCII_FORMAT\0" + "TOO_LONG\0" + "TYPE_NOT_CONSTRUCTED\0" + "TYPE_NOT_PRIMITIVE\0" + "UNEXPECTED_EOC\0" + "UNIVERSALSTRING_IS_WRONG_LENGTH\0" + "UNKNOWN_FORMAT\0" + "UNKNOWN_MESSAGE_DIGEST_ALGORITHM\0" + "UNKNOWN_SIGNATURE_ALGORITHM\0" + "UNKNOWN_TAG\0" + "UNSUPPORTED_ANY_DEFINED_BY_TYPE\0" + "UNSUPPORTED_PUBLIC_KEY_TYPE\0" + "UNSUPPORTED_TYPE\0" + "WRONG_PUBLIC_KEY_TYPE\0" + "WRONG_TAG\0" + "WRONG_TYPE\0" + "BAD_FOPEN_MODE\0" + "BROKEN_PIPE\0" + "CONNECT_ERROR\0" + "ERROR_SETTING_NBIO\0" + "INVALID_ARGUMENT\0" + "IN_USE\0" + "KEEPALIVE\0" + "NBIO_CONNECT_ERROR\0" + "NO_HOSTNAME_SPECIFIED\0" + "NO_PORT_SPECIFIED\0" + "NO_SUCH_FILE\0" + "NULL_PARAMETER\0" + "SYS_LIB\0" + "UNABLE_TO_CREATE_SOCKET\0" + "UNINITIALIZED\0" + "UNSUPPORTED_METHOD\0" + "WRITE_TO_READ_ONLY_BIO\0" + "ARG2_LT_ARG3\0" + "BAD_ENCODING\0" + "BAD_RECIPROCAL\0" + "BIGNUM_TOO_LONG\0" + "BITS_TOO_SMALL\0" + "CALLED_WITH_EVEN_MODULUS\0" + "DIV_BY_ZERO\0" + "EXPAND_ON_STATIC_BIGNUM_DATA\0" + "INPUT_NOT_REDUCED\0" + "INVALID_RANGE\0" + "NEGATIVE_NUMBER\0" + "NOT_A_SQUARE\0" + "NOT_INITIALIZED\0" + "NO_INVERSE\0" + "PRIVATE_KEY_TOO_LARGE\0" + "P_IS_NOT_PRIME\0" + "TOO_MANY_ITERATIONS\0" + "TOO_MANY_TEMPORARY_VARIABLES\0" + "AES_KEY_SETUP_FAILED\0" + "BAD_DECRYPT\0" + "BAD_KEY_LENGTH\0" + "CTRL_NOT_IMPLEMENTED\0" + "CTRL_OPERATION_NOT_IMPLEMENTED\0" + "DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH\0" + "INITIALIZATION_ERROR\0" + "INPUT_NOT_INITIALIZED\0" + "INVALID_AD_SIZE\0" + "INVALID_KEY_LENGTH\0" + "INVALID_NONCE_SIZE\0" + "INVALID_OPERATION\0" + "IV_TOO_LARGE\0" + "NO_CIPHER_SET\0" + "NO_DIRECTION_SET\0" + "OUTPUT_ALIASES_INPUT\0" + "TAG_TOO_LARGE\0" + "TOO_LARGE\0" + "UNSUPPORTED_AD_SIZE\0" + "UNSUPPORTED_INPUT_SIZE\0" + "UNSUPPORTED_KEY_SIZE\0" + "UNSUPPORTED_NONCE_SIZE\0" + "UNSUPPORTED_TAG_SIZE\0" + "WRONG_FINAL_BLOCK_LENGTH\0" + "LIST_CANNOT_BE_NULL\0" + "MISSING_CLOSE_SQUARE_BRACKET\0" + "MISSING_EQUAL_SIGN\0" + "NO_CLOSE_BRACE\0" + "UNABLE_TO_CREATE_NEW_SECTION\0" + "VARIABLE_HAS_NO_VALUE\0" + "BAD_GENERATOR\0" + "INVALID_PUBKEY\0" + "MODULUS_TOO_LARGE\0" + "NO_PRIVATE_VALUE\0" + "BAD_Q_VALUE\0" + "BAD_VERSION\0" + "MISSING_PARAMETERS\0" + "NEED_NEW_SETUP_VALUES\0" + "BIGNUM_OUT_OF_RANGE\0" + "COORDINATES_OUT_OF_RANGE\0" + "D2I_ECPKPARAMETERS_FAILURE\0" + "EC_GROUP_NEW_BY_NAME_FAILURE\0" + "GROUP2PKPARAMETERS_FAILURE\0" + "GROUP_MISMATCH\0" + "I2D_ECPKPARAMETERS_FAILURE\0" + "INCOMPATIBLE_OBJECTS\0" + "INVALID_COMPRESSED_POINT\0" + "INVALID_COMPRESSION_BIT\0" + "INVALID_ENCODING\0" + "INVALID_FIELD\0" + "INVALID_FORM\0" + "INVALID_GROUP_ORDER\0" + "INVALID_PRIVATE_KEY\0" + "MISSING_PRIVATE_KEY\0" + "NON_NAMED_CURVE\0" + "PKPARAMETERS2GROUP_FAILURE\0" + "POINT_AT_INFINITY\0" + "POINT_IS_NOT_ON_CURVE\0" + "SLOT_FULL\0" + "UNDEFINED_GENERATOR\0" + "UNKNOWN_GROUP\0" + "UNKNOWN_ORDER\0" + "WRONG_CURVE_PARAMETERS\0" + "WRONG_ORDER\0" + "KDF_FAILED\0" + "POINT_ARITHMETIC_FAILURE\0" + "BAD_SIGNATURE\0" + "NOT_IMPLEMENTED\0" + "RANDOM_NUMBER_GENERATION_FAILED\0" + "OPERATION_NOT_SUPPORTED\0" + "COMMAND_NOT_SUPPORTED\0" + "DIFFERENT_KEY_TYPES\0" + "DIFFERENT_PARAMETERS\0" + "EXPECTING_AN_EC_KEY_KEY\0" + "EXPECTING_AN_RSA_KEY\0" + "EXPECTING_A_DSA_KEY\0" + "ILLEGAL_OR_UNSUPPORTED_PADDING_MODE\0" + "INVALID_DIGEST_LENGTH\0" + "INVALID_DIGEST_TYPE\0" + "INVALID_KEYBITS\0" + "INVALID_MGF1_MD\0" + "INVALID_PADDING_MODE\0" + "INVALID_PSS_SALTLEN\0" + "KEYS_NOT_SET\0" + "NO_DEFAULT_DIGEST\0" + "NO_KEY_SET\0" + "NO_MDC2_SUPPORT\0" + "NO_NID_FOR_CURVE\0" + "NO_OPERATION_SET\0" + "NO_PARAMETERS_SET\0" + "OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE\0" + "OPERATON_NOT_INITIALIZED\0" + "UNKNOWN_PUBLIC_KEY_TYPE\0" + "UNSUPPORTED_ALGORITHM\0" + "OUTPUT_TOO_LARGE\0" + "UNKNOWN_NID\0" + "BAD_BASE64_DECODE\0" + "BAD_END_LINE\0" + "BAD_IV_CHARS\0" + "BAD_PASSWORD_READ\0" + "CIPHER_IS_NULL\0" + "ERROR_CONVERTING_PRIVATE_KEY\0" + "NOT_DEK_INFO\0" + "NOT_ENCRYPTED\0" + "NOT_PROC_TYPE\0" + "NO_START_LINE\0" + "READ_KEY\0" + "SHORT_HEADER\0" + "UNSUPPORTED_CIPHER\0" + "UNSUPPORTED_ENCRYPTION\0" + "BAD_PKCS12_DATA\0" + "BAD_PKCS12_VERSION\0" + "CIPHER_HAS_NO_OBJECT_IDENTIFIER\0" + "CRYPT_ERROR\0" + "ENCRYPT_ERROR\0" + "ERROR_SETTING_CIPHER_PARAMS\0" + "INCORRECT_PASSWORD\0" + "KEYGEN_FAILURE\0" + "KEY_GEN_ERROR\0" + "METHOD_NOT_SUPPORTED\0" + "MISSING_MAC\0" + "MULTIPLE_PRIVATE_KEYS_IN_PKCS12\0" + "PKCS12_PUBLIC_KEY_INTEGRITY_NOT_SUPPORTED\0" + "PKCS12_TOO_DEEPLY_NESTED\0" + "PRIVATE_KEY_DECODE_ERROR\0" + "PRIVATE_KEY_ENCODE_ERROR\0" + "UNKNOWN_ALGORITHM\0" + "UNKNOWN_CIPHER\0" + "UNKNOWN_CIPHER_ALGORITHM\0" + "UNKNOWN_DIGEST\0" + "UNKNOWN_HASH\0" + "UNSUPPORTED_PRIVATE_KEY_ALGORITHM\0" + "BAD_E_VALUE\0" + "BAD_FIXED_HEADER_DECRYPT\0" + "BAD_PAD_BYTE_COUNT\0" + "BAD_RSA_PARAMETERS\0" + "BLOCK_TYPE_IS_NOT_01\0" + "BN_NOT_INITIALIZED\0" + "CANNOT_RECOVER_MULTI_PRIME_KEY\0" + "CRT_PARAMS_ALREADY_GIVEN\0" + "CRT_VALUES_INCORRECT\0" + "DATA_LEN_NOT_EQUAL_TO_MOD_LEN\0" + "DATA_TOO_LARGE\0" + "DATA_TOO_LARGE_FOR_KEY_SIZE\0" + "DATA_TOO_LARGE_FOR_MODULUS\0" + "DATA_TOO_SMALL\0" + "DATA_TOO_SMALL_FOR_KEY_SIZE\0" + "DIGEST_TOO_BIG_FOR_RSA_KEY\0" + "D_E_NOT_CONGRUENT_TO_1\0" + "EMPTY_PUBLIC_KEY\0" + "FIRST_OCTET_INVALID\0" + "INCONSISTENT_SET_OF_CRT_VALUES\0" + "INTERNAL_ERROR\0" + "INVALID_MESSAGE_LENGTH\0" + "KEY_SIZE_TOO_SMALL\0" + "LAST_OCTET_INVALID\0" + "MUST_HAVE_AT_LEAST_TWO_PRIMES\0" + "NO_PUBLIC_EXPONENT\0" + "NULL_BEFORE_BLOCK_MISSING\0" + "N_NOT_EQUAL_P_Q\0" + "OAEP_DECODING_ERROR\0" + "ONLY_ONE_OF_P_Q_GIVEN\0" + "OUTPUT_BUFFER_TOO_SMALL\0" + "PADDING_CHECK_FAILED\0" + "PKCS_DECODING_ERROR\0" + "SLEN_CHECK_FAILED\0" + "SLEN_RECOVERY_FAILED\0" + "UNKNOWN_ALGORITHM_TYPE\0" + "UNKNOWN_PADDING_TYPE\0" + "VALUE_MISSING\0" + "WRONG_SIGNATURE_LENGTH\0" + "APP_DATA_IN_HANDSHAKE\0" + "ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT\0" + "BAD_ALERT\0" + "BAD_CHANGE_CIPHER_SPEC\0" + "BAD_DATA_RETURNED_BY_CALLBACK\0" + "BAD_DH_P_LENGTH\0" + "BAD_DIGEST_LENGTH\0" + "BAD_ECC_CERT\0" + "BAD_ECPOINT\0" + "BAD_HANDSHAKE_RECORD\0" + "BAD_HELLO_REQUEST\0" + "BAD_LENGTH\0" + "BAD_PACKET_LENGTH\0" + "BAD_RSA_ENCRYPT\0" + "BAD_SRTP_MKI_VALUE\0" + "BAD_SRTP_PROTECTION_PROFILE_LIST\0" + "BAD_SSL_FILETYPE\0" + "BAD_WRITE_RETRY\0" + "BIO_NOT_SET\0" + "CA_DN_LENGTH_MISMATCH\0" + "CA_DN_TOO_LONG\0" + "CCS_RECEIVED_EARLY\0" + "CERTIFICATE_VERIFY_FAILED\0" + "CERT_CB_ERROR\0" + "CERT_LENGTH_MISMATCH\0" + "CHANNEL_ID_NOT_P256\0" + "CHANNEL_ID_SIGNATURE_INVALID\0" + "CIPHER_OR_HASH_UNAVAILABLE\0" + "CLIENTHELLO_PARSE_FAILED\0" + "CLIENTHELLO_TLSEXT\0" + "CONNECTION_REJECTED\0" + "CONNECTION_TYPE_NOT_SET\0" + "CUSTOM_EXTENSION_ERROR\0" + "DATA_LENGTH_TOO_LONG\0" + "DECRYPTION_FAILED\0" + "DECRYPTION_FAILED_OR_BAD_RECORD_MAC\0" + "DH_PUBLIC_VALUE_LENGTH_IS_WRONG\0" + "DH_P_TOO_LONG\0" + "DIGEST_CHECK_FAILED\0" + "DTLS_MESSAGE_TOO_BIG\0" + "ECC_CERT_NOT_FOR_SIGNING\0" + "EMS_STATE_INCONSISTENT\0" + "ENCRYPTED_LENGTH_TOO_LONG\0" + "ERROR_ADDING_EXTENSION\0" + "ERROR_IN_RECEIVED_CIPHER_LIST\0" + "ERROR_PARSING_EXTENSION\0" + "EXCESSIVE_MESSAGE_SIZE\0" + "EXTRA_DATA_IN_MESSAGE\0" + "FRAGMENT_MISMATCH\0" + "GOT_NEXT_PROTO_WITHOUT_EXTENSION\0" + "HANDSHAKE_FAILURE_ON_CLIENT_HELLO\0" + "HTTPS_PROXY_REQUEST\0" + "HTTP_REQUEST\0" + "INAPPROPRIATE_FALLBACK\0" + "INVALID_COMMAND\0" + "INVALID_MESSAGE\0" + "INVALID_SSL_SESSION\0" + "INVALID_TICKET_KEYS_LENGTH\0" + "LENGTH_MISMATCH\0" + "LIBRARY_HAS_NO_CIPHERS\0" + "MISSING_EXTENSION\0" + "MISSING_RSA_CERTIFICATE\0" + "MISSING_TMP_DH_KEY\0" + "MISSING_TMP_ECDH_KEY\0" + "MIXED_SPECIAL_OPERATOR_WITH_GROUPS\0" + "MTU_TOO_SMALL\0" + "NEGOTIATED_BOTH_NPN_AND_ALPN\0" + "NESTED_GROUP\0" + "NO_CERTIFICATES_RETURNED\0" + "NO_CERTIFICATE_ASSIGNED\0" + "NO_CERTIFICATE_SET\0" + "NO_CIPHERS_AVAILABLE\0" + "NO_CIPHERS_PASSED\0" + "NO_CIPHER_MATCH\0" + "NO_COMPRESSION_SPECIFIED\0" + "NO_METHOD_SPECIFIED\0" + "NO_P256_SUPPORT\0" + "NO_PRIVATE_KEY_ASSIGNED\0" + "NO_RENEGOTIATION\0" + "NO_REQUIRED_DIGEST\0" + "NO_SHARED_CIPHER\0" + "NULL_SSL_CTX\0" + "NULL_SSL_METHOD_PASSED\0" + "OLD_SESSION_CIPHER_NOT_RETURNED\0" + "OLD_SESSION_VERSION_NOT_RETURNED\0" + "PARSE_TLSEXT\0" + "PATH_TOO_LONG\0" + "PEER_DID_NOT_RETURN_A_CERTIFICATE\0" + "PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE\0" + "PROTOCOL_IS_SHUTDOWN\0" + "PSK_IDENTITY_NOT_FOUND\0" + "PSK_NO_CLIENT_CB\0" + "PSK_NO_SERVER_CB\0" + "READ_TIMEOUT_EXPIRED\0" + "RECORD_LENGTH_MISMATCH\0" + "RECORD_TOO_LARGE\0" + "RENEGOTIATION_ENCODING_ERR\0" + "RENEGOTIATION_MISMATCH\0" + "REQUIRED_CIPHER_MISSING\0" + "RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION\0" + "RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION\0" + "SCSV_RECEIVED_WHEN_RENEGOTIATING\0" + "SERVERHELLO_TLSEXT\0" + "SESSION_ID_CONTEXT_UNINITIALIZED\0" + "SESSION_MAY_NOT_BE_CREATED\0" + "SHUTDOWN_WHILE_IN_INIT\0" + "SIGNATURE_ALGORITHMS_EXTENSION_SENT_BY_SERVER\0" + "SRTP_COULD_NOT_ALLOCATE_PROFILES\0" + "SRTP_UNKNOWN_PROTECTION_PROFILE\0" + "SSL3_EXT_INVALID_SERVERNAME\0" + "SSLV3_ALERT_BAD_CERTIFICATE\0" + "SSLV3_ALERT_BAD_RECORD_MAC\0" + "SSLV3_ALERT_CERTIFICATE_EXPIRED\0" + "SSLV3_ALERT_CERTIFICATE_REVOKED\0" + "SSLV3_ALERT_CERTIFICATE_UNKNOWN\0" + "SSLV3_ALERT_CLOSE_NOTIFY\0" + "SSLV3_ALERT_DECOMPRESSION_FAILURE\0" + "SSLV3_ALERT_HANDSHAKE_FAILURE\0" + "SSLV3_ALERT_ILLEGAL_PARAMETER\0" + "SSLV3_ALERT_NO_CERTIFICATE\0" + "SSLV3_ALERT_UNEXPECTED_MESSAGE\0" + "SSLV3_ALERT_UNSUPPORTED_CERTIFICATE\0" + "SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION\0" + "SSL_HANDSHAKE_FAILURE\0" + "SSL_SESSION_ID_CONTEXT_TOO_LONG\0" + "TLSV1_ALERT_ACCESS_DENIED\0" + "TLSV1_ALERT_DECODE_ERROR\0" + "TLSV1_ALERT_DECRYPTION_FAILED\0" + "TLSV1_ALERT_DECRYPT_ERROR\0" + "TLSV1_ALERT_EXPORT_RESTRICTION\0" + "TLSV1_ALERT_INAPPROPRIATE_FALLBACK\0" + "TLSV1_ALERT_INSUFFICIENT_SECURITY\0" + "TLSV1_ALERT_INTERNAL_ERROR\0" + "TLSV1_ALERT_NO_RENEGOTIATION\0" + "TLSV1_ALERT_PROTOCOL_VERSION\0" + "TLSV1_ALERT_RECORD_OVERFLOW\0" + "TLSV1_ALERT_UNKNOWN_CA\0" + "TLSV1_ALERT_USER_CANCELLED\0" + "TLSV1_BAD_CERTIFICATE_HASH_VALUE\0" + "TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE\0" + "TLSV1_CERTIFICATE_UNOBTAINABLE\0" + "TLSV1_UNRECOGNIZED_NAME\0" + "TLSV1_UNSUPPORTED_EXTENSION\0" + "TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST\0" + "TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG\0" + "TOO_MANY_EMPTY_FRAGMENTS\0" + "TOO_MANY_WARNING_ALERTS\0" + "UNABLE_TO_FIND_ECDH_PARAMETERS\0" + "UNEXPECTED_EXTENSION\0" + "UNEXPECTED_MESSAGE\0" + "UNEXPECTED_OPERATOR_IN_GROUP\0" + "UNEXPECTED_RECORD\0" + "UNKNOWN_ALERT_TYPE\0" + "UNKNOWN_CERTIFICATE_TYPE\0" + "UNKNOWN_CIPHER_RETURNED\0" + "UNKNOWN_CIPHER_TYPE\0" + "UNKNOWN_KEY_EXCHANGE_TYPE\0" + "UNKNOWN_PROTOCOL\0" + "UNKNOWN_SSL_VERSION\0" + "UNKNOWN_STATE\0" + "UNSAFE_LEGACY_RENEGOTIATION_DISABLED\0" + "UNSUPPORTED_COMPRESSION_ALGORITHM\0" + "UNSUPPORTED_ELLIPTIC_CURVE\0" + "UNSUPPORTED_PROTOCOL\0" + "WRONG_CERTIFICATE_TYPE\0" + "WRONG_CIPHER_RETURNED\0" + "WRONG_CURVE\0" + "WRONG_MESSAGE_TYPE\0" + "WRONG_SIGNATURE_TYPE\0" + "WRONG_SSL_VERSION\0" + "WRONG_VERSION_NUMBER\0" + "X509_LIB\0" + "X509_VERIFICATION_SETUP_PROBLEMS\0" + "AKID_MISMATCH\0" + "BAD_PKCS7_VERSION\0" + "BAD_X509_FILETYPE\0" + "BASE64_DECODE_ERROR\0" + "CANT_CHECK_DH_KEY\0" + "CERT_ALREADY_IN_HASH_TABLE\0" + "CRL_ALREADY_DELTA\0" + "CRL_VERIFY_FAILURE\0" + "IDP_MISMATCH\0" + "INVALID_DIRECTORY\0" + "INVALID_FIELD_NAME\0" + "INVALID_PSS_PARAMETERS\0" + "INVALID_TRUST\0" + "ISSUER_MISMATCH\0" + "KEY_TYPE_MISMATCH\0" + "KEY_VALUES_MISMATCH\0" + "LOADING_CERT_DIR\0" + "LOADING_DEFAULTS\0" + "NAME_TOO_LONG\0" + "NEWER_CRL_NOT_NEWER\0" + "NOT_PKCS7_SIGNED_DATA\0" + "NO_CERTIFICATES_INCLUDED\0" + "NO_CERT_SET_FOR_US_TO_VERIFY\0" + "NO_CRLS_INCLUDED\0" + "NO_CRL_NUMBER\0" + "PUBLIC_KEY_DECODE_ERROR\0" + "PUBLIC_KEY_ENCODE_ERROR\0" + "SHOULD_RETRY\0" + "UNKNOWN_KEY_TYPE\0" + "UNKNOWN_PURPOSE_ID\0" + "UNKNOWN_TRUST_ID\0" + "WRONG_LOOKUP_TYPE\0" + "BAD_IP_ADDRESS\0" + "BAD_OBJECT\0" + "BN_DEC2BN_ERROR\0" + "BN_TO_ASN1_INTEGER_ERROR\0" + "CANNOT_FIND_FREE_FUNCTION\0" + "DIRNAME_ERROR\0" + "DISTPOINT_ALREADY_SET\0" + "DUPLICATE_ZONE_ID\0" + "ERROR_CONVERTING_ZONE\0" + "ERROR_CREATING_EXTENSION\0" + "ERROR_IN_EXTENSION\0" + "EXPECTED_A_SECTION_NAME\0" + "EXTENSION_EXISTS\0" + "EXTENSION_NAME_ERROR\0" + "EXTENSION_NOT_FOUND\0" + "EXTENSION_SETTING_NOT_SUPPORTED\0" + "EXTENSION_VALUE_ERROR\0" + "ILLEGAL_EMPTY_EXTENSION\0" + "ILLEGAL_HEX_DIGIT\0" + "INCORRECT_POLICY_SYNTAX_TAG\0" + "INVALID_BOOLEAN_STRING\0" + "INVALID_EXTENSION_STRING\0" + "INVALID_MULTIPLE_RDNS\0" + "INVALID_NAME\0" + "INVALID_NULL_ARGUMENT\0" + "INVALID_NULL_NAME\0" + "INVALID_NULL_VALUE\0" + "INVALID_NUMBERS\0" + "INVALID_OBJECT_IDENTIFIER\0" + "INVALID_OPTION\0" + "INVALID_POLICY_IDENTIFIER\0" + "INVALID_PROXY_POLICY_SETTING\0" + "INVALID_PURPOSE\0" + "INVALID_SECTION\0" + "INVALID_SYNTAX\0" + "ISSUER_DECODE_ERROR\0" + "NEED_ORGANIZATION_AND_NUMBERS\0" + "NO_CONFIG_DATABASE\0" + "NO_ISSUER_CERTIFICATE\0" + "NO_ISSUER_DETAILS\0" + "NO_POLICY_IDENTIFIER\0" + "NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED\0" + "NO_PUBLIC_KEY\0" + "NO_SUBJECT_DETAILS\0" + "ODD_NUMBER_OF_DIGITS\0" + "OPERATION_NOT_DEFINED\0" + "OTHERNAME_ERROR\0" + "POLICY_LANGUAGE_ALREADY_DEFINED\0" + "POLICY_PATH_LENGTH\0" + "POLICY_PATH_LENGTH_ALREADY_DEFINED\0" + "POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY\0" + "SECTION_NOT_FOUND\0" + "UNABLE_TO_GET_ISSUER_DETAILS\0" + "UNABLE_TO_GET_ISSUER_KEYID\0" + "UNKNOWN_BIT_STRING_ARGUMENT\0" + "UNKNOWN_EXTENSION\0" + "UNKNOWN_EXTENSION_NAME\0" + "UNKNOWN_OPTION\0" + "UNSUPPORTED_OPTION\0" + "USER_TOO_LONG\0" + ""; +