From 1d1ebfc3c5a005e765065c8f3424b46dac5a3b6d Mon Sep 17 00:00:00 2001 From: "Huan.Feng" Date: Thu, 13 Jan 2022 18:11:09 +0800 Subject: add patches for libkcapi tool --- crypto/Kconfig | 19 ++ crypto/Makefile | 9 +- crypto/af_alg.c | 44 ++-- crypto/algif_aead.c | 31 ++- crypto/algif_akcipher.c | 468 +++++++++++++++++++++++++++++++++++++ crypto/algif_kpp.c | 606 ++++++++++++++++++++++++++++++++++++++++++++++++ crypto/algif_skcipher.c | 25 +- crypto/dh.c | 38 ++- crypto/dh_helper.c | 57 ++++- crypto/dhparameter.asn1 | 4 + 10 files changed, 1257 insertions(+), 44 deletions(-) create mode 100644 crypto/algif_akcipher.c create mode 100644 crypto/algif_kpp.c create mode 100644 crypto/dhparameter.asn1 (limited to 'crypto') diff --git a/crypto/Kconfig b/crypto/Kconfig index 536df4b6b825..c8bc811bf2b3 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1916,6 +1916,25 @@ config CRYPTO_STATS - encrypt/decrypt/sign/verify numbers for asymmetric operations - generate/seed numbers for rng operations +config CRYPTO_USER_API_AKCIPHER + tristate "User-space interface for asymmetric key cipher algorithms" + depends on NET + select CRYPTO_AKCIPHER2 + select CRYPTO_USER_API + help + This option enables the user-space interface for asymmetric + key cipher algorithms. + +config CRYPTO_USER_API_KPP + tristate "User-space interface for key protocol primitives algorithms" + depends on NET + select CRYPTO_KPP2 + select CRYPTO_USER_API + help + This option enables the user-spaces interface for key protocol + primitives algorithms. This covers Diffie-Hellman and EC + Diffie-Hellman. + config CRYPTO_HASH_INFO bool diff --git a/crypto/Makefile b/crypto/Makefile index c633f15a0481..81401bb006c8 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -27,7 +27,12 @@ obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o obj-$(CONFIG_CRYPTO_KPP2) += kpp.o -dh_generic-y := dh.o +$(obj)/dhparameter-asn1.o: $(obj)/dhparameter.asn1.c $(obj)/dhparameter.asn1.h +$(obj)/dh_helper.o: $(obj)/dhparameter.asn1.h +clean-files += dhparameter.asn1.c dhparameter.asn1.h + +dh_generic-y := dhparameter.asn1.o +dh_generic-y += dh.o dh_generic-y += dh_helper.o obj-$(CONFIG_CRYPTO_DH) += dh_generic.o @@ -171,6 +176,8 @@ obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o +obj-$(CONFIG_CRYPTO_USER_API_AKCIPHER) += algif_akcipher.o +obj-$(CONFIG_CRYPTO_USER_API_KPP) += algif_kpp.o obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o obj-$(CONFIG_CRYPTO_OFB) += ofb.o obj-$(CONFIG_CRYPTO_ECC) += ecc.o diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 8bd288d2b089..325892ac45c1 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -202,13 +202,17 @@ unlock: return err; } -static int alg_setkey(struct sock *sk, sockptr_t ukey, unsigned int keylen) +static int alg_setkey(struct sock *sk, sockptr_t ukey, unsigned int keylen, + int (*setkey)(void *private, const u8 *key, + unsigned int keylen)) { struct alg_sock *ask = alg_sk(sk); - const struct af_alg_type *type = ask->type; u8 *key; int err; + if (!setkey) + return -ENOPROTOOPT; + key = sock_kmalloc(sk, keylen, GFP_KERNEL); if (!key) return -ENOMEM; @@ -217,8 +221,7 @@ static int alg_setkey(struct sock *sk, sockptr_t ukey, unsigned int keylen) if (copy_from_sockptr(key, ukey, keylen)) goto out; - err = type->setkey(ask->private, key, keylen); - + err = setkey(ask->private, key, keylen); out: sock_kzfree_s(sk, key, keylen); @@ -243,18 +246,23 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, if (level != SOL_ALG || !type) goto unlock; + if (sock->state == SS_CONNECTED) + goto unlock; + switch (optname) { case ALG_SET_KEY: - if (sock->state == SS_CONNECTED) - goto unlock; - if (!type->setkey) - goto unlock; - - err = alg_setkey(sk, optval, optlen); + err = alg_setkey(sk, optval, optlen, type->setkey); + break; + case ALG_SET_PUBKEY: + err = alg_setkey(sk, optval, optlen, type->setpubkey); + break; + case ALG_SET_DH_PARAMETERS: + err = alg_setkey(sk, optval, optlen, type->dhparams); + break; + case ALG_SET_ECDH_CURVE: + err = alg_setkey(sk, optval, optlen, type->ecdhcurve); break; case ALG_SET_AEAD_AUTHSIZE: - if (sock->state == SS_CONNECTED) - goto unlock; if (!type->setauthsize) goto unlock; err = type->setauthsize(ask->private, optlen); @@ -836,7 +844,7 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, struct af_alg_tsgl *sgl; struct af_alg_control con = {}; long copied = 0; - bool enc = false; + int op = 0; bool init = false; int err = 0; @@ -847,11 +855,13 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, init = true; switch (con.op) { + case ALG_OP_VERIFY: + case ALG_OP_SIGN: case ALG_OP_ENCRYPT: - enc = true; - break; case ALG_OP_DECRYPT: - enc = false; + case ALG_OP_KEYGEN: + case ALG_OP_SSGEN: + op = con.op; break; default: return -EINVAL; @@ -875,7 +885,7 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, ctx->init = true; if (init) { - ctx->enc = enc; + ctx->op = op; if (con.iv) memcpy(ctx->iv, con.iv->iv, ivsize); diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 42493b4d8ce4..178eea69af0f 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -55,7 +55,7 @@ static inline bool aead_sufficient_data(struct sock *sk) * The minimum amount of memory needed for an AEAD cipher is * the AAD and in case of decryption the tag. */ - return ctx->used >= ctx->aead_assoclen + (ctx->enc ? 0 : as); + return ctx->used >= ctx->aead_assoclen + (ctx->op ? 0 : as); } static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) @@ -71,6 +71,19 @@ static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return af_alg_sendmsg(sock, msg, size, ivsize); } +static inline int aead_cipher_op(struct af_alg_ctx *ctx, + struct af_alg_async_req *areq) +{ + switch (ctx->op) { + case ALG_OP_ENCRYPT: + return crypto_aead_encrypt(&areq->cra_u.aead_req); + case ALG_OP_DECRYPT: + return crypto_aead_decrypt(&areq->cra_u.aead_req); + default: + return -EOPNOTSUPP; + } +} + static int crypto_aead_copy_sgl(struct crypto_sync_skcipher *null_tfm, struct scatterlist *src, struct scatterlist *dst, unsigned int len) @@ -138,7 +151,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, * buffer provides the tag which is consumed resulting in only the * plaintext without a buffer for the tag returned to the caller. */ - if (ctx->enc) + if (ctx->op) outlen = used + as; else outlen = used - as; @@ -212,7 +225,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, /* Use the RX SGL as source (and destination) for crypto op. */ rsgl_src = areq->first_rsgl.sgl.sg; - if (ctx->enc) { + if (ctx->op == ALG_OP_ENCRYPT) { /* * Encryption operation - The in-place cipher operation is * achieved by the following operation: @@ -228,7 +241,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, if (err) goto free; af_alg_pull_tsgl(sk, processed, NULL, 0); - } else { + } else if (ctx->op == ALG_OP_DECRYPT) { /* * Decryption operation - To achieve an in-place cipher * operation, the following SGL structure is used: @@ -293,9 +306,8 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, aead_request_set_callback(&areq->cra_u.aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, af_alg_async_cb, areq); - err = ctx->enc ? crypto_aead_encrypt(&areq->cra_u.aead_req) : - crypto_aead_decrypt(&areq->cra_u.aead_req); - + err = aead_cipher_op(ctx, areq); + /* AIO operation in progress */ if (err == -EINPROGRESS) return -EIOCBQUEUED; @@ -307,10 +319,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, CRYPTO_TFM_REQ_MAY_SLEEP | CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &ctx->wait); - err = crypto_wait_req(ctx->enc ? - crypto_aead_encrypt(&areq->cra_u.aead_req) : - crypto_aead_decrypt(&areq->cra_u.aead_req), - &ctx->wait); + err = crypto_wait_req(aead_cipher_op(ctx, areq), &ctx->wait); } diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c new file mode 100644 index 000000000000..a9fb618458ce --- /dev/null +++ b/crypto/algif_akcipher.c @@ -0,0 +1,468 @@ +/* + * algif_akcipher: User-space interface for asymmetric cipher algorithms + * + * Copyright (C) 2018 - 2020, Stephan Mueller + * + * This file provides the user-space API for asymmetric ciphers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * The following concept of the memory management is used: + * + * The kernel maintains two SGLs, the TX SGL and the RX SGL. The TX SGL is + * filled by user space with the data submitted via sendpage/sendmsg. Filling + * up the TX SGL does not cause a crypto operation -- the data will only be + * tracked by the kernel. Upon receipt of one recvmsg call, the caller must + * provide a buffer which is tracked with the RX SGL. + * + * During the processing of the recvmsg operation, the cipher request is + * allocated and prepared. As part of the recvmsg operation, the processed + * TX buffers are extracted from the TX SGL into a separate SGL. + * + * After the completion of the crypto operation, the RX SGL and the cipher + * request is released. The extracted TX SGL parts are released together with + * the RX SGL release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct akcipher_tfm { + struct crypto_akcipher *akcipher; + bool has_key; +}; + +static int akcipher_sendmsg(struct socket *sock, struct msghdr *msg, + size_t size) +{ + return af_alg_sendmsg(sock, msg, size, 0); +} + +static inline int akcipher_cipher_op(struct af_alg_ctx *ctx, + struct af_alg_async_req *areq) +{ + switch (ctx->op) { + case ALG_OP_ENCRYPT: + return crypto_akcipher_encrypt(&areq->cra_u.akcipher_req); + case ALG_OP_DECRYPT: + return crypto_akcipher_decrypt(&areq->cra_u.akcipher_req); + case ALG_OP_SIGN: + return crypto_akcipher_sign(&areq->cra_u.akcipher_req); + case ALG_OP_VERIFY: + return crypto_akcipher_verify(&areq->cra_u.akcipher_req); + default: + return -EOPNOTSUPP; + } +} + +static int _akcipher_recvmsg(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct sock *psk = ask->parent; + struct alg_sock *pask = alg_sk(psk); + struct af_alg_ctx *ctx = ask->private; + struct akcipher_tfm *akc = pask->private; + struct crypto_akcipher *tfm = akc->akcipher; + struct af_alg_async_req *areq; + size_t len; + size_t used; + int err; + int maxsize; + + if (!ctx->used) { + err = af_alg_wait_for_data(sk, flags, 0); + if (err) + return err; + } + + maxsize = crypto_akcipher_maxsize(tfm); + if (maxsize < 0) + return maxsize; + + /* Allocate cipher request for current operation. */ + areq = af_alg_alloc_areq(sk, sizeof(struct af_alg_async_req) + + crypto_akcipher_reqsize(tfm)); + if (IS_ERR(areq)) + return PTR_ERR(areq); + + /* convert iovecs of output buffers into RX SGL */ + err = af_alg_get_rsgl(sk, msg, flags, areq, maxsize, &len); + if (err) + goto free; + + /* ensure output buffer is sufficiently large */ + if (len < maxsize) { + err = -EMSGSIZE; + goto free; + } + + /* + * Create a per request TX SGL for this request which tracks the + * SG entries from the global TX SGL. + */ + used = ctx->used; + areq->tsgl_entries = af_alg_count_tsgl(sk, used, 0); + if (!areq->tsgl_entries) + areq->tsgl_entries = 1; + areq->tsgl = sock_kmalloc(sk, sizeof(*areq->tsgl) * areq->tsgl_entries, + GFP_KERNEL); + if (!areq->tsgl) { + err = -ENOMEM; + goto free; + } + sg_init_table(areq->tsgl, areq->tsgl_entries); + af_alg_pull_tsgl(sk, used, areq->tsgl, 0); + + /* Initialize the crypto operation */ + akcipher_request_set_tfm(&areq->cra_u.akcipher_req, tfm); + akcipher_request_set_crypt(&areq->cra_u.akcipher_req, areq->tsgl, + areq->first_rsgl.sgl.sg, used, len); + + if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) { + /* AIO operation */ + sock_hold(sk); + areq->iocb = msg->msg_iocb; + + /* Remember output size that will be generated. */ + areq->outlen = areq->cra_u.akcipher_req.dst_len ? + areq->cra_u.akcipher_req.dst_len : len; + + akcipher_request_set_callback(&areq->cra_u.akcipher_req, + CRYPTO_TFM_REQ_MAY_SLEEP, + af_alg_async_cb, areq); + err = akcipher_cipher_op(ctx, areq); + + /* AIO operation in progress */ + if (err == -EINPROGRESS || err == -EBUSY) + return -EIOCBQUEUED; + + sock_put(sk); + } else { + /* Synchronous operation */ + akcipher_request_set_callback(&areq->cra_u.akcipher_req, + CRYPTO_TFM_REQ_MAY_SLEEP | + CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, + &ctx->wait); + err = crypto_wait_req(akcipher_cipher_op(ctx, areq), + &ctx->wait); + } + +free: + af_alg_free_resources(areq); + + return err ? err : areq->cra_u.akcipher_req.dst_len; +} + +static int akcipher_recvmsg(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct sock *psk = ask->parent; + struct alg_sock *pask = alg_sk(psk); + struct akcipher_tfm *akc = pask->private; + struct crypto_akcipher *tfm = akc->akcipher; + int ret = 0; + int err; + + lock_sock(sk); + + while (msg_data_left(msg)) { + err = _akcipher_recvmsg(sock, msg, ignored, flags); + + /* + * This error covers -EIOCBQUEUED which implies that we can + * only handle one AIO request. If the caller wants to have + * multiple AIO requests in parallel, he must make multiple + * separate AIO calls. + */ + if (err <= 0) { + if (err == -EIOCBQUEUED || err == -EBADMSG || !ret) + ret = err; + goto out; + } + + ret += err; + + /* + * The caller must provide crypto_akcipher_maxsize per request. + * If he provides more, we conclude that multiple akcipher + * operations are requested. + */ + iov_iter_advance(&msg->msg_iter, + crypto_akcipher_maxsize(tfm) - err); + } + +out: + af_alg_wmem_wakeup(sk); + release_sock(sk); + return ret; +} + +static struct proto_ops algif_akcipher_ops = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .accept = sock_no_accept, + + .release = af_alg_release, + .sendmsg = akcipher_sendmsg, + .sendpage = af_alg_sendpage, + .recvmsg = akcipher_recvmsg, + .poll = af_alg_poll, +}; + +static int akcipher_check_key(struct socket *sock) +{ + struct sock *psk; + struct alg_sock *pask; + struct akcipher_tfm *tfm; + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + int err = 0; + + lock_sock(sk); + if (!atomic_read(&ask->nokey_refcnt)) + goto unlock_child; + + psk = ask->parent; + pask = alg_sk(ask->parent); + tfm = pask->private; + + lock_sock_nested(psk, SINGLE_DEPTH_NESTING); + if (!tfm->has_key) { + err = -ENOKEY; + goto unlock; + } + + atomic_dec(&pask->nokey_refcnt); + atomic_set(&ask->nokey_refcnt, 0); + + err = 0; + +unlock: + release_sock(psk); +unlock_child: + release_sock(sk); + + return err; +} + +static int akcipher_sendmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t size) +{ + int err; + + err = akcipher_check_key(sock); + if (err) + return err; + + return akcipher_sendmsg(sock, msg, size); +} + +static ssize_t akcipher_sendpage_nokey(struct socket *sock, struct page *page, + int offset, size_t size, int flags) +{ + int err; + + err = akcipher_check_key(sock); + if (err) + return err; + + return af_alg_sendpage(sock, page, offset, size, flags); +} + +static int akcipher_recvmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + int err; + + err = akcipher_check_key(sock); + if (err) + return err; + + return akcipher_recvmsg(sock, msg, ignored, flags); +} + +static struct proto_ops algif_akcipher_ops_nokey = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .accept = sock_no_accept, + + .release = af_alg_release, + .sendmsg = akcipher_sendmsg_nokey, + .sendpage = akcipher_sendpage_nokey, + .recvmsg = akcipher_recvmsg_nokey, + .poll = af_alg_poll, +}; + +static void *akcipher_bind(const char *name, u32 type, u32 mask) +{ + struct akcipher_tfm *tfm; + struct crypto_akcipher *akcipher; + + tfm = kmalloc(sizeof(*tfm), GFP_KERNEL); + if (!tfm) + return ERR_PTR(-ENOMEM); + + akcipher = crypto_alloc_akcipher(name, type, mask); + if (IS_ERR(akcipher)) { + kfree(tfm); + return ERR_CAST(akcipher); + } + + tfm->akcipher = akcipher; + tfm->has_key = false; + + return tfm; +} + +static void akcipher_release(void *private) +{ + struct akcipher_tfm *tfm = private; + struct crypto_akcipher *akcipher = tfm->akcipher; + + crypto_free_akcipher(akcipher); + kfree(tfm); +} + +static int akcipher_setprivkey(void *private, const u8 *key, + unsigned int keylen) +{ + struct akcipher_tfm *tfm = private; + struct crypto_akcipher *akcipher = tfm->akcipher; + int err; + + err = crypto_akcipher_set_priv_key(akcipher, key, keylen); + tfm->has_key = !err; + + /* Return the maximum size of the akcipher operation. */ + if (!err) + err = crypto_akcipher_maxsize(akcipher); + + return err; +} + +static int akcipher_setpubkey(void *private, const u8 *key, unsigned int keylen) +{ + struct akcipher_tfm *tfm = private; + struct crypto_akcipher *akcipher = tfm->akcipher; + int err; + + err = crypto_akcipher_set_pub_key(akcipher, key, keylen); + tfm->has_key = !err; + + /* Return the maximum size of the akcipher operation. */ + if (!err) + err = crypto_akcipher_maxsize(akcipher); + + return err; +} + +static void akcipher_sock_destruct(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + struct af_alg_ctx *ctx = ask->private; + + af_alg_pull_tsgl(sk, ctx->used, NULL, 0); + sock_kfree_s(sk, ctx, ctx->len); + af_alg_release_parent(sk); +} + +static int akcipher_accept_parent_nokey(void *private, struct sock *sk) +{ + struct af_alg_ctx *ctx; + struct alg_sock *ask = alg_sk(sk); + unsigned int len = sizeof(*ctx); + + ctx = sock_kmalloc(sk, len, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->tsgl_list); + ctx->len = len; + ctx->used = 0; + atomic_set(&ctx->rcvused, 0); + ctx->more = 0; + ctx->merge = 0; + ctx->op = 0; + crypto_init_wait(&ctx->wait); + + ask->private = ctx; + + sk->sk_destruct = akcipher_sock_destruct; + + return 0; +} + +static int akcipher_accept_parent(void *private, struct sock *sk) +{ + struct akcipher_tfm *tfm = private; + + if (!tfm->has_key) + return -ENOKEY; + + return akcipher_accept_parent_nokey(private, sk); +} + +static const struct af_alg_type algif_type_akcipher = { + .bind = akcipher_bind, + .release = akcipher_release, + .setkey = akcipher_setprivkey, + .setpubkey = akcipher_setpubkey, + .setauthsize = NULL, + .accept = akcipher_accept_parent, + .accept_nokey = akcipher_accept_parent_nokey, + .ops = &algif_akcipher_ops, + .ops_nokey = &algif_akcipher_ops_nokey, + .name = "akcipher", + .owner = THIS_MODULE +}; + +static int __init algif_akcipher_init(void) +{ + return af_alg_register_type(&algif_type_akcipher); +} + +static void __exit algif_akcipher_exit(void) +{ + int err = af_alg_unregister_type(&algif_type_akcipher); + + BUG_ON(err); +} + +module_init(algif_akcipher_init); +module_exit(algif_akcipher_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Stephan Mueller "); +MODULE_DESCRIPTION("Asymmetric kernel crypto API user space interface"); + diff --git a/crypto/algif_kpp.c b/crypto/algif_kpp.c new file mode 100644 index 000000000000..03fa7ba382b0 --- /dev/null +++ b/crypto/algif_kpp.c @@ -0,0 +1,606 @@ +/* + * algif_kpp: User-space interface for key protocol primitives algorithms + * + * Copyright (C) 2018 - 2020, Stephan Mueller + * + * This file provides the user-space API for key protocol primitives. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * The following concept of the memory management is used: + * + * The kernel maintains two SGLs, the TX SGL and the RX SGL. The TX SGL is + * filled by user space with the data submitted via sendpage/sendmsg. Filling + * up the TX SGL does not cause a crypto operation -- the data will only be + * tracked by the kernel. Upon receipt of one recvmsg call, the caller must + * provide a buffer which is tracked with the RX SGL. + * + * During the processing of the recvmsg operation, the cipher request is + * allocated and prepared. As part of the recvmsg operation, the processed + * TX buffers are extracted from the TX SGL into a separate SGL. + * + * After the completion of the crypto operation, the RX SGL and the cipher + * request is released. The extracted TX SGL parts are released together with + * the RX SGL release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct kpp_tfm { + struct crypto_kpp *kpp; + bool has_key; + +#define KPP_NO_PARAMS 0 +#define KPP_DH_PARAMS 1 +#define KPP_ECDH_PARAMS 2 + int has_params; /* Type of KPP mechanism */ +}; + +static int kpp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) +{ + return af_alg_sendmsg(sock, msg, size, 0); +} + +static inline int kpp_cipher_op(struct af_alg_ctx *ctx, + struct af_alg_async_req *areq) +{ + switch (ctx->op) { + case ALG_OP_KEYGEN: + return crypto_kpp_generate_public_key(&areq->cra_u.kpp_req); + case ALG_OP_SSGEN: + return crypto_kpp_compute_shared_secret(&areq->cra_u.kpp_req); + default: + return -EOPNOTSUPP; + } +} + +static int _kpp_recvmsg(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct sock *psk = ask->parent; + struct alg_sock *pask = alg_sk(psk); + struct af_alg_ctx *ctx = ask->private; + struct kpp_tfm *kpp = pask->private; + struct crypto_kpp *tfm = kpp->kpp; + struct af_alg_async_req *areq; + size_t len; + size_t used = 0; + int err; + int maxsize; + + if (!ctx->used) { + err = af_alg_wait_for_data(sk, flags, 0); + if (err) + return err; + } + + maxsize = crypto_kpp_maxsize(tfm); + if (maxsize < 0) + return maxsize; + + /* Allocate cipher request for current operation. */ + areq = af_alg_alloc_areq(sk, sizeof(struct af_alg_async_req) + + crypto_kpp_reqsize(tfm)); + if (IS_ERR(areq)) + return PTR_ERR(areq); + + /* convert iovecs of output buffers into RX SGL */ + err = af_alg_get_rsgl(sk, msg, flags, areq, maxsize, &len); + if (err) + goto free; + + /* ensure output buffer is sufficiently large */ + if (len < maxsize) { + err = -EMSGSIZE; + goto free; + } + + /* + * Create a per request TX SGL for this request which tracks the + * SG entries from the global TX SGL. + */ + if (ctx->op == ALG_OP_SSGEN) { + used = ctx->used; + + areq->tsgl_entries = af_alg_count_tsgl(sk, used, 0); + if (!areq->tsgl_entries) + areq->tsgl_entries = 1; + areq->tsgl = sock_kmalloc( + sk, sizeof(*areq->tsgl) * areq->tsgl_entries, + GFP_KERNEL); + if (!areq->tsgl) { + err = -ENOMEM; + goto free; + } + sg_init_table(areq->tsgl, areq->tsgl_entries); + af_alg_pull_tsgl(sk, used, areq->tsgl, 0); + } + + /* Initialize the crypto operation */ + kpp_request_set_input(&areq->cra_u.kpp_req, areq->tsgl, used); + kpp_request_set_output(&areq->cra_u.kpp_req, areq->first_rsgl.sgl.sg, + len); + kpp_request_set_tfm(&areq->cra_u.kpp_req, tfm); + + if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) { + /* AIO operation */ + sock_hold(sk); + areq->iocb = msg->msg_iocb; + + /* Remember output size that will be generated. */ + areq->outlen = len; + + kpp_request_set_callback(&areq->cra_u.kpp_req, + CRYPTO_TFM_REQ_MAY_SLEEP, + af_alg_async_cb, areq); + err = kpp_cipher_op(ctx, areq); + + /* AIO operation in progress */ + if (err == -EINPROGRESS || err == -EBUSY) + return -EIOCBQUEUED; + + sock_put(sk); + } else { + /* Synchronous operation */ + kpp_request_set_callback(&areq->cra_u.kpp_req, + CRYPTO_TFM_REQ_MAY_SLEEP | + CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, + &ctx->wait); + err = crypto_wait_req(kpp_cipher_op(ctx, areq), &ctx->wait); + } + +free: + af_alg_free_resources(areq); + + return err ? err : len; +} + +static int kpp_recvmsg(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct sock *psk = ask->parent; + struct alg_sock *pask = alg_sk(psk); + struct kpp_tfm *kpp = pask->private; + struct crypto_kpp *tfm = kpp->kpp; + int ret = 0; + int err; + + lock_sock(sk); + + while (msg_data_left(msg)) { + err = _kpp_recvmsg(sock, msg, ignored, flags); + + /* + * This error covers -EIOCBQUEUED which implies that we can + * only handle one AIO request. If the caller wants to have + * multiple AIO requests in parallel, he must make multiple + * separate AIO calls. + */ + if (err <= 0) { + if (err == -EIOCBQUEUED || err == -EBADMSG || !ret) + ret = err; + goto out; + } + + ret += err; + + /* + * The caller must provide crypto_kpp_maxsize per request. + * If he provides more, we conclude that multiple kpp + * operations are requested. + */ + iov_iter_advance(&msg->msg_iter, + crypto_kpp_maxsize(tfm) - err); + } + +out: + + af_alg_wmem_wakeup(sk); + release_sock(sk); + return ret; +} + +static struct proto_ops algif_kpp_ops = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .accept = sock_no_accept, + + .release = af_alg_release, + .sendmsg = kpp_sendmsg, + .sendpage = af_alg_sendpage, + .recvmsg = kpp_recvmsg, + .poll = af_alg_poll, +}; + +static int kpp_check_key(struct socket *sock) +{ + struct sock *psk; + struct alg_sock *pask; + struct kpp_tfm *tfm; + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + int err = 0; + + lock_sock(sk); + if (!atomic_read(&ask->refcnt)) + goto unlock_child; + + psk = ask->parent; + pask = alg_sk(ask->parent); + tfm = pask->private; + + lock_sock_nested(psk, SINGLE_DEPTH_NESTING); + if (!tfm->has_key || (tfm->has_params == KPP_NO_PARAMS)) { + err = -ENOKEY; + goto unlock; + } + + atomic_dec(&pask->refcnt); + atomic_set(&ask->refcnt, 0); + + err = 0; + +unlock: + release_sock(psk); +unlock_child: + release_sock(sk); + + return err; +} + +static int kpp_sendmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t size) +{ + int err; + + err = kpp_check_key(sock); + if (err) + return err; + + return kpp_sendmsg(sock, msg, size); +} + +static ssize_t kpp_sendpage_nokey(struct socket *sock, struct page *page, + int offset, size_t size, int flags) +{ + int err; + + err = kpp_check_key(sock); + if (err) + return err; + + return af_alg_sendpage(sock, page, offset, size, flags); +} + +static int kpp_recvmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + int err; + + err = kpp_check_key(sock); + if (err) + return err; + + return kpp_recvmsg(sock, msg, ignored, flags); +} + +static struct proto_ops algif_kpp_ops_nokey = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .accept = sock_no_accept, + + .release = af_alg_release, + .sendmsg = kpp_sendmsg_nokey, + .sendpage = kpp_sendpage_nokey, + .recvmsg = kpp_recvmsg_nokey, + .poll = af_alg_poll, +}; + +static void *kpp_bind(const char *name, u32 type, u32 mask) +{ + struct kpp_tfm *tfm; + struct crypto_kpp *kpp; + + tfm = kmalloc(sizeof(*tfm), GFP_KERNEL); + if (!tfm) + return ERR_PTR(-ENOMEM); + + kpp = crypto_alloc_kpp(name, type, mask); + if (IS_ERR(kpp)) { + kfree(tfm); + return ERR_CAST(kpp); + } + + tfm->kpp = kpp; + tfm->has_key = false; + tfm->has_params = KPP_NO_PARAMS; + + return tfm; +} + +static void kpp_release(void *private) +{ + struct kpp_tfm *tfm = private; + struct crypto_kpp *kpp = tfm->kpp; + + crypto_free_kpp(kpp); + kfree(tfm); +} + +static int kpp_dh_set_secret(struct crypto_kpp *tfm, struct dh *params) +{ + char *packed_key = NULL; + unsigned int packed_key_len; + int ret; + + packed_key_len = crypto_dh_key_len(params); + packed_key = kmalloc(packed_key_len, GFP_KERNEL); + if (!packed_key) + return -ENOMEM; + + ret = crypto_dh_encode_key(packed_key, packed_key_len, params); + if (ret) + goto out; + + ret = crypto_kpp_set_secret(tfm, packed_key, packed_key_len); + +out: + kfree(packed_key); + return ret; +} + +static int kpp_dh_set_privkey(struct crypto_kpp *tfm, const u8 *key, + unsigned int keylen) +{ + struct dh params = { + .key = key, + .key_size = keylen, + .p = NULL, + .p_size = 0, + .g = NULL, + .g_size = 0, + }; + + return kpp_dh_set_secret(tfm, ¶ms); +} + +static int kpp_ecdh_set_secret(struct crypto_kpp *tfm, struct ecdh *params) +{ + char *packed_key = NULL; + unsigned int packed_key_len; + int ret; + + packed_key_len = crypto_ecdh_key_len(params); + packed_key = kmalloc(packed_key_len, GFP_KERNEL); + if (!packed_key) + return -ENOMEM; + + ret = crypto_ecdh_encode_key(packed_key, packed_key_len, params); + if (ret) + goto out; + + ret = crypto_kpp_set_secret(tfm, packed_key, packed_key_len); + +out: + kfree(packed_key); + return ret; +} + +static int kpp_ecdh_set_privkey(struct crypto_kpp *tfm, const u8 *key, + unsigned int keylen) +{ + struct ecdh params = { + .curve_id = 0, + .key = key, + .key_size = keylen, + }; + + return kpp_ecdh_set_secret(tfm, ¶ms); +} + +static int kpp_setprivkey(void *private, const u8 *key, unsigned int keylen) +{ + struct kpp_tfm *kpp = private; + struct crypto_kpp *tfm = kpp->kpp; + int err; + + if (kpp->has_params == KPP_NO_PARAMS) + return -ENOKEY; + + /* The DH code cannot generate private keys. ECDH can do that */ + if ((!key || !keylen) && (kpp->has_params == KPP_DH_PARAMS)) { + kpp->has_key = false; + return -EOPNOTSUPP; + } + + switch (kpp->has_params) { + case KPP_DH_PARAMS: + err = kpp_dh_set_privkey(tfm, key, keylen); + break; + case KPP_ECDH_PARAMS: + err = kpp_ecdh_set_privkey(tfm, key, keylen); + break; + default: + err = -EFAULT; + } + + kpp->has_key = !err; + + /* Return the maximum size of the kpp operation. */ + if (!err) + err = crypto_kpp_maxsize(tfm); + + return err; +} + +static int kpp_dh_setparams_pkcs3(void *private, const u8 *params, + unsigned int paramslen) +{ + struct kpp_tfm *kpp = private; + struct crypto_kpp *tfm = kpp->kpp; + int err; + + /* If parameters were already set, disallow setting them again. */ + if (kpp->has_params != KPP_NO_PARAMS) + return -EINVAL; + + err = crypto_kpp_set_params(tfm, params, paramslen); + if (!err) { + kpp->has_params = KPP_DH_PARAMS; + /* Return the maximum size of the kpp operation. */ + err = crypto_kpp_maxsize(tfm); + } else + kpp->has_params = KPP_NO_PARAMS; + + return err; +} + +static int kpp_ecdh_setcurve(void *private, const u8 *curveid, + unsigned int curveidlen) +{ + struct kpp_tfm *kpp = private; + struct crypto_kpp *tfm = kpp->kpp; + int err; + struct ecdh params = { + .key = NULL, + .key_size = 0, + }; + + /* If parameters were already set, disallow setting them again. */ + if (kpp->has_params != KPP_NO_PARAMS) + return -EINVAL; + + if (curveidlen != sizeof(unsigned long)) + return -EINVAL; + + err = kstrtou16(curveid, 10, ¶ms.curve_id); + if (err) + return err; + + err = kpp_ecdh_set_secret(tfm, ¶ms); + if (!err) { + kpp->has_params = KPP_ECDH_PARAMS; + /* Return the maximum size of the kpp operation. */ + err = crypto_kpp_maxsize(tfm); + } else + kpp->has_params = KPP_NO_PARAMS; + + return err; +} + +static void kpp_sock_destruct(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + struct af_alg_ctx *ctx = ask->private; + + af_alg_pull_tsgl(sk, ctx->used, NULL, 0); + sock_kfree_s(sk, ctx, ctx->len); + af_alg_release_parent(sk); +} + +static int kpp_accept_parent_nokey(void *private, struct sock *sk) +{ + struct af_alg_ctx *ctx; + struct alg_sock *ask = alg_sk(sk); + unsigned int len = sizeof(*ctx); + + ctx = sock_kmalloc(sk, len, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->tsgl_list); + ctx->len = len; + ctx->used = 0; + atomic_set(&ctx->rcvused, 0); + ctx->more = 0; + ctx->merge = 0; + ctx->op = 0; + crypto_init_wait(&ctx->wait); + + ask->private = ctx; + + sk->sk_destruct = kpp_sock_destruct; + + return 0; +} + +static int kpp_accept_parent(void *private, struct sock *sk) +{ + struct kpp_tfm *tfm = private; + + if (!tfm->has_key || (tfm->has_params == KPP_NO_PARAMS)) + return -ENOKEY; + + return kpp_accept_parent_nokey(private, sk); +} + +static const struct af_alg_type algif_type_kpp = { + .bind = kpp_bind, + .release = kpp_release, + .setkey = kpp_setprivkey, + .setpubkey = NULL, + .dhparams = kpp_dh_setparams_pkcs3, + .ecdhcurve = kpp_ecdh_setcurve, + .setauthsize = NULL, + .accept = kpp_accept_parent, + .accept_nokey = kpp_accept_parent_nokey, + .ops = &algif_kpp_ops, + .ops_nokey = &algif_kpp_ops_nokey, + .name = "kpp", + .owner = THIS_MODULE +}; + +static int __init algif_kpp_init(void) +{ + return af_alg_register_type(&algif_type_kpp); +} + +static void __exit algif_kpp_exit(void) +{ + int err = af_alg_unregister_type(&algif_type_kpp); + + BUG_ON(err); +} + +module_init(algif_kpp_init); +module_exit(algif_kpp_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Stephan Mueller "); +MODULE_DESCRIPTION("Key protocol primitives kernel crypto API user space interface"); + diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index ee8890ee8f33..952a8e698b5c 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -47,6 +47,19 @@ static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, return af_alg_sendmsg(sock, msg, size, ivsize); } +static inline int skcipher_cipher_op(struct af_alg_ctx *ctx, + struct af_alg_async_req *areq) +{ + switch (ctx->op) { + case ALG_OP_ENCRYPT: + return crypto_skcipher_encrypt(&areq->cra_u.skcipher_req); + case ALG_OP_DECRYPT: + return crypto_skcipher_decrypt(&areq->cra_u.skcipher_req); + default: + return -EOPNOTSUPP; + } +} + static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) { @@ -118,10 +131,8 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, skcipher_request_set_callback(&areq->cra_u.skcipher_req, CRYPTO_TFM_REQ_MAY_SLEEP, af_alg_async_cb, areq); - err = ctx->enc ? - crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : - crypto_skcipher_decrypt(&areq->cra_u.skcipher_req); - + err = skcipher_cipher_op(ctx, areq); + /* AIO operation in progress */ if (err == -EINPROGRESS) return -EIOCBQUEUED; @@ -133,10 +144,8 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, CRYPTO_TFM_REQ_MAY_SLEEP | CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &ctx->wait); - err = crypto_wait_req(ctx->enc ? - crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : - crypto_skcipher_decrypt(&areq->cra_u.skcipher_req), - &ctx->wait); + err = crypto_wait_req(skcipher_cipher_op(ctx, areq), + &ctx->wait); } diff --git a/crypto/dh.c b/crypto/dh.c index cd4f32092e5c..654cbe77586e 100644 --- a/crypto/dh.c +++ b/crypto/dh.c @@ -19,15 +19,25 @@ struct dh_ctx { MPI xa; /* Value is guaranteed to be set. */ }; -static void dh_clear_ctx(struct dh_ctx *ctx) +static inline void dh_clear_params(struct dh_ctx *ctx) { mpi_free(ctx->p); mpi_free(ctx->q); mpi_free(ctx->g); +} + +static inline void dh_clear_key(struct dh_ctx *ctx) +{ mpi_free(ctx->xa); memset(ctx, 0, sizeof(*ctx)); } +static void dh_clear_ctx(struct dh_ctx *ctx) +{ + dh_clear_params(ctx); + dh_clear_key(ctx); +} + /* * If base is g we compute the public key * ya = g^xa mod p; [RFC2631 sec 2.1.1] @@ -52,6 +62,10 @@ static int dh_check_params_length(unsigned int p_len) static int dh_set_params(struct dh_ctx *ctx, struct dh *params) { + /* If DH parameters are not given, do not check them. */ + if (!params->p_size && !params->g_size) + return 0; + if (dh_check_params_length(params->p_size << 3)) return -EINVAL; @@ -72,6 +86,23 @@ static int dh_set_params(struct dh_ctx *ctx, struct dh *params) return 0; } +static int dh_set_params_pkcs3(struct crypto_kpp *tfm, const void *param, + unsigned int param_len) +{ + struct dh_ctx *ctx = dh_get_ctx(tfm); + struct dh parsed_params; + int ret; + + /* Free the old parameter if any */ + dh_clear_params(ctx); + + ret = dh_parse_params_pkcs3(&parsed_params, param, param_len); + if (ret) + return ret; + + return dh_set_params(ctx, &parsed_params); +} + static int dh_set_secret(struct crypto_kpp *tfm, const void *buf, unsigned int len) { @@ -79,7 +110,7 @@ static int dh_set_secret(struct crypto_kpp *tfm, const void *buf, struct dh params; /* Free the old MPI key if any */ - dh_clear_ctx(ctx); + dh_clear_key(ctx); if (crypto_dh_decode_key(buf, len, ¶ms) < 0) goto err_clear_ctx; @@ -158,7 +189,7 @@ static int dh_compute_value(struct kpp_request *req) if (!val) return -ENOMEM; - if (unlikely(!ctx->xa)) { + if (unlikely(!ctx->xa || !ctx->p || !ctx->g)) { ret = -EINVAL; goto err_free_val; } @@ -246,6 +277,7 @@ static void dh_exit_tfm(struct crypto_kpp *tfm) } static struct kpp_alg dh = { + .set_params = dh_set_params_pkcs3, .set_secret = dh_set_secret, .generate_public_key = dh_compute_value, .compute_shared_secret = dh_compute_value, diff --git a/crypto/dh_helper.c b/crypto/dh_helper.c index 9fd5a42eea15..68986d09c4e6 100644 --- a/crypto/dh_helper.c +++ b/crypto/dh_helper.c @@ -9,6 +9,7 @@ #include #include #include +#include "dhparameter.asn1.h" #define DH_KPP_SECRET_MIN_SIZE (sizeof(struct kpp_secret) + 4 * sizeof(int)) @@ -49,16 +50,26 @@ int crypto_dh_encode_key(char *buf, unsigned int len, const struct dh *params) if (unlikely(!len)) return -EINVAL; + /* Prevention of out-of-bounds access in decode code path */ + if ((!params->key && params->key_size) || + (!params->p && params->p_size) || + (!params->g && params->g_size)) + return -EINVAL; + ptr = dh_pack_data(ptr, end, &secret, sizeof(secret)); ptr = dh_pack_data(ptr, end, ¶ms->key_size, sizeof(params->key_size)); ptr = dh_pack_data(ptr, end, ¶ms->p_size, sizeof(params->p_size)); ptr = dh_pack_data(ptr, end, ¶ms->q_size, sizeof(params->q_size)); ptr = dh_pack_data(ptr, end, ¶ms->g_size, sizeof(params->g_size)); - ptr = dh_pack_data(ptr, end, params->key, params->key_size); - ptr = dh_pack_data(ptr, end, params->p, params->p_size); - ptr = dh_pack_data(ptr, end, params->q, params->q_size); - ptr = dh_pack_data(ptr, end, params->g, params->g_size); + if (params->key) + ptr = dh_pack_data(ptr, end, params->key, params->key_size); + if (params->p) + ptr = dh_pack_data(ptr, end, params->p, params->p_size); + if (params->q) + ptr = dh_pack_data(ptr, end, params->q, params->q_size); + if (params->g) + ptr = dh_pack_data(ptr, end, params->g, params->g_size); if (ptr != end) return -EINVAL; return 0; @@ -116,3 +127,41 @@ int crypto_dh_decode_key(const char *buf, unsigned int len, struct dh *params) return 0; } EXPORT_SYMBOL_GPL(crypto_dh_decode_key); + +int dh_get_p(void *context, size_t hdrlen, unsigned char tag, const void *value, + size_t vlen) +{ + struct dh *dh = context; + + /* invalid key provided */ + if (!value || !vlen) + return -EINVAL; + + dh->p = value; + dh->p_size = vlen; + + return 0; +} + +int dh_get_g(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct dh *dh = context; + + /* invalid base provided */ + if (!value || !dh->p_size || !vlen || vlen > dh->p_size) + return -EINVAL; + + dh->g = value; + dh->g_size = vlen; + + return 0; +} + +int dh_parse_params_pkcs3(struct dh *dh, const void *param, + unsigned int param_len) +{ + return asn1_ber_decoder(&dhparameter_decoder, dh, param, param_len); +} +EXPORT_SYMBOL_GPL(dh_parse_params_pkcs3); + diff --git a/crypto/dhparameter.asn1 b/crypto/dhparameter.asn1 new file mode 100644 index 000000000000..e9107c80478d --- /dev/null +++ b/crypto/dhparameter.asn1 @@ -0,0 +1,4 @@ +DHParameter ::= SEQUENCE { + prime INTEGER ({ dh_get_p }), + base INTEGER ({ dh_get_g }) +} -- cgit v1.2.3