diff mbox

[3/3,RFC] tools: create power/crypto utility

Message ID 78af30838d0bac69bdd6e138b659bcbb8464fd13.1529486870.git.yu.c.chen@intel.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Chen Yu June 20, 2018, 9:40 a.m. UTC
crypto_hibernate is a user-space utility to generate
512bits AES key and pass it to the kernel via ioctl
for hibernation encryption.(We can also add the key
into kernel via keyctl if necessary, but currently
using ioctl seems to be more straightforward as we
need both the key and salt transit).

The key derivation is based on a simplified implementation
of PBKDF2[1] in e2fsprogs - both the key length and the hash
bytes are the same - 512bits. crypto_hibernate will firstly
probe the user for passphrase and get salt from kernel, then
uses them to generate a 512bits AES key based on PBKDF2.

Usage:
1. install the kernel module:
   modprobe crypto_hibernation
2. run this tool to generate the key from
   user provided passphrase (salt is needed too).
3. launch the hibernation process, the kernel
   uses the key from step 2 to encrypt the
   hibernation snapshot.
4. resume the system and the initrd will
   launch cryto_hibernate to read previous salt
   from kernel and probe the user passphrase
   and generate the same key.
5. kernel uses this key to decrypt the hibernation
   snapshot and restore to previous system.

[1] https://www.ietf.org/rfc/rfc2898.txt (5.2 PBKDF2)

Suggested-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: "Theodore Ts'o" <tytso@mit.edu>
Cc: Stephan Mueller <smueller@chronox.de>
Cc: Eric Biggers <ebiggers3@gmail.com>
Cc: Denis Kenzior <denkenz@gmail.com>
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
---
 MAINTAINERS                           |   8 +
 tools/power/crypto/Makefile           |  26 ++
 tools/power/crypto/crypto_hibernate.c | 447 ++++++++++++++++++++++++++++++++++
 3 files changed, 481 insertions(+)
 create mode 100644 tools/power/crypto/Makefile
 create mode 100644 tools/power/crypto/crypto_hibernate.c

Comments

Eric Biggers June 20, 2018, 5:41 p.m. UTC | #1
Hi Chen,

On Wed, Jun 20, 2018 at 05:40:51PM +0800, Chen Yu wrote:
> crypto_hibernate is a user-space utility to generate
> 512bits AES key and pass it to the kernel via ioctl
> for hibernation encryption.(We can also add the key
> into kernel via keyctl if necessary, but currently
> using ioctl seems to be more straightforward as we
> need both the key and salt transit).
> 
> The key derivation is based on a simplified implementation
> of PBKDF2[1] in e2fsprogs - both the key length and the hash
> bytes are the same - 512bits. crypto_hibernate will firstly
> probe the user for passphrase and get salt from kernel, then
> uses them to generate a 512bits AES key based on PBKDF2.

What is a "512bits AES key"?  Do you mean AES-256-XTS (which takes a 512-bit
key, which the XTS mode internally splits into two keys)?  Do you allow for
other algorithms, or is it hardcoded to AES-256-XTS?  What if someone wants to
use a different algorithm?

BTW, it's difficult to review this with only patch 3/3 Cc'ed to me, as there is
no context about the problem you are trying to solve and what your actual
proposed kernel changes are.  I suggest Cc'ing linux-crypto on all 3 patches.

A few more comments below, from a very brief reading of the code:

[...]
> +
> +#define PBKDF2_ITERATIONS          0xFFFF
> +#define SHA512_BLOCKSIZE 128
> +#define SHA512_LENGTH 64
> +#define SALT_BYTES	16
> +#define SYM_KEY_BYTES SHA512_LENGTH
> +#define TOTAL_USER_INFO_LEN	(SALT_BYTES+SYM_KEY_BYTES)
> +#define MAX_PASSPHRASE_SIZE	1024
> +
> +struct hibernation_crypto_keys {
> +	char derived_key[SYM_KEY_BYTES];
> +	char salt[SALT_BYTES];
> +	bool valid;
> +};
> +
> +struct hibernation_crypto_keys hib_keys;
> +
> +static char *get_key_ptr(void)
> +{
> +	return hib_keys.derived_key;
> +}
> +
> +static char *get_salt_ptr(void)
> +{
> +	return hib_keys.salt;
> +}
[...]
> +
> +
> +#define HIBERNATE_SALT_READ      _IOW('C', 3, struct hibernation_crypto_keys)
> +#define HIBERNATE_KEY_WRITE     _IOW('C', 4, struct hibernation_crypto_keys)

Why are the ioctl numbers defined based on the size of 'struct
hibernation_crypto_keys'?  It's not a UAPI structure, right?

> +
> +static void get_passphrase(char *passphrase, int len)
> +{
> +	char *p;
> +	struct termios current_settings;
> +
> +	assert(len > 0);
> +	disable_echo(&current_settings);
> +	p = fgets(passphrase, len, stdin);
> +	tcsetattr(0, TCSANOW, &current_settings);
> +	printf("\n");
> +	if (!p) {
> +		printf("Aborting.\n");
> +		exit(1);
> +	}
> +	p = strrchr(passphrase, '\n');
> +	if (!p)
> +		p = passphrase + len - 1;
> +	*p = '\0';
> +}
> +
> +#define CRYPTO_FILE	"/dev/crypto_hibernate"
> +
> +static int write_keys(void)
> +{
> +	int fd;
> +
> +	fd = open(CRYPTO_FILE, O_RDWR);
> +	if (fd < 0) {
> +		printf("Cannot open device file...\n");
> +		return -EINVAL;
> +	}
> +	ioctl(fd, HIBERNATE_KEY_WRITE, get_key_ptr());
> +	return 0;

No error checking on the ioctl?

Also, how is the kernel supposed to know how long the key is, and which
algorithm it's supposed to be used for?  I assume it's hardcoded to AES-256-XTS?
What if someone wants to use a different algorithm?

> +}
> +
> +static int read_salt(void)
> +{
> +	int fd;
> +
> +	fd = open(CRYPTO_FILE, O_RDWR);
> +	if (fd < 0) {
> +		printf("Cannot open device file...\n");
> +		return -EINVAL;
> +	}
> +	ioctl(fd, HIBERNATE_SALT_READ, get_salt_ptr());
> +	return 0;
> +}

