This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / lib / crypto / dk / dk_encrypt.c
index eb9fe5fa37ebb1b82120512693f29afae7a3264b..9de05fc02bbcd1ea567a18834cab62610f5d16e0 100644 (file)
@@ -108,7 +108,7 @@ krb5_dk_encrypt(enc, hash, key, usage, ivec, input, output)
     d1.data[2] = (usage>>8)&0xff;
     d1.data[3] = usage&0xff;
 
-    d1.data[4] = 0xAA;
+    d1.data[4] = (char) 0xAA;
 
     if ((ret = krb5_derive_key(enc, key, &ke, &d1)))
        goto cleanup;
@@ -177,6 +177,178 @@ cleanup:
     return(ret);
 }
 
+/* Not necessarily "AES", per se, but "a CBC+CTS mode block cipher
+   with a 96-bit truncated HMAC".  */
+void
+krb5int_aes_encrypt_length(enc, hash, inputlen, length)
+     const struct krb5_enc_provider *enc;
+     const struct krb5_hash_provider *hash;
+     size_t inputlen;
+     size_t *length;
+{
+    size_t blocksize, hashsize;
+
+    (*(enc->block_size))(&blocksize);
+    hashsize = 96 / 8;
+
+    /* No roundup, since CTS requires no padding once we've hit the
+       block size.  */
+    *length = blocksize+inputlen + hashsize;
+}
+
+static krb5_error_code
+trunc_hmac (const struct krb5_hash_provider *hash,
+           const krb5_keyblock *ki, int num,
+           const krb5_data *input, const krb5_data *output)
+{
+    size_t hashsize;
+    krb5_data tmp;
+    krb5_error_code ret;
+
+    (hash->hash_size)(&hashsize);
+    if (hashsize < output->length)
+       return KRB5_CRYPTO_INTERNAL;
+    tmp.length = hashsize;
+    tmp.data = malloc(hashsize);
+    if (tmp.data == NULL)
+       return errno;
+    ret = krb5_hmac(hash, ki, num, input, &tmp);
+    if (ret == 0)
+       memcpy(output->data, tmp.data, output->length);
+    memset(tmp.data, 0, hashsize);
+    free(tmp.data);
+    return ret;
+}
+
+krb5_error_code
+krb5int_aes_dk_encrypt(enc, hash, key, usage, ivec, input, output)
+     const struct krb5_enc_provider *enc;
+     const struct krb5_hash_provider *hash;
+     const krb5_keyblock *key;
+     krb5_keyusage usage;
+     const krb5_data *ivec;
+     const krb5_data *input;
+     krb5_data *output;
+{
+    size_t blocksize, keybytes, keylength, plainlen, enclen;
+    krb5_error_code ret;
+    unsigned char constantdata[K5CLENGTH];
+    krb5_data d1, d2;
+    unsigned char *plaintext, *kedata, *kidata, *cn;
+    krb5_keyblock ke, ki;
+
+    /* allocate and set up plaintext and to-be-derived keys */
+
+    (*(enc->block_size))(&blocksize);
+    (*(enc->keysize))(&keybytes, &keylength);
+    plainlen = blocksize+input->length;
+
+    krb5int_aes_encrypt_length(enc, hash, input->length, &enclen);
+
+    /* key->length, ivec will be tested in enc->encrypt */
+
+    if (output->length < enclen)
+       return(KRB5_BAD_MSIZE);
+
+    if ((kedata = (unsigned char *) malloc(keylength)) == NULL)
+       return(ENOMEM);
+    if ((kidata = (unsigned char *) malloc(keylength)) == NULL) {
+       free(kedata);
+       return(ENOMEM);
+    }
+    if ((plaintext = (unsigned char *) malloc(plainlen)) == NULL) {
+       free(kidata);
+       free(kedata);
+       return(ENOMEM);
+    }
+
+    ke.contents = kedata;
+    ke.length = keylength;
+    ki.contents = kidata;
+    ki.length = keylength;
+
+    /* derive the keys */
+
+    d1.data = constantdata;
+    d1.length = K5CLENGTH;
+
+    d1.data[0] = (usage>>24)&0xff;
+    d1.data[1] = (usage>>16)&0xff;
+    d1.data[2] = (usage>>8)&0xff;
+    d1.data[3] = usage&0xff;
+
+    d1.data[4] = (char) 0xAA;
+
+    if ((ret = krb5_derive_key(enc, key, &ke, &d1)))
+       goto cleanup;
+
+    d1.data[4] = 0x55;
+
+    if ((ret = krb5_derive_key(enc, key, &ki, &d1)))
+       goto cleanup;
+
+    /* put together the plaintext */
+
+    d1.length = blocksize;
+    d1.data = plaintext;
+
+    if ((ret = krb5_c_random_make_octets(/* XXX */ 0, &d1)))
+       goto cleanup;
+
+    memcpy(plaintext+blocksize, input->data, input->length);
+
+    /* Ciphertext stealing; there should be no more.  */
+    if (plainlen != blocksize + input->length)
+       abort();
+
+    /* encrypt the plaintext */
+
+    d1.length = plainlen;
+    d1.data = plaintext;
+
+    d2.length = plainlen;
+    d2.data = output->data;
+
+    if ((ret = ((*(enc->encrypt))(&ke, ivec, &d1, &d2))))
+       goto cleanup;
+
+    if (ivec != NULL && ivec->length == blocksize)
+       cn = d2.data + d2.length - blocksize;
+    else
+       cn = NULL;
+
+    /* hash the plaintext */
+
+    d2.length = enclen - plainlen;
+    d2.data = output->data+plainlen;
+    if (d2.length != 96 / 8)
+       abort();
+
+    if ((ret = trunc_hmac(hash, &ki, 1, &d1, &d2))) {
+       memset(d2.data, 0, d2.length);
+       goto cleanup;
+    }
+
+    output->length = enclen;
+
+    /* update ivec */
+    if (cn != NULL)
+       memcpy(ivec->data, cn, blocksize);
+
+    /* ret is set correctly by the prior call */
+
+cleanup:
+    memset(kedata, 0, keylength);
+    memset(kidata, 0, keylength);
+    memset(plaintext, 0, plainlen);
+
+    free(plaintext);
+    free(kidata);
+    free(kedata);
+
+    return(ret);
+}
+
 #ifdef ATHENA_DES3_KLUDGE
 void
 krb5_marc_dk_encrypt_length(enc, hash, inputlen, length)