Tải bản đầy đủ - 0 (trang)
16 Using a High-Level, Error-Resistant Encryption and Decryption API

16 Using a High-Level, Error-Resistant Encryption and Decryption API

Tải bản đầy đủ - 0trang

SPC_CIPHERQ objects are initialized by calling spc_cipherq_setup( ), which requires

the code from Recipe 5.5, as well as an implementation of the randomness API discussed in Recipe 11.2:

#include

#include

#include

#define MAX_KEY_LEN (32)



/* 256 bits */



size_t spc_cipherq_setup(SPC_CIPHERQ *q, unsigned char *basekey, size_t keylen,

size_t keyuses) {

unsigned char dk[MAX_KEY_LEN];

unsigned char salt[5];

spc_rand(salt, 5);

spc_make_derived_key(basekey, keylen, salt, 5, 1, dk, keylen);

if (!cwc_init(&(q->ctx), dk, keylen * 8)) return 0;

memcpy(q->nonce, salt, 5);

spc_memset(basekey, 0, keylen);

return keyuses + 1;

}



The function has the following arguments:

q

SPC_CIPHERQ context object.

basekey



Shared key used by both ends of communication (the “base key” that will be

used to derive session keys).

keylen



Length of the shared key in bytes, which must be 16, 24, or 32.

keyuses



Indicates how many times the current key has been used to initialize a SPC_

CIPHERQ object. If you are going to reuse keys, it is important that this argument

be used properly.

On error, spc_cipherq_setup() returns 0. Otherwise, it returns the

next value it would expect to receive for the keyuses argument. Be sure

to save this value if you ever plan to reuse keys.

Note also that basekey is erased upon successful initialization.



Every time you initialize an SPC_CIPHERQ object, a key specifically for use with that

queue instance is generated, using the basekey and the keyuses arguments. To derive

the key, we use the key derivation function discussed in Recipe 4.11. Note that this is

useful when two parties share a long-term key that they wish to keep reusing. However, if you exchange a session key at connection establishment (i.e., using one of the



218



|



Chapter 5: Symmetric Encryption

This is the Title of the Book, eMatter Edition

Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.



techniques from Chapter 8), the key derivation step is unnecessary, because reusing

{key, nonce} pairs is already incredibly unlikely in such a situation.

Both communicating parties must initialize their queue with identical parameters.

When you’re done with a queue, you should deallocate internally allocated memory

by calling spc_cipherq_cleanup( ):

void spc_cipherq_cleanup(SPC_CIPHERQ *q) {

spc_memset(q, 0, sizeof(SPC_CIPHERQ));

}



Here are implementations of the encryption and decryption operations (including a

helper function), both of which return a newly allocated buffer containing the results

of the appropriate operation:

static void increment_counter(SPC_CIPHERQ *q) {

if (!++q->nonce[10]) if (!++q->nonce[9]) if (!++q->nonce[8]) if (!++q->nonce[7])

if (!++q->nonce[6]) ++q->nonce[5];

}

unsigned char *spc_cipherq_encrypt(SPC_CIPHERQ *q, unsigned char *m, size_t mlen,

size_t *ol) {

unsigned char *ret;

if (!(ret = (unsigned char *)malloc(mlen + 16))) {

if (ol) *ol = 0;

return 0;

}

cwc_encrypt(&(q->ctx), 0, 0, m, mlen, q->nonce, ret);

increment_counter(q);

if (ol) *ol = mlen + 16;

return ret;

}

unsigned char *spc_cipherq_decrypt(SPC_CIPHERQ *q, unsigned char *m, size_t mlen,

size_t *ol) {

unsigned char *ret;

if (!(ret = (unsigned char *)malloc(mlen - 16))) {

if (ol) *ol = 0;

return 0;

}

if (!cwc_decrypt(&(q->ctx), 0, 0, m, mlen, q->nonce, ret)) {

free(ret);

if (ol) *ol = 0;

return 0;

}

increment_counter(q);

if (ol) *ol = mlen - 16;

return ret;

}



Using a High-Level, Error-Resistant Encryption and Decryption API | 219

This is the Title of the Book, eMatter Edition

Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.



The functions spc_cipherq_encrypt( ) and spc_cipherq_decrypt( ) each take four

arguments:

q

SPC_CIPHERQ object to use for encryption or decryption.

m



Message to be encrypted or decrypted.

mlen



Length of the message to be encrypted or decrypted, in bytes.

ol



The number of bytes returned from the encryption or decryption operation is

stored in this integer pointer. This may be NULL if you don’t need the information. The number of bytes returned will always be the message length plus 16

bytes for encryption, or the message length minus 16 bytes for decryption.

These functions don’t check for counter rollover because you can use this API to

send over 250 trillion messages with a single key, which should be adequate for any

use.

Instead of using such a large counter, it is a good idea to use only five

bytes for the counter and initialize the rest with a random salt value.

The random salt helps prevent against a class of problems in which the

attacker amortizes the cost of an attack by targeting a large number of

possible keys at once. In Recipe 9.12, we show a similar construction

that uses both a salt and a counter in the nonce.



If you do think you might send more messages under a single key, be sure to rekey in

time. (This scheme is set up to handle at least four trillion keyings with a single base

key.)

