Ejemplo n.º 1
0
static krb5_error_code
k5_des3_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
                size_t num_data)
{
    int ret, olen = MIT_DES_BLOCK_LENGTH;
    unsigned char iblock[MIT_DES_BLOCK_LENGTH], oblock[MIT_DES_BLOCK_LENGTH];
    struct iov_block_state input_pos, output_pos;
    EVP_CIPHER_CTX ciph_ctx;
    krb5_boolean empty;

    ret = validate(key, ivec, data, num_data, &empty);
    if (ret != 0 || empty)
        return ret;

    IOV_BLOCK_STATE_INIT(&input_pos);
    IOV_BLOCK_STATE_INIT(&output_pos);

    EVP_CIPHER_CTX_init(&ciph_ctx);

    ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_des_ede3_cbc(), NULL,
                             key->keyblock.contents,
                             (ivec) ? (unsigned char*)ivec->data : NULL);
    if (!ret)
        return KRB5_CRYPTO_INTERNAL;

    EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);

    for (;;) {

        if (!krb5int_c_iov_get_block(iblock, MIT_DES_BLOCK_LENGTH,
                                     data, num_data, &input_pos))
            break;

        ret = EVP_EncryptUpdate(&ciph_ctx, oblock, &olen,
                                (unsigned char *)iblock, MIT_DES_BLOCK_LENGTH);
        if (!ret)
            break;

        krb5int_c_iov_put_block(data, num_data,
                                oblock, MIT_DES_BLOCK_LENGTH, &output_pos);
    }

    if (ivec != NULL)
        memcpy(ivec->data, oblock, MIT_DES_BLOCK_LENGTH);

    EVP_CIPHER_CTX_cleanup(&ciph_ctx);

    zap(iblock, sizeof(iblock));
    zap(oblock, sizeof(oblock));

    if (ret != 1)
        return KRB5_CRYPTO_INTERNAL;
    return 0;
}
Ejemplo n.º 2
0
krb5_error_code
krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
                         size_t num_data, const krb5_data *iv,
                         krb5_data *output)
{
    camellia_ctx ctx;
    unsigned char blockY[BLOCK_SIZE];
    struct iov_block_state iov_state;

    if (output->length < BLOCK_SIZE)
        return KRB5_BAD_MSIZE;

    if (camellia_enc_key(key->keyblock.contents,
                         key->keyblock.length, &ctx) != camellia_good)
        abort();

    if (iv != NULL)
        memcpy(blockY, iv->data, BLOCK_SIZE);
    else
        memset(blockY, 0, BLOCK_SIZE);

    IOV_BLOCK_STATE_INIT(&iov_state);
    for (;;) {
        unsigned char blockB[BLOCK_SIZE];

        if (!krb5int_c_iov_get_block(blockB, BLOCK_SIZE, data, num_data,
                                     &iov_state))
            break;

        xorblock(blockB, blockY);
        if (camellia_enc_blk(blockB, blockY, &ctx) != camellia_good)
            abort();
    }

    output->length = BLOCK_SIZE;
    memcpy(output->data, blockY, BLOCK_SIZE);

    return 0;
}
Ejemplo n.º 3
0
krb5_error_code
krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
                         size_t num_data, const krb5_data *iv,
			 krb5_data *output)
{
    CAMELLIA_KEY enck;
    unsigned char blockY[CAMELLIA_BLOCK_SIZE];
    struct iov_block_state iov_state;

    if (output->length < CAMELLIA_BLOCK_SIZE)
        return KRB5_BAD_MSIZE;

    Camellia_set_key(key->keyblock.contents,
                     NUM_BITS * key->keyblock.length, &enck);

    if (iv != NULL)
        memcpy(blockY, iv->data, CAMELLIA_BLOCK_SIZE);
    else
        memset(blockY, 0, CAMELLIA_BLOCK_SIZE);

    IOV_BLOCK_STATE_INIT(&iov_state);

    for (;;) {
        unsigned char blockB[CAMELLIA_BLOCK_SIZE];

        if (!krb5int_c_iov_get_block(blockB, CAMELLIA_BLOCK_SIZE, data,
				     num_data, &iov_state))
            break;

        xorblock(blockB, blockY);

        Camellia_ecb_encrypt(blockB, blockY, &enck, 1);
    }

    output->length = CAMELLIA_BLOCK_SIZE;
    memcpy(output->data, blockY, CAMELLIA_BLOCK_SIZE);

    return 0;
}
Ejemplo n.º 4
0
static krb5_error_code
krb5int_aes_decrypt_iov(const krb5_keyblock *key,
		        const krb5_data *ivec,
		        krb5_crypto_iov *data,
		        size_t num_data)
{
    aes_ctx ctx;
    char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
    int nblocks = 0, blockno, i;
    size_t input_length;
    struct iov_block_state input_pos, output_pos;

    CHECK_SIZES;

    if (aes_dec_key(key->contents, key->length, &ctx) != aes_good)
	abort();

    if (ivec != NULL)
	memcpy(tmp, ivec->data, BLOCK_SIZE);
    else
	memset(tmp, 0, BLOCK_SIZE);

    for (i = 0, input_length = 0; i < num_data; i++) {
	krb5_crypto_iov *iov = &data[i];

	if (ENCRYPT_IOV(iov))
	    input_length += iov->data.length;
    }

    IOV_BLOCK_STATE_INIT(&input_pos);
    IOV_BLOCK_STATE_INIT(&output_pos);

    nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
    if (nblocks == 1) {
	krb5int_c_iov_get_block((unsigned char *)tmp, BLOCK_SIZE,
				data, num_data, &input_pos);
	dec(tmp2, tmp, &ctx);
	krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2,
				BLOCK_SIZE, &output_pos);
    } else if (nblocks > 1) {
	char blockN2[BLOCK_SIZE];   /* second last */
	char blockN1[BLOCK_SIZE];   /* last block */

	for (blockno = 0; blockno < nblocks - 2; blockno++) {
	    char blockN[BLOCK_SIZE];

	    krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos);
	    dec(tmp2, blockN, &ctx);
	    xorblock(tmp2, tmp);
	    krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos);
	    memcpy(tmp, blockN, BLOCK_SIZE);
	}

	/* Do last two blocks, the second of which (next-to-last block
	   of plaintext) may be incomplete.  */

	/* First, get the last two encrypted blocks */
	memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
	krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos);
	krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos);

	/* Decrypt second last block */
	dec(tmp2, blockN2, &ctx);
	/* Set tmp2 to last (possibly partial) plaintext block, and
	   save it.  */
	xorblock(tmp2, blockN1);
	memcpy(blockN2, tmp2, BLOCK_SIZE);

	/* Maybe keep the trailing part, and copy in the last
	   ciphertext block.  */
	input_length %= BLOCK_SIZE;
	memcpy(tmp2, blockN1, input_length ? input_length : BLOCK_SIZE);
	dec(tmp3, tmp2, &ctx);
	xorblock(tmp3, tmp);
	/* Copy out ivec first before we clobber blockN1 with plaintext */
	if (ivec != NULL)
	    memcpy(ivec->data, blockN1, BLOCK_SIZE);
	memcpy(blockN1, tmp3, BLOCK_SIZE);

	/* Put the last two blocks back into the iovec */
	krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos);
	krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos);
    }

    return 0;
}
Ejemplo n.º 5
0
static krb5_error_code
krb5int_aes_encrypt_iov(const krb5_keyblock *key,
		        const krb5_data *ivec,
		        krb5_crypto_iov *data,
		        size_t num_data)
{
    aes_ctx ctx;
    char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE];
    int nblocks = 0, blockno;
    size_t input_length, i;
    struct iov_block_state input_pos, output_pos;

    if (aes_enc_key(key->contents, key->length, &ctx) != aes_good)
	abort();

    if (ivec != NULL)
	memcpy(tmp, ivec->data, BLOCK_SIZE);
    else
	memset(tmp, 0, BLOCK_SIZE);

    for (i = 0, input_length = 0; i < num_data; i++) {
	krb5_crypto_iov *iov = &data[i];

	if (ENCRYPT_IOV(iov))
	    input_length += iov->data.length;
    }

    IOV_BLOCK_STATE_INIT(&input_pos);
    IOV_BLOCK_STATE_INIT(&output_pos);

    nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
    if (nblocks == 1) {
	krb5int_c_iov_get_block((unsigned char *)tmp, BLOCK_SIZE,
				data, num_data, &input_pos);
	enc(tmp2, tmp, &ctx);
	krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2,
				BLOCK_SIZE, &output_pos);
    } else if (nblocks > 1) {
	char blockN2[BLOCK_SIZE];   /* second last */
	char blockN1[BLOCK_SIZE];   /* last block */

	for (blockno = 0; blockno < nblocks - 2; blockno++) {
	    char blockN[BLOCK_SIZE];

	    krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos);
	    xorblock(tmp, blockN);
	    enc(tmp2, tmp, &ctx);
	    krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos);

	    /* Set up for next block.  */
	    memcpy(tmp, tmp2, BLOCK_SIZE);
	}

	/* Do final CTS step for last two blocks (the second of which
	   may or may not be incomplete).  */

	/* First, get the last two blocks */
	memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
	krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos);
	krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos);

	/* Encrypt second last block */
	xorblock(tmp, blockN2);
	enc(tmp2, tmp, &ctx);
	memcpy(blockN2, tmp2, BLOCK_SIZE); /* blockN2 now contains first block */
	memcpy(tmp, tmp2, BLOCK_SIZE);

	/* Encrypt last block */
	xorblock(tmp, blockN1);
	enc(tmp2, tmp, &ctx);
	memcpy(blockN1, tmp2, BLOCK_SIZE);

	/* Put the last two blocks back into the iovec (reverse order) */
	krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos);
	krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos);

	if (ivec != NULL)
	    memcpy(ivec->data, blockN1, BLOCK_SIZE);
    }

    return 0;
}
Ejemplo n.º 6
0
static krb5_error_code
krb5int_camellia_encrypt(krb5_key key, const krb5_data *ivec,
                         krb5_crypto_iov *data, size_t num_data)
{
    unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE];
    int nblocks = 0, blockno;
    size_t input_length, i;
    struct iov_block_state input_pos, output_pos;

    if (key->cache == NULL) {
        key->cache = malloc(sizeof(struct camellia_key_info_cache));
        if (key->cache == NULL)
            return ENOMEM;
        CACHE(key)->enc_ctx.keybitlen = CACHE(key)->dec_ctx.keybitlen = 0;
    }
    if (CACHE(key)->enc_ctx.keybitlen == 0) {
        if (camellia_enc_key(key->keyblock.contents, key->keyblock.length,
                             &CACHE(key)->enc_ctx) != camellia_good)
            abort();
    }
    if (ivec != NULL)
        memcpy(tmp, ivec->data, BLOCK_SIZE);
    else
        memset(tmp, 0, BLOCK_SIZE);

    for (i = 0, input_length = 0; i < num_data; i++) {
        krb5_crypto_iov *iov = &data[i];

        if (ENCRYPT_IOV(iov))
            input_length += iov->data.length;
    }

    IOV_BLOCK_STATE_INIT(&input_pos);
    IOV_BLOCK_STATE_INIT(&output_pos);

    nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
    if (nblocks == 1) {
        krb5int_c_iov_get_block(tmp, BLOCK_SIZE, data, num_data, &input_pos);
        enc(tmp2, tmp, &CACHE(key)->enc_ctx);
        krb5int_c_iov_put_block(data, num_data, tmp2, BLOCK_SIZE, &output_pos);
    } else if (nblocks > 1) {
        unsigned char blockN2[BLOCK_SIZE];   /* second last */
        unsigned char blockN1[BLOCK_SIZE];   /* last block */

        for (blockno = 0; blockno < nblocks - 2; blockno++) {
            unsigned char blockN[BLOCK_SIZE], *block;

            krb5int_c_iov_get_block_nocopy(blockN, BLOCK_SIZE,
                                           data, num_data, &input_pos, &block);
            xorblock(tmp, block);
            enc(block, tmp, &CACHE(key)->enc_ctx);
            krb5int_c_iov_put_block_nocopy(data, num_data, blockN, BLOCK_SIZE,
                                           &output_pos, block);

            /* Set up for next block.  */
            memcpy(tmp, block, BLOCK_SIZE);
        }

        /* Do final CTS step for last two blocks (the second of which
           may or may not be incomplete).  */

        /* First, get the last two blocks */
        memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
        krb5int_c_iov_get_block(blockN2, BLOCK_SIZE, data, num_data,
                                &input_pos);
        krb5int_c_iov_get_block(blockN1, BLOCK_SIZE, data, num_data,
                                &input_pos);

        /* Encrypt second last block */
        xorblock(tmp, blockN2);
        enc(tmp2, tmp, &CACHE(key)->enc_ctx);
        memcpy(blockN2, tmp2, BLOCK_SIZE); /* blockN2 now contains first block */
        memcpy(tmp, tmp2, BLOCK_SIZE);

        /* Encrypt last block */
        xorblock(tmp, blockN1);
        enc(tmp2, tmp, &CACHE(key)->enc_ctx);
        memcpy(blockN1, tmp2, BLOCK_SIZE);

        /* Put the last two blocks back into the iovec (reverse order) */
        krb5int_c_iov_put_block(data, num_data, blockN1, BLOCK_SIZE,
                                &output_pos);
        krb5int_c_iov_put_block(data, num_data, blockN2, BLOCK_SIZE,
                                &output_pos);

        if (ivec != NULL)
            memcpy(ivec->data, blockN1, BLOCK_SIZE);
    }

    return 0;
}
Ejemplo n.º 7
0
static krb5_error_code
krb5int_camellia_decrypt(krb5_key key, const krb5_data *ivec,
                         krb5_crypto_iov *data, size_t num_data)
{
    unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
    int nblocks = 0, blockno;
    unsigned int i;
    size_t input_length;
    struct iov_block_state input_pos, output_pos;

    if (key->cache == NULL) {
        key->cache = malloc(sizeof(struct camellia_key_info_cache));
        if (key->cache == NULL)
            return ENOMEM;
        CACHE(key)->enc_ctx.keybitlen = CACHE(key)->dec_ctx.keybitlen = 0;
    }
    if (CACHE(key)->dec_ctx.keybitlen == 0) {
        if (camellia_dec_key(key->keyblock.contents, key->keyblock.length,
                             &CACHE(key)->dec_ctx) != camellia_good)
            abort();
    }

    if (ivec != NULL)
        memcpy(tmp, ivec->data, BLOCK_SIZE);
    else
        memset(tmp, 0, BLOCK_SIZE);

    for (i = 0, input_length = 0; i < num_data; i++) {
        krb5_crypto_iov *iov = &data[i];

        if (ENCRYPT_IOV(iov))
            input_length += iov->data.length;
    }

    IOV_BLOCK_STATE_INIT(&input_pos);
    IOV_BLOCK_STATE_INIT(&output_pos);

    nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
    if (nblocks == 1) {
        krb5int_c_iov_get_block(tmp, BLOCK_SIZE, data, num_data, &input_pos);
        dec(tmp2, tmp, &CACHE(key)->dec_ctx);
        krb5int_c_iov_put_block(data, num_data, tmp2, BLOCK_SIZE, &output_pos);
    } else if (nblocks > 1) {
        unsigned char blockN2[BLOCK_SIZE];   /* second last */
        unsigned char blockN1[BLOCK_SIZE];   /* last block */

        for (blockno = 0; blockno < nblocks - 2; blockno++) {
            unsigned char blockN[BLOCK_SIZE], *block;

            krb5int_c_iov_get_block_nocopy(blockN, BLOCK_SIZE,
                                           data, num_data, &input_pos, &block);
            memcpy(tmp2, block, BLOCK_SIZE);
            dec(block, block, &CACHE(key)->dec_ctx);
            xorblock(block, tmp);
            memcpy(tmp, tmp2, BLOCK_SIZE);
            krb5int_c_iov_put_block_nocopy(data, num_data, blockN, BLOCK_SIZE,
                                           &output_pos, block);
        }

        /* Do last two blocks, the second of which (next-to-last block
           of plaintext) may be incomplete.  */

        /* First, get the last two encrypted blocks */
        memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
        krb5int_c_iov_get_block(blockN2, BLOCK_SIZE, data, num_data,
                                &input_pos);
        krb5int_c_iov_get_block(blockN1, BLOCK_SIZE, data, num_data,
                                &input_pos);

        if (ivec != NULL)
            memcpy(ivec->data, blockN2, BLOCK_SIZE);

        /* Decrypt second last block */
        dec(tmp2, blockN2, &CACHE(key)->dec_ctx);
        /* Set tmp2 to last (possibly partial) plaintext block, and
           save it.  */
        xorblock(tmp2, blockN1);
        memcpy(blockN2, tmp2, BLOCK_SIZE);

        /* Maybe keep the trailing part, and copy in the last
           ciphertext block.  */
        input_length %= BLOCK_SIZE;
        memcpy(tmp2, blockN1, input_length ? input_length : BLOCK_SIZE);
        dec(tmp3, tmp2, &CACHE(key)->dec_ctx);
        xorblock(tmp3, tmp);
        memcpy(blockN1, tmp3, BLOCK_SIZE);

        /* Put the last two blocks back into the iovec */
        krb5int_c_iov_put_block(data, num_data, blockN1, BLOCK_SIZE,
                                &output_pos);
        krb5int_c_iov_put_block(data, num_data, blockN2, BLOCK_SIZE,
                                &output_pos);
    }

    return 0;
}
Ejemplo n.º 8
0
void
krb5int_des3_cbc_encrypt_iov(krb5_crypto_iov *data,
			     unsigned long num_data,
			     const mit_des_key_schedule ks1,
			     const mit_des_key_schedule ks2,
			     const mit_des_key_schedule ks3,
			     mit_des_cblock ivec)
{
    unsigned DES_INT32 left, right;
    const unsigned DES_INT32 *kp1, *kp2, *kp3;
    const unsigned char *ip;
    unsigned char *op;
    struct iov_block_state input_pos, output_pos;
    unsigned char iblock[MIT_DES_BLOCK_LENGTH];
    unsigned char oblock[MIT_DES_BLOCK_LENGTH];

    IOV_BLOCK_STATE_INIT(&input_pos);
    IOV_BLOCK_STATE_INIT(&output_pos);

    /*
     * Get key pointer here.  This won't need to be reinitialized
     */
    kp1 = (const unsigned DES_INT32 *)ks1;
    kp2 = (const unsigned DES_INT32 *)ks2;
    kp3 = (const unsigned DES_INT32 *)ks3;

    /*
     * Initialize left and right with the contents of the initial
     * vector.
     */
    if (ivec != NULL)
	ip = ivec;
    else
	ip = mit_des_zeroblock;
    GET_HALF_BLOCK(left, ip);
    GET_HALF_BLOCK(right, ip);

    /*
     * Suitably initialized, now work the length down 8 bytes
     * at a time.
     */
    for (;;) {
	unsigned DES_INT32 temp;

	ip = iblock;
	op = oblock;

	if (!krb5int_c_iov_get_block(iblock, MIT_DES_BLOCK_LENGTH, data, num_data, &input_pos))
	    break;

	if (input_pos.iov_pos == num_data)
	    break;

	GET_HALF_BLOCK(temp, ip);
	left  ^= temp;
	GET_HALF_BLOCK(temp, ip);
	right ^= temp;

	/*
	 * Encrypt what we have
	 */
	DES_DO_ENCRYPT(left, right, kp1);
	DES_DO_DECRYPT(left, right, kp2);
	DES_DO_ENCRYPT(left, right, kp3);

	/*
	 * Copy the results out
	 */
	PUT_HALF_BLOCK(left, op);
	PUT_HALF_BLOCK(right, op);

	krb5int_c_iov_put_block(data, num_data, oblock, MIT_DES_BLOCK_LENGTH, &output_pos);
    }

    if (ivec != NULL)
	memcpy(ivec, oblock, MIT_DES_BLOCK_LENGTH);
}
Ejemplo n.º 9
0
void
krb5int_des3_cbc_decrypt_iov(krb5_crypto_iov *data,
			     unsigned long num_data,
			     const mit_des_key_schedule ks1,
			     const mit_des_key_schedule ks2,
			     const mit_des_key_schedule ks3,
			     mit_des_cblock ivec)
{
    unsigned DES_INT32 left, right;
    const unsigned DES_INT32 *kp1, *kp2, *kp3;
    const unsigned char *ip;
    unsigned DES_INT32 ocipherl, ocipherr;
    unsigned DES_INT32 cipherl, cipherr;
    unsigned char *op;
    struct iov_block_state input_pos, output_pos;
    unsigned char iblock[MIT_DES_BLOCK_LENGTH];
    unsigned char oblock[MIT_DES_BLOCK_LENGTH];

    IOV_BLOCK_STATE_INIT(&input_pos);
    IOV_BLOCK_STATE_INIT(&output_pos);

    /*
     * Get key pointer here.  This won't need to be reinitialized
     */
    kp1 = (const unsigned DES_INT32 *)ks1;
    kp2 = (const unsigned DES_INT32 *)ks2;
    kp3 = (const unsigned DES_INT32 *)ks3;

    /*
     * Decrypting is harder than encrypting because of
     * the necessity of remembering a lot more things.
     * Should think about this a little more...
     */

    if (num_data == 0)
	return;

    /*
     * Prime the old cipher with ivec.
     */
    if (ivec != NULL)
	ip = ivec;
    else
	ip = mit_des_zeroblock;
    GET_HALF_BLOCK(ocipherl, ip);
    GET_HALF_BLOCK(ocipherr, ip);

    /*
     * Now do this in earnest until we run out of length.
     */
    for (;;) {
	/*
	 * Read a block from the input into left and
	 * right.  Save this cipher block for later.
	 */

	if (!krb5int_c_iov_get_block(iblock, MIT_DES_BLOCK_LENGTH, data, num_data, &input_pos))
	    break;

	if (input_pos.iov_pos == num_data)
	    break;

	ip = iblock;
	op = oblock;

	GET_HALF_BLOCK(left, ip);
	GET_HALF_BLOCK(right, ip);
	cipherl = left;
	cipherr = right;

	/*
	 * Decrypt this.
	 */
	DES_DO_DECRYPT(left, right, kp3);
	DES_DO_ENCRYPT(left, right, kp2);
	DES_DO_DECRYPT(left, right, kp1);

	/*
	 * Xor with the old cipher to get plain
	 * text.  Output 8 or less bytes of this.
	 */
	left ^= ocipherl;
	right ^= ocipherr;

	PUT_HALF_BLOCK(left, op);
	PUT_HALF_BLOCK(right, op);

	/*
	 * Save current cipher block here
	 */
	ocipherl = cipherl;
	ocipherr = cipherr;

	krb5int_c_iov_put_block(data, num_data, oblock, MIT_DES_BLOCK_LENGTH, &output_pos);
    }

    if (ivec != NULL) {
	op = ivec;
	PUT_HALF_BLOCK(ocipherl,op);
	PUT_HALF_BLOCK(ocipherr, op);
    }
}