dcrypt
LibraryBelow is a description of some of the functions implemented in the dcrypt library that you may find useful in completing the lab assignments. You will need to include the dcrypt.h header file to access these functions. You may also want to take a look at these sample programs (tst.c, tst_sha1.c) to see some of these functions in action.
void putint (void *dp, u_int32_t val);
void puthyper (void *dp, u_int64_t val);
putint
function puts the 32-bit integer value of
val
into memory in big-endian order at location
dp
. dp
does not need to be aligned. The
bytes stored at dp
will be the same on big- and
little-endian machines. puthyper
is like
putint
but puts a 64-bit value into 8 bytes of memory.
u_int32_t getint (const void *dp);
u_int64_t gethyper (const void *dp);
getint
and gethyper
routines retrieve
values stored by putint
and puthyper
respectively.
char *armor64 (const void *dp, size_t len);
len
bytes from the binary string pointed by
dp
to a longer, base-64, printable ASCII string. You
will need to use this to transform random session keys (which could
contain zero-bytes) into a NULL
-terminated ANSI C string.
ssize_t dearmor64 (void *out, const char *s);
armor64
function, and return the number of
bytes that were placed at out
. The return value is
negative if the NULL
-terminated ANSI C string
s
is not the output of armor64
.
ssize_t armor64len (const char *s);
s
. If some prefix of s
represents a valid
armor64 string, then the length of such prefix is returned. Otherwise,
-1 is returned, indicating that s is not the output of armor64
.
ssize_t dearmor64len (const char *s);
s
. If some prefix of s
represents a valid
armor64 string, then the length of the decoded data that would result
by "dearmoring" s
is returned. Otherwise, -1 is
returned, indicating that s is not the output of armor64
.
The libraries you are using contain a cryptographic pseudo-random
number generator, whose state is kept in a global 16-byte array called
prng_state
. Before using the random number generator,
you must initialize it.
void prng_seed (void *buf, size_t len);
len
bytes from buf
as seed. Providing
a good seed may be a difficult task; some Operating Systems
(including FreeBSD, OpenBSD and most Linux distributions) provide
you with a source of randomness under /dev/random
(or one
of its variants: /dev/srandom
, /dev/urandom
,
etc.). If a random
device is available, you should read (at least) 128 bits from it and
use it as a seed; otherwise, as a very rough
approximation, you could supply some information about your local
machine (e.g., time of the day, PID/GID value) that is
difficult to predict.
void prng_getbytes (void *buf, size_t len);
len
pseudo-random bytes to memory at location
buf
.
u_int32_t prng_getword ();
u_int64_t prng_gethyper ();
For actually encrypting and decrypting file data, you will use the Rijndael [FIPS-197] block cipher (also called AES---Advanced Encryption Standard). Rijndael is a 128-bit block cipher. It supports two operations--encryption, and decryption. Encryption transforms 16 bytes (128 bits) of plaintext data into 16 bytes of ciphertext data using a secret key. Someone who does not know the secret key cannot recover the plaintext from the ciphertext. The decryption algorithm, given knowledge of the secret key, transforms ciphertext into plaintext.
The libraries you are using define a struct
called
aes_ctx
that you should use to hold secret keys for AES.
You should manipulate your AES secret keys with the following functions:
void aes_setkey (aes_ctx *aes, const void *key, u_int
len);
len
bytes fro
the buffer key
. The key must be 16-, 24-, or
32-long.
void aes_encrypt (const aes_ctx *aes, void *buf, const void
*ibuf);
aes_encrypt
transforms 16 bytes of plaintext data at
ibuf
into 16 bytes of ciphertext data which it writes to
buf
. It uses the secret key previously stored within
aes
using the aes_setkey
function.
void aes_decrypt (const aes_ctx *aes, void *buf, const void
*ibuf);
aes_decrypt
decrypts 16 bytes, inverting the
aes_encrypt
function.
void aes_clrkey (aes_ctx *aes);
aes
, thus wiping out the secret
encryption key that was previosly stored there. If you only used symmetric-key cryptography, you would need to exchange a secret key with all the friends with whom you want to have confidential communication. With Public-Key Cryptography, instead, you can give all your friend the same public key, and they will be able to send you encrypted content that only you can recover. Similarly, you only need to know your friend's public key to create a ciphertext that only he/she will be able to decrypt. The libraries you are going to use contain an implementation of two very well-known Public-Key Encryption schemes: Rabin [Wil80] and ElGamal [ElG86] To complete the assignment, you don't need to know anything about how these schemes work, except for the interface that they provide, which is described below:
dckey *dckeygen (const char *type, size_t k, const char
*extra);
struct
that holds a new private
key/public key pair, of the type specified by type
. The
value of type
should be one of the constants defined in
dcrypt.h
, namely
DC_ELGAMAL
or DC_RABIN
.
The cryptographic strength of the key pair generated can be tuned
using the parameter k
: a value of at least 1024 is
recommended to get a reasonable level of security.
The value of extra
should be either NULL
, or
an ASCII string representing information about the parameters that
should be used in generating the private key/public key pair.
For
this assignment, you can always supply a value of NULL
.
Notice that the dckeygen
function internally makes use of
the pseudo-random number generator provided by libdcrypt
:
therefore, you shouldn't call dckeygen
before
initializing the pseudo-random number generator with prng_seed
.
char *dcexport_pub (const dckey *key);
char *dcexport_priv (const dckey *key);
dcexport_pub
and dcexport_priv
functions
return a base-64, printable, NULL
-terminated ANSI C
string representing the public key or private key stored within
key
, respectively.
dckey *dcimport_pub (const char *asc);
dckey *dcimport_priv (const char *asc);
dcimport_pub
and dcimport_priv
functions
retrieve the dckey
that was previously exported into
the ASCII string asc
using dcexport_pub
or
dcexport_priv
, respectively.
If asc
is not of the form expected,
dcimport_pub
and dcimport_priv
return
NULL
.
char *dcencrypt (const dckey *key, const char *msg);
dcencrypt
uses the public key contained in
key
to transform the plaintext data contained in the
NULL
-terminated ANSI C string msg
into a
NULL
-terminated ANSI C ciphertext string, which is
returned as output.
The encryption algorithm used is CCA-secure.
Since dcencrypt
expects the plaintext to be a
NULL
-terminated ANSI C string, you should use
the armor64
function if you need to encrypt an arbitrary
bit string (such as a secret key for AES).
char *dcdecrypt (const dckey *key, const char *cmsg);
aes_decrypt
decrypts the ciphertext contained in the
NULL
-terminated ANSI C string cmsg
,
inverting the dcencrypt
function.
void dcfree (dckey *key);
key
.
Once you are done with a private key, you
should always wipe out the memory location where it was stored!
int dcispriv (const dckey *key);
key
is a private key; otherwise it returns 0.
The SHA-1 [FIPS-180-1] hash function hashes an arbitrary-length input (up to 2^64 bytes) to a 20-byte output. SHA-1 is known as a cryptographic hash function. While nothing has been formally proven about the function, it is generally assumed that SHA-1 is one-way and collision-resistant. These properties are defined as follows:
For someone who steals the file of password hashes, there is no known way of recovering passwords more efficient than guessing passwords and verifying the guesses. (Of course, the fact that users often choose easily-guessed passwords is a problem.)
Collision-resistant functions have many uses, stemming from the fact that the short output value effectively uniquely specifies an arbitrary-length input. One cannot recover the input from the output, but given the input, one can verify that it does, indeed, match the output. One might, for instance, implement a web cache in which contents is indexed by a SHA-1 hash of the URL. Having fixed-length names for stored content would simplify the implementation.
void sha1_hash (void *digest, const void *buf, size_t
len);
len
bytes of data at buf
, and places
the resulting 20 bytes at digest
.
Sometimes the input that you want to hash is so long that it is
inconvenient to store it entirely in memory before being able to
hash it. This is the case for example when hashing the entire content
of a file into a short digest.
For this reason, the libraries you are using allow you to process a
long input "one chunk at a time." To do that, you should use a
struct
called sha1_ctx
, which will store the
"partial digest" as you keep providing new input to be hashed. You
should manipulate sha1_ctx
struct
s with the
following functions:
void sha1_init (sha1_ctx *sc);
sha1_ctx struct
that will contain the
partial hash.
void sha1_update (sha1_ctx *sc, const void *data, size_t
len);
len
bytes at data
to the input being
hashed, but does not produce a result. Thus, one can hash a large
amount of data without having it all in memory, by calling
sha1_update
on one chunk at a time.
void sha1_final (sha1_ctx *sc, void *digest);
digest
.
Message Authentication Codes (MACs) are a symmetric-key primitive allowing you to check the integrity of the information to which the MAC is applied. Recall that encryption does not guarantee integrity! The fact that you were able to decrypt a ciphertext is not enough to be sure that nobody tampered with its content. For integrity, you should always append a MAC to the content.
You use MACs as follows. Let's say that you want to store a file on your file-server, but you are afraid that its content will be changed behind your back. Then, you use a secret key to "mac" the file, and store the resulting MAC along with the file. Now, when you check back with your file-server and retrieve your file, you will also retrieve the MAC that you appended. Then, you will use the secret key to compute the MAC again, and if the MAC you just computed is the same as the value that you retrieved from the file-server, then you are sure that nobody touched your file. This is because, if somebody had changed the file, then they should have computed the corresponding MAC in order to fool you. However, secure MACs are concocted such that, without knowing the secret key, it is computationally intractable to compute the right MAC, even after having seen a lot of valid (message, MAC) pairs.
The Keyed-Hash Message Authentication Code (HMAC) [FIPS-198a] is a secure Message Authentication Code based on the use of any cryptographic hash function, like SHA-1. The libraries you are using contain an implementation of HMAC, instantiated with the SHA-1 cryptographic hash function.
void hmac_sha1 (const char *key, size_t keylen, void *out, const void
*data, size_t dlen);
len
bytes of data at
buf
using the key key
, and places
the resulting 20 bytes at out
.
Similarly to what discussed for the case of SHA-1, the libraries you are using allow you to process a long input "one chunk at a time."
void hmac_sha1_init (const char *key, size_t keylen,
sha1_ctx *sc);
sha1_ctx struct
that will contain the
partial HMAC under the key key
.
void hmac_sha1_update (sha1_ctx *sc, const void *data, size_t
len);
len
bytes from data
to the input being
hmac'ed, but does not produce a result. Thus, one can hmac a large
amount of data without having it all in memory, by calling
hmac_sha1_update
on one chunk at a time.
void hmac_sha1_final (const char *key, size_t keylen,
sha1_ctx *sc, void *out);
out
. key
used in
hmac_sha1_final
is different from the one initially used in
hmac_sha1_init
.
[ElG85] |
T. ElGamal, A public key cryptosystem and a signature
scheme based on discrete logarithms. IEEE Transactions on Information Theory, Vol. IT-31, No. 4, pp. 469--472, July 1985. |
|
---|---|---|
[FIPS-180-1] |
FIPS-180-1, Secure Hash Standard. U.S. Department of Commerce/N.I.S.T., 1994 |
|
[FIPS-197] |
FIPS-197, Announcing the Advanced Encryption Standard. U.S. Department of Commerce/N.I.S.T., 2001 |
|
[FIPS-198a] |
FIPS-198a, The Keyed-Hash Message Authentication Code
(HMAC). U.S. Department of Commerce/N.I.S.T., 2002 |
|
[Wil80] |
H. C. Williams, A Modification of the RSA Public-Key
Encryption Procedure. IEEE Transactions on Information Theory, Vol. IT-26, No. 6, November 1980. |