/*
 * Set the key using an ioctl for rt-privacy NFSv4 mounts.
 *
 * Copyright (C) 2007 Avishay Traeger, Kumar Thangavelu, Erez Zadok
 * Copyright (C) 2007 Stony Brook University (SUNY)
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/hmac.h>

/* Copied from the custom header file nfs4_crypt.h */
struct _nfs4_ioctl_SETKEY {
	char ukey [16];
};
#define NFS4_IOCTL_SETKEY	_IOW(0x15, 10, struct _nfs4_ioctl_SETKEY)
/* End Copied from nfs4_crypt.h */

int print_hex(unsigned char *buf, int len)
{
	int i;
	int n;

	for(i=0,n=0;i<len;i++){
		if(n > 7){
			printf("\n");
			n = 0;
		}
		printf("0x%02x, ",buf[i]);
		n++;
	}
	printf("\n");

	return(0);
}

int calculate_salt(char *passwd, int passwd_len, char *salt, int salt_len) {
	EVP_MD_CTX mdctx;
	unsigned char md_value[EVP_MAX_MD_SIZE];
	unsigned int md_len;

	OpenSSL_add_all_digests();
	EVP_MD_CTX_init(&mdctx);
	EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
	EVP_DigestUpdate(&mdctx, passwd, passwd_len);
	EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
	EVP_MD_CTX_cleanup(&mdctx);

	memset(salt, 0, salt_len);
	if (md_len > salt_len) 
		md_len = salt_len;

	memcpy(salt, md_value, md_len);
	return 0;
}

#define MAX_PASSWORD 64
#define MAX_KEY 16 
int main(int argc, char *argv[]) {
	int fd, ret=0;
	struct _nfs4_ioctl_SETKEY val;
	char password[MAX_PASSWORD];
	char salt[PKCS5_SALT_LEN];

	if (argc != 2 && argc != 3) {
		fprintf(stderr, "Usage: %s file [passphrase]\n", argv[0]);
		exit(1);
	}
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		perror(argv[1]);
		exit(1);
	}
	memset(password, 0, MAX_PASSWORD);
	if (argc != 3)  {
		printf("Enter key: ");
		fgets(password, sizeof(password), stdin);
		/* Remove the newline */
		password[strlen(password)-1] = 0;
	} else {
		/* We cannot use the last 2 chars as then it will not be 
		 * compatible with fgets impl */
		strncpy(password, argv[2], sizeof(password)-2);
	}

	if(calculate_salt(password, MAX_PASSWORD, salt, PKCS5_SALT_LEN)) {
		printf("ERROR: Unable to initialize salt. Please check if you have openssl libraries\n");
		exit(-1);
	}

	memset(val.ukey, 0, sizeof(val.ukey));
	PKCS5_PBKDF2_HMAC_SHA1(password, strlen(password), (unsigned char*)salt, PKCS5_SALT_LEN, PKCS5_DEFAULT_ITER, MAX_KEY, (unsigned char*)val.ukey);

	ret = ioctl(fd, NFS4_IOCTL_SETKEY, &val);
	if (ret < 0) {
		perror("ioctl");
	}
	close(fd);
	exit(ret);
}

