+
+static int
+spkm3_checksummer(struct scatterlist *sg, void *data)
+{
+ struct hash_desc *desc = data;
+
+ return crypto_hash_update(desc, sg, sg->length);
+}
+
+/* checksum the plaintext data and hdrlen bytes of the token header */
+s32
+make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
+ unsigned int hdrlen, struct xdr_buf *body,
+ unsigned int body_offset, struct xdr_netobj *cksum)
+{
+ char *cksumname;
+ struct hash_desc desc; /* XXX add to ctx? */
+ struct scatterlist sg[1];
+ int err;
+
+ switch (cksumtype) {
+ case CKSUMTYPE_HMAC_MD5:
+ cksumname = "md5";
+ break;
+ default:
+ dprintk("RPC: spkm3_make_checksum:"
+ " unsupported checksum %d", cksumtype);
+ return GSS_S_FAILURE;
+ }
+
+ if (key->data == NULL || key->len <= 0) return GSS_S_FAILURE;
+
+ desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc.tfm))
+ return GSS_S_FAILURE;
+ cksum->len = crypto_hash_digestsize(desc.tfm);
+ desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ err = crypto_hash_setkey(desc.tfm, key->data, key->len);
+ if (err)
+ goto out;
+
+ sg_set_buf(sg, header, hdrlen);
+ crypto_hash_update(&desc, sg, 1);
+
+ xdr_process_buf(body, body_offset, body->len - body_offset,
+ spkm3_checksummer, &desc);
+ crypto_hash_final(&desc, cksum->data);
+
+out:
+ crypto_free_hash(desc.tfm);
+
+ return err ? GSS_S_FAILURE : 0;
+}
+
+EXPORT_SYMBOL(make_spkm3_checksum);