1*6822f9c8Santon /* $OpenBSD: key_schedule.c,v 1.11 2024/08/23 12:56:26 anton Exp $ */ 2af2a35e9Sbeck /* 381a25c1eSbeck * Copyright (c) 2018-2019 Bob Beck <beck@openbsd.org> 4af2a35e9Sbeck * 5af2a35e9Sbeck * Permission to use, copy, modify, and distribute this software for any 6af2a35e9Sbeck * purpose with or without fee is hereby granted, provided that the above 7af2a35e9Sbeck * copyright notice and this permission notice appear in all copies. 8af2a35e9Sbeck * 9af2a35e9Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10af2a35e9Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11af2a35e9Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12af2a35e9Sbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13af2a35e9Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14af2a35e9Sbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15af2a35e9Sbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16af2a35e9Sbeck */ 17af2a35e9Sbeck 18af2a35e9Sbeck #include <err.h> 19af2a35e9Sbeck 20c9675a23Stb #include "ssl_local.h" 21af2a35e9Sbeck 22af2a35e9Sbeck #include "bytestring.h" 23af2a35e9Sbeck #include "ssl_tlsext.h" 24af2a35e9Sbeck #include "tls13_internal.h" 25af2a35e9Sbeck 26af2a35e9Sbeck static int failures = 0; 27af2a35e9Sbeck 28af2a35e9Sbeck static void 29af2a35e9Sbeck hexdump(const unsigned char *buf, size_t len) 30af2a35e9Sbeck { 31af2a35e9Sbeck size_t i; 32af2a35e9Sbeck 33af2a35e9Sbeck for (i = 1; i <= len; i++) 34af2a35e9Sbeck fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n"); 35af2a35e9Sbeck 36af2a35e9Sbeck fprintf(stderr, "\n"); 37af2a35e9Sbeck } 38af2a35e9Sbeck 39af2a35e9Sbeck static void 40af2a35e9Sbeck compare_data(const uint8_t *recv, size_t recv_len, const uint8_t *expect, 41af2a35e9Sbeck size_t expect_len) 42af2a35e9Sbeck { 43af2a35e9Sbeck fprintf(stderr, "received:\n"); 44af2a35e9Sbeck hexdump(recv, recv_len); 45af2a35e9Sbeck 46af2a35e9Sbeck fprintf(stderr, "test data:\n"); 47af2a35e9Sbeck hexdump(expect, expect_len); 48af2a35e9Sbeck } 49af2a35e9Sbeck 50af2a35e9Sbeck #define FAIL(msg, ...) \ 51af2a35e9Sbeck do { \ 52af2a35e9Sbeck fprintf(stderr, "[%s:%d] FAIL: ", __FILE__, __LINE__); \ 53af2a35e9Sbeck fprintf(stderr, msg, ##__VA_ARGS__); \ 54af2a35e9Sbeck failures++; \ 55af2a35e9Sbeck } while(0) 56af2a35e9Sbeck 57af2a35e9Sbeck /* Hashes and secrets from test vector */ 58af2a35e9Sbeck 59af2a35e9Sbeck uint8_t chello[] = { 60af2a35e9Sbeck 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 61af2a35e9Sbeck 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 62af2a35e9Sbeck 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 63af2a35e9Sbeck 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 64af2a35e9Sbeck }; 65af2a35e9Sbeck const struct tls13_secret chello_hash = { 66af2a35e9Sbeck .data = chello, 67af2a35e9Sbeck .len = 32, 68af2a35e9Sbeck }; 69af2a35e9Sbeck 70af2a35e9Sbeck uint8_t cshello [] = { 71af2a35e9Sbeck 0x86, 0x0c, 0x06, 0xed, 0xc0, 0x78, 0x58, 0xee, 72af2a35e9Sbeck 0x8e, 0x78, 0xf0, 0xe7, 0x42, 0x8c, 0x58, 0xed, 73af2a35e9Sbeck 0xd6, 0xb4, 0x3f, 0x2c, 0xa3, 0xe6, 0xe9, 0x5f, 74af2a35e9Sbeck 0x02, 0xed, 0x06, 0x3c, 0xf0, 0xe1, 0xca, 0xd8 75af2a35e9Sbeck }; 76af2a35e9Sbeck 77af2a35e9Sbeck const struct tls13_secret cshello_hash = { 78af2a35e9Sbeck .data = cshello, 79af2a35e9Sbeck .len = 32, 80af2a35e9Sbeck }; 81af2a35e9Sbeck 82af2a35e9Sbeck const uint8_t ecdhe [] = { 83af2a35e9Sbeck 0x8b, 0xd4, 0x05, 0x4f, 0xb5, 0x5b, 0x9d, 0x63, 84af2a35e9Sbeck 0xfd, 0xfb, 0xac, 0xf9, 0xf0, 0x4b, 0x9f, 0x0d, 85af2a35e9Sbeck 0x35, 0xe6, 0xd6, 0x3f, 0x53, 0x75, 0x63, 0xef, 86af2a35e9Sbeck 0xd4, 0x62, 0x72, 0x90, 0x0f, 0x89, 0x49, 0x2d 87af2a35e9Sbeck }; 88af2a35e9Sbeck 89d3914aeeSbeck uint8_t csfhello [] = { 90d3914aeeSbeck 0x96, 0x08, 0x10, 0x2a, 0x0f, 0x1c, 0xcc, 0x6d, 91d3914aeeSbeck 0xb6, 0x25, 0x0b, 0x7b, 0x7e, 0x41, 0x7b, 0x1a, 92d3914aeeSbeck 0x00, 0x0e, 0xaa, 0xda, 0x3d, 0xaa, 0xe4, 0x77, 93d3914aeeSbeck 0x7a, 0x76, 0x86, 0xc9, 0xff, 0x83, 0xdf, 0x13 94d3914aeeSbeck }; 95d3914aeeSbeck 96d3914aeeSbeck const struct tls13_secret csfhello_hash = { 97d3914aeeSbeck .data = csfhello, 98d3914aeeSbeck .len = 32, 99d3914aeeSbeck }; 100d3914aeeSbeck 101d3914aeeSbeck 102af2a35e9Sbeck /* Expected Values */ 103af2a35e9Sbeck 104af2a35e9Sbeck uint8_t expected_extracted_early[] = { 105af2a35e9Sbeck 0x33, 0xad, 0x0a, 0x1c, 0x60, 0x7e, 0xc0, 0x3b, 106af2a35e9Sbeck 0x09, 0xe6, 0xcd, 0x98, 0x93, 0x68, 0x0c, 0xe2, 107af2a35e9Sbeck 0x10, 0xad, 0xf3, 0x00, 0xaa, 0x1f, 0x26, 0x60, 108af2a35e9Sbeck 0xe1, 0xb2, 0x2e, 0x10, 0xf1, 0x70, 0xf9, 0x2a 109af2a35e9Sbeck }; 110af2a35e9Sbeck uint8_t expected_derived_early[] = { 111af2a35e9Sbeck 0x6f, 0x26, 0x15, 0xa1, 0x08, 0xc7, 0x02, 0xc5, 112af2a35e9Sbeck 0x67, 0x8f, 0x54, 0xfc, 0x9d, 0xba, 0xb6, 0x97, 113af2a35e9Sbeck 0x16, 0xc0, 0x76, 0x18, 0x9c, 0x48, 0x25, 0x0c, 114af2a35e9Sbeck 0xeb, 0xea, 0xc3, 0x57, 0x6c, 0x36, 0x11, 0xba 115af2a35e9Sbeck }; 116af2a35e9Sbeck uint8_t expected_extracted_handshake[] = { 117af2a35e9Sbeck 0x1d, 0xc8, 0x26, 0xe9, 0x36, 0x06, 0xaa, 0x6f, 118af2a35e9Sbeck 0xdc, 0x0a, 0xad, 0xc1, 0x2f, 0x74, 0x1b, 0x01, 119af2a35e9Sbeck 0x04, 0x6a, 0xa6, 0xb9, 0x9f, 0x69, 0x1e, 0xd2, 120af2a35e9Sbeck 0x21, 0xa9, 0xf0, 0xca, 0x04, 0x3f, 0xbe, 0xac 121af2a35e9Sbeck }; 122af2a35e9Sbeck uint8_t expected_client_handshake_traffic[] = { 123af2a35e9Sbeck 0xb3, 0xed, 0xdb, 0x12, 0x6e, 0x06, 0x7f, 0x35, 124af2a35e9Sbeck 0xa7, 0x80, 0xb3, 0xab, 0xf4, 0x5e, 0x2d, 0x8f, 125af2a35e9Sbeck 0x3b, 0x1a, 0x95, 0x07, 0x38, 0xf5, 0x2e, 0x96, 126af2a35e9Sbeck 0x00, 0x74, 0x6a, 0x0e, 0x27, 0xa5, 0x5a, 0x21 127af2a35e9Sbeck }; 128af2a35e9Sbeck 129af2a35e9Sbeck uint8_t expected_server_handshake_traffic[] = { 130af2a35e9Sbeck 0xb6, 0x7b, 0x7d, 0x69, 0x0c, 0xc1, 0x6c, 0x4e, 131af2a35e9Sbeck 0x75, 0xe5, 0x42, 0x13, 0xcb, 0x2d, 0x37, 0xb4, 132af2a35e9Sbeck 0xe9, 0xc9, 0x12, 0xbc, 0xde, 0xd9, 0x10, 0x5d, 133af2a35e9Sbeck 0x42, 0xbe, 0xfd, 0x59, 0xd3, 0x91, 0xad, 0x38 134af2a35e9Sbeck }; 135af2a35e9Sbeck 136af2a35e9Sbeck uint8_t expected_derived_handshake[] = { 137af2a35e9Sbeck 0x43, 0xde, 0x77, 0xe0, 0xc7, 0x77, 0x13, 0x85, 138af2a35e9Sbeck 0x9a, 0x94, 0x4d, 0xb9, 0xdb, 0x25, 0x90, 0xb5, 139af2a35e9Sbeck 0x31, 0x90, 0xa6, 0x5b, 0x3e, 0xe2, 0xe4, 0xf1, 140af2a35e9Sbeck 0x2d, 0xd7, 0xa0, 0xbb, 0x7c, 0xe2, 0x54, 0xb4 141af2a35e9Sbeck }; 142af2a35e9Sbeck 143af2a35e9Sbeck uint8_t expected_extracted_master[] = { 144af2a35e9Sbeck 0x18, 0xdf, 0x06, 0x84, 0x3d, 0x13, 0xa0, 0x8b, 145af2a35e9Sbeck 0xf2, 0xa4, 0x49, 0x84, 0x4c, 0x5f, 0x8a, 0x47, 146af2a35e9Sbeck 0x80, 0x01, 0xbc, 0x4d, 0x4c, 0x62, 0x79, 0x84, 147af2a35e9Sbeck 0xd5, 0xa4, 0x1d, 0xa8, 0xd0, 0x40, 0x29, 0x19 148af2a35e9Sbeck }; 149af2a35e9Sbeck 150d3914aeeSbeck uint8_t expected_server_application_traffic[] = { 151d3914aeeSbeck 0xa1, 0x1a, 0xf9, 0xf0, 0x55, 0x31, 0xf8, 0x56, 152d3914aeeSbeck 0xad, 0x47, 0x11, 0x6b, 0x45, 0xa9, 0x50, 0x32, 153d3914aeeSbeck 0x82, 0x04, 0xb4, 0xf4, 0x4b, 0xfb, 0x6b, 0x3a, 154d3914aeeSbeck 0x4b, 0x4f, 0x1f, 0x3f, 0xcb, 0x63, 0x16, 0x43 155d3914aeeSbeck }; 156d3914aeeSbeck 15781a25c1eSbeck uint8_t expected_server_application_traffic_updated[] = { 15881a25c1eSbeck 0x51, 0x92, 0x1b, 0x8a, 0xa3, 0x00, 0x19, 0x76, 15981a25c1eSbeck 0xeb, 0x40, 0x1d, 0x0a, 0x43, 0x19, 0xa8, 0x51, 16081a25c1eSbeck 0x64, 0x16, 0xa6, 0xc5, 0x60, 0x01, 0xa3, 0x57, 16181a25c1eSbeck 0xe5, 0xd1, 0x62, 0x03, 0x1e, 0x84, 0xf9, 0x16, 16281a25c1eSbeck }; 16381a25c1eSbeck 16481a25c1eSbeck uint8_t expected_client_application_traffic[] = { 16581a25c1eSbeck 0x9e, 0x40, 0x64, 0x6c, 0xe7, 0x9a, 0x7f, 0x9d, 16681a25c1eSbeck 0xc0, 0x5a, 0xf8, 0x88, 0x9b, 0xce, 0x65, 0x52, 16781a25c1eSbeck 0x87, 0x5a, 0xfa, 0x0b, 0x06, 0xdf, 0x00, 0x87, 16881a25c1eSbeck 0xf7, 0x92, 0xeb, 0xb7, 0xc1, 0x75, 0x04, 0xa5, 16981a25c1eSbeck }; 17081a25c1eSbeck 17181a25c1eSbeck uint8_t expected_client_application_traffic_updated[] = { 17281a25c1eSbeck 0xfc, 0xdf, 0xcc, 0x72, 0x72, 0x5a, 0xae, 0xe4, 17381a25c1eSbeck 0x8b, 0xf6, 0x4e, 0x4f, 0xd8, 0xb7, 0x49, 0xcd, 17481a25c1eSbeck 0xbd, 0xba, 0xb3, 0x9d, 0x90, 0xda, 0x0b, 0x26, 17581a25c1eSbeck 0xe2, 0x24, 0x5c, 0xa6, 0xea, 0x16, 0x72, 0x07, 17681a25c1eSbeck }; 17781a25c1eSbeck 178d3914aeeSbeck uint8_t expected_exporter_master[] = { 179d3914aeeSbeck 0xfe, 0x22, 0xf8, 0x81, 0x17, 0x6e, 0xda, 0x18, 180d3914aeeSbeck 0xeb, 0x8f, 0x44, 0x52, 0x9e, 0x67, 0x92, 0xc5, 181d3914aeeSbeck 0x0c, 0x9a, 0x3f, 0x89, 0x45, 0x2f, 0x68, 0xd8, 182d3914aeeSbeck 0xae, 0x31, 0x1b, 0x43, 0x09, 0xd3, 0xcf, 0x50 183d3914aeeSbeck }; 184d3914aeeSbeck 185f24fff32Sclaudio int 186f24fff32Sclaudio main (int argc, char **argv) 187f24fff32Sclaudio { 188af2a35e9Sbeck struct tls13_secrets *secrets; 189af2a35e9Sbeck 190caf5f745Sjsing if ((secrets = tls13_secrets_create(EVP_sha256(), 0)) == NULL) 191*6822f9c8Santon errx(1, "failed to create secrets"); 192af2a35e9Sbeck 193af2a35e9Sbeck secrets->insecure = 1; /* don't explicit_bzero when done */ 194af2a35e9Sbeck 195caf5f745Sjsing if (tls13_derive_handshake_secrets(secrets, ecdhe, 32, &cshello_hash)) 196af2a35e9Sbeck FAIL("derive_handshake_secrets worked when it shouldn't\n"); 197caf5f745Sjsing if (tls13_derive_application_secrets(secrets, 198af2a35e9Sbeck &chello_hash)) 199af2a35e9Sbeck FAIL("derive_application_secrets worked when it shouldn't\n"); 200af2a35e9Sbeck 201caf5f745Sjsing if (!tls13_derive_early_secrets(secrets, 202af2a35e9Sbeck secrets->zeros.data, secrets->zeros.len, &chello_hash)) 203af2a35e9Sbeck FAIL("derive_early_secrets failed\n"); 204caf5f745Sjsing if (tls13_derive_early_secrets(secrets, 205af2a35e9Sbeck secrets->zeros.data, secrets->zeros.len, &chello_hash)) 206af2a35e9Sbeck FAIL("derive_early_secrets worked when it shouldn't(2)\n"); 207af2a35e9Sbeck 208caf5f745Sjsing if (!tls13_derive_handshake_secrets(secrets, ecdhe, 32, &cshello_hash)) 209af2a35e9Sbeck FAIL("derive_handshake_secrets failed\n"); 210caf5f745Sjsing if (tls13_derive_handshake_secrets(secrets, ecdhe, 32, &cshello_hash)) 211af2a35e9Sbeck FAIL("derive_handshake_secrets worked when it shouldn't(2)\n"); 212af2a35e9Sbeck 213af2a35e9Sbeck /* XXX fix hash here once test vector sorted */ 214d3914aeeSbeck if (!tls13_derive_application_secrets(secrets, &csfhello_hash)) 215af2a35e9Sbeck FAIL("derive_application_secrets failed\n"); 216d3914aeeSbeck if (tls13_derive_application_secrets(secrets, &csfhello_hash)) 217af2a35e9Sbeck FAIL("derive_application_secrets worked when it " 218af2a35e9Sbeck "shouldn't(2)\n"); 219af2a35e9Sbeck 220af2a35e9Sbeck fprintf(stderr, "extracted_early:\n"); 221af2a35e9Sbeck compare_data(secrets->extracted_early.data, 32, 222af2a35e9Sbeck expected_extracted_early, 32); 223af2a35e9Sbeck if (memcmp(secrets->extracted_early.data, 224af2a35e9Sbeck expected_extracted_early, 32) != 0) 225af2a35e9Sbeck FAIL("extracted_early does not match\n"); 226af2a35e9Sbeck 227af2a35e9Sbeck fprintf(stderr, "derived_early:\n"); 228af2a35e9Sbeck compare_data(secrets->derived_early.data, 32, 229af2a35e9Sbeck expected_derived_early, 32); 230af2a35e9Sbeck if (memcmp(secrets->derived_early.data, 231af2a35e9Sbeck expected_derived_early, 32) != 0) 232af2a35e9Sbeck FAIL("derived_early does not match\n"); 233af2a35e9Sbeck 234af2a35e9Sbeck fprintf(stderr, "extracted_handshake:\n"); 235af2a35e9Sbeck compare_data(secrets->extracted_handshake.data, 32, 236af2a35e9Sbeck expected_extracted_handshake, 32); 237af2a35e9Sbeck if (memcmp(secrets->extracted_handshake.data, 238af2a35e9Sbeck expected_extracted_handshake, 32) != 0) 239af2a35e9Sbeck FAIL("extracted_handshake does not match\n"); 240af2a35e9Sbeck 241af2a35e9Sbeck fprintf(stderr, "client_handshake_traffic:\n"); 242af2a35e9Sbeck compare_data(secrets->client_handshake_traffic.data, 32, 243af2a35e9Sbeck expected_client_handshake_traffic, 32); 244af2a35e9Sbeck if (memcmp(secrets->client_handshake_traffic.data, 245af2a35e9Sbeck expected_client_handshake_traffic, 32) != 0) 246af2a35e9Sbeck FAIL("client_handshake_traffic does not match\n"); 247af2a35e9Sbeck 248af2a35e9Sbeck fprintf(stderr, "server_handshake_traffic:\n"); 249af2a35e9Sbeck compare_data(secrets->server_handshake_traffic.data, 32, 250af2a35e9Sbeck expected_server_handshake_traffic, 32); 251af2a35e9Sbeck if (memcmp(secrets->server_handshake_traffic.data, 252af2a35e9Sbeck expected_server_handshake_traffic, 32) != 0) 253af2a35e9Sbeck FAIL("server_handshake_traffic does not match\n"); 254af2a35e9Sbeck 255af2a35e9Sbeck fprintf(stderr, "derived_early:\n"); 256af2a35e9Sbeck compare_data(secrets->derived_early.data, 32, 257af2a35e9Sbeck expected_derived_early, 32); 258af2a35e9Sbeck if (memcmp(secrets->derived_early.data, 259af2a35e9Sbeck expected_derived_early, 32) != 0) 260af2a35e9Sbeck FAIL("derived_early does not match\n"); 261af2a35e9Sbeck 262af2a35e9Sbeck fprintf(stderr, "derived_handshake:\n"); 263af2a35e9Sbeck compare_data(secrets->derived_handshake.data, 32, 264af2a35e9Sbeck expected_derived_handshake, 32); 265af2a35e9Sbeck if (memcmp(secrets->derived_handshake.data, 266af2a35e9Sbeck expected_derived_handshake, 32) != 0) 267af2a35e9Sbeck FAIL("derived_handshake does not match\n"); 268af2a35e9Sbeck 269af2a35e9Sbeck fprintf(stderr, "extracted_master:\n"); 270af2a35e9Sbeck compare_data(secrets->extracted_master.data, 32, 271af2a35e9Sbeck expected_extracted_master, 32); 272af2a35e9Sbeck if (memcmp(secrets->extracted_master.data, 273af2a35e9Sbeck expected_extracted_master, 32) != 0) 274af2a35e9Sbeck FAIL("extracted_master does not match\n"); 275af2a35e9Sbeck 276d3914aeeSbeck fprintf(stderr, "server_application_traffic:\n"); 277d3914aeeSbeck compare_data(secrets->server_application_traffic.data, 32, 278d3914aeeSbeck expected_server_application_traffic, 32); 279d3914aeeSbeck if (memcmp(secrets->server_application_traffic.data, 280d3914aeeSbeck expected_server_application_traffic, 32) != 0) 281d3914aeeSbeck FAIL("server_application_traffic does not match\n"); 282d3914aeeSbeck 28381a25c1eSbeck fprintf(stderr, "client_application_traffic:\n"); 28481a25c1eSbeck compare_data(secrets->client_application_traffic.data, 32, 28581a25c1eSbeck expected_client_application_traffic, 32); 28681a25c1eSbeck if (memcmp(secrets->client_application_traffic.data, 28781a25c1eSbeck expected_client_application_traffic, 32) != 0) 28881a25c1eSbeck FAIL("server_application_traffic does not match\n"); 28981a25c1eSbeck 290d3914aeeSbeck fprintf(stderr, "exporter_master:\n"); 291d3914aeeSbeck compare_data(secrets->exporter_master.data, 32, 292d3914aeeSbeck expected_exporter_master, 32); 293d3914aeeSbeck if (memcmp(secrets->exporter_master.data, 294d3914aeeSbeck expected_exporter_master, 32) != 0) 295d3914aeeSbeck FAIL("exporter_master does not match\n"); 296d3914aeeSbeck 29781a25c1eSbeck tls13_update_server_traffic_secret(secrets); 29881a25c1eSbeck fprintf(stderr, "server_application_traffic after update:\n"); 29981a25c1eSbeck compare_data(secrets->server_application_traffic.data, 32, 30081a25c1eSbeck expected_server_application_traffic_updated, 32); 30181a25c1eSbeck if (memcmp(secrets->server_application_traffic.data, 30281a25c1eSbeck expected_server_application_traffic_updated, 32) != 0) 30381a25c1eSbeck FAIL("server_application_traffic does not match after update\n"); 30481a25c1eSbeck 30581a25c1eSbeck 30681a25c1eSbeck tls13_update_client_traffic_secret(secrets); 30781a25c1eSbeck fprintf(stderr, "client_application_traffic after update:\n"); 30881a25c1eSbeck compare_data(secrets->client_application_traffic.data, 32, 3092a57cb45Sbeck expected_client_application_traffic_updated, 32); 31081a25c1eSbeck if (memcmp(secrets->client_application_traffic.data, 31181a25c1eSbeck expected_client_application_traffic_updated, 32) != 0) 31281a25c1eSbeck FAIL("client_application_traffic does not match after update\n"); 31381a25c1eSbeck 314cb2e5b75Stb tls13_secrets_destroy(secrets); 315cb2e5b75Stb 316caf5f745Sjsing return failures; 317af2a35e9Sbeck } 318