* RFC: Crypto API User-interface
@ 2010-09-07 8:42 Herbert Xu
2010-09-07 9:18 ` Tomas Mraz
` (6 more replies)
0 siblings, 7 replies; 25+ messages in thread
From: Herbert Xu @ 2010-09-07 8:42 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev
Hi:
This is what I am proposing for the Crypto API user-interface.
Note that this is the interface for operations. There will be
a separate interface (most likely netlink) for configuring crypto
algorithms, e.g., picking a specific AES implementation as the
system default.
First of all let's have a quick look at what the user-space side
looks like for AEAD:
int op;
/* This fd corresponds to a tfm object. */
tfmfd = socket(AF_ALG, SOCK_STREAM, 0);
alg.type = "aead";
alg.name = "ccm(aes)";
bind(tfmfd, &alg, sizeof(alg));
setsockopt(tfmfd, SOL_ALG, ALG_AEAD_SET_KEY, key, keylen);
The idea here is that each tfm corresponds to a listening socket.
/* Each listen call generates one or more fds for input/output
* that behave like pipes.
*/
listen(tfmfd, 0);
/* fd for encryption/decryption */
opfd = accept(tfmfd, NULL, 0);
/* fd for associated data */
adfd = accept(tfmfd, NULL, 0);
Each session corresponds to one or more connections obtained from
that socket. The number depends on the number of inputs/outputs
of that particular type of operation. For most types, there will
be a s ingle connection/file descriptor that is used for both input
and output. AEAD is one of the few that require two inputs.
/* These may also be set through sendmsg(2) cmsgs. */
op = ALG_AEAD_OP_ENCRYPT;
setsockopt(opfd, SOL_ALG, ALG_AEAD_OP, op, sizeof(op));
setsockopt(opfd, SOL_ALG, ALG_AEAD_SET_IV, iv, ivlen);
/* Like pipes, larges writes will block!
* For AEAD, ensure the socket buffer is large enough.
* For ciphers, whenever the write blocks start reading.
* For hashes, writes should never block.
*/
write(opfd, plain, datalen);
write(adfd, ad, adlen);
/* The first read triggers the operation. */
read(opfd, crypt, datalen);
op = ALG_AEAD_OP_DECRYPT;
setsockopt(opfd, SOL_ALG, ALG_AEAD_OP, op, sizeof(op));
write(opfd, crypt, datalen);
write(adfd, ad, adlen);
/* Returns -1 with errno EBADMSG if auth fails */
read(defd, plain, datalen);
/* Zero-copy */
splice(cryptfd, NULL, opfd, NULL, datalen, SPLICE_F_MOVE|SPLIFE_F_MORE);
/* We allow writes to be split into multiple system calls. */
splice(cryptfd2, NULL, opfd, NULL, datalen, SPLICE_F_MOVE);
splice(adatafd, NULL, adfd, NULL, adlen, SPLICE_F_MOVE);
/* For now reading is copy-only, if and when vmsplice
* starts supporting zero-copy to user then we can do it
* as well.
*/
read(opfd, plain, datalen);
Ciphers/compression are pretty much the same sans adfd.
For hashes:
/* This fd corresponds to a tfm object. */
tfmfd = socket(AF_ALG, SOCK_STREAM, 0);
alg.type = "hash";
alg.name = "xcbc(aes)";
bind(tfmfd, &alg, sizeof(alg));
setsockopt(tfmfd, SOL_ALG, ALG_HASH_SET_KEY, key, keylen);
/* Each listen call generates one or more fds for input/output
* that behave like pipes.
*/
listen(tfmfd, 0);
/* fd for hashing */
opfd = accept(tfmfd, NULL, 0);
/* MSG_MORE prevents finalisation */
send(opfd, plain, datalen, MSG_MORE);
/* Reads partial hash state */
read(opfd, state, statelen);
/* Restore from a partial hash state */
send(opfd, state, statelen, MSG_OOB);
/* Finalise */
send(opfd, plain, 0, 0);
read(opfd, hash, hashlen);
Please comment.
Thanks,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
@ 2010-09-07 9:18 ` Tomas Mraz
2010-09-07 14:06 ` Christoph Hellwig
` (5 subsequent siblings)
6 siblings, 0 replies; 25+ messages in thread
From: Tomas Mraz @ 2010-09-07 9:18 UTC (permalink / raw)
To: Herbert Xu; +Cc: Linux Crypto Mailing List, netdev
On Tue, 2010-09-07 at 16:42 +0800, Herbert Xu wrote:
> Hi:
>
> This is what I am proposing for the Crypto API user-interface.
>
> Note that this is the interface for operations. There will be
> a separate interface (most likely netlink) for configuring crypto
> algorithms, e.g., picking a specific AES implementation as the
> system default.
>
> First of all let's have a quick look at what the user-space side
> looks like for AEAD:
>
> int op;
>
> /* This fd corresponds to a tfm object. */
> tfmfd = socket(AF_ALG, SOCK_STREAM, 0);
>
> alg.type = "aead";
> alg.name = "ccm(aes)";
> bind(tfmfd, &alg, sizeof(alg));
>
> setsockopt(tfmfd, SOL_ALG, ALG_AEAD_SET_KEY, key, keylen);
>
> The idea here is that each tfm corresponds to a listening socket.
>
> /* Each listen call generates one or more fds for input/output
> * that behave like pipes.
> */
> listen(tfmfd, 0);
> /* fd for encryption/decryption */
> opfd = accept(tfmfd, NULL, 0);
> /* fd for associated data */
> adfd = accept(tfmfd, NULL, 0);
This has much much higher overhead in terms of number of needed syscalls
than the previously proposed ioctl interface. Of course in case of large
data operation the overhead converges to just one or two (for AEAD) more
syscalls (1. ioctl vs. 2. write+read). But there will be many real
use-cases where all the setup of the fds will be done again and again.
And of course it adds an overhead in terms of number of file descriptors
needed for each crypto operation. Where the old interface had just
constant one fd overhead per lifetime of the process, this interface has
3 fds per crypto context in use.
> Each session corresponds to one or more connections obtained from
> that socket. The number depends on the number of inputs/outputs
> of that particular type of operation. For most types, there will
> be a s ingle connection/file descriptor that is used for both input
> and output. AEAD is one of the few that require two inputs.
>
> /* These may also be set through sendmsg(2) cmsgs. */
> op = ALG_AEAD_OP_ENCRYPT;
> setsockopt(opfd, SOL_ALG, ALG_AEAD_OP, op, sizeof(op));
> setsockopt(opfd, SOL_ALG, ALG_AEAD_SET_IV, iv, ivlen);
>
> /* Like pipes, larges writes will block!
> * For AEAD, ensure the socket buffer is large enough.
> * For ciphers, whenever the write blocks start reading.
> * For hashes, writes should never block.
> */
> write(opfd, plain, datalen);
> write(adfd, ad, adlen);
>
> /* The first read triggers the operation. */
> read(opfd, crypt, datalen);
>
> op = ALG_AEAD_OP_DECRYPT;
> setsockopt(opfd, SOL_ALG, ALG_AEAD_OP, op, sizeof(op));
>
> write(opfd, crypt, datalen);
> write(adfd, ad, adlen);
>
> /* Returns -1 with errno EBADMSG if auth fails */
> read(defd, plain, datalen);
>
> /* Zero-copy */
> splice(cryptfd, NULL, opfd, NULL, datalen, SPLICE_F_MOVE|SPLIFE_F_MORE);
> /* We allow writes to be split into multiple system calls. */
> splice(cryptfd2, NULL, opfd, NULL, datalen, SPLICE_F_MOVE);
> splice(adatafd, NULL, adfd, NULL, adlen, SPLICE_F_MOVE);
>
> /* For now reading is copy-only, if and when vmsplice
> * starts supporting zero-copy to user then we can do it
> * as well.
This is also serious performance penalty for now.
> read(opfd, plain, datalen);
>
> Ciphers/compression are pretty much the same sans adfd.
>
> For hashes:
>
> /* This fd corresponds to a tfm object. */
> tfmfd = socket(AF_ALG, SOCK_STREAM, 0);
>
> alg.type = "hash";
> alg.name = "xcbc(aes)";
> bind(tfmfd, &alg, sizeof(alg));
>
> setsockopt(tfmfd, SOL_ALG, ALG_HASH_SET_KEY, key, keylen);
>
> /* Each listen call generates one or more fds for input/output
> * that behave like pipes.
> */
> listen(tfmfd, 0);
> /* fd for hashing */
> opfd = accept(tfmfd, NULL, 0);
>
> /* MSG_MORE prevents finalisation */
> send(opfd, plain, datalen, MSG_MORE);
>
> /* Reads partial hash state */
> read(opfd, state, statelen);
>
> /* Restore from a partial hash state */
> send(opfd, state, statelen, MSG_OOB);
>
> /* Finalise */
> send(opfd, plain, 0, 0);
> read(opfd, hash, hashlen);
Note, that one of frequent hash operations is duplicating the internal
hash state. How this would be done with this API?
--
Tomas Mraz
No matter how far down the wrong road you've gone, turn back.
Turkish proverb
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
2010-09-07 9:18 ` Tomas Mraz
@ 2010-09-07 14:06 ` Christoph Hellwig
2010-09-07 14:11 ` Herbert Xu
2010-09-07 14:57 ` Nikos Mavrogiannopoulos
2010-10-19 13:44 ` Herbert Xu
` (4 subsequent siblings)
6 siblings, 2 replies; 25+ messages in thread
From: Christoph Hellwig @ 2010-09-07 14:06 UTC (permalink / raw)
To: Herbert Xu; +Cc: Linux Crypto Mailing List, netdev, linux-kernel
On Tue, Sep 07, 2010 at 04:42:13PM +0800, Herbert Xu wrote:
> Hi:
>
> This is what I am proposing for the Crypto API user-interface.
Can you explain why we would ever want a userspace interface to it?
doing crypto in kernel for userspace consumers sis simply insane.
It's computational intensive code which has no business in kernel space
unless absolutely required (e.g. for kernel consumers). In addition
to that adding the context switch overhead and address space transitions
is god awfull too.
This all very much sounds like someone had far too much crack.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 14:06 ` Christoph Hellwig
@ 2010-09-07 14:11 ` Herbert Xu
2010-09-07 14:24 ` Christoph Hellwig
2010-09-07 14:49 ` Nikos Mavrogiannopoulos
2010-09-07 14:57 ` Nikos Mavrogiannopoulos
1 sibling, 2 replies; 25+ messages in thread
From: Herbert Xu @ 2010-09-07 14:11 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Linux Crypto Mailing List, netdev, linux-kernel
On Tue, Sep 07, 2010 at 10:06:46AM -0400, Christoph Hellwig wrote:
> On Tue, Sep 07, 2010 at 04:42:13PM +0800, Herbert Xu wrote:
> > Hi:
> >
> > This is what I am proposing for the Crypto API user-interface.
>
> Can you explain why we would ever want a userspace interface to it?
>
> doing crypto in kernel for userspace consumers sis simply insane.
> It's computational intensive code which has no business in kernel space
> unless absolutely required (e.g. for kernel consumers). In addition
> to that adding the context switch overhead and address space transitions
> is god awfull too.
>
> This all very much sounds like someone had far too much crack.
FWIW I don't care about user-space using kernel software crypto at
all. It's the security people that do.
The purpose of the user-space API is to export the hardware crypto
devices to user-space. This means PCI devices mostly, as things
like aesni-intel can already be used without kernel help.
Now as a side-effect if this means that we can shut the security
people up about adding another interface then all the better. But
I will certainly not go out of the way to add more crap to the
kernel for that purpose.
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 14:11 ` Herbert Xu
@ 2010-09-07 14:24 ` Christoph Hellwig
2010-09-07 14:39 ` Herbert Xu
2010-09-07 14:49 ` Nikos Mavrogiannopoulos
1 sibling, 1 reply; 25+ messages in thread
From: Christoph Hellwig @ 2010-09-07 14:24 UTC (permalink / raw)
To: Herbert Xu
Cc: Christoph Hellwig, Linux Crypto Mailing List, netdev,
linux-kernel
On Tue, Sep 07, 2010 at 10:11:12PM +0800, Herbert Xu wrote:
> FWIW I don't care about user-space using kernel software crypto at
> all. It's the security people that do.
And since when did we care about their crack pipe dreams?
> The purpose of the user-space API is to export the hardware crypto
> devices to user-space. This means PCI devices mostly, as things
> like aesni-intel can already be used without kernel help.
I don't think they matter in practice. We have less than a handfull
of drivers for them, and with CPUs gaining proper instructions they
are even less useful. In addition any sane PCI card should just
allow userspace mapping of their descriptors.
> Now as a side-effect if this means that we can shut the security
> people up about adding another interface then all the better. But
> I will certainly not go out of the way to add more crap to the
> kernel for that purpose.
So what is the real use case for this? In addition to kernel bloat
the real fear I have is that the security wankers will just configure
the userspace crypto libraries to always use the kernel interface
just in case, and once that happens we will have to deal with the whole
mess. Especially for RHEL and Fedora where the inmantes now run the
asylum in that respect.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 14:24 ` Christoph Hellwig
@ 2010-09-07 14:39 ` Herbert Xu
0 siblings, 0 replies; 25+ messages in thread
From: Herbert Xu @ 2010-09-07 14:39 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Linux Crypto Mailing List, netdev, linux-kernel
On Tue, Sep 07, 2010 at 10:24:27AM -0400, Christoph Hellwig wrote:
>
> I don't think they matter in practice. We have less than a handfull
> of drivers for them, and with CPUs gaining proper instructions they
> are even less useful. In addition any sane PCI card should just
> allow userspace mapping of their descriptors.
I totally agree that mainstream CPUs won't need this at all.
However we still have embedded users where the CPUs may not be
powerful enough per se or where they want to use their CPUs for
other work.
There are also cases such as the Niagra SPU which may not be
easy to manage from user-space (correct me if I'm wrong Dave).
> > Now as a side-effect if this means that we can shut the security
> > people up about adding another interface then all the better. But
> > I will certainly not go out of the way to add more crap to the
> > kernel for that purpose.
>
> So what is the real use case for this? In addition to kernel bloat
> the real fear I have is that the security wankers will just configure
> the userspace crypto libraries to always use the kernel interface
> just in case, and once that happens we will have to deal with the whole
> mess. Especially for RHEL and Fedora where the inmantes now run the
> asylum in that respect.
I will let the security people answer this :)
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 14:11 ` Herbert Xu
2010-09-07 14:24 ` Christoph Hellwig
@ 2010-09-07 14:49 ` Nikos Mavrogiannopoulos
1 sibling, 0 replies; 25+ messages in thread
From: Nikos Mavrogiannopoulos @ 2010-09-07 14:49 UTC (permalink / raw)
To: Herbert Xu
Cc: Christoph Hellwig, Linux Crypto Mailing List, netdev,
linux-kernel
On Tue, Sep 7, 2010 at 4:11 PM, Herbert Xu <herbert@gondor.hengli.com.au> wrote:
>> > This is what I am proposing for the Crypto API user-interface.
>>
>> Can you explain why we would ever want a userspace interface to it?
>>
>> doing crypto in kernel for userspace consumers sis simply insane.
>> It's computational intensive code which has no business in kernel space
>> unless absolutely required (e.g. for kernel consumers). In addition
>> to that adding the context switch overhead and address space transitions
>> is god awfull too.
>>
>> This all very much sounds like someone had far too much crack.
>
> FWIW I don't care about user-space using kernel software crypto at
> all. It's the security people that do.
Then I'd suggest to not enforce your design over to people who have
thought and have interests on that. The NCR api which you rejected
(for not supporting kernel keyring - which your design also doesn't!),
has specific security goals and protects against specific threats.
This design here has been proposed by you quite many times in the past
and neither you, nor anyone else bothered implementing it. Now we have
two working implementations that offer user-space access to crypto
operations, (the openbsd cryptodev port), and NCR, but you discard
them and insist on a different design. Maybe yours is better (you have
to argue about that)... Probably I'd use it if it was there, but it
isn't.
regards,
Nikos
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 14:06 ` Christoph Hellwig
2010-09-07 14:11 ` Herbert Xu
@ 2010-09-07 14:57 ` Nikos Mavrogiannopoulos
2010-09-07 14:59 ` Christoph Hellwig
1 sibling, 1 reply; 25+ messages in thread
From: Nikos Mavrogiannopoulos @ 2010-09-07 14:57 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Herbert Xu, Linux Crypto Mailing List, netdev, linux-kernel
On Tue, Sep 7, 2010 at 4:06 PM, Christoph Hellwig <hch@infradead.org> wrote:
>> This is what I am proposing for the Crypto API user-interface.
>
> Can you explain why we would ever want a userspace interface to it?
>
> doing crypto in kernel for userspace consumers sis simply insane.
> It's computational intensive code which has no business in kernel space
> unless absolutely required (e.g. for kernel consumers). In addition
> to that adding the context switch overhead and address space transitions
> is god awfull too.
> This all very much sounds like someone had far too much crack.
Or that someone is not really aware of some cryptographic uses.
Embedded systems have crypto accelerators in hardware available
through kernel device drivers. In the systems I worked the
accelerators via a crypto device interface gave a 50x to 100x boost in
crypto operations and relieved the CPU from doing them.
regards,
Nikos
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 14:57 ` Nikos Mavrogiannopoulos
@ 2010-09-07 14:59 ` Christoph Hellwig
0 siblings, 0 replies; 25+ messages in thread
From: Christoph Hellwig @ 2010-09-07 14:59 UTC (permalink / raw)
To: Nikos Mavrogiannopoulos
Cc: Christoph Hellwig, Herbert Xu, Linux Crypto Mailing List, netdev,
linux-kernel
On Tue, Sep 07, 2010 at 04:57:04PM +0200, Nikos Mavrogiannopoulos wrote:
> Or that someone is not really aware of some cryptographic uses.
> Embedded systems have crypto accelerators in hardware available
> through kernel device drivers. In the systems I worked the
> accelerators via a crypto device interface gave a 50x to 100x boost in
> crypto operations and relieved the CPU from doing them.
An interface to external crypto co-process _can_ be useful. It
certainly isn't for the tiny requests where mr crackhead complains about
the overhead. So if we do want to design an interface for addons cards
we need to expose a threshold from which it makes sense to use it, and
not even bother using for the simply software in-kernel algorithms.
Which is something that could be done easily using a variant of
Herbert's interface.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
2010-09-07 9:18 ` Tomas Mraz
2010-09-07 14:06 ` Christoph Hellwig
@ 2010-10-19 13:44 ` Herbert Xu
2010-10-20 10:24 ` Nikos Mavrogiannopoulos
2010-11-04 17:34 ` Herbert Xu
2010-10-19 13:46 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
` (3 subsequent siblings)
6 siblings, 2 replies; 25+ messages in thread
From: Herbert Xu @ 2010-10-19 13:44 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
On Tue, Sep 07, 2010 at 04:42:13PM +0800, Herbert Xu wrote:
>
> This is what I am proposing for the Crypto API user-interface.
>
> Note that this is the interface for operations. There will be
> a separate interface (most likely netlink) for configuring crypto
> algorithms, e.g., picking a specific AES implementation as the
> system default.
OK I've gone ahead and implemented the user-space API for hashes
and ciphers.
To recap this interface is designed to allow user-space programs
to access hardware cryptographic accelerators that we have added
to the kernel.
The intended usage scenario is where a large amount of data needs
to be processed where the benefits offered by hardware acceleration
that is normally unavailable in user-space (as opposed to ones
such as the Intel AES instruction which may be used directly from
user-space) outweigh the overhead of going through the kernel.
In order to further minimise the overhead in these cases, this
interface offers the option of avoiding copying data between
user-space and the kernel where possible and appropriate. For
ciphers this means the use of the splice(2) interface instead of
sendmsg(2)
Here is a sample hash program (note that these only illustrate
what the interface looks like and are not meant to be good examples
of coding :)
int main(void)
{
int opfd;
int tfmfd;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "hash",
.salg_name = "sha1"
};
char buf[20];
int i;
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa));
opfd = accept(tfmfd, NULL, 0);
write(opfd, "abc", 3);
read(opfd, buf, 20);
for (i = 0; i < 20; i++) {
printf("%02x", (unsigned char)buf[i]);
}
printf("\n");
close(opfd);
close(tfmfd);
return 0;
}
And here is one for ciphers:
int main(void)
{
int opfd;
int tfmfd;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "skcipher",
.salg_name = "cbc(aes)"
};
struct msghdr msg = {};
struct cmsghdr *cmsg;
char cbuf[CMSG_SPACE(4) + CMSG_SPACE(20)];
char buf[16];
struct af_alg_iv *iv;
struct iovec iov;
int i;
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa));
setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
"\x06\xa9\x21\x40\x36\xb8\xa1\x5b"
"\x51\x2e\x03\xd5\x34\x12\x00\x06", 16);
opfd = accept(tfmfd, NULL, 0);
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_OP;
cmsg->cmsg_len = CMSG_LEN(4);
*(__u32 *)CMSG_DATA(cmsg) = ALG_OP_ENCRYPT;
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_IV;
cmsg->cmsg_len = CMSG_LEN(20);
iv = (void *)CMSG_DATA(cmsg);
iv->ivlen = 16;
memcpy(iv->iv, "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30"
"\xb4\x22\xda\x80\x2c\x9f\xac\x41", 16);
iov.iov_base = "Single block msg";
iov.iov_len = 16;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sendmsg(opfd, &msg, 0);
read(opfd, buf, 16);
for (i = 0; i < 16; i++) {
printf("%02x", (unsigned char)buf[i]);
}
printf("\n");
close(opfd);
close(tfmfd);
return 0;
}
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 1/4] net - Add AF_ALG macros
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
` (3 preceding siblings ...)
2010-10-19 13:46 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
@ 2010-10-19 13:46 ` Herbert Xu
2010-10-20 9:01 ` David Miller
2010-10-19 13:46 ` [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations Herbert Xu
2010-10-19 13:46 ` [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations Herbert Xu
6 siblings, 1 reply; 25+ messages in thread
From: Herbert Xu @ 2010-10-19 13:46 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
net - Add AF_ALG macros
This patch adds the socket family/level macros for the yet-to-be-born
AF_ALG family. The AF_ALG family provides the user-space interface
for the kernel crypto API.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
include/linux/socket.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index a2fada9..3dd2472 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -193,7 +193,8 @@ struct ucred {
#define AF_PHONET 35 /* Phonet sockets */
#define AF_IEEE802154 36 /* IEEE802154 sockets */
#define AF_CAIF 37 /* CAIF sockets */
-#define AF_MAX 38 /* For now.. */
+#define AF_ALG 38 /* Algorithm sockets */
+#define AF_MAX 39 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -234,6 +235,7 @@ struct ucred {
#define PF_PHONET AF_PHONET
#define PF_IEEE802154 AF_IEEE802154
#define PF_CAIF AF_CAIF
+#define PF_ALG AF_ALG
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -307,6 +309,7 @@ struct ucred {
#define SOL_RDS 276
#define SOL_IUCV 277
#define SOL_CAIF 278
+#define SOL_ALG 279
/* IPX options */
#define IPX_TYPE 1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
` (2 preceding siblings ...)
2010-10-19 13:44 ` Herbert Xu
@ 2010-10-19 13:46 ` Herbert Xu
2010-10-19 13:46 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
` (2 subsequent siblings)
6 siblings, 0 replies; 25+ messages in thread
From: Herbert Xu @ 2010-10-19 13:46 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
crypto: af_alg - User-space interface for Crypto API
This patch creates the backbone of the user-space interface for
the Crypto API, through a new socket family AF_ALG.
Each session corresponds to one or more connections obtained from
that socket. The number depends on the number of inputs/outputs
of that particular type of operation. For most types there will
be a s ingle connection/file descriptor that is used for both input
and output. AEAD is one of the few that require two inputs.
Each algorithm type will provide its own implementation that plugs
into af_alg. They're keyed using a string such as "skcipher" or
"hash".
IOW this patch only contains the boring bits that is required
to hold everything together.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
crypto/Kconfig | 3
crypto/Makefile | 1
crypto/af_alg.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++++
include/crypto/if_alg.h | 75 ++++++++
include/linux/if_alg.h | 40 ++++
5 files changed, 552 insertions(+)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index e4bac29..357e3ca 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -841,6 +841,9 @@ config CRYPTO_ANSI_CPRNG
ANSI X9.31 A.2.4. Note that this option must be enabled if
CRYPTO_FIPS is selected
+config CRYPTO_USER_API
+ tristate
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 423b7de..0b13197 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_CRYPTO_RNG2) += krng.o
obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
+obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
#
# generic algorithms and the async_tx api
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
new file mode 100644
index 0000000..f816850
--- /dev/null
+++ b/crypto/af_alg.c
@@ -0,0 +1,433 @@
+/*
+ * af_alg: User-space algorithm interface
+ *
+ * This file provides the user-space API for algorithms.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 <asm/atomic.h>
+#include <crypto/if_alg.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/rwsem.h>
+
+struct alg_type_list {
+ const struct af_alg_type *type;
+ struct list_head list;
+};
+
+static atomic_t alg_memory_allocated;
+
+static struct proto alg_proto = {
+ .name = "ALG",
+ .owner = THIS_MODULE,
+ .memory_allocated = &alg_memory_allocated,
+ .obj_size = sizeof(struct alg_sock),
+};
+
+static LIST_HEAD(alg_types);
+static DECLARE_RWSEM(alg_types_sem);
+
+static const struct af_alg_type *alg_get_type(const char *name)
+{
+ const struct af_alg_type *type = ERR_PTR(-ENOENT);
+ struct alg_type_list *node;
+
+ down_read(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (strcmp(node->type->name, name))
+ continue;
+
+ if (try_module_get(node->type->owner))
+ type = node->type;
+ break;
+ }
+ up_read(&alg_types_sem);
+
+ return type;
+}
+
+int af_alg_register_type(const struct af_alg_type *type)
+{
+ struct alg_type_list *node;
+ int err = -EEXIST;
+
+ down_write(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (!strcmp(node->type->name, type->name))
+ goto unlock;
+ }
+
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!node)
+ goto unlock;
+
+ type->ops->owner = THIS_MODULE;
+ node->type = type;
+ list_add(&node->list, &alg_types);
+ err = 0;
+
+unlock:
+ up_write(&alg_types_sem);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_register_type);
+
+int af_alg_unregister_type(const struct af_alg_type *type)
+{
+ struct alg_type_list *node;
+ int err = -ENOENT;
+
+ down_write(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (strcmp(node->type->name, type->name))
+ continue;
+
+ list_del(&node->list);
+ kfree(node);
+ err = 0;
+ break;
+ }
+ up_write(&alg_types_sem);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_unregister_type);
+
+static void alg_do_release(const struct af_alg_type *type, void *private)
+{
+ if (!type)
+ return;
+
+ type->release(private);
+ module_put(type->owner);
+}
+
+int af_alg_release(struct socket *sock)
+{
+ sock_put(sock->sk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_release);
+
+static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct sockaddr_alg *sa = (void *)uaddr;
+ const struct af_alg_type *type;
+ void *private;
+
+ if (sock->state == SS_CONNECTED)
+ return -EINVAL;
+
+ if (addr_len != sizeof(*sa))
+ return -EINVAL;
+
+ sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
+ sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
+
+ type = alg_get_type(sa->salg_type);
+ if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
+ request_module("algif-%s", sa->salg_type);
+ type = alg_get_type(sa->salg_type);
+ }
+
+ if (IS_ERR(type))
+ return PTR_ERR(type);
+
+ private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
+ if (IS_ERR(private)) {
+ module_put(type->owner);
+ return PTR_ERR(private);
+ }
+
+ lock_sock(sk);
+
+ swap(ask->type, type);
+ swap(ask->private, private);
+
+ release_sock(sk);
+
+ alg_do_release(type, private);
+
+ return 0;
+}
+
+static int alg_setkey(struct sock *sk, char __user *ukey,
+ unsigned int keylen)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type = ask->type;
+ u8 *key;
+ int err;
+
+ key = sock_kmalloc(sk, keylen, GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ if (copy_from_user(key, ukey, keylen))
+ return -EFAULT;
+
+ err = type->setkey(ask->private, key, keylen);
+
+ sock_kfree_s(sk, key, keylen);
+
+ return err;
+}
+
+static int alg_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type = ask->type;
+
+ if (level != SOL_ALG || !type)
+ return -ENOPROTOOPT;
+
+ switch (optname) {
+ case ALG_SET_KEY:
+ if (sock->state == SS_CONNECTED)
+ return -ENOPROTOOPT;
+ if (!type->setkey)
+ return -ENOPROTOOPT;
+
+ return alg_setkey(sk, optval, optlen);
+ }
+
+ return -ENOPROTOOPT;
+}
+
+int af_alg_accept(struct sock *sk, struct socket *newsock)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type = ask->type;
+ struct sock *sk2;
+ int err;
+
+ if (!type)
+ return -EINVAL;
+
+ sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto);
+ if (!sk2)
+ return -ENOMEM;
+
+ sock_init_data(newsock, sk2);
+
+ err = type->accept(ask->private, sk2);
+ if (err) {
+ sk_free(sk2);
+ return err;
+ }
+
+ sk2->sk_family = PF_ALG;
+
+ sock_hold(sk);
+ alg_sk(sk2)->parent = sk;
+ alg_sk(sk2)->type = type;
+
+ newsock->ops = type->ops;
+ newsock->state = SS_CONNECTED;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_accept);
+
+static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ return af_alg_accept(sock->sk, newsock);
+}
+
+static const struct proto_ops alg_proto_ops = {
+ .family = PF_ALG,
+ .owner = THIS_MODULE,
+
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .getname = sock_no_getname,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .poll = sock_no_poll,
+
+ .bind = alg_bind,
+ .release = af_alg_release,
+ .setsockopt = alg_setsockopt,
+ .accept = alg_accept,
+};
+
+static void alg_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+
+ alg_do_release(ask->type, ask->private);
+}
+
+static int alg_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk;
+ int err;
+
+ if (sock->type != SOCK_SEQPACKET)
+ return -ESOCKTNOSUPPORT;
+ if (protocol != 0)
+ return -EPROTONOSUPPORT;
+
+ err = -ENOMEM;
+ sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto);
+ if (!sk)
+ goto out;
+
+ sock->ops = &alg_proto_ops;
+ sock_init_data(sock, sk);
+
+ sk->sk_family = PF_ALG;
+ sk->sk_destruct = alg_sock_destruct;
+
+ return 0;
+out:
+ return err;
+}
+
+static const struct net_proto_family alg_family = {
+ .family = PF_ALG,
+ .create = alg_create,
+ .owner = THIS_MODULE,
+};
+
+int af_alg_make_sg(struct af_alg_sgl *sgl, void *addr, int len, int write)
+{
+ unsigned long from = (unsigned long)addr;
+ unsigned long npages;
+ unsigned off;
+ int err;
+ int i;
+
+ err = -EFAULT;
+ if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, from, len))
+ goto out;
+
+ off = from & ~PAGE_MASK;
+ npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ err = get_user_pages_fast(from, npages, write, sgl->pages);
+ if (err < 0)
+ goto out;
+
+ npages = err;
+ err = -EINVAL;
+ if (WARN_ON(npages == 0))
+ goto out;
+
+ err = 0;
+
+ sg_init_table(sgl->sg, npages);
+
+ for (i = 0; i < npages; i++) {
+ int plen = min_t(int, len, PAGE_SIZE - off);
+
+ sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
+
+ off = 0;
+ len -= plen;
+ err += plen;
+ }
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_make_sg);
+
+void af_alg_free_sg(struct af_alg_sgl *sgl)
+{
+ int i;
+
+ i = 0;
+ do {
+ put_page(sgl->pages[i]);
+ } while (!sg_is_last(sgl->sg + (i++)));
+}
+EXPORT_SYMBOL_GPL(af_alg_free_sg);
+
+int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (!CMSG_OK(msg, cmsg))
+ return -EINVAL;
+ if (cmsg->cmsg_level != SOL_ALG)
+ continue;
+
+ switch(cmsg->cmsg_type) {
+ case ALG_SET_IV:
+ if (cmsg->cmsg_len < sizeof(*con->iv))
+ return -EINVAL;
+ con->iv = (void *)CMSG_DATA(cmsg);
+ if (cmsg->cmsg_len < con->iv->ivlen +
+ sizeof(con->iv->ivlen))
+ return -EINVAL;
+ break;
+
+ case ALG_SET_OP:
+ if (cmsg->cmsg_len < sizeof(u32))
+ return -EINVAL;
+ con->op = *(u32 *)CMSG_DATA(cmsg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
+
+static int __init af_alg_init(void)
+{
+ int err = proto_register(&alg_proto, 0);
+
+ if (err)
+ goto out;
+
+ err = sock_register(&alg_family);
+ if (err != 0)
+ goto out_unregister_proto;
+
+out:
+ return err;
+
+out_unregister_proto:
+ proto_unregister(&alg_proto);
+ goto out;
+}
+
+static void __exit af_alg_exit(void)
+{
+ sock_unregister(PF_ALG);
+ proto_unregister(&alg_proto);
+}
+
+module_init(af_alg_init);
+module_exit(af_alg_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_ALG);
diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h
new file mode 100644
index 0000000..e303910
--- /dev/null
+++ b/include/crypto/if_alg.h
@@ -0,0 +1,75 @@
+/*
+ * if_alg: User-space algorithm interface
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _CRYPTO_IF_ALG_H
+#define _CRYPTO_IF_ALG_H
+
+#include <linux/compiler.h>
+#include <linux/if_alg.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+#define ALG_MAX_PAGES 16
+
+struct alg_sock {
+ /* struct sock must be the first member of struct alg_sock */
+ struct sock sk;
+
+ struct sock *parent;
+
+ const struct af_alg_type *type;
+ void *private;
+};
+
+struct af_alg_control {
+ struct af_alg_iv *iv;
+ int op;
+};
+
+struct af_alg_type {
+ void *(*bind)(const char *name, u32 type, u32 mask);
+ void (*release)(void *private);
+ int (*setkey)(void *private, const u8 *key, unsigned int keylen);
+ int (*accept)(void *private, struct sock *sk);
+
+ struct proto_ops *ops;
+ struct module *owner;
+ char name[14];
+};
+
+struct af_alg_sgl {
+ struct scatterlist sg[ALG_MAX_PAGES];
+ struct page *pages[ALG_MAX_PAGES];
+};
+
+int af_alg_register_type(const struct af_alg_type *type);
+int af_alg_unregister_type(const struct af_alg_type *type);
+
+int af_alg_release(struct socket *sock);
+int af_alg_accept(struct sock *sk, struct socket *newsock);
+
+int af_alg_make_sg(struct af_alg_sgl *sgl, void *addr, int len, int write);
+void af_alg_free_sg(struct af_alg_sgl *sgl);
+
+int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con);
+
+static inline struct alg_sock *alg_sk(struct sock *sk)
+{
+ return (struct alg_sock *)sk;
+}
+
+static inline void af_alg_release_parent(struct sock *sk)
+{
+ sock_put(alg_sk(sk)->parent);
+}
+
+#endif /* _CRYPTO_IF_ALG_H */
diff --git a/include/linux/if_alg.h b/include/linux/if_alg.h
new file mode 100644
index 0000000..0f9acce
--- /dev/null
+++ b/include/linux/if_alg.h
@@ -0,0 +1,40 @@
+/*
+ * if_alg: User-space algorithm interface
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_IF_ALG_H
+#define _LINUX_IF_ALG_H
+
+#include <linux/types.h>
+
+struct sockaddr_alg {
+ __u16 salg_family;
+ __u8 salg_type[14];
+ __u32 salg_feat;
+ __u32 salg_mask;
+ __u8 salg_name[64];
+};
+
+struct af_alg_iv {
+ __u32 ivlen;
+ __u8 iv[0];
+};
+
+/* Socket options */
+#define ALG_SET_KEY 1
+#define ALG_SET_IV 2
+#define ALG_SET_OP 3
+
+/* Operations */
+#define ALG_OP_DECRYPT 0
+#define ALG_OP_ENCRYPT 1
+
+#endif /* _LINUX_IF_ALG_H */
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
` (4 preceding siblings ...)
2010-10-19 13:46 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
@ 2010-10-19 13:46 ` Herbert Xu
2010-10-19 13:46 ` [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations Herbert Xu
6 siblings, 0 replies; 25+ messages in thread
From: Herbert Xu @ 2010-10-19 13:46 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
crypto: algif_skcipher - User-space interface for skcipher operations
This patch adds the af_alg plugin for symmetric key ciphers,
corresponding to the ablkcipher kernel operation type.
Keys can optionally be set through the setsockopt interface.
Once a sendmsg call occurs without MSG_MORE no further writes
may be made to the socket until all previous data has been read.
IVs and and whether encryption/decryption is performed can be
set through the setsockopt interface or as a control message
to sendmsg.
The interface is completely synchronous, all operations are
carried out in recvmsg(2) and will complete prior to the system
call returning.
The splice(2) interface support reading the user-space data directly
without copying (except that the Crypto API itself may copy the data
if alignment is off).
The recvmsg(2) interface supports directly writing to user-space
without additional copying, i.e., the kernel crypto interface will
receive the user-space address as its output SG list.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
crypto/Kconfig | 8
crypto/Makefile | 1
crypto/algif_skcipher.c | 664 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 673 insertions(+)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 6db27d7..69437e2 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -852,6 +852,14 @@ config CRYPTO_USER_API_HASH
This option enables the user-spaces interface for hash
algorithms.
+config CRYPTO_USER_API_SKCIPHER
+ tristate "User-space interface for symmetric key cipher algorithms"
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_USER_API
+ help
+ This option enables the user-spaces interface for symmetric
+ key cipher algorithms.
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 14ab405..efc0f18 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
+obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
#
# generic algorithms and the async_tx api
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
new file mode 100644
index 0000000..af26040
--- /dev/null
+++ b/crypto/algif_skcipher.c
@@ -0,0 +1,664 @@
+/*
+ * algif_skcipher: User-space interface for skcipher algorithms
+ *
+ * This file provides the user-space API for symmetric key ciphers.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 <crypto/skcipher.h>
+#include <crypto/if_alg.h>
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+struct skcipher_sg_list {
+ struct list_head list;
+
+ int cur;
+
+ struct scatterlist sg[0];
+};
+
+struct skcipher_ctx {
+ struct list_head tsgl;
+ struct af_alg_sgl rsgl;
+
+ struct completion completion;
+
+ void *iv;
+
+ unsigned used;
+
+ unsigned int len;
+ int err;
+ bool more;
+ bool merge;
+ bool enc;
+
+ struct ablkcipher_request req;
+};
+
+#define MAX_SGL_ENTS ((PAGE_SIZE - sizeof(struct skcipher_sg_list)) / \
+ sizeof(struct scatterlist) - 1)
+
+static inline bool skcipher_writable(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+
+ return ctx->used + PAGE_SIZE <= max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+}
+
+static void skcipher_done(struct crypto_async_request *req, int err)
+{
+ struct sock *sk = req->data;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+
+ ctx->err = err;
+ complete(&ctx->completion);
+}
+
+static int skcipher_alloc_sgl(struct sock *sk, int size)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg = NULL;
+
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list, list);
+ if (!list_empty(&ctx->tsgl))
+ sg = sgl->sg;
+
+ if (!sg || sgl->cur >= MAX_SGL_ENTS) {
+ sgl = sock_kmalloc(sk, sizeof(*sgl) +
+ sizeof(sgl->sg[0]) * (MAX_SGL_ENTS + 1),
+ GFP_KERNEL);
+ if (!sgl)
+ return -ENOMEM;
+
+ sg_init_table(sgl->sg, MAX_SGL_ENTS + 1);
+ sgl->cur = 0;
+
+ if (sg)
+ sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg);
+
+ list_add_tail(&sgl->list, &ctx->tsgl);
+ }
+
+ return 0;
+}
+
+static void skcipher_pull_sgl(struct sock *sk, int used)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg;
+ int i;
+
+ while (!list_empty(&ctx->tsgl)) {
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list,
+ list);
+ sg = sgl->sg;
+
+ for (i = 0; i < sgl->cur; i++) {
+ int plen = min_t(int, used, sg[i].length);
+
+ if (!sg_page(sg + i))
+ continue;
+
+ if (!used)
+ return;
+
+ sg[i].length -= plen;
+ sg[i].offset += plen;
+
+ if (!sg[i].length) {
+ put_page(sg_page(sg + i));
+ sg_assign_page(sg + i, NULL);
+ }
+
+ used -= plen;
+ ctx->used -= plen;
+ }
+
+ list_del(&sgl->list);
+ sock_kfree_s(sk, sgl,
+ sizeof(*sgl) + sizeof(sgl->sg[0]) *
+ (MAX_SGL_ENTS + 1));
+ }
+
+ if (!ctx->used)
+ ctx->merge = 0;
+}
+
+static void skcipher_free_sgl(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+
+ skcipher_pull_sgl(sk, ctx->used);
+}
+
+static int skcipher_wait_for_wmem(struct sock *sk, unsigned flags)
+{
+ DEFINE_WAIT(wait);
+ int err = -ERESTARTSYS;
+
+ if (flags & MSG_DONTWAIT)
+ return -EAGAIN;
+
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ if (skcipher_writable(sk)) {
+ err = 0;
+ break;
+ }
+ schedule();
+ }
+ finish_wait(sk_sleep(sk), &wait);
+
+ return err;
+}
+
+static void skcipher_wmem_wakeup(struct sock *sk)
+{
+ struct socket_wq *wq;
+
+ if (!skcipher_writable(sk))
+ return;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
+ POLLRDNORM |
+ POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
+ rcu_read_unlock();
+}
+
+static int skcipher_wait_for_data(struct sock *sk, unsigned flags)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ DEFINE_WAIT(wait);
+ int err = -ERESTARTSYS;
+
+ if (flags & MSG_DONTWAIT) {
+ return -EAGAIN;
+ }
+
+ set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ if (ctx->used) {
+ err = 0;
+ break;
+ }
+ schedule();
+ }
+ finish_wait(sk_sleep(sk), &wait);
+
+ clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+
+ return err;
+}
+
+static void skcipher_data_wakeup(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct socket_wq *wq;
+
+ if (!ctx->used);
+ return;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
+ POLLRDNORM |
+ POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
+ rcu_read_unlock();
+}
+
+static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
+ unsigned ivsize = crypto_ablkcipher_ivsize(tfm);
+ struct skcipher_sg_list *sgl;
+ struct af_alg_control con = {};
+ long copied = 0;
+ bool enc = 0;
+ int limit;
+ int err;
+ int i;
+
+ if (msg->msg_controllen) {
+ err = af_alg_cmsg_send(msg, &con);
+ if (err)
+ return err;
+
+ switch (con.op) {
+ case ALG_OP_ENCRYPT:
+ enc = 1;
+ break;
+ case ALG_OP_DECRYPT:
+ enc = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (con.iv && con.iv->ivlen != ivsize)
+ return -EINVAL;
+ }
+
+ err = -EINVAL;
+
+ lock_sock(sk);
+ if (!ctx->more && ctx->used)
+ goto unlock;
+
+ if (!ctx->used) {
+ ctx->enc = enc;
+ if (con.iv)
+ memcpy(ctx->iv, con.iv->iv, ivsize);
+ }
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+
+ while (size) {
+ struct scatterlist *sg;
+ unsigned long len = size;
+ int plen;
+
+ if (ctx->merge) {
+ sgl = list_entry(ctx->tsgl.prev,
+ struct skcipher_sg_list, list);
+ sg = sgl->sg + sgl->cur - 1;
+ len = min_t(unsigned long, len, PAGE_SIZE - sg->length);
+
+ err = memcpy_fromiovec(page_address(sg_page(sg)) +
+ sg->length, msg->msg_iov, len);
+ if (err)
+ goto unlock;
+
+ sg->length += len;
+ ctx->merge = sg->length & (PAGE_SIZE - 1);
+
+ size -= len;
+ copied += len;
+ continue;
+ }
+
+ if (limit < PAGE_SIZE) {
+ release_sock(sk);
+ err = skcipher_wait_for_wmem(sk, msg->msg_flags);
+ lock_sock(sk);
+ if (err)
+ goto unlock;
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+ }
+
+ len = min_t(unsigned long, len, limit);
+
+ err = skcipher_alloc_sgl(sk, len);
+ if (err)
+ goto unlock;
+
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list,
+ list);
+ sg = sgl->sg;
+ do {
+ i = sgl->cur;
+ plen = min_t(int, len, PAGE_SIZE);
+
+ sg_assign_page(sg + i, alloc_page(GFP_KERNEL));
+ err = -ENOMEM;
+ if (!sg_page(sg + i))
+ goto unlock;
+
+ err = memcpy_fromiovec(page_address(sg_page(sg + i)),
+ msg->msg_iov, plen);
+ if (err) {
+ __free_page(sg_page(sg + i));
+ sg_assign_page(sg + i, NULL);
+ goto unlock;
+ }
+
+ sg[i].length = plen;
+ len -= plen;
+ ctx->used += plen;
+ copied += plen;
+ size -= plen;
+ limit -= plen;
+ sgl->cur++;
+ } while (sg_page(sg + ++i));
+
+ ctx->merge = plen & (PAGE_SIZE - 1);
+ if (ctx->merge)
+ sgl->cur--;
+ }
+
+ err = 0;
+
+ ctx->more = msg->msg_flags & MSG_MORE;
+ if (!ctx->more && !list_empty(&ctx->tsgl)) {
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+ sg_mark_end(sgl->sg + sgl->cur - 1);
+ }
+
+unlock:
+ skcipher_data_wakeup(sk);
+ release_sock(sk);
+
+ return copied ?: err;
+}
+
+static ssize_t skcipher_sendpage(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ int err = -EINVAL;
+ int limit;
+
+ lock_sock(sk);
+ if (!ctx->more && ctx->used)
+ goto unlock;
+
+ if (!size)
+ goto done;
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+
+ if (limit < PAGE_SIZE) {
+ release_sock(sk);
+ err = skcipher_wait_for_wmem(sk, flags);
+ lock_sock(sk);
+ if (err)
+ goto unlock;
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+ }
+
+ err = skcipher_alloc_sgl(sk, 0);
+ if (err)
+ goto unlock;
+
+ ctx->merge = 0;
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+
+ sg_set_page(sgl->sg + sgl->cur, page, size, offset);
+ sgl->cur++;
+
+done:
+ ctx->more = flags & MSG_MORE;
+ if (!ctx->more && !list_empty(&ctx->tsgl)) {
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+ sg_mark_end(sgl->sg + sgl->cur - 1);
+ }
+
+unlock:
+ skcipher_data_wakeup(sk);
+ release_sock(sk);
+
+ return err ?: size;
+}
+
+static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t ignored, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ unsigned bs = crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(
+ &ctx->req));
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg;
+ unsigned long iovlen;
+ struct iovec *iov;
+ int err = -EAGAIN;
+ int used;
+ long copied = 0;
+
+ lock_sock(sk);
+ for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
+ iovlen--, iov++) {
+ unsigned long seglen = iov->iov_len;
+ char *from = iov->iov_base;
+
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list,
+ list);
+ sg = sgl->sg;
+ while (!sg->length)
+ sg++;
+
+ while (seglen) {
+ used = ctx->used;
+ if (!used) {
+ release_sock(sk);
+ err = skcipher_wait_for_data(sk, flags);
+ lock_sock(sk);
+ if (err)
+ goto unlock;
+ }
+
+ used = min_t(unsigned long, used, seglen);
+
+ if (ctx->more || used < ctx->used)
+ used -= used % bs;
+
+ err = -EINVAL;
+ if (!used)
+ goto unlock;
+
+ used = af_alg_make_sg(&ctx->rsgl, from, used, 1);
+ if (used < 0)
+ goto unlock;
+
+ ablkcipher_request_set_crypt(&ctx->req, sg,
+ ctx->rsgl.sg, used,
+ ctx->iv);
+
+ err = ctx->enc ?
+ crypto_ablkcipher_encrypt(&ctx->req) :
+ crypto_ablkcipher_decrypt(&ctx->req);
+
+ switch (err) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ wait_for_completion(&ctx->completion);
+ INIT_COMPLETION(ctx->completion);
+ err = ctx->err;
+ break;
+ }
+
+ af_alg_free_sg(&ctx->rsgl);
+
+ if (err)
+ goto unlock;
+
+ copied += used;
+ from += used;
+ seglen -= used;
+ skcipher_pull_sgl(sk, used);
+ }
+ }
+
+ err = 0;
+
+unlock:
+ skcipher_wmem_wakeup(sk);
+ release_sock(sk);
+
+ return copied ?: err;
+}
+
+
+unsigned int skcipher_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ unsigned int mask;
+
+ sock_poll_wait(file, sk_sleep(sk), wait);
+ mask = 0;
+
+ if (ctx->used)
+ mask |= POLLIN | POLLRDNORM;
+
+ if (skcipher_writable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+
+ return mask;
+}
+
+static struct proto_ops algif_skcipher_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,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+ .setsockopt = sock_no_setsockopt,
+
+ .release = af_alg_release,
+ .sendmsg = skcipher_sendmsg,
+ .sendpage = skcipher_sendpage,
+ .recvmsg = skcipher_recvmsg,
+ .poll = skcipher_poll,
+};
+
+static void *skcipher_bind(const char *name, u32 type, u32 mask)
+{
+ return crypto_alloc_ablkcipher(name, type, mask);
+}
+
+static void skcipher_release(void *private)
+{
+ crypto_free_ablkcipher(private);
+}
+
+static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen)
+{
+ return crypto_ablkcipher_setkey(private, key, keylen);
+}
+
+static void skcipher_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
+
+ skcipher_free_sgl(sk);
+ sock_kfree_s(sk, ctx->iv, crypto_ablkcipher_blocksize(tfm));
+ sock_kfree_s(sk, ctx, ctx->len);
+ af_alg_release_parent(sk);
+}
+
+static int skcipher_accept_parent(void *private, struct sock *sk)
+{
+ struct skcipher_ctx *ctx;
+ struct alg_sock *ask = alg_sk(sk);
+ unsigned int len = sizeof(*ctx) + crypto_ablkcipher_reqsize(private);
+
+ ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->iv = sock_kmalloc(sk, crypto_ablkcipher_ivsize(private),
+ GFP_KERNEL);
+ if (!ctx->iv) {
+ sock_kfree_s(sk, ctx, len);
+ return -ENOMEM;
+ }
+
+ memset(ctx->iv, 0, crypto_ablkcipher_ivsize(private));
+
+ INIT_LIST_HEAD(&ctx->tsgl);
+ ctx->len = len;
+ ctx->used = 0;
+ ctx->more = 0;
+ ctx->merge = 0;
+ ctx->enc = 0;
+ init_completion(&ctx->completion);
+
+ ask->private = ctx;
+
+ ablkcipher_request_set_tfm(&ctx->req, private);
+ ablkcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ skcipher_done, sk);
+
+ sk->sk_destruct = skcipher_sock_destruct;
+
+ return 0;
+}
+
+static const struct af_alg_type algif_type_skcipher = {
+ .bind = skcipher_bind,
+ .release = skcipher_release,
+ .setkey = skcipher_setkey,
+ .accept = skcipher_accept_parent,
+ .ops = &algif_skcipher_ops,
+ .name = "skcipher",
+ .owner = THIS_MODULE
+};
+
+static int __init algif_skcipher_init(void)
+{
+ return af_alg_register_type(&algif_type_skcipher);
+}
+
+static void __exit algif_skcipher_exit(void)
+{
+ int err = af_alg_unregister_type(&algif_type_skcipher);
+ BUG_ON(err);
+}
+
+module_init(algif_skcipher_init);
+module_exit(algif_skcipher_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_ALG);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
` (5 preceding siblings ...)
2010-10-19 13:46 ` [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations Herbert Xu
@ 2010-10-19 13:46 ` Herbert Xu
6 siblings, 0 replies; 25+ messages in thread
From: Herbert Xu @ 2010-10-19 13:46 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
crypto: algif_hash - User-space interface for hash operations
This patch adds the af_alg plugin for hash, corresponding to
the ahash kernel operation type.
Keys can optionally be set through the setsockopt interface.
Each sendmsg call will finalise the hash unless sent with a MSG_MORE
flag.
Partial hash states can be cloned using accept(2).
The interface is completely synchronous, all operations will
complete prior to the system call returning.
Both sendmsg(2) and splice(2) support reading the user-space
data directly without copying (except that the Crypto API itself
may copy the data if alignment is off).
For now only the splice(2) interface supports performing digest
instead of init/update/final. In future the sendmsg(2) interface
will also be modified to use digest/finup where possible so that
hardware that cannot return a partial hash state can still benefit
from this interface.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
crypto/Kconfig | 8 +
crypto/Makefile | 1
crypto/algif_hash.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 354 insertions(+)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 357e3ca..6db27d7 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -844,6 +844,14 @@ config CRYPTO_ANSI_CPRNG
config CRYPTO_USER_API
tristate
+config CRYPTO_USER_API_HASH
+ tristate "User-space interface for hash algorithms"
+ select CRYPTO_HASH
+ select CRYPTO_USER_API
+ help
+ This option enables the user-spaces interface for hash
+ algorithms.
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 0b13197..14ab405 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
+obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
#
# generic algorithms and the async_tx api
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
new file mode 100644
index 0000000..28f29aa
--- /dev/null
+++ b/crypto/algif_hash.c
@@ -0,0 +1,345 @@
+/*
+ * algif_hash: User-space interface for hash algorithms
+ *
+ * This file provides the user-space API for hash algorithms.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 <crypto/hash.h>
+#include <crypto/if_alg.h>
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+struct hash_ctx {
+ struct af_alg_sgl sgl;
+
+ struct completion completion;
+ u8 *result;
+
+ unsigned int len;
+ int err;
+ bool more;
+
+ struct ahash_request req;
+};
+
+static void hash_done(struct crypto_async_request *req, int err)
+{
+ struct sock *sk = req->data;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+
+ ctx->err = err;
+ complete(&ctx->completion);
+}
+
+static int hash_sendmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t ignored)
+{
+ int limit = ALG_MAX_PAGES * PAGE_SIZE;
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ unsigned long iovlen;
+ struct iovec *iov;
+ long copied = 0;
+ int err;
+
+ if (limit > sk->sk_sndbuf)
+ limit = sk->sk_sndbuf;
+
+ lock_sock(sk);
+ if (!ctx->more) {
+ err = crypto_ahash_init(&ctx->req);
+ if (err)
+ goto unlock;
+ }
+
+ ctx->more = 0;
+
+ for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
+ iovlen--, iov++) {
+ unsigned long seglen = iov->iov_len;
+ char *from = iov->iov_base;
+
+ while (seglen) {
+ int len = min_t(unsigned long, seglen, limit);
+ int newlen;
+
+ newlen = af_alg_make_sg(&ctx->sgl, from, len, 0);
+ if (newlen < 0)
+ goto unlock;
+
+ ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL,
+ newlen);
+
+ err = crypto_ahash_update(&ctx->req);
+ switch (err) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ wait_for_completion(&ctx->completion);
+ INIT_COMPLETION(ctx->completion);
+ err = ctx->err;
+ break;
+ }
+
+ af_alg_free_sg(&ctx->sgl);
+
+ if (err)
+ goto unlock;
+
+ seglen -= newlen;
+ from += newlen;
+ copied += newlen;
+ }
+ }
+
+ err = 0;
+
+ ctx->more = msg->msg_flags & MSG_MORE;
+ if (!ctx->more) {
+ ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
+ err = crypto_ahash_final(&ctx->req);
+
+ switch (err) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ wait_for_completion(&ctx->completion);
+ INIT_COMPLETION(ctx->completion);
+ err = ctx->err;
+ break;
+ }
+ }
+
+unlock:
+ release_sock(sk);
+
+ return err ?: copied;
+}
+
+static ssize_t hash_sendpage(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ int err;
+
+ lock_sock(sk);
+ sg_init_table(ctx->sgl.sg, 1);
+ sg_set_page(ctx->sgl.sg, page, size, offset);
+
+ ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ hash_done, sk);
+ ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size);
+
+ if (!(flags & MSG_MORE)) {
+ if (ctx->more)
+ err = crypto_ahash_finup(&ctx->req);
+ else
+ err = crypto_ahash_digest(&ctx->req);
+ } else {
+ if (!ctx->more) {
+ err = crypto_ahash_init(&ctx->req);
+ if (err)
+ goto unlock;
+ }
+
+ err = crypto_ahash_update(&ctx->req);
+ }
+
+ switch (err) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ wait_for_completion(&ctx->completion);
+ INIT_COMPLETION(ctx->completion);
+ err = ctx->err;
+ if (err)
+ goto unlock;
+ break;
+ }
+
+ err = 0;
+
+ ctx->more = flags & MSG_MORE;
+
+unlock:
+ release_sock(sk);
+
+ return err ?: size;
+}
+
+static int hash_recvmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));
+ int err = -EINVAL;
+
+ if (len > ds)
+ len = ds;
+ else if (len < ds)
+ msg->msg_flags |= MSG_TRUNC;
+
+ lock_sock(sk);
+ if (!ctx->more)
+ err = memcpy_toiovec(msg->msg_iov, ctx->result, len);
+ release_sock(sk);
+
+ return err ?: len;
+}
+
+static int hash_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ struct ahash_request *req = &ctx->req;
+ char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))];
+ struct sock *sk2;
+ struct alg_sock *ask2;
+ struct hash_ctx *ctx2;
+ int err;
+
+ err = crypto_ahash_export(req, state);
+ if (err)
+ return err;
+
+ err = af_alg_accept(sk, newsock);
+ if (err)
+ return err;
+
+ sk2 = newsock->sk;
+ ask2 = alg_sk(sk2);
+ ctx2 = ask2->private;
+
+ err = crypto_ahash_import(&ctx2->req, state);
+ if (err) {
+ sock_orphan(sk2);
+ sock_put(sk2);
+ }
+
+ return err;
+}
+
+static struct proto_ops algif_hash_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,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .setsockopt = sock_no_setsockopt,
+ .poll = sock_no_poll,
+
+ .release = af_alg_release,
+ .sendmsg = hash_sendmsg,
+ .sendpage = hash_sendpage,
+ .recvmsg = hash_recvmsg,
+ .accept = hash_accept,
+};
+
+static void *hash_bind(const char *name, u32 type, u32 mask)
+{
+ return crypto_alloc_ahash(name, type, mask);
+}
+
+static void hash_release(void *private)
+{
+ crypto_free_ahash(private);
+}
+
+static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
+{
+ return crypto_ahash_setkey(private, key, keylen);
+}
+
+static void hash_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+
+ sock_kfree_s(sk, ctx->result,
+ crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
+ sock_kfree_s(sk, ctx, ctx->len);
+ af_alg_release_parent(sk);
+}
+
+static int hash_accept_parent(void *private, struct sock *sk)
+{
+ struct hash_ctx *ctx;
+ struct alg_sock *ask = alg_sk(sk);
+ unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private);
+ unsigned ds = crypto_ahash_digestsize(private);
+
+ ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL);
+ if (!ctx->result) {
+ sock_kfree_s(sk, ctx, len);
+ return -ENOMEM;
+ }
+
+ memset(ctx->result, 0, ds);
+
+ ctx->len = len;
+ ctx->more = 0;
+ init_completion(&ctx->completion);
+
+ ask->private = ctx;
+
+ ahash_request_set_tfm(&ctx->req, private);
+ ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ hash_done, sk);
+
+ sk->sk_destruct = hash_sock_destruct;
+
+ return 0;
+}
+
+static const struct af_alg_type algif_type_hash = {
+ .bind = hash_bind,
+ .release = hash_release,
+ .setkey = hash_setkey,
+ .accept = hash_accept_parent,
+ .ops = &algif_hash_ops,
+ .name = "hash",
+ .owner = THIS_MODULE
+};
+
+static int __init algif_hash_init(void)
+{
+ return af_alg_register_type(&algif_type_hash);
+}
+
+static void __exit algif_hash_exit(void)
+{
+ int err = af_alg_unregister_type(&algif_type_hash);
+ BUG_ON(err);
+}
+
+module_init(algif_hash_init);
+module_exit(algif_hash_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_ALG);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH 1/4] net - Add AF_ALG macros
2010-10-19 13:46 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
@ 2010-10-20 9:01 ` David Miller
0 siblings, 0 replies; 25+ messages in thread
From: David Miller @ 2010-10-20 9:01 UTC (permalink / raw)
To: herbert; +Cc: linux-crypto, netdev, linux-kernel
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Tue, 19 Oct 2010 21:46:01 +0800
> net - Add AF_ALG macros
>
> This patch adds the socket family/level macros for the yet-to-be-born
> AF_ALG family. The AF_ALG family provides the user-space interface
> for the kernel crypto API.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-10-19 13:44 ` Herbert Xu
@ 2010-10-20 10:24 ` Nikos Mavrogiannopoulos
2010-11-04 17:34 ` Herbert Xu
1 sibling, 0 replies; 25+ messages in thread
From: Nikos Mavrogiannopoulos @ 2010-10-20 10:24 UTC (permalink / raw)
To: Herbert Xu
Cc: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List,
Cryptodev-linux-devel
On Tue, Oct 19, 2010 at 3:44 PM, Herbert Xu
<herbert@gondor.hengli.com.au> wrote:
> OK I've gone ahead and implemented the user-space API for hashes
> and ciphers.
> To recap this interface is designed to allow user-space programs
> to access hardware cryptographic accelerators that we have added
> to the kernel.
> The intended usage scenario is where a large amount of data needs
> to be processed where the benefits offered by hardware acceleration
> that is normally unavailable in user-space (as opposed to ones
> such as the Intel AES instruction which may be used directly from
> user-space) outweigh the overhead of going through the kernel.
What is the overall advantage of this API comparing to other existing
ones that achieve similar goals[0][1]?
Some observations:
1. To perform an encryption of data 6 system calls are made (I don't
count the 2 used for socket initialization since I suppose can be global
for all operations) and a file descriptor is assigned. The number of
system calls
made has great impact to the actual speed seen by userspace (as you said this
API is for user-space to access the high-speed peripherals that do encryption).
2. Due to the usage of read() and write() no zero-copy can happen for
user-space buffers[3].
regards,
Nikos
[0]. http://home.gna.org/cryptodev-linux/
[1]. http://home.gna.org/cryptodev-linux/ncr.html
[2]. The openbsd[0] api can do it with 3 system calls and NCR[1] with one,
and both require no file descriptor for each operation.
[3]. The openbsd[0] api and NCR[1] do zero-copy for user-space buffers.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: RFC: Crypto API User-interface
2010-10-19 13:44 ` Herbert Xu
2010-10-20 10:24 ` Nikos Mavrogiannopoulos
@ 2010-11-04 17:34 ` Herbert Xu
2010-11-04 17:36 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
` (3 more replies)
1 sibling, 4 replies; 25+ messages in thread
From: Herbert Xu @ 2010-11-04 17:34 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
On Tue, Oct 19, 2010 at 09:44:18PM +0800, Herbert Xu wrote:
>
> OK I've gone ahead and implemented the user-space API for hashes
> and ciphers.
Here is a revised series with bug fixes and improvements. The
main change is that hashes can now be finalised by recvmsg instead
of requiring a preceding sendmsg with no MSG_MORE.
Thakns to Miloslav Trmac for reviewing this and contributing
fixes and improvements.
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 1/4] net - Add AF_ALG macros
2010-11-04 17:34 ` Herbert Xu
2010-11-04 17:36 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
@ 2010-11-04 17:36 ` Herbert Xu
2010-11-04 19:22 ` David Miller
2010-11-04 17:36 ` [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations Herbert Xu
2010-11-04 17:36 ` [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations Herbert Xu
3 siblings, 1 reply; 25+ messages in thread
From: Herbert Xu @ 2010-11-04 17:36 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
net - Add AF_ALG macros
This patch adds the socket family/level macros for the yet-to-be-born
AF_ALG family. The AF_ALG family provides the user-space interface
for the kernel crypto API.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
include/linux/socket.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 5146b50..ebc081b 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -193,7 +193,8 @@ struct ucred {
#define AF_PHONET 35 /* Phonet sockets */
#define AF_IEEE802154 36 /* IEEE802154 sockets */
#define AF_CAIF 37 /* CAIF sockets */
-#define AF_MAX 38 /* For now.. */
+#define AF_ALG 38 /* Algorithm sockets */
+#define AF_MAX 39 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -234,6 +235,7 @@ struct ucred {
#define PF_PHONET AF_PHONET
#define PF_IEEE802154 AF_IEEE802154
#define PF_CAIF AF_CAIF
+#define PF_ALG AF_ALG
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -307,6 +309,7 @@ struct ucred {
#define SOL_RDS 276
#define SOL_IUCV 277
#define SOL_CAIF 278
+#define SOL_ALG 279
/* IPX options */
#define IPX_TYPE 1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API
2010-11-04 17:34 ` Herbert Xu
@ 2010-11-04 17:36 ` Herbert Xu
2010-11-04 19:23 ` David Miller
2010-11-04 17:36 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
` (2 subsequent siblings)
3 siblings, 1 reply; 25+ messages in thread
From: Herbert Xu @ 2010-11-04 17:36 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
crypto: af_alg - User-space interface for Crypto API
This patch creates the backbone of the user-space interface for
the Crypto API, through a new socket family AF_ALG.
Each session corresponds to one or more connections obtained from
that socket. The number depends on the number of inputs/outputs
of that particular type of operation. For most types there will
be a s ingle connection/file descriptor that is used for both input
and output. AEAD is one of the few that require two inputs.
Each algorithm type will provide its own implementation that plugs
into af_alg. They're keyed using a string such as "skcipher" or
"hash".
IOW this patch only contains the boring bits that is required
to hold everything together.
Thakns to Miloslav Trmac for reviewing this and contributing
fixes and improvements.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
crypto/Kconfig | 3
crypto/Makefile | 1
crypto/af_alg.c | 460 ++++++++++++++++++++++++++++++++++++++++++++++++
include/crypto/if_alg.h | 92 +++++++++
include/linux/if_alg.h | 40 ++++
5 files changed, 596 insertions(+)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index e4bac29..357e3ca 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -841,6 +841,9 @@ config CRYPTO_ANSI_CPRNG
ANSI X9.31 A.2.4. Note that this option must be enabled if
CRYPTO_FIPS is selected
+config CRYPTO_USER_API
+ tristate
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 423b7de..0b13197 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_CRYPTO_RNG2) += krng.o
obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
+obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
#
# generic algorithms and the async_tx api
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
new file mode 100644
index 0000000..92cd9b1
--- /dev/null
+++ b/crypto/af_alg.c
@@ -0,0 +1,460 @@
+/*
+ * af_alg: User-space algorithm interface
+ *
+ * This file provides the user-space API for algorithms.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 <asm/atomic.h>
+#include <crypto/if_alg.h>
+#include <linux/crypto.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/rwsem.h>
+
+struct alg_type_list {
+ const struct af_alg_type *type;
+ struct list_head list;
+};
+
+static atomic_t alg_memory_allocated;
+
+static struct proto alg_proto = {
+ .name = "ALG",
+ .owner = THIS_MODULE,
+ .memory_allocated = &alg_memory_allocated,
+ .obj_size = sizeof(struct alg_sock),
+};
+
+static LIST_HEAD(alg_types);
+static DECLARE_RWSEM(alg_types_sem);
+
+static const struct af_alg_type *alg_get_type(const char *name)
+{
+ const struct af_alg_type *type = ERR_PTR(-ENOENT);
+ struct alg_type_list *node;
+
+ down_read(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (strcmp(node->type->name, name))
+ continue;
+
+ if (try_module_get(node->type->owner))
+ type = node->type;
+ break;
+ }
+ up_read(&alg_types_sem);
+
+ return type;
+}
+
+int af_alg_register_type(const struct af_alg_type *type)
+{
+ struct alg_type_list *node;
+ int err = -EEXIST;
+
+ down_write(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (!strcmp(node->type->name, type->name))
+ goto unlock;
+ }
+
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!node)
+ goto unlock;
+
+ type->ops->owner = THIS_MODULE;
+ node->type = type;
+ list_add(&node->list, &alg_types);
+ err = 0;
+
+unlock:
+ up_write(&alg_types_sem);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_register_type);
+
+int af_alg_unregister_type(const struct af_alg_type *type)
+{
+ struct alg_type_list *node;
+ int err = -ENOENT;
+
+ down_write(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (strcmp(node->type->name, type->name))
+ continue;
+
+ list_del(&node->list);
+ kfree(node);
+ err = 0;
+ break;
+ }
+ up_write(&alg_types_sem);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_unregister_type);
+
+static void alg_do_release(const struct af_alg_type *type, void *private)
+{
+ if (!type)
+ return;
+
+ type->release(private);
+ module_put(type->owner);
+}
+
+int af_alg_release(struct socket *sock)
+{
+ if (sock->sk)
+ sock_put(sock->sk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_release);
+
+static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct sockaddr_alg *sa = (void *)uaddr;
+ const struct af_alg_type *type;
+ void *private;
+
+ if (sock->state == SS_CONNECTED)
+ return -EINVAL;
+
+ if (addr_len != sizeof(*sa))
+ return -EINVAL;
+
+ sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
+ sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
+
+ type = alg_get_type(sa->salg_type);
+ if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
+ request_module("algif-%s", sa->salg_type);
+ type = alg_get_type(sa->salg_type);
+ }
+
+ if (IS_ERR(type))
+ return PTR_ERR(type);
+
+ private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
+ if (IS_ERR(private)) {
+ module_put(type->owner);
+ return PTR_ERR(private);
+ }
+
+ lock_sock(sk);
+
+ swap(ask->type, type);
+ swap(ask->private, private);
+
+ release_sock(sk);
+
+ alg_do_release(type, private);
+
+ return 0;
+}
+
+static int alg_setkey(struct sock *sk, char __user *ukey,
+ unsigned int keylen)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type = ask->type;
+ u8 *key;
+ int err;
+
+ key = sock_kmalloc(sk, keylen, GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ if (copy_from_user(key, ukey, keylen))
+ return -EFAULT;
+
+ err = type->setkey(ask->private, key, keylen);
+
+ sock_kfree_s(sk, key, keylen);
+
+ return err;
+}
+
+static int alg_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type = ask->type;
+
+ if (level != SOL_ALG || !type)
+ return -ENOPROTOOPT;
+
+ switch (optname) {
+ case ALG_SET_KEY:
+ if (sock->state == SS_CONNECTED)
+ return -ENOPROTOOPT;
+ if (!type->setkey)
+ return -ENOPROTOOPT;
+
+ return alg_setkey(sk, optval, optlen);
+ }
+
+ return -ENOPROTOOPT;
+}
+
+int af_alg_accept(struct sock *sk, struct socket *newsock)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type = ask->type;
+ struct sock *sk2;
+ int err;
+
+ if (!type)
+ return -EINVAL;
+
+ sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto);
+ if (!sk2)
+ return -ENOMEM;
+
+ sock_init_data(newsock, sk2);
+
+ err = type->accept(ask->private, sk2);
+ if (err) {
+ sk_free(sk2);
+ return err;
+ }
+
+ sk2->sk_family = PF_ALG;
+
+ sock_hold(sk);
+ alg_sk(sk2)->parent = sk;
+ alg_sk(sk2)->type = type;
+
+ newsock->ops = type->ops;
+ newsock->state = SS_CONNECTED;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_accept);
+
+static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ return af_alg_accept(sock->sk, newsock);
+}
+
+static const struct proto_ops alg_proto_ops = {
+ .family = PF_ALG,
+ .owner = THIS_MODULE,
+
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .getname = sock_no_getname,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .poll = sock_no_poll,
+
+ .bind = alg_bind,
+ .release = af_alg_release,
+ .setsockopt = alg_setsockopt,
+ .accept = alg_accept,
+};
+
+static void alg_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+
+ alg_do_release(ask->type, ask->private);
+}
+
+static int alg_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk;
+ int err;
+
+ if (sock->type != SOCK_SEQPACKET)
+ return -ESOCKTNOSUPPORT;
+ if (protocol != 0)
+ return -EPROTONOSUPPORT;
+
+ err = -ENOMEM;
+ sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto);
+ if (!sk)
+ goto out;
+
+ sock->ops = &alg_proto_ops;
+ sock_init_data(sock, sk);
+
+ sk->sk_family = PF_ALG;
+ sk->sk_destruct = alg_sock_destruct;
+
+ return 0;
+out:
+ return err;
+}
+
+static const struct net_proto_family alg_family = {
+ .family = PF_ALG,
+ .create = alg_create,
+ .owner = THIS_MODULE,
+};
+
+int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
+ int write)
+{
+ unsigned long from = (unsigned long)addr;
+ unsigned long npages;
+ unsigned off;
+ int err;
+ int i;
+
+ err = -EFAULT;
+ if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, addr, len))
+ goto out;
+
+ off = from & ~PAGE_MASK;
+ npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ err = get_user_pages_fast(from, npages, write, sgl->pages);
+ if (err < 0)
+ goto out;
+
+ npages = err;
+ err = -EINVAL;
+ if (WARN_ON(npages == 0))
+ goto out;
+
+ err = 0;
+
+ sg_init_table(sgl->sg, npages);
+
+ for (i = 0; i < npages; i++) {
+ int plen = min_t(int, len, PAGE_SIZE - off);
+
+ sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
+
+ off = 0;
+ len -= plen;
+ err += plen;
+ }
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_make_sg);
+
+void af_alg_free_sg(struct af_alg_sgl *sgl)
+{
+ int i;
+
+ i = 0;
+ do {
+ put_page(sgl->pages[i]);
+ } while (!sg_is_last(sgl->sg + (i++)));
+}
+EXPORT_SYMBOL_GPL(af_alg_free_sg);
+
+int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (!CMSG_OK(msg, cmsg))
+ return -EINVAL;
+ if (cmsg->cmsg_level != SOL_ALG)
+ continue;
+
+ switch(cmsg->cmsg_type) {
+ case ALG_SET_IV:
+ if (cmsg->cmsg_len < sizeof(*con->iv))
+ return -EINVAL;
+ con->iv = (void *)CMSG_DATA(cmsg);
+ if (cmsg->cmsg_len < con->iv->ivlen +
+ sizeof(con->iv->ivlen))
+ return -EINVAL;
+ break;
+
+ case ALG_SET_OP:
+ if (cmsg->cmsg_len < sizeof(u32))
+ return -EINVAL;
+ con->op = *(u32 *)CMSG_DATA(cmsg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
+
+int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
+{
+ switch (err) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ wait_for_completion(&completion->completion);
+ INIT_COMPLETION(completion->completion);
+ err = completion->err;
+ break;
+ };
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
+
+void af_alg_complete(struct crypto_async_request *req, int err)
+{
+ struct af_alg_completion *completion = req->data;
+
+ completion->err = err;
+ complete(&completion->completion);
+}
+EXPORT_SYMBOL_GPL(af_alg_complete);
+
+static int __init af_alg_init(void)
+{
+ int err = proto_register(&alg_proto, 0);
+
+ if (err)
+ goto out;
+
+ err = sock_register(&alg_family);
+ if (err != 0)
+ goto out_unregister_proto;
+
+out:
+ return err;
+
+out_unregister_proto:
+ proto_unregister(&alg_proto);
+ goto out;
+}
+
+static void __exit af_alg_exit(void)
+{
+ sock_unregister(PF_ALG);
+ proto_unregister(&alg_proto);
+}
+
+module_init(af_alg_init);
+module_exit(af_alg_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_ALG);
diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h
new file mode 100644
index 0000000..c5813c8
--- /dev/null
+++ b/include/crypto/if_alg.h
@@ -0,0 +1,92 @@
+/*
+ * if_alg: User-space algorithm interface
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _CRYPTO_IF_ALG_H
+#define _CRYPTO_IF_ALG_H
+
+#include <linux/compiler.h>
+#include <linux/completion.h>
+#include <linux/if_alg.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+#define ALG_MAX_PAGES 16
+
+struct crypto_async_request;
+
+struct alg_sock {
+ /* struct sock must be the first member of struct alg_sock */
+ struct sock sk;
+
+ struct sock *parent;
+
+ const struct af_alg_type *type;
+ void *private;
+};
+
+struct af_alg_completion {
+ struct completion completion;
+ int err;
+};
+
+struct af_alg_control {
+ struct af_alg_iv *iv;
+ int op;
+};
+
+struct af_alg_type {
+ void *(*bind)(const char *name, u32 type, u32 mask);
+ void (*release)(void *private);
+ int (*setkey)(void *private, const u8 *key, unsigned int keylen);
+ int (*accept)(void *private, struct sock *sk);
+
+ struct proto_ops *ops;
+ struct module *owner;
+ char name[14];
+};
+
+struct af_alg_sgl {
+ struct scatterlist sg[ALG_MAX_PAGES];
+ struct page *pages[ALG_MAX_PAGES];
+};
+
+int af_alg_register_type(const struct af_alg_type *type);
+int af_alg_unregister_type(const struct af_alg_type *type);
+
+int af_alg_release(struct socket *sock);
+int af_alg_accept(struct sock *sk, struct socket *newsock);
+
+int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
+ int write);
+void af_alg_free_sg(struct af_alg_sgl *sgl);
+
+int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con);
+
+int af_alg_wait_for_completion(int err, struct af_alg_completion *completion);
+void af_alg_complete(struct crypto_async_request *req, int err);
+
+static inline struct alg_sock *alg_sk(struct sock *sk)
+{
+ return (struct alg_sock *)sk;
+}
+
+static inline void af_alg_release_parent(struct sock *sk)
+{
+ sock_put(alg_sk(sk)->parent);
+}
+
+static inline void af_alg_init_completion(struct af_alg_completion *completion)
+{
+ init_completion(&completion->completion);
+}
+
+#endif /* _CRYPTO_IF_ALG_H */
diff --git a/include/linux/if_alg.h b/include/linux/if_alg.h
new file mode 100644
index 0000000..0f9acce
--- /dev/null
+++ b/include/linux/if_alg.h
@@ -0,0 +1,40 @@
+/*
+ * if_alg: User-space algorithm interface
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_IF_ALG_H
+#define _LINUX_IF_ALG_H
+
+#include <linux/types.h>
+
+struct sockaddr_alg {
+ __u16 salg_family;
+ __u8 salg_type[14];
+ __u32 salg_feat;
+ __u32 salg_mask;
+ __u8 salg_name[64];
+};
+
+struct af_alg_iv {
+ __u32 ivlen;
+ __u8 iv[0];
+};
+
+/* Socket options */
+#define ALG_SET_KEY 1
+#define ALG_SET_IV 2
+#define ALG_SET_OP 3
+
+/* Operations */
+#define ALG_OP_DECRYPT 0
+#define ALG_OP_ENCRYPT 1
+
+#endif /* _LINUX_IF_ALG_H */
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations
2010-11-04 17:34 ` Herbert Xu
2010-11-04 17:36 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
2010-11-04 17:36 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
@ 2010-11-04 17:36 ` Herbert Xu
2010-11-04 19:23 ` David Miller
2010-11-04 17:36 ` [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations Herbert Xu
3 siblings, 1 reply; 25+ messages in thread
From: Herbert Xu @ 2010-11-04 17:36 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
crypto: algif_hash - User-space interface for hash operations
This patch adds the af_alg plugin for hash, corresponding to
the ahash kernel operation type.
Keys can optionally be set through the setsockopt interface.
Each sendmsg call will finalise the hash unless sent with a MSG_MORE
flag.
Partial hash states can be cloned using accept(2).
The interface is completely synchronous, all operations will
complete prior to the system call returning.
Both sendmsg(2) and splice(2) support reading the user-space
data directly without copying (except that the Crypto API itself
may copy the data if alignment is off).
For now only the splice(2) interface supports performing digest
instead of init/update/final. In future the sendmsg(2) interface
will also be modified to use digest/finup where possible so that
hardware that cannot return a partial hash state can still benefit
from this interface.
Thakns to Miloslav Trmac for reviewing this and contributing
fixes and improvements.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
crypto/Kconfig | 8 +
crypto/Makefile | 1
crypto/algif_hash.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 330 insertions(+)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 357e3ca..6db27d7 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -844,6 +844,14 @@ config CRYPTO_ANSI_CPRNG
config CRYPTO_USER_API
tristate
+config CRYPTO_USER_API_HASH
+ tristate "User-space interface for hash algorithms"
+ select CRYPTO_HASH
+ select CRYPTO_USER_API
+ help
+ This option enables the user-spaces interface for hash
+ algorithms.
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 0b13197..14ab405 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
+obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
#
# generic algorithms and the async_tx api
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
new file mode 100644
index 0000000..014ea43
--- /dev/null
+++ b/crypto/algif_hash.c
@@ -0,0 +1,321 @@
+/*
+ * algif_hash: User-space interface for hash algorithms
+ *
+ * This file provides the user-space API for hash algorithms.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 <crypto/hash.h>
+#include <crypto/if_alg.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+struct hash_ctx {
+ struct af_alg_sgl sgl;
+
+ u8 *result;
+
+ struct af_alg_completion completion;
+
+ unsigned int len;
+ int err;
+ bool more;
+
+ struct ahash_request req;
+};
+
+static int hash_sendmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t ignored)
+{
+ int limit = ALG_MAX_PAGES * PAGE_SIZE;
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ unsigned long iovlen;
+ struct iovec *iov;
+ long copied = 0;
+ int err;
+
+ if (limit > sk->sk_sndbuf)
+ limit = sk->sk_sndbuf;
+
+ lock_sock(sk);
+ if (!ctx->more) {
+ err = crypto_ahash_init(&ctx->req);
+ if (err)
+ goto unlock;
+ }
+
+ ctx->more = 0;
+
+ for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
+ iovlen--, iov++) {
+ unsigned long seglen = iov->iov_len;
+ char __user *from = iov->iov_base;
+
+ while (seglen) {
+ int len = min_t(unsigned long, seglen, limit);
+ int newlen;
+
+ newlen = af_alg_make_sg(&ctx->sgl, from, len, 0);
+ if (newlen < 0)
+ goto unlock;
+
+ ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL,
+ newlen);
+
+ err = af_alg_wait_for_completion(
+ crypto_ahash_update(&ctx->req),
+ &ctx->completion);
+
+ af_alg_free_sg(&ctx->sgl);
+
+ if (err)
+ goto unlock;
+
+ seglen -= newlen;
+ from += newlen;
+ copied += newlen;
+ }
+ }
+
+ err = 0;
+
+ ctx->more = msg->msg_flags & MSG_MORE;
+ if (!ctx->more) {
+ ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
+ err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
+ &ctx->completion);
+ }
+
+unlock:
+ release_sock(sk);
+
+ return err ?: copied;
+}
+
+static ssize_t hash_sendpage(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ int err;
+
+ lock_sock(sk);
+ sg_init_table(ctx->sgl.sg, 1);
+ sg_set_page(ctx->sgl.sg, page, size, offset);
+
+ ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size);
+
+ if (!(flags & MSG_MORE)) {
+ if (ctx->more)
+ err = crypto_ahash_finup(&ctx->req);
+ else
+ err = crypto_ahash_digest(&ctx->req);
+ } else {
+ if (!ctx->more) {
+ err = crypto_ahash_init(&ctx->req);
+ if (err)
+ goto unlock;
+ }
+
+ err = crypto_ahash_update(&ctx->req);
+ }
+
+ err = af_alg_wait_for_completion(err, &ctx->completion);
+ if (err)
+ goto unlock;
+
+ ctx->more = flags & MSG_MORE;
+
+unlock:
+ release_sock(sk);
+
+ return err ?: size;
+}
+
+static int hash_recvmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));
+ int err;
+
+ if (len > ds)
+ len = ds;
+ else if (len < ds)
+ msg->msg_flags |= MSG_TRUNC;
+
+ lock_sock(sk);
+ if (ctx->more) {
+ ctx->more = 0;
+ ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
+ err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
+ &ctx->completion);
+ if (err)
+ goto unlock;
+ }
+
+ err = memcpy_toiovec(msg->msg_iov, ctx->result, len);
+
+unlock:
+ release_sock(sk);
+
+ return err ?: len;
+}
+
+static int hash_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ struct ahash_request *req = &ctx->req;
+ char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))];
+ struct sock *sk2;
+ struct alg_sock *ask2;
+ struct hash_ctx *ctx2;
+ int err;
+
+ err = crypto_ahash_export(req, state);
+ if (err)
+ return err;
+
+ err = af_alg_accept(ask->parent, newsock);
+ if (err)
+ return err;
+
+ sk2 = newsock->sk;
+ ask2 = alg_sk(sk2);
+ ctx2 = ask2->private;
+ ctx2->more = 1;
+
+ err = crypto_ahash_import(&ctx2->req, state);
+ if (err) {
+ sock_orphan(sk2);
+ sock_put(sk2);
+ }
+
+ return err;
+}
+
+static struct proto_ops algif_hash_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,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .setsockopt = sock_no_setsockopt,
+ .poll = sock_no_poll,
+
+ .release = af_alg_release,
+ .sendmsg = hash_sendmsg,
+ .sendpage = hash_sendpage,
+ .recvmsg = hash_recvmsg,
+ .accept = hash_accept,
+};
+
+static void *hash_bind(const char *name, u32 type, u32 mask)
+{
+ return crypto_alloc_ahash(name, type, mask);
+}
+
+static void hash_release(void *private)
+{
+ crypto_free_ahash(private);
+}
+
+static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
+{
+ return crypto_ahash_setkey(private, key, keylen);
+}
+
+static void hash_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+
+ sock_kfree_s(sk, ctx->result,
+ crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
+ sock_kfree_s(sk, ctx, ctx->len);
+ af_alg_release_parent(sk);
+}
+
+static int hash_accept_parent(void *private, struct sock *sk)
+{
+ struct hash_ctx *ctx;
+ struct alg_sock *ask = alg_sk(sk);
+ unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private);
+ unsigned ds = crypto_ahash_digestsize(private);
+
+ ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL);
+ if (!ctx->result) {
+ sock_kfree_s(sk, ctx, len);
+ return -ENOMEM;
+ }
+
+ memset(ctx->result, 0, ds);
+
+ ctx->len = len;
+ ctx->more = 0;
+ af_alg_init_completion(&ctx->completion);
+
+ ask->private = ctx;
+
+ ahash_request_set_tfm(&ctx->req, private);
+ ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ af_alg_complete, &ctx->completion);
+
+ sk->sk_destruct = hash_sock_destruct;
+
+ return 0;
+}
+
+static const struct af_alg_type algif_type_hash = {
+ .bind = hash_bind,
+ .release = hash_release,
+ .setkey = hash_setkey,
+ .accept = hash_accept_parent,
+ .ops = &algif_hash_ops,
+ .name = "hash",
+ .owner = THIS_MODULE
+};
+
+static int __init algif_hash_init(void)
+{
+ return af_alg_register_type(&algif_type_hash);
+}
+
+static void __exit algif_hash_exit(void)
+{
+ int err = af_alg_unregister_type(&algif_type_hash);
+ BUG_ON(err);
+}
+
+module_init(algif_hash_init);
+module_exit(algif_hash_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_ALG);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations
2010-11-04 17:34 ` Herbert Xu
` (2 preceding siblings ...)
2010-11-04 17:36 ` [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations Herbert Xu
@ 2010-11-04 17:36 ` Herbert Xu
2010-11-04 19:23 ` David Miller
3 siblings, 1 reply; 25+ messages in thread
From: Herbert Xu @ 2010-11-04 17:36 UTC (permalink / raw)
To: Linux Crypto Mailing List, netdev, Linux Kernel Mailing List
crypto: algif_skcipher - User-space interface for skcipher operations
This patch adds the af_alg plugin for symmetric key ciphers,
corresponding to the ablkcipher kernel operation type.
Keys can optionally be set through the setsockopt interface.
Once a sendmsg call occurs without MSG_MORE no further writes
may be made to the socket until all previous data has been read.
IVs and and whether encryption/decryption is performed can be
set through the setsockopt interface or as a control message
to sendmsg.
The interface is completely synchronous, all operations are
carried out in recvmsg(2) and will complete prior to the system
call returning.
The splice(2) interface support reading the user-space data directly
without copying (except that the Crypto API itself may copy the data
if alignment is off).
The recvmsg(2) interface supports directly writing to user-space
without additional copying, i.e., the kernel crypto interface will
receive the user-space address as its output SG list.
Thakns to Miloslav Trmac for reviewing this and contributing
fixes and improvements.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
crypto/Kconfig | 8
crypto/Makefile | 1
crypto/algif_skcipher.c | 647 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 656 insertions(+)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 6db27d7..69437e2 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -852,6 +852,14 @@ config CRYPTO_USER_API_HASH
This option enables the user-spaces interface for hash
algorithms.
+config CRYPTO_USER_API_SKCIPHER
+ tristate "User-space interface for symmetric key cipher algorithms"
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_USER_API
+ help
+ This option enables the user-spaces interface for symmetric
+ key cipher algorithms.
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 14ab405..efc0f18 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
+obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
#
# generic algorithms and the async_tx api
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
new file mode 100644
index 0000000..abc15b2
--- /dev/null
+++ b/crypto/algif_skcipher.c
@@ -0,0 +1,647 @@
+/*
+ * algif_skcipher: User-space interface for skcipher algorithms
+ *
+ * This file provides the user-space API for symmetric key ciphers.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 <crypto/skcipher.h>
+#include <crypto/if_alg.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+struct skcipher_sg_list {
+ struct list_head list;
+
+ int cur;
+
+ struct scatterlist sg[0];
+};
+
+struct skcipher_ctx {
+ struct list_head tsgl;
+ struct af_alg_sgl rsgl;
+
+ void *iv;
+
+ struct af_alg_completion completion;
+
+ unsigned used;
+
+ unsigned int len;
+ bool more;
+ bool merge;
+ bool enc;
+
+ struct ablkcipher_request req;
+};
+
+#define MAX_SGL_ENTS ((PAGE_SIZE - sizeof(struct skcipher_sg_list)) / \
+ sizeof(struct scatterlist) - 1)
+
+static inline bool skcipher_writable(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+
+ return ctx->used + PAGE_SIZE <= max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+}
+
+static int skcipher_alloc_sgl(struct sock *sk, int size)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg = NULL;
+
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list, list);
+ if (!list_empty(&ctx->tsgl))
+ sg = sgl->sg;
+
+ if (!sg || sgl->cur >= MAX_SGL_ENTS) {
+ sgl = sock_kmalloc(sk, sizeof(*sgl) +
+ sizeof(sgl->sg[0]) * (MAX_SGL_ENTS + 1),
+ GFP_KERNEL);
+ if (!sgl)
+ return -ENOMEM;
+
+ sg_init_table(sgl->sg, MAX_SGL_ENTS + 1);
+ sgl->cur = 0;
+
+ if (sg)
+ sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg);
+
+ list_add_tail(&sgl->list, &ctx->tsgl);
+ }
+
+ return 0;
+}
+
+static void skcipher_pull_sgl(struct sock *sk, int used)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg;
+ int i;
+
+ while (!list_empty(&ctx->tsgl)) {
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list,
+ list);
+ sg = sgl->sg;
+
+ for (i = 0; i < sgl->cur; i++) {
+ int plen = min_t(int, used, sg[i].length);
+
+ if (!sg_page(sg + i))
+ continue;
+
+ if (!used)
+ return;
+
+ sg[i].length -= plen;
+ sg[i].offset += plen;
+
+ if (!sg[i].length) {
+ put_page(sg_page(sg + i));
+ sg_assign_page(sg + i, NULL);
+ }
+
+ used -= plen;
+ ctx->used -= plen;
+ }
+
+ list_del(&sgl->list);
+ sock_kfree_s(sk, sgl,
+ sizeof(*sgl) + sizeof(sgl->sg[0]) *
+ (MAX_SGL_ENTS + 1));
+ }
+
+ if (!ctx->used)
+ ctx->merge = 0;
+}
+
+static void skcipher_free_sgl(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+
+ skcipher_pull_sgl(sk, ctx->used);
+}
+
+static int skcipher_wait_for_wmem(struct sock *sk, unsigned flags)
+{
+ DEFINE_WAIT(wait);
+ int err = -ERESTARTSYS;
+
+ if (flags & MSG_DONTWAIT)
+ return -EAGAIN;
+
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ if (skcipher_writable(sk)) {
+ err = 0;
+ break;
+ }
+ schedule();
+ }
+ finish_wait(sk_sleep(sk), &wait);
+
+ return err;
+}
+
+static void skcipher_wmem_wakeup(struct sock *sk)
+{
+ struct socket_wq *wq;
+
+ if (!skcipher_writable(sk))
+ return;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
+ POLLRDNORM |
+ POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
+ rcu_read_unlock();
+}
+
+static int skcipher_wait_for_data(struct sock *sk, unsigned flags)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ DEFINE_WAIT(wait);
+ int err = -ERESTARTSYS;
+
+ if (flags & MSG_DONTWAIT) {
+ return -EAGAIN;
+ }
+
+ set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ if (ctx->used) {
+ err = 0;
+ break;
+ }
+ schedule();
+ }
+ finish_wait(sk_sleep(sk), &wait);
+
+ clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+
+ return err;
+}
+
+static void skcipher_data_wakeup(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct socket_wq *wq;
+
+ if (!ctx->used);
+ return;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
+ POLLRDNORM |
+ POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
+ rcu_read_unlock();
+}
+
+static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
+ unsigned ivsize = crypto_ablkcipher_ivsize(tfm);
+ struct skcipher_sg_list *sgl;
+ struct af_alg_control con = {};
+ long copied = 0;
+ bool enc = 0;
+ int limit;
+ int err;
+ int i;
+
+ if (msg->msg_controllen) {
+ err = af_alg_cmsg_send(msg, &con);
+ if (err)
+ return err;
+
+ switch (con.op) {
+ case ALG_OP_ENCRYPT:
+ enc = 1;
+ break;
+ case ALG_OP_DECRYPT:
+ enc = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (con.iv && con.iv->ivlen != ivsize)
+ return -EINVAL;
+ }
+
+ err = -EINVAL;
+
+ lock_sock(sk);
+ if (!ctx->more && ctx->used)
+ goto unlock;
+
+ if (!ctx->used) {
+ ctx->enc = enc;
+ if (con.iv)
+ memcpy(ctx->iv, con.iv->iv, ivsize);
+ }
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+
+ while (size) {
+ struct scatterlist *sg;
+ unsigned long len = size;
+ int plen;
+
+ if (ctx->merge) {
+ sgl = list_entry(ctx->tsgl.prev,
+ struct skcipher_sg_list, list);
+ sg = sgl->sg + sgl->cur - 1;
+ len = min_t(unsigned long, len, PAGE_SIZE - sg->length);
+
+ err = memcpy_fromiovec(page_address(sg_page(sg)) +
+ sg->length, msg->msg_iov, len);
+ if (err)
+ goto unlock;
+
+ sg->length += len;
+ ctx->merge = sg->length & (PAGE_SIZE - 1);
+
+ size -= len;
+ copied += len;
+ continue;
+ }
+
+ if (limit < PAGE_SIZE) {
+ release_sock(sk);
+ err = skcipher_wait_for_wmem(sk, msg->msg_flags);
+ lock_sock(sk);
+ if (err)
+ goto unlock;
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+ }
+
+ len = min_t(unsigned long, len, limit);
+
+ err = skcipher_alloc_sgl(sk, len);
+ if (err)
+ goto unlock;
+
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list,
+ list);
+ sg = sgl->sg;
+ do {
+ i = sgl->cur;
+ plen = min_t(int, len, PAGE_SIZE);
+
+ sg_assign_page(sg + i, alloc_page(GFP_KERNEL));
+ err = -ENOMEM;
+ if (!sg_page(sg + i))
+ goto unlock;
+
+ err = memcpy_fromiovec(page_address(sg_page(sg + i)),
+ msg->msg_iov, plen);
+ if (err) {
+ __free_page(sg_page(sg + i));
+ sg_assign_page(sg + i, NULL);
+ goto unlock;
+ }
+
+ sg[i].length = plen;
+ len -= plen;
+ ctx->used += plen;
+ copied += plen;
+ size -= plen;
+ limit -= plen;
+ sgl->cur++;
+ } while (sg_page(sg + ++i));
+
+ ctx->merge = plen & (PAGE_SIZE - 1);
+ if (ctx->merge)
+ sgl->cur--;
+ }
+
+ err = 0;
+
+ ctx->more = msg->msg_flags & MSG_MORE;
+ if (!ctx->more && !list_empty(&ctx->tsgl)) {
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+ sg_mark_end(sgl->sg + sgl->cur - 1);
+ }
+
+unlock:
+ skcipher_data_wakeup(sk);
+ release_sock(sk);
+
+ return copied ?: err;
+}
+
+static ssize_t skcipher_sendpage(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ int err = -EINVAL;
+ int limit;
+
+ lock_sock(sk);
+ if (!ctx->more && ctx->used)
+ goto unlock;
+
+ if (!size)
+ goto done;
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+
+ if (limit < PAGE_SIZE) {
+ release_sock(sk);
+ err = skcipher_wait_for_wmem(sk, flags);
+ lock_sock(sk);
+ if (err)
+ goto unlock;
+
+ limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE);
+ limit -= ctx->used;
+ }
+
+ err = skcipher_alloc_sgl(sk, 0);
+ if (err)
+ goto unlock;
+
+ ctx->merge = 0;
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+
+ get_page(page);
+ sg_set_page(sgl->sg + sgl->cur, page, size, offset);
+ sgl->cur++;
+ ctx->used += size;
+
+done:
+ ctx->more = flags & MSG_MORE;
+ if (!ctx->more && !list_empty(&ctx->tsgl)) {
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+ sg_mark_end(sgl->sg + sgl->cur - 1);
+ }
+
+unlock:
+ skcipher_data_wakeup(sk);
+ release_sock(sk);
+
+ return err ?: size;
+}
+
+static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t ignored, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ unsigned bs = crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(
+ &ctx->req));
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg;
+ unsigned long iovlen;
+ struct iovec *iov;
+ int err = -EAGAIN;
+ int used;
+ long copied = 0;
+
+ lock_sock(sk);
+ for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
+ iovlen--, iov++) {
+ unsigned long seglen = iov->iov_len;
+ char __user *from = iov->iov_base;
+
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list,
+ list);
+ sg = sgl->sg;
+ while (!sg->length)
+ sg++;
+
+ while (seglen) {
+ used = ctx->used;
+ if (!used) {
+ release_sock(sk);
+ err = skcipher_wait_for_data(sk, flags);
+ lock_sock(sk);
+ if (err)
+ goto unlock;
+ }
+
+ used = min_t(unsigned long, used, seglen);
+
+ if (ctx->more || used < ctx->used)
+ used -= used % bs;
+
+ err = -EINVAL;
+ if (!used)
+ goto unlock;
+
+ used = af_alg_make_sg(&ctx->rsgl, from, used, 1);
+ if (used < 0)
+ goto unlock;
+
+ ablkcipher_request_set_crypt(&ctx->req, sg,
+ ctx->rsgl.sg, used,
+ ctx->iv);
+
+ err = af_alg_wait_for_completion(
+ ctx->enc ?
+ crypto_ablkcipher_encrypt(&ctx->req) :
+ crypto_ablkcipher_decrypt(&ctx->req),
+ &ctx->completion);
+
+ af_alg_free_sg(&ctx->rsgl);
+
+ if (err)
+ goto unlock;
+
+ copied += used;
+ from += used;
+ seglen -= used;
+ skcipher_pull_sgl(sk, used);
+ }
+ }
+
+ err = 0;
+
+unlock:
+ skcipher_wmem_wakeup(sk);
+ release_sock(sk);
+
+ return copied ?: err;
+}
+
+
+static unsigned int skcipher_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ unsigned int mask;
+
+ sock_poll_wait(file, sk_sleep(sk), wait);
+ mask = 0;
+
+ if (ctx->used)
+ mask |= POLLIN | POLLRDNORM;
+
+ if (skcipher_writable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+
+ return mask;
+}
+
+static struct proto_ops algif_skcipher_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,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+ .setsockopt = sock_no_setsockopt,
+
+ .release = af_alg_release,
+ .sendmsg = skcipher_sendmsg,
+ .sendpage = skcipher_sendpage,
+ .recvmsg = skcipher_recvmsg,
+ .poll = skcipher_poll,
+};
+
+static void *skcipher_bind(const char *name, u32 type, u32 mask)
+{
+ return crypto_alloc_ablkcipher(name, type, mask);
+}
+
+static void skcipher_release(void *private)
+{
+ crypto_free_ablkcipher(private);
+}
+
+static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen)
+{
+ return crypto_ablkcipher_setkey(private, key, keylen);
+}
+
+static void skcipher_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
+
+ skcipher_free_sgl(sk);
+ sock_kfree_s(sk, ctx->iv, crypto_ablkcipher_blocksize(tfm));
+ sock_kfree_s(sk, ctx, ctx->len);
+ af_alg_release_parent(sk);
+}
+
+static int skcipher_accept_parent(void *private, struct sock *sk)
+{
+ struct skcipher_ctx *ctx;
+ struct alg_sock *ask = alg_sk(sk);
+ unsigned int len = sizeof(*ctx) + crypto_ablkcipher_reqsize(private);
+
+ ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->iv = sock_kmalloc(sk, crypto_ablkcipher_ivsize(private),
+ GFP_KERNEL);
+ if (!ctx->iv) {
+ sock_kfree_s(sk, ctx, len);
+ return -ENOMEM;
+ }
+
+ memset(ctx->iv, 0, crypto_ablkcipher_ivsize(private));
+
+ INIT_LIST_HEAD(&ctx->tsgl);
+ ctx->len = len;
+ ctx->used = 0;
+ ctx->more = 0;
+ ctx->merge = 0;
+ ctx->enc = 0;
+ af_alg_init_completion(&ctx->completion);
+
+ ask->private = ctx;
+
+ ablkcipher_request_set_tfm(&ctx->req, private);
+ ablkcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ af_alg_complete, &ctx->completion);
+
+ sk->sk_destruct = skcipher_sock_destruct;
+
+ return 0;
+}
+
+static const struct af_alg_type algif_type_skcipher = {
+ .bind = skcipher_bind,
+ .release = skcipher_release,
+ .setkey = skcipher_setkey,
+ .accept = skcipher_accept_parent,
+ .ops = &algif_skcipher_ops,
+ .name = "skcipher",
+ .owner = THIS_MODULE
+};
+
+static int __init algif_skcipher_init(void)
+{
+ return af_alg_register_type(&algif_type_skcipher);
+}
+
+static void __exit algif_skcipher_exit(void)
+{
+ int err = af_alg_unregister_type(&algif_type_skcipher);
+ BUG_ON(err);
+}
+
+module_init(algif_skcipher_init);
+module_exit(algif_skcipher_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_ALG);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH 1/4] net - Add AF_ALG macros
2010-11-04 17:36 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
@ 2010-11-04 19:22 ` David Miller
0 siblings, 0 replies; 25+ messages in thread
From: David Miller @ 2010-11-04 19:22 UTC (permalink / raw)
To: herbert; +Cc: linux-crypto, netdev, linux-kernel
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Thu, 04 Nov 2010 12:36:19 -0500
> net - Add AF_ALG macros
>
> This patch adds the socket family/level macros for the yet-to-be-born
> AF_ALG family. The AF_ALG family provides the user-space interface
> for the kernel crypto API.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API
2010-11-04 17:36 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
@ 2010-11-04 19:23 ` David Miller
0 siblings, 0 replies; 25+ messages in thread
From: David Miller @ 2010-11-04 19:23 UTC (permalink / raw)
To: herbert; +Cc: linux-crypto, netdev, linux-kernel
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Thu, 04 Nov 2010 12:36:19 -0500
> crypto: af_alg - User-space interface for Crypto API
>
> This patch creates the backbone of the user-space interface for
> the Crypto API, through a new socket family AF_ALG.
>
> Each session corresponds to one or more connections obtained from
> that socket. The number depends on the number of inputs/outputs
> of that particular type of operation. For most types there will
> be a s ingle connection/file descriptor that is used for both input
> and output. AEAD is one of the few that require two inputs.
>
> Each algorithm type will provide its own implementation that plugs
> into af_alg. They're keyed using a string such as "skcipher" or
> "hash".
>
> IOW this patch only contains the boring bits that is required
> to hold everything together.
>
> Thakns to Miloslav Trmac for reviewing this and contributing
> fixes and improvements.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations
2010-11-04 17:36 ` [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations Herbert Xu
@ 2010-11-04 19:23 ` David Miller
0 siblings, 0 replies; 25+ messages in thread
From: David Miller @ 2010-11-04 19:23 UTC (permalink / raw)
To: herbert; +Cc: linux-crypto, netdev, linux-kernel
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Thu, 04 Nov 2010 12:36:19 -0500
> crypto: algif_hash - User-space interface for hash operations
>
> This patch adds the af_alg plugin for hash, corresponding to
> the ahash kernel operation type.
>
> Keys can optionally be set through the setsockopt interface.
>
> Each sendmsg call will finalise the hash unless sent with a MSG_MORE
> flag.
>
> Partial hash states can be cloned using accept(2).
>
> The interface is completely synchronous, all operations will
> complete prior to the system call returning.
>
> Both sendmsg(2) and splice(2) support reading the user-space
> data directly without copying (except that the Crypto API itself
> may copy the data if alignment is off).
>
> For now only the splice(2) interface supports performing digest
> instead of init/update/final. In future the sendmsg(2) interface
> will also be modified to use digest/finup where possible so that
> hardware that cannot return a partial hash state can still benefit
> from this interface.
>
> Thakns to Miloslav Trmac for reviewing this and contributing
> fixes and improvements.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations
2010-11-04 17:36 ` [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations Herbert Xu
@ 2010-11-04 19:23 ` David Miller
0 siblings, 0 replies; 25+ messages in thread
From: David Miller @ 2010-11-04 19:23 UTC (permalink / raw)
To: herbert; +Cc: linux-crypto, netdev, linux-kernel
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Thu, 04 Nov 2010 12:36:20 -0500
> crypto: algif_skcipher - User-space interface for skcipher operations
>
> This patch adds the af_alg plugin for symmetric key ciphers,
> corresponding to the ablkcipher kernel operation type.
>
> Keys can optionally be set through the setsockopt interface.
>
> Once a sendmsg call occurs without MSG_MORE no further writes
> may be made to the socket until all previous data has been read.
>
> IVs and and whether encryption/decryption is performed can be
> set through the setsockopt interface or as a control message
> to sendmsg.
>
> The interface is completely synchronous, all operations are
> carried out in recvmsg(2) and will complete prior to the system
> call returning.
>
> The splice(2) interface support reading the user-space data directly
> without copying (except that the Crypto API itself may copy the data
> if alignment is off).
>
> The recvmsg(2) interface supports directly writing to user-space
> without additional copying, i.e., the kernel crypto interface will
> receive the user-space address as its output SG list.
>
> Thakns to Miloslav Trmac for reviewing this and contributing
> fixes and improvements.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2010-11-04 19:23 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-09-07 8:42 RFC: Crypto API User-interface Herbert Xu
2010-09-07 9:18 ` Tomas Mraz
2010-09-07 14:06 ` Christoph Hellwig
2010-09-07 14:11 ` Herbert Xu
2010-09-07 14:24 ` Christoph Hellwig
2010-09-07 14:39 ` Herbert Xu
2010-09-07 14:49 ` Nikos Mavrogiannopoulos
2010-09-07 14:57 ` Nikos Mavrogiannopoulos
2010-09-07 14:59 ` Christoph Hellwig
2010-10-19 13:44 ` Herbert Xu
2010-10-20 10:24 ` Nikos Mavrogiannopoulos
2010-11-04 17:34 ` Herbert Xu
2010-11-04 17:36 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
2010-11-04 19:23 ` David Miller
2010-11-04 17:36 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
2010-11-04 19:22 ` David Miller
2010-11-04 17:36 ` [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations Herbert Xu
2010-11-04 19:23 ` David Miller
2010-11-04 17:36 ` [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations Herbert Xu
2010-11-04 19:23 ` David Miller
2010-10-19 13:46 ` [PATCH 2/4] crypto: af_alg - User-space interface for Crypto API Herbert Xu
2010-10-19 13:46 ` [PATCH 1/4] net - Add AF_ALG macros Herbert Xu
2010-10-20 9:01 ` David Miller
2010-10-19 13:46 ` [PATCH 4/4] crypto: algif_skcipher - User-space interface for skcipher operations Herbert Xu
2010-10-19 13:46 ` [PATCH 3/4] crypto: algif_hash - User-space interface for hash operations Herbert Xu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).