No error checking on the ioctl?

> +int main(int argc, char *argv[])
> +{
> +	int opt, option_index = 0;
> +	char in_passphrase[MAX_PASSPHRASE_SIZE];
> +
> +	while ((opt = getopt_long_only(argc, argv, "+p:s:h",
> +				NULL, &option_index)) != -1) {
> +		switch (opt) {
> +		case 'p':
> +			{
> +				char *p = optarg;
> +
> +				if (strlen(p) >= (MAX_PASSPHRASE_SIZE - 1)) {
> +					printf("Please provide passphrase less than %d bytes.\n",
> +						MAX_PASSPHRASE_SIZE);
> +					exit(1);
> +				}
> +				strcpy(in_passphrase, p);

I haven't read this super closely, but this really looks like an off-by-one
error.  It seems you intended MAX_PASSPHRASE_SIZE to include a null terminator,
so the correct check would be 'strlen(p) >= MAX_PASSPHRASE_SIZE'.

> +			}
> +			break;
> +		case 's':
> +			{
> +				char *p = optarg;
> +
> +				if (strlen(p) != (SALT_BYTES - 1)) {
> +					printf("Please provide salt with len less than %d bytes.\n",
> +						SALT_BYTES);
> +					exit(1);
> +				}
> +				strcpy(get_salt_ptr(), p);
> +			}
> +			break;

Salts don't need to be human-readable.  How about making the salt binary?  So, a
salt specified on the command-line would be hex.

Eric
Pavel Machek June 21, 2018, 9:01 a.m. UTC | #2
On Wed 2018-06-20 17:40:51, Chen Yu wrote:
> crypto_hibernate is a user-space utility to generate
> 512bits AES key and pass it to the kernel via ioctl
> for hibernation encryption.(We can also add the key
> into kernel via keyctl if necessary, but currently
> using ioctl seems to be more straightforward as we
> need both the key and salt transit).

Ah, here you go:

https://en.wikipedia.org/wiki/Uswsusp

It can already do encryption. If you see problems there, patches will
be gladly accepted to fix them.

If you want to maintain uswsusp in kernel tree, I guess that would be
an option, too.

If you want to just help swsusp, I have some patches that reduce
duplicities between x86-32 and x86-64. It would be cool to clean them
up and get them merged.

> Suggested-by: "Theodore Ts'o" <tytso@mit.edu>

Best regards,
									Pavel
Rafael J. Wysocki June 21, 2018, 12:10 p.m. UTC | #3
On Thu, Jun 21, 2018 at 11:01 AM, Pavel Machek <pavel@ucw.cz> wrote:
>
>
> On Wed 2018-06-20 17:40:51, Chen Yu wrote:
>> crypto_hibernate is a user-space utility to generate
>> 512bits AES key and pass it to the kernel via ioctl
>> for hibernation encryption.(We can also add the key
>> into kernel via keyctl if necessary, but currently
>> using ioctl seems to be more straightforward as we
>> need both the key and salt transit).
>
> Ah, here you go:
>
> https://en.wikipedia.org/wiki/Uswsusp

No, not really.

> It can already do encryption. If you see problems there, patches will
> be gladly accepted to fix them.

By whom?

> If you want to maintain uswsusp in kernel tree, I guess that would be
> an option, too.

No, it isn't an option due to the dependencies on crypto libraries.

> If you want to just help swsusp, I have some patches that reduce
> duplicities between x86-32 and x86-64. It would be cool to clean them
> up and get them merged.

Sure, please submit them then.

Thanks,
Rafael
Pavel Machek June 21, 2018, 7:04 p.m. UTC | #4
On Thu 2018-06-21 14:10:06, Rafael J. Wysocki wrote:
> On Thu, Jun 21, 2018 at 11:01 AM, Pavel Machek <pavel@ucw.cz> wrote:
> >
> >
> > On Wed 2018-06-20 17:40:51, Chen Yu wrote:
> >> crypto_hibernate is a user-space utility to generate
> >> 512bits AES key and pass it to the kernel via ioctl
> >> for hibernation encryption.(We can also add the key
> >> into kernel via keyctl if necessary, but currently
> >> using ioctl seems to be more straightforward as we
> >> need both the key and salt transit).
> >
> > Ah, here you go:
> >
> > https://en.wikipedia.org/wiki/Uswsusp
> 
> No, not really.
> 
> > It can already do encryption. If you see problems there, patches will
> > be gladly accepted to fix them.
> 
> By whom?

By me.

> > If you want to maintain uswsusp in kernel tree, I guess that would be
> > an option, too.
> 
> No, it isn't an option due to the dependencies on crypto libraries.

Why not?

									Pavel
Chen Yu June 22, 2018, 2:39 a.m. UTC | #5
Hi Eric,
On Wed, Jun 20, 2018 at 10:41:42AM -0700, Eric Biggers wrote:
> Hi Chen,
> 
> On Wed, Jun 20, 2018 at 05:40:51PM +0800, Chen Yu wrote:
> > crypto_hibernate is a user-space utility to generate
> > 512bits AES key and pass it to the kernel via ioctl
> > for hibernation encryption.(We can also add the key
> > into kernel via keyctl if necessary, but currently
> > using ioctl seems to be more straightforward as we
> > need both the key and salt transit).
> > 
> > The key derivation is based on a simplified implementation
> > of PBKDF2[1] in e2fsprogs - both the key length and the hash
> > bytes are the same - 512bits. crypto_hibernate will firstly
> > probe the user for passphrase and get salt from kernel, then
> > uses them to generate a 512bits AES key based on PBKDF2.
Thanks for reviewing this.
> 
> What is a "512bits AES key"?  Do you mean AES-256-XTS (which takes a 512-bit
> key, which the XTS mode internally splits into two keys)? 
Yes, it is AES-256-XTS.
> Do you allow for
> other algorithms, or is it hardcoded to AES-256-XTS? 
Currently it is hardcoded to AES-256-XTS. It is copied from implementation
of PBKDF2 in e2fsprogs, which is hardcoded to useAES-256-XTS for ext4 encryption
in the kernel(pbkdf2_sha512() in e2fsprogs)
> What if someone wants to
> use a different algorithm?
> 
If user wants to use a different algorithm, then I think we need to
port the code from openssl, which is the full implementation of PBKDF2
for:
https://www.ietf.org/rfc/rfc2898.txt 
> BTW, it's difficult to review this with only patch 3/3 Cc'ed to me, as there is
> no context about the problem you are trying to solve and what your actual
> proposed kernel changes are.  I suggest Cc'ing linux-crypto on all 3 patches.
> 
Ok, I'll send a refreshed version.
> A few more comments below, from a very brief reading of the code:
> 
> [...]
> > +
> > +#define PBKDF2_ITERATIONS          0xFFFF
> > +#define SHA512_BLOCKSIZE 128
> > +#define SHA512_LENGTH 64
> > +#define SALT_BYTES	16
> > +#define SYM_KEY_BYTES SHA512_LENGTH
> > +#define TOTAL_USER_INFO_LEN	(SALT_BYTES+SYM_KEY_BYTES)
> > +#define MAX_PASSPHRASE_SIZE	1024
> > +
> > +struct hibernation_crypto_keys {
> > +	char derived_key[SYM_KEY_BYTES];
> > +	char salt[SALT_BYTES];
> > +	bool valid;
> > +};
> > +
> > +struct hibernation_crypto_keys hib_keys;
> > +
> > +static char *get_key_ptr(void)
> > +{
> > +	return hib_keys.derived_key;
> > +}
> > +
> > +static char *get_salt_ptr(void)
> > +{
> > +	return hib_keys.salt;
> > +}
> [...]
> > +
> > +
> > +#define HIBERNATE_SALT_READ      _IOW('C', 3, struct hibernation_crypto_keys)
> > +#define HIBERNATE_KEY_WRITE     _IOW('C', 4, struct hibernation_crypto_keys)
> 
> Why are the ioctl numbers defined based on the size of 'struct
> hibernation_crypto_keys'?  It's not a UAPI structure, right?
> 
It's not a UAPI structure, and it is defined both in user space tool
and in kernel. Do you mean, I should put the defination of this
structure under include/uapi ?
> > +
> > +static void get_passphrase(char *passphrase, int len)
> > +{
> > +	char *p;
> > +	struct termios current_settings;
> > +
> > +	assert(len > 0);
> > +	disable_echo(&current_settings);
> > +	p = fgets(passphrase, len, stdin);
> > +	tcsetattr(0, TCSANOW, &current_settings);
> > +	printf("\n");
> > +	if (!p) {
> > +		printf("Aborting.\n");
> > +		exit(1);
> > +	}
> > +	p = strrchr(passphrase, '\n');
> > +	if (!p)
> > +		p = passphrase + len - 1;
> > +	*p = '\0';
> > +}
> > +
> > +#define CRYPTO_FILE	"/dev/crypto_hibernate"
> > +
> > +static int write_keys(void)
> > +{
> > +	int fd;
> > +
> > +	fd = open(CRYPTO_FILE, O_RDWR);
> > +	if (fd < 0) {
> > +		printf("Cannot open device file...\n");
> > +		return -EINVAL;
> > +	}
> > +	ioctl(fd, HIBERNATE_KEY_WRITE, get_key_ptr());
> > +	return 0;
> 
> No error checking on the ioctl?
> 
Ok, will add error checking for it.
> Also, how is the kernel supposed to know how long the key is, and which
> algorithm it's supposed to be used for?  I assume it's hardcoded to AES-256-XTS?
> What if someone wants to use a different algorithm?
> 
Yes, the length in both user space and kernel are hardcoded to AES-256-XTS.
I can add the support for other algorithm, but might need to port from
openssl first.
> > +}
> > +
> > +static int read_salt(void)
> > +{
> > +	int fd;
> > +
> > +	fd = open(CRYPTO_FILE, O_RDWR);
> > +	if (fd < 0) {
> > +		printf("Cannot open device file...\n");
> > +		return -EINVAL;
> > +	}
> > +	ioctl(fd, HIBERNATE_SALT_READ, get_salt_ptr());
> > +	return 0;
> > +}
> 
> No error checking on the ioctl?
> 
Ok, will add checkings.
> > +int main(int argc, char *argv[])
> > +{
> > +	int opt, option_index = 0;
> > +	char in_passphrase[MAX_PASSPHRASE_SIZE];
> > +
> > +	while ((opt = getopt_long_only(argc, argv, "+p:s:h",
> > +				NULL, &option_index)) != -1) {
> > +		switch (opt) {
> > +		case 'p':
> > +			{
> > +				char *p = optarg;
> > +
> > +				if (strlen(p) >= (MAX_PASSPHRASE_SIZE - 1)) {
> > +					printf("Please provide passphrase less than %d bytes.\n",
> > +						MAX_PASSPHRASE_SIZE);
> > +					exit(1);
> > +				}
> > +				strcpy(in_passphrase, p);
> 
> I haven't read this super closely, but this really looks like an off-by-one
> error.  It seems you intended MAX_PASSPHRASE_SIZE to include a null terminator,
> so the correct check would be 'strlen(p) >= MAX_PASSPHRASE_SIZE'.
> 
Ah, right, will change it.
> > +			}
> > +			break;
> > +		case 's':
> > +			{
> > +				char *p = optarg;
> > +
> > +				if (strlen(p) != (SALT_BYTES - 1)) {
> > +					printf("Please provide salt with len less than %d bytes.\n",
> > +						SALT_BYTES);
> > +					exit(1);
> > +				}
> > +				strcpy(get_salt_ptr(), p);
> > +			}
> > +			break;
> 
> Salts don't need to be human-readable.  How about making the salt binary?  So, a
> salt specified on the command-line would be hex.
>
Ok, I will change it to hex form.
Best,
Yu
> Eric
Eric Biggers June 22, 2018, 2:59 a.m. UTC | #6
Hi Yu,

On Fri, Jun 22, 2018 at 10:39:13AM +0800, Yu Chen wrote:
> Hi Eric,
> On Wed, Jun 20, 2018 at 10:41:42AM -0700, Eric Biggers wrote:
> > Hi Chen,
> > 
> > On Wed, Jun 20, 2018 at 05:40:51PM +0800, Chen Yu wrote:
> > > crypto_hibernate is a user-space utility to generate
> > > 512bits AES key and pass it to the kernel via ioctl
> > > for hibernation encryption.(We can also add the key
> > > into kernel via keyctl if necessary, but currently
> > > using ioctl seems to be more straightforward as we
> > > need both the key and salt transit).
> > > 
> > > The key derivation is based on a simplified implementation
> > > of PBKDF2[1] in e2fsprogs - both the key length and the hash
> > > bytes are the same - 512bits. crypto_hibernate will firstly
> > > probe the user for passphrase and get salt from kernel, then
> > > uses them to generate a 512bits AES key based on PBKDF2.
> Thanks for reviewing this.
> > 
> > What is a "512bits AES key"?  Do you mean AES-256-XTS (which takes a 512-bit
> > key, which the XTS mode internally splits into two keys)? 
> Yes, it is AES-256-XTS.
> > Do you allow for
> > other algorithms, or is it hardcoded to AES-256-XTS? 
> Currently it is hardcoded to AES-256-XTS. It is copied from implementation
> of PBKDF2 in e2fsprogs, which is hardcoded to useAES-256-XTS for ext4 encryption
> in the kernel(pbkdf2_sha512() in e2fsprogs)
>
> > What if someone wants to
> > use a different algorithm?
> > 
> If user wants to use a different algorithm, then I think we need to
> port the code from openssl, which is the full implementation of PBKDF2
> for:
> https://www.ietf.org/rfc/rfc2898.txt 

You seem to be confusing the key derivation function (KDF) with the encryption
algorithm the derived key is used for.  The purpose of a KDF is to derive key
material.  The resulting key material can be used for any encryption algorithm,
provided that you derive the needed amount of key material.  Note that different
encryption algorithms can use the same length key, e.g. SERPENT-256-XTS and
AES-256-XTS are both examples of algorithms that use a 64 byte (512-bit) key.
So your design probably should provide a way for an *algorithm* to be selected,
not just a key length.

> > BTW, it's difficult to review this with only patch 3/3 Cc'ed to me, as there is
> > no context about the problem you are trying to solve and what your actual
> > proposed kernel changes are.  I suggest Cc'ing linux-crypto on all 3 patches.
> > 
> Ok, I'll send a refreshed version.
> > A few more comments below, from a very brief reading of the code:
> > 
> > [...]
> > > +
> > > +#define PBKDF2_ITERATIONS          0xFFFF
> > > +#define SHA512_BLOCKSIZE 128
> > > +#define SHA512_LENGTH 64
> > > +#define SALT_BYTES	16
> > > +#define SYM_KEY_BYTES SHA512_LENGTH
> > > +#define TOTAL_USER_INFO_LEN	(SALT_BYTES+SYM_KEY_BYTES)
> > > +#define MAX_PASSPHRASE_SIZE	1024
> > > +
> > > +struct hibernation_crypto_keys {
> > > +	char derived_key[SYM_KEY_BYTES];
> > > +	char salt[SALT_BYTES];
> > > +	bool valid;
> > > +};
> > > +
> > > +struct hibernation_crypto_keys hib_keys;
> > > +
> > > +static char *get_key_ptr(void)
> > > +{
> > > +	return hib_keys.derived_key;
> > > +}
> > > +
> > > +static char *get_salt_ptr(void)
> > > +{
> > > +	return hib_keys.salt;
> > > +}
> > [...]
> > > +
> > > +
> > > +#define HIBERNATE_SALT_READ      _IOW('C', 3, struct hibernation_crypto_keys)
> > > +#define HIBERNATE_KEY_WRITE     _IOW('C', 4, struct hibernation_crypto_keys)
> > 
> > Why are the ioctl numbers defined based on the size of 'struct
> > hibernation_crypto_keys'?  It's not a UAPI structure, right?
> > 
> It's not a UAPI structure, and it is defined both in user space tool
> and in kernel. Do you mean, I should put the defination of this
> structure under include/uapi ?

No, I'm saying that you shouldn't define ioctl numbers (permanent ABI) based on
the size of some random structure that is subject to change.

> > > +
> > > +static void get_passphrase(char *passphrase, int len)
> > > +{
> > > +	char *p;
> > > +	struct termios current_settings;
> > > +
> > > +	assert(len > 0);
> > > +	disable_echo(&current_settings);
> > > +	p = fgets(passphrase, len, stdin);
> > > +	tcsetattr(0, TCSANOW, &current_settings);
> > > +	printf("\n");
> > > +	if (!p) {
> > > +		printf("Aborting.\n");
> > > +		exit(1);
> > > +	}
> > > +	p = strrchr(passphrase, '\n');
> > > +	if (!p)
> > > +		p = passphrase + len - 1;
> > > +	*p = '\0';
> > > +}
> > > +
> > > +#define CRYPTO_FILE	"/dev/crypto_hibernate"
> > > +
> > > +static int write_keys(void)
> > > +{
> > > +	int fd;
> > > +
> > > +	fd = open(CRYPTO_FILE, O_RDWR);
> > > +	if (fd < 0) {
> > > +		printf("Cannot open device file...\n");
> > > +		return -EINVAL;
> > > +	}
> > > +	ioctl(fd, HIBERNATE_KEY_WRITE, get_key_ptr());
> > > +	return 0;
> > 
> > No error checking on the ioctl?
> > 
> Ok, will add error checking for it.
> > Also, how is the kernel supposed to know how long the key is, and which
> > algorithm it's supposed to be used for?  I assume it's hardcoded to AES-256-XTS?
> > What if someone wants to use a different algorithm?
> > 
> Yes, the length in both user space and kernel are hardcoded to AES-256-XTS.
> I can add the support for other algorithm, but might need to port from
> openssl first.

The kernel crypto API already supports many other ciphers.  It's the kernel that
actually does the encryption, right?

Again, you should Cc the whole patchset to linux-crypto so that people can
review it context, including your new kernel code that actually does the
encryption, and see what problem you are actually trying to solve.

Thanks!

- Eric
Rafael J. Wysocki June 25, 2018, 7:06 a.m. UTC | #7
On Thu, Jun 21, 2018 at 9:04 PM, Pavel Machek <pavel@ucw.cz> wrote:
> On Thu 2018-06-21 14:10:06, Rafael J. Wysocki wrote:
>> On Thu, Jun 21, 2018 at 11:01 AM, Pavel Machek <pavel@ucw.cz> wrote:
>> >
>> >
>> > On Wed 2018-06-20 17:40:51, Chen Yu wrote:
>> >> crypto_hibernate is a user-space utility to generate
>> >> 512bits AES key and pass it to the kernel via ioctl
>> >> for hibernation encryption.(We can also add the key
>> >> into kernel via keyctl if necessary, but currently
>> >> using ioctl seems to be more straightforward as we
>> >> need both the key and salt transit).
>> >
>> > Ah, here you go:
>> >
>> > https://en.wikipedia.org/wiki/Uswsusp
>>
>> No, not really.
>>
>> > It can already do encryption. If you see problems there, patches will
>> > be gladly accepted to fix them.
>>
>> By whom?
>
> By me.

Well, OK

Where's the repository the patches will go to when you accept them?

>> > If you want to maintain uswsusp in kernel tree, I guess that would be
>> > an option, too.
>>
>> No, it isn't an option due to the dependencies on crypto libraries.
>
> Why not?

You need a specific set of devel libraries for the utilities to build
and that need not be present on all systems I suppose?

Besides, s2disk is currently tangled with s2ram which is obsolete, so
I'd rather not.
Pavel Machek June 25, 2018, 11:54 a.m. UTC | #8
Hi!

> >> > It can already do encryption. If you see problems there, patches will
> >> > be gladly accepted to fix them.
> >>
> >> By whom?
> >
> > By me.
> 
> Well, OK
> 
> Where's the repository the patches will go to when you accept them?

On sourceforge.

> >> > If you want to maintain uswsusp in kernel tree, I guess that would be
> >> > an option, too.
> >>
> >> No, it isn't an option due to the dependencies on crypto libraries.
> >
> > Why not?
> 
> You need a specific set of devel libraries for the utilities to build
> and that need not be present on all systems I suppose?
> 
> Besides, s2disk is currently tangled with s2ram which is obsolete, so
> I'd rather not.

Its not tangled.
Rafael J. Wysocki June 25, 2018, 9:56 p.m. UTC | #9
On Mon, Jun 25, 2018 at 1:54 PM, Pavel Machek <pavel@ucw.cz> wrote:
> Hi!
>
>> >> > It can already do encryption. If you see problems there, patches will
>> >> > be gladly accepted to fix them.
>> >>
>> >> By whom?
>> >
>> > By me.
>>
>> Well, OK
>>
>> Where's the repository the patches will go to when you accept them?
>
> On sourceforge.

No, it's not there or at least it's outdated AFAICS.

>> >> > If you want to maintain uswsusp in kernel tree, I guess that would be
>> >> > an option, too.
>> >>
>> >> No, it isn't an option due to the dependencies on crypto libraries.
>> >
>> > Why not?
>>
>> You need a specific set of devel libraries for the utilities to build
>> and that need not be present on all systems I suppose?
>>
>> Besides, s2disk is currently tangled with s2ram which is obsolete, so
>> I'd rather not.
>
> Its not tangled.

Yes, it is.
Pavel Machek June 25, 2018, 10:16 p.m. UTC | #10
Hi!

> >> >> > If you want to maintain uswsusp in kernel tree, I guess that would be
> >> >> > an option, too.
> >> >>
> >> >> No, it isn't an option due to the dependencies on crypto libraries.
> >> >
> >> > Why not?
> >>
> >> You need a specific set of devel libraries for the utilities to build
> >> and that need not be present on all systems I suppose?
> >>
> >> Besides, s2disk is currently tangled with s2ram which is obsolete, so
> >> I'd rather not.
> >
> > Its not tangled.
> 
> Yes, it is.

I checked the Makefile, and it builds from separate sources. Yes, it
is in one repository, but s2disk does not depend on s2ram (s2both does).

Anyway... that's really besides a point.

Interested parties can easily fix up the userland parts of uswsusp,
change crypto, add or remove dependencies, move it to other hosting,
or drop it and start again. Kernel interface is flexible enough. If
Chen wants to move the s2disk encryption into kernel, it is his task
to explain why that is neccessary.

								Pavel
Pavel Machek June 26, 2018, 11:12 a.m. UTC | #11
On Tue 2018-06-26 12:30:24, Oliver Neukum wrote:
> On Di, 2018-06-26 at 00:16 +0200, Pavel Machek wrote:
> > Interested parties can easily fix up the userland parts of uswsusp,
> > 
> > change crypto, add or remove dependencies, move it to other hosting,
> > 
> > or drop it and start again. Kernel interface is flexible enough. If
> > 
> > Chen wants to move the s2disk encryption into kernel, it is his task
> > 
> > to explain why that is neccessary.
> 
> We would have to assume that the kernel is on a higher level of trust.
> To a certain extent it is.You cannot drop support for /dev/kmem conceptionally
> if there is an ioctl to snapshot it.

If I understood the description, proposed patches give userspace encryption
key + image encrypted with that key. So... that's not really an
improvement.

Anyway, I guess it makes sense to wait for v2 of patches with better
description of security goals of this.

									Pavel
diff mbox

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index cb468a5..e851afb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6393,6 +6393,14 @@  F:	include/linux/freezer.h
 F:	include/linux/pm.h
 F:	arch/*/include/asm/suspend*.h
 
+HIBERNATION CRYPTO UTILITY
+M:	"Chen Yu" <yu.c.chen@intel.com>
+M:	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+L:	linux-pm@vger.kernel.org
+B:	https://bugzilla.kernel.org
+S:	Supported
+F:	tools/power/crypto/
+
 HID CORE LAYER
 M:	Jiri Kosina <jikos@kernel.org>
 R:	Benjamin Tissoires <benjamin.tissoires@redhat.com>
diff --git a/tools/power/crypto/Makefile b/tools/power/crypto/Makefile
new file mode 100644
index 0000000..420301b
--- /dev/null
+++ b/tools/power/crypto/Makefile
@@ -0,0 +1,26 @@ 
+# SPDX-License-Identifier: GPL-2.0
+CC		= $(CROSS_COMPILE)gcc
+BUILD_OUTPUT	:= $(CURDIR)
+PREFIX		?= /usr
+DESTDIR		?=
+
+ifeq ("$(origin O)", "command line")
+	BUILD_OUTPUT := $(O)
+endif
+
+crypto_hibernate : crypto_hibernate.c
+CFLAGS +=	-Wall
+
+%: %.c
+	@mkdir -p $(BUILD_OUTPUT)
+	$(CC) $(CFLAGS) $< -o $(BUILD_OUTPUT)/$@
+
+.PHONY : clean
+clean :
+	@rm -f $(BUILD_OUTPUT)/cryphiber
+
+install : cryphiber
+	install -d  $(DESTDIR)$(PREFIX)/bin
+	install $(BUILD_OUTPUT)/cryphiber $(DESTDIR)$(PREFIX)/bin/cryphiber
+	install -d  $(DESTDIR)$(PREFIX)/share/man/man8
+	install turbostat.8 $(DESTDIR)$(PREFIX)/share/man/man8
diff --git a/tools/power/crypto/crypto_hibernate.c b/tools/power/crypto/crypto_hibernate.c
new file mode 100644
index 0000000..149ba78
--- /dev/null
+++ b/tools/power/crypto/crypto_hibernate.c
@@ -0,0 +1,447 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The hibernation key based derived function algorithm
+ *
+ * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+ * Copyright (C) 2018 Theodore Ts'o <tytso@mit.edu>
+ * (copied from e2fsprogs)
+ *
+ * The key derivation is based on a simplified implementation
+ * of PBKDF2 in e2fsprogs - both the key length and the hash
+ * bytes are the same - 512bits. crypto_hibernate will firstly
+ * probe the user for passphrase and salt, then uses them to
+ * generate a 512bits AES key by SHA512 hash and PBKDF2.
+ *
+ * Usage:
+ * 1. install the kernel module:
+ *    modprobe crypto_hibernation
+ * 2. run this tool to generate the key from
+ *    user provided passphrase (salt is read from kernel)
+ * 3. launch the hibernation process, the kernel
+ *    uses the key from step 2 to encrypt the
+ *    hibernation snapshot
+ * 4. resume the system and the initrd will
+ *    launch cryto_hibernate to read previous salt
+ *    from kernel and probe the user passphrase
+ *    and generate the same key
+ * 5. kernel uses this key to decrypt the hibernation
+ *    snapshot.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#define PBKDF2_ITERATIONS          0xFFFF
+#define SHA512_BLOCKSIZE 128
+#define SHA512_LENGTH 64
+#define SALT_BYTES	16
+#define SYM_KEY_BYTES SHA512_LENGTH
+#define TOTAL_USER_INFO_LEN	(SALT_BYTES+SYM_KEY_BYTES)
+#define MAX_PASSPHRASE_SIZE	1024
+
+struct hibernation_crypto_keys {
+	char derived_key[SYM_KEY_BYTES];
+	char salt[SALT_BYTES];
+	bool valid;
+};
+
+struct hibernation_crypto_keys hib_keys;
+
+static char *get_key_ptr(void)
+{
+	return hib_keys.derived_key;
+}
+
+static char *get_salt_ptr(void)
+{
+	return hib_keys.salt;
+}
+
+static const unsigned int K[64] = {
+    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+    0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+    0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+    0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+    0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+    0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+    0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+    0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+    0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+    0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+    0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+    0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+    0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Various logical functions */
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
+#define S(x, n)         RORc((x),(n))
+#define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#define RORc(x, y) ( ((((unsigned int)(x)&0xFFFFFFFFUL)>>(unsigned int)((y)&31)) | ((unsigned int)(x)<<(unsigned int)(32-((y)&31)))) & 0xFFFFFFFFUL)
+
+#define RND(a,b,c,d,e,f,g,h,i)                         \
+     t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];   \
+     t1 = Sigma0(a) + Maj(a, b, c);                    \
+     d += t0;                                          \
+     h  = t0 + t1;
+
+#define STORE64H(x, y) \
+	do { \
+		(y)[0] = (unsigned char)(((x)>>56)&255);\
+		(y)[1] = (unsigned char)(((x)>>48)&255);\
+		(y)[2] = (unsigned char)(((x)>>40)&255);\
+		(y)[3] = (unsigned char)(((x)>>32)&255);\
+		(y)[4] = (unsigned char)(((x)>>24)&255);\
+		(y)[5] = (unsigned char)(((x)>>16)&255);\
+		(y)[6] = (unsigned char)(((x)>>8)&255);\
+		(y)[7] = (unsigned char)((x)&255); } while(0)
+
+#define STORE32H(x, y)                                                                     \
+  do { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255);   \
+       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } while(0)
+
+#define LOAD32H(x, y)                            \
+  do { x = ((unsigned int)((y)[0] & 255)<<24) | \
+           ((unsigned int)((y)[1] & 255)<<16) | \
+           ((unsigned int)((y)[2] & 255)<<8)  | \
+           ((unsigned int)((y)[3] & 255)); } while(0)
+
+struct sha512_state {
+	unsigned long long length;
+	unsigned int state[8], curlen;
+	unsigned char buf[64];
+};
+
+/* This is a highly simplified version from libtomcrypt */
+struct hash_state {
+	struct sha512_state sha512;
+};
+
+static void sha512_compress(struct hash_state * md, const unsigned char *buf)
+{
+	unsigned int S[8], W[64], t0, t1;
+	unsigned int t;
+	int i;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++) {
+		S[i] = md->sha512.state[i];
+	}
+
+	/* copy the state into 512-bits into W[0..15] */
+	for (i = 0; i < 16; i++) {
+		LOAD32H(W[i], buf + (4*i));
+	}
+
+	/* fill W[16..63] */
+	for (i = 16; i < 64; i++) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+	}
+
+	/* Compress */
+	for (i = 0; i < 64; ++i) {
+		RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i);
+		t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+		S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+	}
+
+	/* feedback */
+	for (i = 0; i < 8; i++) {
+		md->sha512.state[i] = md->sha512.state[i] + S[i];
+	}
+}
+
+static void sha512_init(struct hash_state * md)
+{
+	md->sha512.curlen = 0;
+	md->sha512.length = 0;
+	md->sha512.state[0] = 0x6A09E667UL;
+	md->sha512.state[1] = 0xBB67AE85UL;
+	md->sha512.state[2] = 0x3C6EF372UL;
+	md->sha512.state[3] = 0xA54FF53AUL;
+	md->sha512.state[4] = 0x510E527FUL;
+	md->sha512.state[5] = 0x9B05688CUL;
+	md->sha512.state[6] = 0x1F83D9ABUL;
+	md->sha512.state[7] = 0x5BE0CD19UL;
+}
+
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+
+static void sha512_process(struct hash_state * md, const unsigned char *in, unsigned long inlen)
+{
+	unsigned long n;
+
+	while (inlen > 0) {
+		if (md->sha512.curlen == 0 && inlen >= SHA512_BLOCKSIZE) {
+			sha512_compress(md, in);
+			md->sha512.length += SHA512_BLOCKSIZE * 8;
+			in += SHA512_BLOCKSIZE;
+			inlen -= SHA512_BLOCKSIZE;
+		} else {
+			n = MIN(inlen, (SHA512_BLOCKSIZE - md->sha512.curlen));
+			memcpy(md->sha512.buf + md->sha512.curlen, in, (size_t)n);
+			md->sha512.curlen += n;
+			in += n;
+			inlen -= n;
+			if (md->sha512.curlen == SHA512_BLOCKSIZE) {
+				sha512_compress(md, md->sha512.buf);
+				md->sha512.length += 8*SHA512_BLOCKSIZE;
+				md->sha512.curlen = 0;
+			}
+		}
+	}
+}
+
+static void sha512_done(struct hash_state * md, unsigned char *out)
+{
+	int i;
+
+	/* increase the length of the message */
+	md->sha512.length += md->sha512.curlen * 8;
+
+	/* append the '1' bit */
+	md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80;
+
+	/* if the length is currently above 56 bytes we append zeros
+	 * then compress.  Then we can fall back to padding zeros and length
+	 * encoding like normal.
+	 */
+	if (md->sha512.curlen > 56) {
+	while (md->sha512.curlen < 64) {
+		md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+	}
+	sha512_compress(md, md->sha512.buf);
+	md->sha512.curlen = 0;
+	}
+
+	/* pad upto 56 bytes of zeroes */
+	while (md->sha512.curlen < 56) {
+		md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+	}
+
+	/* store length */
+	STORE64H(md->sha512.length, md->sha512.buf+56);
+	sha512_compress(md, md->sha512.buf);
+
+	/* copy output */
+	for (i = 0; i < 8; i++) {
+		STORE32H(md->sha512.state[i], out+(4*i));
+	}
+}
+
+void start_sha512(const unsigned char *in, unsigned long in_size,
+		   unsigned char out[SHA512_LENGTH])
+{
+	struct hash_state md;
+
+	sha512_init(&md);
+	sha512_process(&md, in, in_size);
+	sha512_done(&md, out);
+}
+
+static void pbkdf2_sha512(const char *passphrase, const char *salt,
+			  unsigned int count,
+			  char *derived_key)
+{
+	size_t passphrase_size = strlen(passphrase);
+	unsigned char buf[SHA512_LENGTH + MAX_PASSPHRASE_SIZE] = {0};
+	unsigned char tempbuf[SHA512_LENGTH] = {0};
+	char final[SHA512_LENGTH] = {0};
+	unsigned char saltbuf[SALT_BYTES + MAX_PASSPHRASE_SIZE] = {0};
+	int actual_buf_len = SHA512_LENGTH + passphrase_size;
+	int actual_saltbuf_len = SALT_BYTES + passphrase_size;
+	unsigned int x, y;
+	unsigned int *final_u32 = (unsigned int *)final;
+	unsigned int *temp_u32 = (unsigned int *)tempbuf;
+
+	memcpy(saltbuf, salt, SALT_BYTES);
+	memcpy(&saltbuf[SALT_BYTES], passphrase, passphrase_size);
+	memcpy(&buf[SHA512_LENGTH], passphrase, passphrase_size);
+
+	for (x = 0; x < count; ++x) {
+		if (x == 0) {
+			start_sha512(saltbuf, actual_saltbuf_len, tempbuf);
+		} else {
+			/*
+			 * buf: [previous hash || passphrase]
+			 */
+			memcpy(buf, tempbuf, SHA512_LENGTH);
+			start_sha512(buf, actual_buf_len, tempbuf);
+		}
+		for (y = 0; y < (sizeof(final) / sizeof(*final_u32)); ++y)
+			final_u32[y] = final_u32[y] ^ temp_u32[y];
+	}
+	memcpy(derived_key, final, SYM_KEY_BYTES);
+}
+
+#define HIBERNATE_SALT_READ      _IOW('C', 3, struct hibernation_crypto_keys)
+#define HIBERNATE_KEY_WRITE     _IOW('C', 4, struct hibernation_crypto_keys)
+
+static int disable_echo(struct termios *saved_settings)
+{
+	struct termios current_settings;
+	int rc = 0;
+
+	rc = tcgetattr(0, &current_settings);
+	if (rc)
+		return rc;
+	*saved_settings = current_settings;
+	current_settings.c_lflag &= ~ECHO;
+	rc = tcsetattr(0, TCSANOW, &current_settings);
+
+	return rc;
+}
+
+static void get_passphrase(char *passphrase, int len)
+{
+	char *p;
+	struct termios current_settings;
+
+	assert(len > 0);
+	disable_echo(&current_settings);
+	p = fgets(passphrase, len, stdin);
+	tcsetattr(0, TCSANOW, &current_settings);
+	printf("\n");
+	if (!p) {
+		printf("Aborting.\n");
+		exit(1);
+	}
+	p = strrchr(passphrase, '\n');
+	if (!p)
+		p = passphrase + len - 1;
+	*p = '\0';
+}
+
+#define CRYPTO_FILE	"/dev/crypto_hibernate"
+
+static int write_keys(void)
+{
+	int fd;
+
+	fd = open(CRYPTO_FILE, O_RDWR);
+	if (fd < 0) {
+		printf("Cannot open device file...\n");
+		return -EINVAL;
+	}
+	ioctl(fd, HIBERNATE_KEY_WRITE, get_key_ptr());
+	return 0;
+}
+
+static int read_salt(void)
+{
+	int fd;
+
+	fd = open(CRYPTO_FILE, O_RDWR);
+	if (fd < 0) {
+		printf("Cannot open device file...\n");
+		return -EINVAL;
+	}
+	ioctl(fd, HIBERNATE_SALT_READ, get_salt_ptr());
+	return 0;
+}
+
+int key_derive_from_passphrase(const char *pass)
+{
+	unsigned int pass_len = strlen(pass);
+
+	if (pass_len > MAX_PASSPHRASE_SIZE) {
+		printf("Passphrase size is %d; max is %d.\n", pass_len,
+		       MAX_PASSPHRASE_SIZE);
+		exit(1);
+	}
+
+	/* Need to get salt from
+	 * kernel first.
+	 */
+	if (read_salt())
+		exit(1);
+	/* Store the derived key in result buf. */
+	pbkdf2_sha512(pass, get_salt_ptr(), PBKDF2_ITERATIONS, get_key_ptr());
+	if (write_keys())
+		exit(1);
+
+	return 0;
+}
+
+void help(void)
+{
+	printf(
+	"Usage: crypto_hibernate [OPTIONS]\n"
+	"-p	passphrase [probed from user if not given]\n"
+	"-s salt [read from kernel if not given]\n");
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, option_index = 0;
+	char in_passphrase[MAX_PASSPHRASE_SIZE];
+
+	while ((opt = getopt_long_only(argc, argv, "+p:s:h",
+				NULL, &option_index)) != -1) {
+		switch (opt) {
+		case 'p':
+			{
+				char *p = optarg;
+
+				if (strlen(p) >= (MAX_PASSPHRASE_SIZE - 1)) {
+					printf("Please provide passphrase less than %d bytes.\n",
+						MAX_PASSPHRASE_SIZE);
+					exit(1);
+				}
+				strcpy(in_passphrase, p);
+			}
+			break;
+		case 's':
+			{
+				char *p = optarg;
+
+				if (strlen(p) != (SALT_BYTES - 1)) {
+					printf("Please provide salt with len less than %d bytes.\n",
+						SALT_BYTES);
+					exit(1);
+				}
+				strcpy(get_salt_ptr(), p);
+			}
+			break;
+		case 'h':
+		default:
+			help();
+			exit(1);
+		}
+	}
+
+	printf("Enter passphrase (echo disabled): ");
+	get_passphrase(in_passphrase, sizeof(in_passphrase));
+
+	if (key_derive_from_passphrase(in_passphrase))
+		exit(1);
+
+	return 0;
+}