In the previous code, the nonces are separately managed by both parties in the communication. They each increment by one when appropriate, and will fail to decrypt a

message with the wrong nonce. Thus, this solution prevents capture replay attacks

and detects message drops or message reordering, all as a result of implicit message

numbering. Some people like explicit message numbering and would send at least a

message number, if not the entire nonce, with each message (though you should

always compare against the previous nonce to make sure it’s increasing). In addition, if there’s a random portion to the nonce as we suggested above, the random

portion needs to be communicated to both parties. In Recipe 9.12, we send the

nonce explicitly with each message, which helps communicate the portion randomly

selected at connection setup time.

It’s possible to mix and match calls to spc_cipherq_encrypt( ) and spc_cipherq_

decrypt( ) using a single context. However, if you want to use this API in this manner, do so only if the communicating parties send messages in lockstep. If parties can



220



|



Chapter 5: Symmetric Encryption

This is the Title of the Book, eMatter Edition

Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.



communicate asynchronously (that is, without taking turns), there is the possibility

for a race condition in which the SPC_CIPHERQ states on each side of the communication get out of sync, which will needlessly cause decryption operations to fail.

If you need to perform asynchronous communication with an infrastructure like this,

you could use two SPC_CIPHERQ instances, one where the client encrypts messages for

the server to decrypt, and another where the server encrypts messages for the client

to decrypt.

The choice you need to make is whether each SPC_CIPHERQ object should be keyed

separately or should share the same key. Sharing the same key is possible, as long as

you ensure that the same {key, nonce} pair is never reused. The way to do this is to

manage two sets of nonces that can never collide. Generally, you do this by setting

the high bit of the nonce buffer to 1 in one context and 0 in another context.

Here’s a function that takes an existing context that has been set up, but not otherwise used, and turns it into two contexts with the same key:

void spc_cipherq_async_setup(SPC_CIPHERQ *q1, SPC_CIPHERQ *q2) {

memcpy(q2, q1, sizeof(SPC_CIPHERQ));

q1->nonce[0] &= 0x7f; /* The upper bit of q1's nonce is always 0. */

q2->nonce[0] |= 0x80; /* The upper bit of q2's nonce is always 1. */

}



We show a similar trick in which we use only one abstraction in Recipe 9.12.



See Also

Recipes 4.11, 5.5, 5.10, 9.12, 11.2



5.17 Performing Block Cipher Setup (for CBC,

CFB, OFB, and ECB Modes) in OpenSSL

Problem

You need to set up a cipher so that you can perform encryption and/or decryption

operations in CBC, CFB, OFB, or ECB mode.



Solution

Here are the steps you need to perform for cipher setup in OpenSSL, using their

high-level API:

1. Make sure your code includes openssl/evp.h and links to libcrypto (-lcrypto).

2. Decide which algorithm and mode you want to use, looking up the mode in

Table 5-6 to determine which function instantiates an OpenSSL object repre-



Performing Block Cipher Setup (for CBC, CFB, OFB, and ECB Modes) in OpenSSL | 221

This is the Title of the Book, eMatter Edition

Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.



senting that mode. Note that OpenSSL provides only a CTR mode implementation for AES. See Recipe 5.9 for more on CTR mode.

3. Instantiate a cipher context (type EVP_CIPHER_CTX).

4. Pass a pointer to the cipher context to EVP_CIPHER_CTX_init( ) to initialize memory properly.

5. Choose an IV or nonce, if appropriate to the mode (all except ECB).

6. Initialize the mode by calling EVP_EncryptInit_ex( ) or EVP_DecryptInit_ex( ), as

appropriate:

int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER

*engine, unsigned char *key, unsigned

int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER

*engine, unsigned char *key, unsigned



*type, ENGINE

char *ivornonce);

*type, ENGINE

char *ivornonce);



7. If desired, perform any additional configuration the cipher may allow (see Recipe 5.20).



Discussion

Use the raw OpenSSL API only when absolutely necessary because

there is a huge potential for introducing a security vulnerability by

accident. For general-purpose use, we recommend a high-level

abstraction, such as that discussed in Recipe 5.16.



The OpenSSL EVP API is a reasonably high-level interface to a multitude of cryptographic primitives. It attempts to abstract out most algorithm dependencies, so that

algorithms are easy to swap.*

The EVP_EncryptInit_ex( ) and EVP_DecryptInit_ex( ) functions set up a cipher context object to be used for further operations. It takes four arguments that provide all

the information necessary before encryption or decryption can begin. Both take the

same arguments:

ctx



Pointer to an EVP_CIPHER_CTX object, which stores cipher state across calls.

type



Pointer to an EVP_CIPHER object, which represents the cipher configuration to use

(see the later discussion).

engine



Pointer to an ENGINE object representing the actual implementation to use. For

example, if you want to use hardware acceleration, you can pass in an ENGINE

object that represents your cryptographic accelerator.



* EVP stands for “envelope.”



222



|



Chapter 5: Symmetric Encryption

This is the Title of the Book, eMatter Edition

Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

16 Using a High-Level, Error-Resistant Encryption and Decryption API

Tải bản đầy đủ ngay(0 tr)

×