diff options
| -rw-r--r-- | crypto/asymmetric_keys/pkcs7_trust.c | 1 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/pkcs7_verify.c | 12 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/public_key.c | 4 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/restrict.c | 21 | ||||
| -rw-r--r-- | security/keys/big_key.c | 110 |
5 files changed, 111 insertions, 37 deletions
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 1f4e25f10049..598906b1e28d 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c | |||
| @@ -106,6 +106,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, | |||
| 106 | pr_devel("sinfo %u: Direct signer is key %x\n", | 106 | pr_devel("sinfo %u: Direct signer is key %x\n", |
| 107 | sinfo->index, key_serial(key)); | 107 | sinfo->index, key_serial(key)); |
| 108 | x509 = NULL; | 108 | x509 = NULL; |
| 109 | sig = sinfo->sig; | ||
| 109 | goto matched; | 110 | goto matched; |
| 110 | } | 111 | } |
| 111 | if (PTR_ERR(key) != -ENOKEY) | 112 | if (PTR_ERR(key) != -ENOKEY) |
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 39e6de0c2761..97c77f66b20d 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c | |||
| @@ -270,7 +270,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, | |||
| 270 | sinfo->index); | 270 | sinfo->index); |
| 271 | return 0; | 271 | return 0; |
| 272 | } | 272 | } |
| 273 | ret = public_key_verify_signature(p->pub, p->sig); | 273 | ret = public_key_verify_signature(p->pub, x509->sig); |
| 274 | if (ret < 0) | 274 | if (ret < 0) |
| 275 | return ret; | 275 | return ret; |
| 276 | x509->signer = p; | 276 | x509->signer = p; |
| @@ -366,8 +366,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, | |||
| 366 | * | 366 | * |
| 367 | * (*) -EBADMSG if some part of the message was invalid, or: | 367 | * (*) -EBADMSG if some part of the message was invalid, or: |
| 368 | * | 368 | * |
| 369 | * (*) 0 if no signature chains were found to be blacklisted or to contain | 369 | * (*) 0 if a signature chain passed verification, or: |
| 370 | * unsupported crypto, or: | ||
| 371 | * | 370 | * |
| 372 | * (*) -EKEYREJECTED if a blacklisted key was encountered, or: | 371 | * (*) -EKEYREJECTED if a blacklisted key was encountered, or: |
| 373 | * | 372 | * |
| @@ -423,8 +422,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, | |||
| 423 | 422 | ||
| 424 | for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { | 423 | for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { |
| 425 | ret = pkcs7_verify_one(pkcs7, sinfo); | 424 | ret = pkcs7_verify_one(pkcs7, sinfo); |
| 426 | if (sinfo->blacklisted && actual_ret == -ENOPKG) | 425 | if (sinfo->blacklisted) { |
| 427 | actual_ret = -EKEYREJECTED; | 426 | if (actual_ret == -ENOPKG) |
| 427 | actual_ret = -EKEYREJECTED; | ||
| 428 | continue; | ||
| 429 | } | ||
| 428 | if (ret < 0) { | 430 | if (ret < 0) { |
| 429 | if (ret == -ENOPKG) { | 431 | if (ret == -ENOPKG) { |
| 430 | sinfo->unsupported_crypto = true; | 432 | sinfo->unsupported_crypto = true; |
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index de996586762a..e929fe1e4106 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c | |||
| @@ -79,9 +79,11 @@ int public_key_verify_signature(const struct public_key *pkey, | |||
| 79 | 79 | ||
| 80 | BUG_ON(!pkey); | 80 | BUG_ON(!pkey); |
| 81 | BUG_ON(!sig); | 81 | BUG_ON(!sig); |
| 82 | BUG_ON(!sig->digest); | ||
| 83 | BUG_ON(!sig->s); | 82 | BUG_ON(!sig->s); |
| 84 | 83 | ||
| 84 | if (!sig->digest) | ||
| 85 | return -ENOPKG; | ||
| 86 | |||
| 85 | alg_name = sig->pkey_algo; | 87 | alg_name = sig->pkey_algo; |
| 86 | if (strcmp(sig->pkey_algo, "rsa") == 0) { | 88 | if (strcmp(sig->pkey_algo, "rsa") == 0) { |
| 87 | /* The data wangled by the RSA algorithm is typically padded | 89 | /* The data wangled by the RSA algorithm is typically padded |
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 86fb68508952..7c93c7728454 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c | |||
| @@ -67,8 +67,9 @@ __setup("ca_keys=", ca_keys_setup); | |||
| 67 | * | 67 | * |
| 68 | * Returns 0 if the new certificate was accepted, -ENOKEY if we couldn't find a | 68 | * Returns 0 if the new certificate was accepted, -ENOKEY if we couldn't find a |
| 69 | * matching parent certificate in the trusted list, -EKEYREJECTED if the | 69 | * matching parent certificate in the trusted list, -EKEYREJECTED if the |
| 70 | * signature check fails or the key is blacklisted and some other error if | 70 | * signature check fails or the key is blacklisted, -ENOPKG if the signature |
| 71 | * there is a matching certificate but the signature check cannot be performed. | 71 | * uses unsupported crypto, or some other error if there is a matching |
| 72 | * certificate but the signature check cannot be performed. | ||
| 72 | */ | 73 | */ |
| 73 | int restrict_link_by_signature(struct key *dest_keyring, | 74 | int restrict_link_by_signature(struct key *dest_keyring, |
| 74 | const struct key_type *type, | 75 | const struct key_type *type, |
| @@ -88,6 +89,8 @@ int restrict_link_by_signature(struct key *dest_keyring, | |||
| 88 | return -EOPNOTSUPP; | 89 | return -EOPNOTSUPP; |
| 89 | 90 | ||
| 90 | sig = payload->data[asym_auth]; | 91 | sig = payload->data[asym_auth]; |
| 92 | if (!sig) | ||
| 93 | return -ENOPKG; | ||
| 91 | if (!sig->auth_ids[0] && !sig->auth_ids[1]) | 94 | if (!sig->auth_ids[0] && !sig->auth_ids[1]) |
| 92 | return -ENOKEY; | 95 | return -ENOKEY; |
| 93 | 96 | ||
| @@ -139,6 +142,8 @@ static int key_or_keyring_common(struct key *dest_keyring, | |||
| 139 | return -EOPNOTSUPP; | 142 | return -EOPNOTSUPP; |
| 140 | 143 | ||
| 141 | sig = payload->data[asym_auth]; | 144 | sig = payload->data[asym_auth]; |
| 145 | if (!sig) | ||
| 146 | return -ENOPKG; | ||
| 142 | if (!sig->auth_ids[0] && !sig->auth_ids[1]) | 147 | if (!sig->auth_ids[0] && !sig->auth_ids[1]) |
| 143 | return -ENOKEY; | 148 | return -ENOKEY; |
| 144 | 149 | ||
| @@ -222,9 +227,9 @@ static int key_or_keyring_common(struct key *dest_keyring, | |||
| 222 | * | 227 | * |
| 223 | * Returns 0 if the new certificate was accepted, -ENOKEY if we | 228 | * Returns 0 if the new certificate was accepted, -ENOKEY if we |
| 224 | * couldn't find a matching parent certificate in the trusted list, | 229 | * couldn't find a matching parent certificate in the trusted list, |
| 225 | * -EKEYREJECTED if the signature check fails, and some other error if | 230 | * -EKEYREJECTED if the signature check fails, -ENOPKG if the signature uses |
| 226 | * there is a matching certificate but the signature check cannot be | 231 | * unsupported crypto, or some other error if there is a matching certificate |
| 227 | * performed. | 232 | * but the signature check cannot be performed. |
| 228 | */ | 233 | */ |
| 229 | int restrict_link_by_key_or_keyring(struct key *dest_keyring, | 234 | int restrict_link_by_key_or_keyring(struct key *dest_keyring, |
| 230 | const struct key_type *type, | 235 | const struct key_type *type, |
| @@ -249,9 +254,9 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, | |||
| 249 | * | 254 | * |
| 250 | * Returns 0 if the new certificate was accepted, -ENOKEY if we | 255 | * Returns 0 if the new certificate was accepted, -ENOKEY if we |
| 251 | * couldn't find a matching parent certificate in the trusted list, | 256 | * couldn't find a matching parent certificate in the trusted list, |
| 252 | * -EKEYREJECTED if the signature check fails, and some other error if | 257 | * -EKEYREJECTED if the signature check fails, -ENOPKG if the signature uses |
| 253 | * there is a matching certificate but the signature check cannot be | 258 | * unsupported crypto, or some other error if there is a matching certificate |
| 254 | * performed. | 259 | * but the signature check cannot be performed. |
| 255 | */ | 260 | */ |
| 256 | int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring, | 261 | int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring, |
| 257 | const struct key_type *type, | 262 | const struct key_type *type, |
diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 929e14978c42..fa728f662a6f 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c | |||
| @@ -22,6 +22,13 @@ | |||
| 22 | #include <keys/big_key-type.h> | 22 | #include <keys/big_key-type.h> |
| 23 | #include <crypto/aead.h> | 23 | #include <crypto/aead.h> |
| 24 | 24 | ||
| 25 | struct big_key_buf { | ||
| 26 | unsigned int nr_pages; | ||
| 27 | void *virt; | ||
| 28 | struct scatterlist *sg; | ||
| 29 | struct page *pages[]; | ||
| 30 | }; | ||
| 31 | |||
| 25 | /* | 32 | /* |
| 26 | * Layout of key payload words. | 33 | * Layout of key payload words. |
| 27 | */ | 34 | */ |
| @@ -91,10 +98,9 @@ static DEFINE_MUTEX(big_key_aead_lock); | |||
| 91 | /* | 98 | /* |
| 92 | * Encrypt/decrypt big_key data | 99 | * Encrypt/decrypt big_key data |
| 93 | */ | 100 | */ |
| 94 | static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) | 101 | static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t datalen, u8 *key) |
| 95 | { | 102 | { |
| 96 | int ret; | 103 | int ret; |
| 97 | struct scatterlist sgio; | ||
| 98 | struct aead_request *aead_req; | 104 | struct aead_request *aead_req; |
| 99 | /* We always use a zero nonce. The reason we can get away with this is | 105 | /* We always use a zero nonce. The reason we can get away with this is |
| 100 | * because we're using a different randomly generated key for every | 106 | * because we're using a different randomly generated key for every |
| @@ -109,8 +115,7 @@ static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) | |||
| 109 | return -ENOMEM; | 115 | return -ENOMEM; |
| 110 | 116 | ||
| 111 | memset(zero_nonce, 0, sizeof(zero_nonce)); | 117 | memset(zero_nonce, 0, sizeof(zero_nonce)); |
| 112 | sg_init_one(&sgio, data, datalen + (op == BIG_KEY_ENC ? ENC_AUTHTAG_SIZE : 0)); | 118 | aead_request_set_crypt(aead_req, buf->sg, buf->sg, datalen, zero_nonce); |
| 113 | aead_request_set_crypt(aead_req, &sgio, &sgio, datalen, zero_nonce); | ||
| 114 | aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); | 119 | aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); |
| 115 | aead_request_set_ad(aead_req, 0); | 120 | aead_request_set_ad(aead_req, 0); |
| 116 | 121 | ||
| @@ -130,21 +135,81 @@ error: | |||
| 130 | } | 135 | } |
| 131 | 136 | ||
| 132 | /* | 137 | /* |
| 138 | * Free up the buffer. | ||
| 139 | */ | ||
| 140 | static void big_key_free_buffer(struct big_key_buf *buf) | ||
| 141 | { | ||
| 142 | unsigned int i; | ||
| 143 | |||
| 144 | if (buf->virt) { | ||
| 145 | memset(buf->virt, 0, buf->nr_pages * PAGE_SIZE); | ||
| 146 | vunmap(buf->virt); | ||
| 147 | } | ||
| 148 | |||
| 149 | for (i = 0; i < buf->nr_pages; i++) | ||
| 150 | if (buf->pages[i]) | ||
| 151 | __free_page(buf->pages[i]); | ||
| 152 | |||
| 153 | kfree(buf); | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | * Allocate a buffer consisting of a set of pages with a virtual mapping | ||
| 158 | * applied over them. | ||
| 159 | */ | ||
| 160 | static void *big_key_alloc_buffer(size_t len) | ||
| 161 | { | ||
| 162 | struct big_key_buf *buf; | ||
| 163 | unsigned int npg = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 164 | unsigned int i, l; | ||
| 165 | |||
| 166 | buf = kzalloc(sizeof(struct big_key_buf) + | ||
| 167 | sizeof(struct page) * npg + | ||
| 168 | sizeof(struct scatterlist) * npg, | ||
| 169 | GFP_KERNEL); | ||
| 170 | if (!buf) | ||
| 171 | return NULL; | ||
| 172 | |||
| 173 | buf->nr_pages = npg; | ||
| 174 | buf->sg = (void *)(buf->pages + npg); | ||
| 175 | sg_init_table(buf->sg, npg); | ||
| 176 | |||
| 177 | for (i = 0; i < buf->nr_pages; i++) { | ||
| 178 | buf->pages[i] = alloc_page(GFP_KERNEL); | ||
| 179 | if (!buf->pages[i]) | ||
| 180 | goto nomem; | ||
| 181 | |||
| 182 | l = min_t(size_t, len, PAGE_SIZE); | ||
| 183 | sg_set_page(&buf->sg[i], buf->pages[i], l, 0); | ||
| 184 | len -= l; | ||
| 185 | } | ||
| 186 | |||
| 187 | buf->virt = vmap(buf->pages, buf->nr_pages, VM_MAP, PAGE_KERNEL); | ||
| 188 | if (!buf->virt) | ||
| 189 | goto nomem; | ||
| 190 | |||
| 191 | return buf; | ||
| 192 | |||
| 193 | nomem: | ||
| 194 | big_key_free_buffer(buf); | ||
| 195 | return NULL; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* | ||
| 133 | * Preparse a big key | 199 | * Preparse a big key |
| 134 | */ | 200 | */ |
| 135 | int big_key_preparse(struct key_preparsed_payload *prep) | 201 | int big_key_preparse(struct key_preparsed_payload *prep) |
| 136 | { | 202 | { |
| 203 | struct big_key_buf *buf; | ||
| 137 | struct path *path = (struct path *)&prep->payload.data[big_key_path]; | 204 | struct path *path = (struct path *)&prep->payload.data[big_key_path]; |
| 138 | struct file *file; | 205 | struct file *file; |
| 139 | u8 *enckey; | 206 | u8 *enckey; |
| 140 | u8 *data = NULL; | ||
| 141 | ssize_t written; | 207 | ssize_t written; |
| 142 | size_t datalen = prep->datalen; | 208 | size_t datalen = prep->datalen, enclen = datalen + ENC_AUTHTAG_SIZE; |
| 143 | int ret; | 209 | int ret; |
| 144 | 210 | ||
| 145 | ret = -EINVAL; | ||
| 146 | if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) | 211 | if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) |
| 147 | goto error; | 212 | return -EINVAL; |
| 148 | 213 | ||
| 149 | /* Set an arbitrary quota */ | 214 | /* Set an arbitrary quota */ |
| 150 | prep->quotalen = 16; | 215 | prep->quotalen = 16; |
| @@ -157,13 +222,12 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
| 157 | * | 222 | * |
| 158 | * File content is stored encrypted with randomly generated key. | 223 | * File content is stored encrypted with randomly generated key. |
| 159 | */ | 224 | */ |
| 160 | size_t enclen = datalen + ENC_AUTHTAG_SIZE; | ||
| 161 | loff_t pos = 0; | 225 | loff_t pos = 0; |
| 162 | 226 | ||
| 163 | data = kmalloc(enclen, GFP_KERNEL); | 227 | buf = big_key_alloc_buffer(enclen); |
| 164 | if (!data) | 228 | if (!buf) |
| 165 | return -ENOMEM; | 229 | return -ENOMEM; |
| 166 | memcpy(data, prep->data, datalen); | 230 | memcpy(buf->virt, prep->data, datalen); |
| 167 | 231 | ||
| 168 | /* generate random key */ | 232 | /* generate random key */ |
| 169 | enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); | 233 | enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); |
| @@ -176,7 +240,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
| 176 | goto err_enckey; | 240 | goto err_enckey; |
| 177 | 241 | ||
| 178 | /* encrypt aligned data */ | 242 | /* encrypt aligned data */ |
| 179 | ret = big_key_crypt(BIG_KEY_ENC, data, datalen, enckey); | 243 | ret = big_key_crypt(BIG_KEY_ENC, buf, datalen, enckey); |
| 180 | if (ret) | 244 | if (ret) |
| 181 | goto err_enckey; | 245 | goto err_enckey; |
| 182 | 246 | ||
| @@ -187,7 +251,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
| 187 | goto err_enckey; | 251 | goto err_enckey; |
| 188 | } | 252 | } |
| 189 | 253 | ||
| 190 | written = kernel_write(file, data, enclen, &pos); | 254 | written = kernel_write(file, buf->virt, enclen, &pos); |
| 191 | if (written != enclen) { | 255 | if (written != enclen) { |
| 192 | ret = written; | 256 | ret = written; |
| 193 | if (written >= 0) | 257 | if (written >= 0) |
| @@ -202,7 +266,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
| 202 | *path = file->f_path; | 266 | *path = file->f_path; |
| 203 | path_get(path); | 267 | path_get(path); |
| 204 | fput(file); | 268 | fput(file); |
| 205 | kzfree(data); | 269 | big_key_free_buffer(buf); |
| 206 | } else { | 270 | } else { |
| 207 | /* Just store the data in a buffer */ | 271 | /* Just store the data in a buffer */ |
| 208 | void *data = kmalloc(datalen, GFP_KERNEL); | 272 | void *data = kmalloc(datalen, GFP_KERNEL); |
| @@ -220,7 +284,7 @@ err_fput: | |||
| 220 | err_enckey: | 284 | err_enckey: |
| 221 | kzfree(enckey); | 285 | kzfree(enckey); |
| 222 | error: | 286 | error: |
| 223 | kzfree(data); | 287 | big_key_free_buffer(buf); |
| 224 | return ret; | 288 | return ret; |
| 225 | } | 289 | } |
| 226 | 290 | ||
| @@ -298,15 +362,15 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) | |||
| 298 | return datalen; | 362 | return datalen; |
| 299 | 363 | ||
| 300 | if (datalen > BIG_KEY_FILE_THRESHOLD) { | 364 | if (datalen > BIG_KEY_FILE_THRESHOLD) { |
| 365 | struct big_key_buf *buf; | ||
| 301 | struct path *path = (struct path *)&key->payload.data[big_key_path]; | 366 | struct path *path = (struct path *)&key->payload.data[big_key_path]; |
| 302 | struct file *file; | 367 | struct file *file; |
| 303 | u8 *data; | ||
| 304 | u8 *enckey = (u8 *)key->payload.data[big_key_data]; | 368 | u8 *enckey = (u8 *)key->payload.data[big_key_data]; |
| 305 | size_t enclen = datalen + ENC_AUTHTAG_SIZE; | 369 | size_t enclen = datalen + ENC_AUTHTAG_SIZE; |
| 306 | loff_t pos = 0; | 370 | loff_t pos = 0; |
| 307 | 371 | ||
| 308 | data = kmalloc(enclen, GFP_KERNEL); | 372 | buf = big_key_alloc_buffer(enclen); |
| 309 | if (!data) | 373 | if (!buf) |
| 310 | return -ENOMEM; | 374 | return -ENOMEM; |
| 311 | 375 | ||
| 312 | file = dentry_open(path, O_RDONLY, current_cred()); | 376 | file = dentry_open(path, O_RDONLY, current_cred()); |
| @@ -316,26 +380,26 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) | |||
| 316 | } | 380 | } |
| 317 | 381 | ||
| 318 | /* read file to kernel and decrypt */ | 382 | /* read file to kernel and decrypt */ |
| 319 | ret = kernel_read(file, data, enclen, &pos); | 383 | ret = kernel_read(file, buf->virt, enclen, &pos); |
| 320 | if (ret >= 0 && ret != enclen) { | 384 | if (ret >= 0 && ret != enclen) { |
| 321 | ret = -EIO; | 385 | ret = -EIO; |
| 322 | goto err_fput; | 386 | goto err_fput; |
| 323 | } | 387 | } |
| 324 | 388 | ||
| 325 | ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey); | 389 | ret = big_key_crypt(BIG_KEY_DEC, buf, enclen, enckey); |
| 326 | if (ret) | 390 | if (ret) |
| 327 | goto err_fput; | 391 | goto err_fput; |
| 328 | 392 | ||
| 329 | ret = datalen; | 393 | ret = datalen; |
| 330 | 394 | ||
| 331 | /* copy decrypted data to user */ | 395 | /* copy decrypted data to user */ |
| 332 | if (copy_to_user(buffer, data, datalen) != 0) | 396 | if (copy_to_user(buffer, buf->virt, datalen) != 0) |
| 333 | ret = -EFAULT; | 397 | ret = -EFAULT; |
| 334 | 398 | ||
| 335 | err_fput: | 399 | err_fput: |
| 336 | fput(file); | 400 | fput(file); |
| 337 | error: | 401 | error: |
| 338 | kzfree(data); | 402 | big_key_free_buffer(buf); |
| 339 | } else { | 403 | } else { |
| 340 | ret = datalen; | 404 | ret = datalen; |
| 341 | if (copy_to_user(buffer, key->payload.data[big_key_data], | 405 | if (copy_to_user(buffer, key->payload.data[big_key_data], |
