|
@@ -0,0 +1,160 @@
|
|
|
+/* Crypto operations using stored keys
|
|
|
+ *
|
|
|
+ * Copyright (c) 2016, Intel Corporation
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/mpi.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <keys/user-type.h>
|
|
|
+#include "internal.h"
|
|
|
+
|
|
|
+/*
|
|
|
+ * Public key or shared secret generation function [RFC2631 sec 2.1.1]
|
|
|
+ *
|
|
|
+ * ya = g^xa mod p;
|
|
|
+ * or
|
|
|
+ * ZZ = yb^xa mod p;
|
|
|
+ *
|
|
|
+ * where xa is the local private key, ya is the local public key, g is
|
|
|
+ * the generator, p is the prime, yb is the remote public key, and ZZ
|
|
|
+ * is the shared secret.
|
|
|
+ *
|
|
|
+ * Both are the same calculation, so g or yb are the "base" and ya or
|
|
|
+ * ZZ are the "result".
|
|
|
+ */
|
|
|
+static int do_dh(MPI result, MPI base, MPI xa, MPI p)
|
|
|
+{
|
|
|
+ return mpi_powm(result, base, xa, p);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
|
|
|
+{
|
|
|
+ struct key *key;
|
|
|
+ key_ref_t key_ref;
|
|
|
+ long status;
|
|
|
+ ssize_t ret;
|
|
|
+
|
|
|
+ key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
|
|
|
+ if (IS_ERR(key_ref)) {
|
|
|
+ ret = -ENOKEY;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ key = key_ref_to_ptr(key_ref);
|
|
|
+
|
|
|
+ ret = -EOPNOTSUPP;
|
|
|
+ if (key->type == &key_type_user) {
|
|
|
+ down_read(&key->sem);
|
|
|
+ status = key_validate(key);
|
|
|
+ if (status == 0) {
|
|
|
+ const struct user_key_payload *payload;
|
|
|
+
|
|
|
+ payload = user_key_payload(key);
|
|
|
+
|
|
|
+ if (maxlen == 0) {
|
|
|
+ *mpi = NULL;
|
|
|
+ ret = payload->datalen;
|
|
|
+ } else if (payload->datalen <= maxlen) {
|
|
|
+ *mpi = mpi_read_raw_data(payload->data,
|
|
|
+ payload->datalen);
|
|
|
+ if (*mpi)
|
|
|
+ ret = payload->datalen;
|
|
|
+ } else {
|
|
|
+ ret = -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ up_read(&key->sem);
|
|
|
+ }
|
|
|
+
|
|
|
+ key_put(key);
|
|
|
+error:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
|
|
+ char __user *buffer, size_t buflen)
|
|
|
+{
|
|
|
+ long ret;
|
|
|
+ MPI base, private, prime, result;
|
|
|
+ unsigned nbytes;
|
|
|
+ struct keyctl_dh_params pcopy;
|
|
|
+ uint8_t *kbuf;
|
|
|
+ ssize_t keylen;
|
|
|
+ size_t resultlen;
|
|
|
+
|
|
|
+ if (!params || (!buffer && buflen)) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
|
|
|
+ ret = -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ keylen = mpi_from_key(pcopy.prime, buflen, &prime);
|
|
|
+ if (keylen < 0 || !prime) {
|
|
|
+ /* buflen == 0 may be used to query the required buffer size,
|
|
|
+ * which is the prime key length.
|
|
|
+ */
|
|
|
+ ret = keylen;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* The result is never longer than the prime */
|
|
|
+ resultlen = keylen;
|
|
|
+
|
|
|
+ keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base);
|
|
|
+ if (keylen < 0 || !base) {
|
|
|
+ ret = keylen;
|
|
|
+ goto error1;
|
|
|
+ }
|
|
|
+
|
|
|
+ keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private);
|
|
|
+ if (keylen < 0 || !private) {
|
|
|
+ ret = keylen;
|
|
|
+ goto error2;
|
|
|
+ }
|
|
|
+
|
|
|
+ result = mpi_alloc(0);
|
|
|
+ if (!result) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto error3;
|
|
|
+ }
|
|
|
+
|
|
|
+ kbuf = kmalloc(resultlen, GFP_KERNEL);
|
|
|
+ if (!kbuf) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto error4;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = do_dh(result, base, private, prime);
|
|
|
+ if (ret)
|
|
|
+ goto error5;
|
|
|
+
|
|
|
+ ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL);
|
|
|
+ if (ret != 0)
|
|
|
+ goto error5;
|
|
|
+
|
|
|
+ ret = nbytes;
|
|
|
+ if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
|
|
+ ret = -EFAULT;
|
|
|
+
|
|
|
+error5:
|
|
|
+ kfree(kbuf);
|
|
|
+error4:
|
|
|
+ mpi_free(result);
|
|
|
+error3:
|
|
|
+ mpi_free(private);
|
|
|
+error2:
|
|
|
+ mpi_free(base);
|
|
|
+error1:
|
|
|
+ mpi_free(prime);
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|