1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com> 5d4afb5ceSopenharmony_ci * 6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to 8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the 9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is 11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions: 12d4afb5ceSopenharmony_ci * 13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in 14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software. 15d4afb5ceSopenharmony_ci * 16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22d4afb5ceSopenharmony_ci * IN THE SOFTWARE. 23d4afb5ceSopenharmony_ci * 24d4afb5ceSopenharmony_ci * JOSE-specific JWK code 25d4afb5ceSopenharmony_ci */ 26d4afb5ceSopenharmony_ci 27d4afb5ceSopenharmony_ci#include "private-lib-core.h" 28d4afb5ceSopenharmony_ci#include "private-lib-jose.h" 29d4afb5ceSopenharmony_ci 30d4afb5ceSopenharmony_ci#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) 31d4afb5ceSopenharmony_ci#include <fcntl.h> 32d4afb5ceSopenharmony_ci#endif 33d4afb5ceSopenharmony_ci 34d4afb5ceSopenharmony_cistatic const char * const kty_names[] = { 35d4afb5ceSopenharmony_ci "unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */ 36d4afb5ceSopenharmony_ci "oct", /* LWS_GENCRYPTO_KTY_OCT */ 37d4afb5ceSopenharmony_ci "RSA", /* LWS_GENCRYPTO_KTY_RSA */ 38d4afb5ceSopenharmony_ci "EC" /* LWS_GENCRYPTO_KTY_EC */ 39d4afb5ceSopenharmony_ci}; 40d4afb5ceSopenharmony_ci 41d4afb5ceSopenharmony_ci/* 42d4afb5ceSopenharmony_ci * These are the entire legal token set for names in jwk. 43d4afb5ceSopenharmony_ci * 44d4afb5ceSopenharmony_ci * The first version is used to parse a detached single jwk that don't have any 45d4afb5ceSopenharmony_ci * parent JSON context. The second version is used to parse full jwk objects 46d4afb5ceSopenharmony_ci * that has a "keys": [ ] array containing the keys. 47d4afb5ceSopenharmony_ci */ 48d4afb5ceSopenharmony_ci 49d4afb5ceSopenharmony_ciconst char * const jwk_tok[] = { 50d4afb5ceSopenharmony_ci "keys[]", /* dummy */ 51d4afb5ceSopenharmony_ci "e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */ 52d4afb5ceSopenharmony_ci "kty", /* generic */ 53d4afb5ceSopenharmony_ci "k", /* symmetric key data */ 54d4afb5ceSopenharmony_ci "crv", "x", "y", /* EC (also "D") */ 55d4afb5ceSopenharmony_ci "kid", /* generic */ 56d4afb5ceSopenharmony_ci "use" /* mutually exclusive with "key_ops" */, 57d4afb5ceSopenharmony_ci "key_ops" /* mutually exclusive with "use" */, 58d4afb5ceSopenharmony_ci "x5c", /* generic */ 59d4afb5ceSopenharmony_ci "alg" /* generic */ 60d4afb5ceSopenharmony_ci}, * const jwk_outer_tok[] = { 61d4afb5ceSopenharmony_ci "keys[]", 62d4afb5ceSopenharmony_ci "keys[].e", "keys[].n", "keys[].d", "keys[].p", "keys[].q", "keys[].dp", 63d4afb5ceSopenharmony_ci "keys[].dq", "keys[].qi", 64d4afb5ceSopenharmony_ci 65d4afb5ceSopenharmony_ci "keys[].kty", "keys[].k", /* generic */ 66d4afb5ceSopenharmony_ci "keys[].crv", "keys[].x", "keys[].y", /* EC (also "D") */ 67d4afb5ceSopenharmony_ci "keys[].kid", "keys[].use" /* mutually exclusive with "key_ops" */, 68d4afb5ceSopenharmony_ci "keys[].key_ops", /* mutually exclusive with "use" */ 69d4afb5ceSopenharmony_ci "keys[].x5c", "keys[].alg" 70d4afb5ceSopenharmony_ci}; 71d4afb5ceSopenharmony_ci 72d4afb5ceSopenharmony_cistatic unsigned short tok_map[] = { 73d4afb5ceSopenharmony_ci F_RSA | F_EC | F_OCT | F_META | 0xff, 74d4afb5ceSopenharmony_ci F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E, 75d4afb5ceSopenharmony_ci F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N, 76d4afb5ceSopenharmony_ci F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D, 77d4afb5ceSopenharmony_ci F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P, 78d4afb5ceSopenharmony_ci F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q, 79d4afb5ceSopenharmony_ci F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP, 80d4afb5ceSopenharmony_ci F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ, 81d4afb5ceSopenharmony_ci F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI, 82d4afb5ceSopenharmony_ci 83d4afb5ceSopenharmony_ci F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY, 84d4afb5ceSopenharmony_ci F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K, 85d4afb5ceSopenharmony_ci 86d4afb5ceSopenharmony_ci F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV, 87d4afb5ceSopenharmony_ci F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X, 88d4afb5ceSopenharmony_ci F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y, 89d4afb5ceSopenharmony_ci 90d4afb5ceSopenharmony_ci F_RSA | F_EC | F_OCT | F_META | JWK_META_KID, 91d4afb5ceSopenharmony_ci F_RSA | F_EC | F_OCT | F_META | JWK_META_USE, 92d4afb5ceSopenharmony_ci 93d4afb5ceSopenharmony_ci F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS, 94d4afb5ceSopenharmony_ci F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C, 95d4afb5ceSopenharmony_ci F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG, 96d4afb5ceSopenharmony_ci}; 97d4afb5ceSopenharmony_ci 98d4afb5ceSopenharmony_cistruct lexico { 99d4afb5ceSopenharmony_ci const char *name; 100d4afb5ceSopenharmony_ci int idx; 101d4afb5ceSopenharmony_ci char meta; 102d4afb5ceSopenharmony_ci} lexico_ec[] = { 103d4afb5ceSopenharmony_ci { "alg", JWK_META_ALG, 1 }, 104d4afb5ceSopenharmony_ci { "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 }, 105d4afb5ceSopenharmony_ci { "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 }, 106d4afb5ceSopenharmony_ci { "key_ops", JWK_META_KEY_OPS, 1 }, 107d4afb5ceSopenharmony_ci { "kid", JWK_META_KID, 1 }, 108d4afb5ceSopenharmony_ci { "kty", JWK_META_KTY, 1 }, 109d4afb5ceSopenharmony_ci { "use", JWK_META_USE, 1 }, 110d4afb5ceSopenharmony_ci { "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 }, 111d4afb5ceSopenharmony_ci { "x5c", JWK_META_X5C, 1 }, 112d4afb5ceSopenharmony_ci { "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 } 113d4afb5ceSopenharmony_ci}, lexico_oct[] = { 114d4afb5ceSopenharmony_ci { "alg", JWK_META_ALG, 1 }, 115d4afb5ceSopenharmony_ci { "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 }, 116d4afb5ceSopenharmony_ci { "key_ops", JWK_META_KEY_OPS, 1 }, 117d4afb5ceSopenharmony_ci { "kid", JWK_META_KID, 1 }, 118d4afb5ceSopenharmony_ci { "kty", JWK_META_KTY, 1 }, 119d4afb5ceSopenharmony_ci { "use", JWK_META_USE, 1 }, 120d4afb5ceSopenharmony_ci { "x5c", JWK_META_X5C, 1 } 121d4afb5ceSopenharmony_ci}, lexico_rsa[] = { 122d4afb5ceSopenharmony_ci { "alg", JWK_META_ALG, 1 }, 123d4afb5ceSopenharmony_ci { "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 }, 124d4afb5ceSopenharmony_ci { "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 }, 125d4afb5ceSopenharmony_ci { "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 }, 126d4afb5ceSopenharmony_ci { "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 }, 127d4afb5ceSopenharmony_ci { "key_ops", JWK_META_KEY_OPS, 1 }, 128d4afb5ceSopenharmony_ci { "kid", JWK_META_KID, 1 }, 129d4afb5ceSopenharmony_ci { "kty", JWK_META_KTY, 1 }, 130d4afb5ceSopenharmony_ci { "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 }, 131d4afb5ceSopenharmony_ci { "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 }, 132d4afb5ceSopenharmony_ci { "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 }, 133d4afb5ceSopenharmony_ci { "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 }, 134d4afb5ceSopenharmony_ci { "use", JWK_META_USE, 1 }, 135d4afb5ceSopenharmony_ci { "x5c", JWK_META_X5C, 1 } 136d4afb5ceSopenharmony_ci}; 137d4afb5ceSopenharmony_ci 138d4afb5ceSopenharmony_cistatic int 139d4afb5ceSopenharmony_ci_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len) 140d4afb5ceSopenharmony_ci{ 141d4afb5ceSopenharmony_ci size_t dec_size = (unsigned int)lws_base64_size(len); 142d4afb5ceSopenharmony_ci int n; 143d4afb5ceSopenharmony_ci 144d4afb5ceSopenharmony_ci e->buf = lws_malloc(dec_size, "jwk"); 145d4afb5ceSopenharmony_ci if (!e->buf) 146d4afb5ceSopenharmony_ci return -1; 147d4afb5ceSopenharmony_ci 148d4afb5ceSopenharmony_ci /* same decoder accepts both url or original styles */ 149d4afb5ceSopenharmony_ci 150d4afb5ceSopenharmony_ci n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1); 151d4afb5ceSopenharmony_ci if (n < 0) 152d4afb5ceSopenharmony_ci return -1; 153d4afb5ceSopenharmony_ci e->len = (uint32_t)n; 154d4afb5ceSopenharmony_ci 155d4afb5ceSopenharmony_ci return 0; 156d4afb5ceSopenharmony_ci} 157d4afb5ceSopenharmony_ci 158d4afb5ceSopenharmony_cistatic int 159d4afb5ceSopenharmony_ci_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len) 160d4afb5ceSopenharmony_ci{ 161d4afb5ceSopenharmony_ci size_t dec_size = (size_t)lws_base64_size(len); 162d4afb5ceSopenharmony_ci int n; 163d4afb5ceSopenharmony_ci 164d4afb5ceSopenharmony_ci e->buf = lws_malloc(dec_size, "jwk"); 165d4afb5ceSopenharmony_ci if (!e->buf) 166d4afb5ceSopenharmony_ci return -1; 167d4afb5ceSopenharmony_ci 168d4afb5ceSopenharmony_ci /* same decoder accepts both url or original styles */ 169d4afb5ceSopenharmony_ci 170d4afb5ceSopenharmony_ci n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1); 171d4afb5ceSopenharmony_ci if (n < 0) 172d4afb5ceSopenharmony_ci return -1; 173d4afb5ceSopenharmony_ci e->len = (uint32_t)n; 174d4afb5ceSopenharmony_ci 175d4afb5ceSopenharmony_ci return 0; 176d4afb5ceSopenharmony_ci} 177d4afb5ceSopenharmony_ci 178d4afb5ceSopenharmony_ci 179d4afb5ceSopenharmony_cisigned char 180d4afb5ceSopenharmony_cicb_jwk(struct lejp_ctx *ctx, char reason) 181d4afb5ceSopenharmony_ci{ 182d4afb5ceSopenharmony_ci struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user; 183d4afb5ceSopenharmony_ci struct lws_jwk *jwk = jps->jwk; 184d4afb5ceSopenharmony_ci unsigned int idx, n; 185d4afb5ceSopenharmony_ci unsigned short poss; 186d4afb5ceSopenharmony_ci char dotstar[64]; 187d4afb5ceSopenharmony_ci 188d4afb5ceSopenharmony_ci if (reason == LEJPCB_VAL_STR_START) 189d4afb5ceSopenharmony_ci jps->pos = 0; 190d4afb5ceSopenharmony_ci 191d4afb5ceSopenharmony_ci if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1) 192d4afb5ceSopenharmony_ci /* 193d4afb5ceSopenharmony_ci * new keys[] member is starting 194d4afb5ceSopenharmony_ci * 195d4afb5ceSopenharmony_ci * Until we see some JSON names, it could be anything... 196d4afb5ceSopenharmony_ci * there is no requirement for kty to be given first and eg, 197d4afb5ceSopenharmony_ci * ACME specifies the keys must be ordered in lexographic 198d4afb5ceSopenharmony_ci * order - where kty is not first. 199d4afb5ceSopenharmony_ci */ 200d4afb5ceSopenharmony_ci jps->possible = F_RSA | F_EC | F_OCT; 201d4afb5ceSopenharmony_ci 202d4afb5ceSopenharmony_ci if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) { 203d4afb5ceSopenharmony_ci /* we completed parsing a key */ 204d4afb5ceSopenharmony_ci if (jps->per_key_cb && jps->possible) { 205d4afb5ceSopenharmony_ci if (jps->per_key_cb(jps->jwk, jps->user)) { 206d4afb5ceSopenharmony_ci 207d4afb5ceSopenharmony_ci lwsl_notice("%s: user cb halts import\n", 208d4afb5ceSopenharmony_ci __func__); 209d4afb5ceSopenharmony_ci 210d4afb5ceSopenharmony_ci return -2; 211d4afb5ceSopenharmony_ci } 212d4afb5ceSopenharmony_ci 213d4afb5ceSopenharmony_ci /* clear it down */ 214d4afb5ceSopenharmony_ci lws_jwk_destroy(jps->jwk); 215d4afb5ceSopenharmony_ci jps->possible = 0; 216d4afb5ceSopenharmony_ci } 217d4afb5ceSopenharmony_ci } 218d4afb5ceSopenharmony_ci 219d4afb5ceSopenharmony_ci if (reason == LEJPCB_COMPLETE) { 220d4afb5ceSopenharmony_ci 221d4afb5ceSopenharmony_ci /* 222d4afb5ceSopenharmony_ci * Now we saw the whole jwk and know the key type, let'jwk insist 223d4afb5ceSopenharmony_ci * that as a whole, it must be consistent and complete. 224d4afb5ceSopenharmony_ci * 225d4afb5ceSopenharmony_ci * The tracking of ->possible bits from even before we know the 226d4afb5ceSopenharmony_ci * kty already makes certain we cannot have key element members 227d4afb5ceSopenharmony_ci * defined that are inconsistent with the key type. 228d4afb5ceSopenharmony_ci */ 229d4afb5ceSopenharmony_ci 230d4afb5ceSopenharmony_ci for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++) 231d4afb5ceSopenharmony_ci /* 232d4afb5ceSopenharmony_ci * All mandataory elements for the key type 233d4afb5ceSopenharmony_ci * must be present 234d4afb5ceSopenharmony_ci */ 235d4afb5ceSopenharmony_ci if ((tok_map[n] & jps->possible) && ( 236d4afb5ceSopenharmony_ci ((tok_map[n] & (F_M | F_META)) == (F_M | F_META) && 237d4afb5ceSopenharmony_ci !jwk->meta[tok_map[n] & 0xff].buf) || 238d4afb5ceSopenharmony_ci ((tok_map[n] & (F_M | F_META)) == F_M && 239d4afb5ceSopenharmony_ci !jwk->e[tok_map[n] & 0xff].buf))) { 240d4afb5ceSopenharmony_ci lwsl_notice("%s: missing %s\n", __func__, 241d4afb5ceSopenharmony_ci jwk_tok[n]); 242d4afb5ceSopenharmony_ci return -3; 243d4afb5ceSopenharmony_ci } 244d4afb5ceSopenharmony_ci 245d4afb5ceSopenharmony_ci /* 246d4afb5ceSopenharmony_ci * When the key may be public or public + private, ensure the 247d4afb5ceSopenharmony_ci * intra-key members related to that are consistent. 248d4afb5ceSopenharmony_ci * 249d4afb5ceSopenharmony_ci * Only RSA keys need extra care, since EC keys are already 250d4afb5ceSopenharmony_ci * confirmed by making CRV, X and Y mandatory and only D 251d4afb5ceSopenharmony_ci * (the singular private part) optional. For RSA, N and E are 252d4afb5ceSopenharmony_ci * also already known to be present using mandatory checking. 253d4afb5ceSopenharmony_ci */ 254d4afb5ceSopenharmony_ci 255d4afb5ceSopenharmony_ci /* 256d4afb5ceSopenharmony_ci * If a private key, it must have all D, P and Q. Public key 257d4afb5ceSopenharmony_ci * must have none of them. 258d4afb5ceSopenharmony_ci */ 259d4afb5ceSopenharmony_ci if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && 260d4afb5ceSopenharmony_ci !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) && 261d4afb5ceSopenharmony_ci (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) && 262d4afb5ceSopenharmony_ci (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) || 263d4afb5ceSopenharmony_ci (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && 264d4afb5ceSopenharmony_ci jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf && 265d4afb5ceSopenharmony_ci jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) 266d4afb5ceSopenharmony_ci ) { 267d4afb5ceSopenharmony_ci lwsl_notice("%s: RSA requires D, P and Q for private\n", 268d4afb5ceSopenharmony_ci __func__); 269d4afb5ceSopenharmony_ci return -3; 270d4afb5ceSopenharmony_ci } 271d4afb5ceSopenharmony_ci 272d4afb5ceSopenharmony_ci /* 273d4afb5ceSopenharmony_ci * If the precomputed private key terms appear, they must all 274d4afb5ceSopenharmony_ci * appear together. 275d4afb5ceSopenharmony_ci */ 276d4afb5ceSopenharmony_ci if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && 277d4afb5ceSopenharmony_ci !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) && 278d4afb5ceSopenharmony_ci (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) && 279d4afb5ceSopenharmony_ci (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) || 280d4afb5ceSopenharmony_ci (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf && 281d4afb5ceSopenharmony_ci jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf && 282d4afb5ceSopenharmony_ci jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) 283d4afb5ceSopenharmony_ci ) { 284d4afb5ceSopenharmony_ci lwsl_notice("%s: RSA DP, DQ, QI must all appear " 285d4afb5ceSopenharmony_ci "or none\n", __func__); 286d4afb5ceSopenharmony_ci return -3; 287d4afb5ceSopenharmony_ci } 288d4afb5ceSopenharmony_ci 289d4afb5ceSopenharmony_ci /* 290d4afb5ceSopenharmony_ci * The precomputed private key terms must not appear without 291d4afb5ceSopenharmony_ci * the private key itself also appearing. 292d4afb5ceSopenharmony_ci */ 293d4afb5ceSopenharmony_ci if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && 294d4afb5ceSopenharmony_ci !jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && 295d4afb5ceSopenharmony_ci jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) { 296d4afb5ceSopenharmony_ci lwsl_notice("%s: RSA DP, DQ, QI can appear only with " 297d4afb5ceSopenharmony_ci "private key\n", __func__); 298d4afb5ceSopenharmony_ci return -3; 299d4afb5ceSopenharmony_ci } 300d4afb5ceSopenharmony_ci 301d4afb5ceSopenharmony_ci if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA || 302d4afb5ceSopenharmony_ci jwk->kty == LWS_GENCRYPTO_KTY_EC) && 303d4afb5ceSopenharmony_ci jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) 304d4afb5ceSopenharmony_ci jwk->private_key = 1; 305d4afb5ceSopenharmony_ci } 306d4afb5ceSopenharmony_ci 307d4afb5ceSopenharmony_ci if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) 308d4afb5ceSopenharmony_ci return 0; 309d4afb5ceSopenharmony_ci 310d4afb5ceSopenharmony_ci if (ctx->path_match == 0 + 1) 311d4afb5ceSopenharmony_ci return 0; 312d4afb5ceSopenharmony_ci 313d4afb5ceSopenharmony_ci idx = tok_map[ctx->path_match - 1]; 314d4afb5ceSopenharmony_ci if ((idx & 0xff) == 0xff) 315d4afb5ceSopenharmony_ci return 0; 316d4afb5ceSopenharmony_ci 317d4afb5ceSopenharmony_ci switch (idx) { 318d4afb5ceSopenharmony_ci /* note: kty is not necessarily first... we have to keep track of 319d4afb5ceSopenharmony_ci * what could match given which element names have already been 320d4afb5ceSopenharmony_ci * seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk 321d4afb5ceSopenharmony_ci * not trying to tell us that it'jwk RSA now when we saw a "crv" 322d4afb5ceSopenharmony_ci * earlier) and then reduce the possibilities to just the one that 323d4afb5ceSopenharmony_ci * kty told. */ 324d4afb5ceSopenharmony_ci case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY: 325d4afb5ceSopenharmony_ci 326d4afb5ceSopenharmony_ci if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) { 327d4afb5ceSopenharmony_ci if (!(jps->possible & F_OCT)) 328d4afb5ceSopenharmony_ci goto elements_mismatch; 329d4afb5ceSopenharmony_ci jwk->kty = LWS_GENCRYPTO_KTY_OCT; 330d4afb5ceSopenharmony_ci jps->possible = F_OCT; 331d4afb5ceSopenharmony_ci goto cont; 332d4afb5ceSopenharmony_ci } 333d4afb5ceSopenharmony_ci if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) { 334d4afb5ceSopenharmony_ci if (!(jps->possible & F_RSA)) 335d4afb5ceSopenharmony_ci goto elements_mismatch; 336d4afb5ceSopenharmony_ci jwk->kty = LWS_GENCRYPTO_KTY_RSA; 337d4afb5ceSopenharmony_ci jps->possible = F_RSA; 338d4afb5ceSopenharmony_ci goto cont; 339d4afb5ceSopenharmony_ci } 340d4afb5ceSopenharmony_ci if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) { 341d4afb5ceSopenharmony_ci if (!(jps->possible & F_EC)) 342d4afb5ceSopenharmony_ci goto elements_mismatch; 343d4afb5ceSopenharmony_ci jwk->kty = LWS_GENCRYPTO_KTY_EC; 344d4afb5ceSopenharmony_ci jps->possible = F_EC; 345d4afb5ceSopenharmony_ci goto cont; 346d4afb5ceSopenharmony_ci } 347d4afb5ceSopenharmony_ci lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); 348d4afb5ceSopenharmony_ci lwsl_err("%s: Unknown KTY '%s'\n", __func__, dotstar); 349d4afb5ceSopenharmony_ci return -1; 350d4afb5ceSopenharmony_ci 351d4afb5ceSopenharmony_ci default: 352d4afb5ceSopenharmony_cicont: 353d4afb5ceSopenharmony_ci if (jps->pos + ctx->npos >= (int)sizeof(jps->b64)) 354d4afb5ceSopenharmony_ci goto bail; 355d4afb5ceSopenharmony_ci 356d4afb5ceSopenharmony_ci memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos); 357d4afb5ceSopenharmony_ci jps->pos += ctx->npos; 358d4afb5ceSopenharmony_ci 359d4afb5ceSopenharmony_ci if (reason == LEJPCB_VAL_STR_CHUNK) 360d4afb5ceSopenharmony_ci return 0; 361d4afb5ceSopenharmony_ci 362d4afb5ceSopenharmony_ci /* chunking has been collated */ 363d4afb5ceSopenharmony_ci 364d4afb5ceSopenharmony_ci poss = idx & (F_RSA | F_EC | F_OCT); 365d4afb5ceSopenharmony_ci jps->possible &= poss; 366d4afb5ceSopenharmony_ci if (!jps->possible) 367d4afb5ceSopenharmony_ci goto elements_mismatch; 368d4afb5ceSopenharmony_ci 369d4afb5ceSopenharmony_ci if (idx & F_META) { 370d4afb5ceSopenharmony_ci if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f], 371d4afb5ceSopenharmony_ci jps->b64, (unsigned int)jps->pos) < 0) 372d4afb5ceSopenharmony_ci goto bail; 373d4afb5ceSopenharmony_ci 374d4afb5ceSopenharmony_ci break; 375d4afb5ceSopenharmony_ci } 376d4afb5ceSopenharmony_ci 377d4afb5ceSopenharmony_ci if (idx & F_B64U) { 378d4afb5ceSopenharmony_ci /* key data... do the base64 decode as needed */ 379d4afb5ceSopenharmony_ci if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f], 380d4afb5ceSopenharmony_ci jps->b64, jps->pos) < 0) 381d4afb5ceSopenharmony_ci goto bail; 382d4afb5ceSopenharmony_ci 383d4afb5ceSopenharmony_ci if (jwk->e[idx & 0x7f].len > 384d4afb5ceSopenharmony_ci LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) { 385d4afb5ceSopenharmony_ci lwsl_notice("%s: oversize keydata\n", __func__); 386d4afb5ceSopenharmony_ci goto bail; 387d4afb5ceSopenharmony_ci } 388d4afb5ceSopenharmony_ci 389d4afb5ceSopenharmony_ci return 0; 390d4afb5ceSopenharmony_ci } 391d4afb5ceSopenharmony_ci 392d4afb5ceSopenharmony_ci if (idx & F_B64) { 393d4afb5ceSopenharmony_ci 394d4afb5ceSopenharmony_ci /* cert data... do non-urlcoded base64 decode */ 395d4afb5ceSopenharmony_ci if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f], 396d4afb5ceSopenharmony_ci jps->b64, jps->pos) < 0) 397d4afb5ceSopenharmony_ci goto bail; 398d4afb5ceSopenharmony_ci return 0; 399d4afb5ceSopenharmony_ci } 400d4afb5ceSopenharmony_ci 401d4afb5ceSopenharmony_ci if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f], 402d4afb5ceSopenharmony_ci jps->b64, (unsigned int)jps->pos) < 0) 403d4afb5ceSopenharmony_ci goto bail; 404d4afb5ceSopenharmony_ci break; 405d4afb5ceSopenharmony_ci } 406d4afb5ceSopenharmony_ci 407d4afb5ceSopenharmony_ci return 0; 408d4afb5ceSopenharmony_ci 409d4afb5ceSopenharmony_cielements_mismatch: 410d4afb5ceSopenharmony_ci lwsl_err("%s: jwk elements mismatch\n", __func__); 411d4afb5ceSopenharmony_ci 412d4afb5ceSopenharmony_cibail: 413d4afb5ceSopenharmony_ci lwsl_err("%s: element failed\n", __func__); 414d4afb5ceSopenharmony_ci 415d4afb5ceSopenharmony_ci return -1; 416d4afb5ceSopenharmony_ci} 417d4afb5ceSopenharmony_ci 418d4afb5ceSopenharmony_ciint 419d4afb5ceSopenharmony_cilws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user, 420d4afb5ceSopenharmony_ci const char *in, size_t len) 421d4afb5ceSopenharmony_ci{ 422d4afb5ceSopenharmony_ci struct lejp_ctx jctx; 423d4afb5ceSopenharmony_ci struct lws_jwk_parse_state jps; 424d4afb5ceSopenharmony_ci int m; 425d4afb5ceSopenharmony_ci 426d4afb5ceSopenharmony_ci lws_jwk_init_jps(&jps, jwk, cb, user); 427d4afb5ceSopenharmony_ci 428d4afb5ceSopenharmony_ci lejp_construct(&jctx, cb_jwk, &jps, cb ? jwk_outer_tok: jwk_tok, 429d4afb5ceSopenharmony_ci LWS_ARRAY_SIZE(jwk_tok)); 430d4afb5ceSopenharmony_ci 431d4afb5ceSopenharmony_ci m = lejp_parse(&jctx, (uint8_t *)in, (int)len); 432d4afb5ceSopenharmony_ci lejp_destruct(&jctx); 433d4afb5ceSopenharmony_ci 434d4afb5ceSopenharmony_ci if (m < 0) { 435d4afb5ceSopenharmony_ci lwsl_notice("%s: parse got %d\n", __func__, m); 436d4afb5ceSopenharmony_ci lws_jwk_destroy(jwk); 437d4afb5ceSopenharmony_ci return -1; 438d4afb5ceSopenharmony_ci } 439d4afb5ceSopenharmony_ci 440d4afb5ceSopenharmony_ci switch (jwk->kty) { 441d4afb5ceSopenharmony_ci case LWS_GENCRYPTO_KTY_UNKNOWN: 442d4afb5ceSopenharmony_ci lwsl_notice("%s: missing or unknown kty\n", __func__); 443d4afb5ceSopenharmony_ci lws_jwk_destroy(jwk); 444d4afb5ceSopenharmony_ci return -1; 445d4afb5ceSopenharmony_ci default: 446d4afb5ceSopenharmony_ci break; 447d4afb5ceSopenharmony_ci } 448d4afb5ceSopenharmony_ci 449d4afb5ceSopenharmony_ci return 0; 450d4afb5ceSopenharmony_ci} 451d4afb5ceSopenharmony_ci 452d4afb5ceSopenharmony_ci 453d4afb5ceSopenharmony_ciint 454d4afb5ceSopenharmony_cilws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len) 455d4afb5ceSopenharmony_ci{ 456d4afb5ceSopenharmony_ci char *start = p, *end = &p[*len - 1]; 457d4afb5ceSopenharmony_ci int n, m, limit, first = 1, asym = 0; 458d4afb5ceSopenharmony_ci struct lexico *l; 459d4afb5ceSopenharmony_ci 460d4afb5ceSopenharmony_ci /* RFC7638 lexicographic order requires 461d4afb5ceSopenharmony_ci * RSA: e -> kty -> n 462d4afb5ceSopenharmony_ci * oct: k -> kty 463d4afb5ceSopenharmony_ci * 464d4afb5ceSopenharmony_ci * ie, meta and key data elements appear interleaved in name alpha order 465d4afb5ceSopenharmony_ci */ 466d4afb5ceSopenharmony_ci 467d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{"); 468d4afb5ceSopenharmony_ci 469d4afb5ceSopenharmony_ci switch (jwk->kty) { 470d4afb5ceSopenharmony_ci case LWS_GENCRYPTO_KTY_OCT: 471d4afb5ceSopenharmony_ci l = lexico_oct; 472d4afb5ceSopenharmony_ci limit = LWS_ARRAY_SIZE(lexico_oct); 473d4afb5ceSopenharmony_ci break; 474d4afb5ceSopenharmony_ci case LWS_GENCRYPTO_KTY_RSA: 475d4afb5ceSopenharmony_ci l = lexico_rsa; 476d4afb5ceSopenharmony_ci limit = LWS_ARRAY_SIZE(lexico_rsa); 477d4afb5ceSopenharmony_ci asym = 1; 478d4afb5ceSopenharmony_ci break; 479d4afb5ceSopenharmony_ci case LWS_GENCRYPTO_KTY_EC: 480d4afb5ceSopenharmony_ci l = lexico_ec; 481d4afb5ceSopenharmony_ci limit = LWS_ARRAY_SIZE(lexico_ec); 482d4afb5ceSopenharmony_ci asym = 1; 483d4afb5ceSopenharmony_ci break; 484d4afb5ceSopenharmony_ci default: 485d4afb5ceSopenharmony_ci return -1; 486d4afb5ceSopenharmony_ci } 487d4afb5ceSopenharmony_ci 488d4afb5ceSopenharmony_ci for (n = 0; n < limit; n++) { 489d4afb5ceSopenharmony_ci const char *q, *q_end; 490d4afb5ceSopenharmony_ci char tok[12]; 491d4afb5ceSopenharmony_ci int pos = 0, f = 1; 492d4afb5ceSopenharmony_ci 493d4afb5ceSopenharmony_ci if ((l->meta & 1) && (jwk->meta[l->idx].buf || 494d4afb5ceSopenharmony_ci l->idx == (int)JWK_META_KTY)) { 495d4afb5ceSopenharmony_ci 496d4afb5ceSopenharmony_ci switch (l->idx) { 497d4afb5ceSopenharmony_ci case JWK_META_KTY: 498d4afb5ceSopenharmony_ci if (!first) 499d4afb5ceSopenharmony_ci *p++ = ','; 500d4afb5ceSopenharmony_ci first = 0; 501d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"%s\"", 502d4afb5ceSopenharmony_ci l->name, kty_names[jwk->kty]); 503d4afb5ceSopenharmony_ci break; 504d4afb5ceSopenharmony_ci case JWK_META_KEY_OPS: 505d4afb5ceSopenharmony_ci if (!first) 506d4afb5ceSopenharmony_ci *p++ = ','; 507d4afb5ceSopenharmony_ci first = 0; 508d4afb5ceSopenharmony_ci q = (const char *)jwk->meta[l->idx].buf; 509d4afb5ceSopenharmony_ci q_end = q + jwk->meta[l->idx].len; 510d4afb5ceSopenharmony_ci 511d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), 512d4afb5ceSopenharmony_ci "\"%s\":[", l->name); 513d4afb5ceSopenharmony_ci /* 514d4afb5ceSopenharmony_ci * For the public version, usages that 515d4afb5ceSopenharmony_ci * require the private part must be 516d4afb5ceSopenharmony_ci * snipped 517d4afb5ceSopenharmony_ci */ 518d4afb5ceSopenharmony_ci 519d4afb5ceSopenharmony_ci while (q < q_end) { 520d4afb5ceSopenharmony_ci if (*q != ' ' && pos < (int)sizeof(tok) - 1) { 521d4afb5ceSopenharmony_ci tok[pos++] = *q++; 522d4afb5ceSopenharmony_ci if (q != q_end) 523d4afb5ceSopenharmony_ci continue; 524d4afb5ceSopenharmony_ci } 525d4afb5ceSopenharmony_ci tok[pos] = '\0'; 526d4afb5ceSopenharmony_ci pos = 0; 527d4afb5ceSopenharmony_ci if ((flags & LWSJWKF_EXPORT_PRIVATE) || 528d4afb5ceSopenharmony_ci !asym || (strcmp(tok, "sign") && 529d4afb5ceSopenharmony_ci strcmp(tok, "encrypt"))) { 530d4afb5ceSopenharmony_ci if (!f) 531d4afb5ceSopenharmony_ci *p++ = ','; 532d4afb5ceSopenharmony_ci f = 0; 533d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), 534d4afb5ceSopenharmony_ci "\"%s\"", tok); 535d4afb5ceSopenharmony_ci } 536d4afb5ceSopenharmony_ci q++; 537d4afb5ceSopenharmony_ci } 538d4afb5ceSopenharmony_ci 539d4afb5ceSopenharmony_ci *p++ = ']'; 540d4afb5ceSopenharmony_ci 541d4afb5ceSopenharmony_ci break; 542d4afb5ceSopenharmony_ci 543d4afb5ceSopenharmony_ci default: 544d4afb5ceSopenharmony_ci /* both sig and enc require asym private key */ 545d4afb5ceSopenharmony_ci if (!(flags & LWSJWKF_EXPORT_PRIVATE) && 546d4afb5ceSopenharmony_ci asym && l->idx == (int)JWK_META_USE) 547d4afb5ceSopenharmony_ci break; 548d4afb5ceSopenharmony_ci if (!first) 549d4afb5ceSopenharmony_ci *p++ = ','; 550d4afb5ceSopenharmony_ci first = 0; 551d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"", 552d4afb5ceSopenharmony_ci l->name); 553d4afb5ceSopenharmony_ci lws_strnncpy(p, (const char *)jwk->meta[l->idx].buf, 554d4afb5ceSopenharmony_ci jwk->meta[l->idx].len, end - p); 555d4afb5ceSopenharmony_ci p += strlen(p); 556d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\""); 557d4afb5ceSopenharmony_ci break; 558d4afb5ceSopenharmony_ci } 559d4afb5ceSopenharmony_ci } 560d4afb5ceSopenharmony_ci 561d4afb5ceSopenharmony_ci if ((!(l->meta & 1)) && jwk->e[l->idx].buf && 562d4afb5ceSopenharmony_ci ((flags & LWSJWKF_EXPORT_PRIVATE) || !(l->meta & 2))) { 563d4afb5ceSopenharmony_ci if (!first) 564d4afb5ceSopenharmony_ci *p++ = ','; 565d4afb5ceSopenharmony_ci first = 0; 566d4afb5ceSopenharmony_ci 567d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"", l->name); 568d4afb5ceSopenharmony_ci 569d4afb5ceSopenharmony_ci if (jwk->kty == LWS_GENCRYPTO_KTY_EC && 570d4afb5ceSopenharmony_ci l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV) { 571d4afb5ceSopenharmony_ci lws_strnncpy(p, 572d4afb5ceSopenharmony_ci (const char *)jwk->e[l->idx].buf, 573d4afb5ceSopenharmony_ci jwk->e[l->idx].len, end - p); 574d4afb5ceSopenharmony_ci m = (int)strlen(p); 575d4afb5ceSopenharmony_ci } else 576d4afb5ceSopenharmony_ci m = lws_jws_base64_enc( 577d4afb5ceSopenharmony_ci (const char *)jwk->e[l->idx].buf, 578d4afb5ceSopenharmony_ci jwk->e[l->idx].len, p, lws_ptr_diff_size_t(end, p) - 4); 579d4afb5ceSopenharmony_ci if (m < 0) { 580d4afb5ceSopenharmony_ci lwsl_notice("%s: enc failed\n", __func__); 581d4afb5ceSopenharmony_ci return -1; 582d4afb5ceSopenharmony_ci } 583d4afb5ceSopenharmony_ci p += m; 584d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\""); 585d4afb5ceSopenharmony_ci } 586d4afb5ceSopenharmony_ci 587d4afb5ceSopenharmony_ci l++; 588d4afb5ceSopenharmony_ci } 589d4afb5ceSopenharmony_ci 590d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), 591d4afb5ceSopenharmony_ci (flags & LWSJWKF_EXPORT_NOCRLF) ? "}" : "}\n"); 592d4afb5ceSopenharmony_ci 593d4afb5ceSopenharmony_ci *len -= lws_ptr_diff(p, start); 594d4afb5ceSopenharmony_ci 595d4afb5ceSopenharmony_ci return lws_ptr_diff(p, start); 596d4afb5ceSopenharmony_ci} 597d4afb5ceSopenharmony_ci 598d4afb5ceSopenharmony_ciint 599d4afb5ceSopenharmony_cilws_jwk_load(struct lws_jwk *jwk, const char *filename, 600d4afb5ceSopenharmony_ci lws_jwk_key_import_callback cb, void *user) 601d4afb5ceSopenharmony_ci{ 602d4afb5ceSopenharmony_ci unsigned int buflen = 4096; 603d4afb5ceSopenharmony_ci char *buf = lws_malloc(buflen, "jwk-load"); 604d4afb5ceSopenharmony_ci int n; 605d4afb5ceSopenharmony_ci 606d4afb5ceSopenharmony_ci if (!buf) 607d4afb5ceSopenharmony_ci return -1; 608d4afb5ceSopenharmony_ci 609d4afb5ceSopenharmony_ci n = lws_plat_read_file(filename, buf, buflen); 610d4afb5ceSopenharmony_ci if (n < 0) 611d4afb5ceSopenharmony_ci goto bail; 612d4afb5ceSopenharmony_ci 613d4afb5ceSopenharmony_ci n = lws_jwk_import(jwk, cb, user, buf, (unsigned int)n); 614d4afb5ceSopenharmony_ci lws_free(buf); 615d4afb5ceSopenharmony_ci 616d4afb5ceSopenharmony_ci return n; 617d4afb5ceSopenharmony_cibail: 618d4afb5ceSopenharmony_ci lws_free(buf); 619d4afb5ceSopenharmony_ci 620d4afb5ceSopenharmony_ci return -1; 621d4afb5ceSopenharmony_ci} 622d4afb5ceSopenharmony_ci 623d4afb5ceSopenharmony_ciint 624d4afb5ceSopenharmony_cilws_jwk_save(struct lws_jwk *jwk, const char *filename) 625d4afb5ceSopenharmony_ci{ 626d4afb5ceSopenharmony_ci int buflen = 4096; 627d4afb5ceSopenharmony_ci char *buf = lws_malloc((unsigned int)buflen, "jwk-save"); 628d4afb5ceSopenharmony_ci int n, m; 629d4afb5ceSopenharmony_ci 630d4afb5ceSopenharmony_ci if (!buf) 631d4afb5ceSopenharmony_ci return -1; 632d4afb5ceSopenharmony_ci 633d4afb5ceSopenharmony_ci n = lws_jwk_export(jwk, LWSJWKF_EXPORT_PRIVATE, buf, &buflen); 634d4afb5ceSopenharmony_ci if (n < 0) 635d4afb5ceSopenharmony_ci goto bail; 636d4afb5ceSopenharmony_ci 637d4afb5ceSopenharmony_ci m = lws_plat_write_file(filename, buf, (size_t)n); 638d4afb5ceSopenharmony_ci 639d4afb5ceSopenharmony_ci lws_free(buf); 640d4afb5ceSopenharmony_ci if (m) 641d4afb5ceSopenharmony_ci return -1; 642d4afb5ceSopenharmony_ci 643d4afb5ceSopenharmony_ci return 0; 644d4afb5ceSopenharmony_ci 645d4afb5ceSopenharmony_cibail: 646d4afb5ceSopenharmony_ci lws_free(buf); 647d4afb5ceSopenharmony_ci 648d4afb5ceSopenharmony_ci return -1; 649d4afb5ceSopenharmony_ci} 650