chaps: Cancel operations on error returns

The PKCS#11 spec indicates that if errors are returned from
crypto operation functions, the in-progress operation should
be terminated.  For example, in section 11.8 (p.140), the
description of C_Encrypt has:
  "A call to C_Encrypt always terminates the active encryption
   operation unless it returns CKR_BUFFER_TOO_SMALL or is a
   successful call (i.e., one which returns CKR_OK) to determine
   the length of the buffer needed to hold the ciphertext."

Some errors from these functions (typically CKR_ARGUMENTS_BAD)
are detected on the client side; to ensure that the chapsd state
is correctly updated to cancel the operation, we add a collection
of <Operation>Cancel methods to the D-Bus interface.

There are also some errors detected at the daemon side (in
particular, the interspersal of one-shot and incremental
operations); these can just trigger a local call to the appropriate
<Operation>Cancel function.

BUG=None
TEST=Chaps unit tests (with ASAN) plus PKCS11 tests

Change-Id: I8108907bfad9b5ba4b81ba25cf253b4e03ca9b9f
Reviewed-on: https://chromium-review.googlesource.com/221938
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
Commit-Queue: David Drysdale <drysdale@google.com>
Tested-by: David Drysdale <drysdale@google.com>
diff --git a/chaps/chaps.cc b/chaps/chaps.cc
index 60abdf9..bd9d7df 100644
--- a/chaps/chaps.cc
+++ b/chaps/chaps.cc
@@ -722,8 +722,10 @@
                 CK_BYTE_PTR pEncryptedData,
                 CK_ULONG_PTR pulEncryptedDataLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  if ((!pData && ulDataLen > 0) || !pulEncryptedDataLen)
+  if ((!pData && ulDataLen > 0) || !pulEncryptedDataLen) {
+    g_proxy->EncryptCancel(*g_user_isolate, hSession);
     LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length =
@@ -752,7 +754,10 @@
                       CK_BYTE_PTR pEncryptedPart,
                       CK_ULONG_PTR pulEncryptedPartLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pPart || !pulEncryptedPartLen, CKR_ARGUMENTS_BAD);
+  if (!pPart || !pulEncryptedPartLen) {
+    g_proxy->EncryptCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length =
@@ -779,7 +784,10 @@
                      CK_BYTE_PTR pLastEncryptedPart,
                      CK_ULONG_PTR pulLastEncryptedPartLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pulLastEncryptedPartLen, CKR_ARGUMENTS_BAD);
+  if (!pulLastEncryptedPartLen) {
+    g_proxy->EncryptCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length =
@@ -825,8 +833,10 @@
                 CK_BYTE_PTR pData,
                 CK_ULONG_PTR pulDataLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  if ((!pEncryptedData && ulEncryptedDataLen > 0) || !pulDataLen)
+  if ((!pEncryptedData && ulEncryptedDataLen > 0) || !pulDataLen) {
+    g_proxy->DecryptCancel(*g_user_isolate, hSession);
     LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length = pData ? static_cast<uint64_t>(*pulDataLen) : 0;
@@ -855,7 +865,10 @@
                       CK_BYTE_PTR pPart,
                       CK_ULONG_PTR pulPartLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pEncryptedPart || !pulPartLen, CKR_ARGUMENTS_BAD);
+  if (!pEncryptedPart || !pulPartLen) {
+    g_proxy->DecryptCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length = pPart ? static_cast<uint64_t>(*pulPartLen) : 0;
@@ -882,7 +895,10 @@
                      CK_BYTE_PTR pLastPart,
                      CK_ULONG_PTR pulLastPartLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pulLastPartLen, CKR_ARGUMENTS_BAD);
+  if (!pulLastPartLen) {
+    g_proxy->DecryptCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length =
@@ -926,8 +942,10 @@
                CK_BYTE_PTR pDigest,
                CK_ULONG_PTR pulDigestLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  if ((!pData && ulDataLen > 0) || !pulDigestLen)
+  if ((!pData && ulDataLen > 0) || !pulDigestLen) {
+    g_proxy->DigestCancel(*g_user_isolate, hSession);
     LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length = pDigest ? static_cast<uint64_t>(*pulDigestLen) : 0;
@@ -953,7 +971,10 @@
                      CK_BYTE_PTR pPart,
                      CK_ULONG ulPartLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pPart, CKR_ARGUMENTS_BAD);
+  if (!pPart) {
+    g_proxy->DigestCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   CK_RV result = g_proxy->DigestUpdate(
       *g_user_isolate,
       hSession,
@@ -977,7 +998,10 @@
                     CK_BYTE_PTR pDigest,
                     CK_ULONG_PTR pulDigestLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pulDigestLen, CKR_ARGUMENTS_BAD);
+  if (!pulDigestLen) {
+    g_proxy->DigestCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length = pDigest ? static_cast<uint64_t>(*pulDigestLen) : 0;
@@ -1022,8 +1046,10 @@
              CK_BYTE_PTR pSignature,
              CK_ULONG_PTR pulSignatureLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  if ((!pData && ulDataLen > 0) || !pulSignatureLen)
+  if ((!pData && ulDataLen > 0) || !pulSignatureLen) {
+    g_proxy->SignCancel(*g_user_isolate, hSession);
     LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length =
@@ -1050,7 +1076,10 @@
                    CK_BYTE_PTR pPart,
                    CK_ULONG ulPartLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pPart, CKR_ARGUMENTS_BAD);
+  if (!pPart) {
+    g_proxy->SignCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   CK_RV result = g_proxy->SignUpdate(*g_user_isolate,
                                      hSession,
                                      chaps::ConvertByteBufferToVector(pPart,
@@ -1065,7 +1094,10 @@
                   CK_BYTE_PTR pSignature,
                   CK_ULONG_PTR pulSignatureLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pulSignatureLen, CKR_ARGUMENTS_BAD);
+  if (!pulSignatureLen) {
+    g_proxy->SignCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   vector<uint8_t> data_out;
   uint64_t data_out_length;
   uint64_t max_out_length =
@@ -1160,8 +1192,10 @@
                CK_BYTE_PTR pSignature,
                CK_ULONG ulSignatureLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  if (!pSignature || (!pData && ulDataLen > 0))
+  if (!pSignature || (!pData && ulDataLen > 0)) {
+    g_proxy->VerifyCancel(*g_user_isolate, hSession);
     LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   CK_RV result = g_proxy->Verify(
       *g_user_isolate,
       hSession,
@@ -1177,7 +1211,10 @@
                      CK_BYTE_PTR pPart,
                      CK_ULONG ulPartLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pPart, CKR_ARGUMENTS_BAD);
+  if (!pPart) {
+    g_proxy->VerifyCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   CK_RV result = g_proxy->VerifyUpdate(
       *g_user_isolate,
       hSession,
@@ -1192,7 +1229,10 @@
                     CK_BYTE_PTR pSignature,
                     CK_ULONG ulSignatureLen) {
   LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
-  LOG_CK_RV_AND_RETURN_IF(!pSignature, CKR_ARGUMENTS_BAD);
+  if (!pSignature) {
+    g_proxy->VerifyCancel(*g_user_isolate, hSession);
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  }
   CK_RV result = g_proxy->VerifyFinal(
       *g_user_isolate,
       hSession,
diff --git a/chaps/chaps_adaptor.cc b/chaps/chaps_adaptor.cc
index 7f3eb2b..fbb87dc 100644
--- a/chaps/chaps_adaptor.cc
+++ b/chaps/chaps_adaptor.cc
@@ -1093,6 +1093,22 @@
                actual_out_length, data_out, result);
 }
 
+void ChapsAdaptor::EncryptCancel(const vector<uint8_t>& isolate_credential,
+                                 const uint64_t& session_id) {
+  AutoLock lock(*lock_);
+  VLOG(1) << "CALL: " << __func__;
+  SecureBlob isolate_credential_blob(&isolate_credential.front(),
+                                     isolate_credential.size());
+  ClearVector(const_cast<vector<uint8_t>*>(&isolate_credential));
+  service_->EncryptCancel(isolate_credential_blob, session_id);
+}
+
+void ChapsAdaptor::EncryptCancel(const vector<uint8_t>& isolate_credential,
+                                 const uint64_t& session_id,
+                                 ::DBus::Error& /*error*/) {
+  EncryptCancel(isolate_credential, session_id);
+}
+
 uint32_t ChapsAdaptor::DecryptInit(
     const vector<uint8_t>& isolate_credential,
     const uint64_t& session_id,
@@ -1232,6 +1248,22 @@
                actual_out_length, data_out, result);
 }
 
+void ChapsAdaptor::DecryptCancel(const vector<uint8_t>& isolate_credential,
+                                 const uint64_t& session_id) {
+  AutoLock lock(*lock_);
+  VLOG(1) << "CALL: " << __func__;
+  SecureBlob isolate_credential_blob(&isolate_credential.front(),
+                                     isolate_credential.size());
+  ClearVector(const_cast<vector<uint8_t>*>(&isolate_credential));
+  service_->DecryptCancel(isolate_credential_blob, session_id);
+}
+
+void ChapsAdaptor::DecryptCancel(const vector<uint8_t>& isolate_credential,
+                                 const uint64_t& session_id,
+                                 ::DBus::Error& /*error*/) {
+  DecryptCancel(isolate_credential, session_id);
+}
+
 uint32_t ChapsAdaptor::DigestInit(
     const vector<uint8_t>& isolate_credential,
     const uint64_t& session_id,
@@ -1375,6 +1407,22 @@
               digest, result);
 }
 
+void ChapsAdaptor::DigestCancel(const vector<uint8_t>& isolate_credential,
+                                const uint64_t& session_id) {
+  AutoLock lock(*lock_);
+  VLOG(1) << "CALL: " << __func__;
+  SecureBlob isolate_credential_blob(&isolate_credential.front(),
+                                     isolate_credential.size());
+  ClearVector(const_cast<vector<uint8_t>*>(&isolate_credential));
+  service_->DigestCancel(isolate_credential_blob, session_id);
+}
+
+void ChapsAdaptor::DigestCancel(const vector<uint8_t>& isolate_credential,
+                                const uint64_t& session_id,
+                                ::DBus::Error& /*error*/) {
+  DigestCancel(isolate_credential, session_id);
+}
+
 uint32_t ChapsAdaptor::SignInit(const vector<uint8_t>& isolate_credential,
                                 const uint64_t& session_id,
                                 const uint64_t& mechanism_type,
@@ -1497,6 +1545,22 @@
             signature, result);
 }
 
+void ChapsAdaptor::SignCancel(const vector<uint8_t>& isolate_credential,
+                              const uint64_t& session_id) {
+  AutoLock lock(*lock_);
+  VLOG(1) << "CALL: " << __func__;
+  SecureBlob isolate_credential_blob(&isolate_credential.front(),
+                                     isolate_credential.size());
+  ClearVector(const_cast<vector<uint8_t>*>(&isolate_credential));
+  service_->SignCancel(isolate_credential_blob, session_id);
+}
+
+void ChapsAdaptor::SignCancel(const vector<uint8_t>& isolate_credential,
+                              const uint64_t& session_id,
+                              ::DBus::Error& /*error*/) {
+  SignCancel(isolate_credential, session_id);
+}
+
 uint32_t ChapsAdaptor::SignRecoverInit(
       const vector<uint8_t>& isolate_credential,
       const uint64_t& session_id,
@@ -1665,6 +1729,23 @@
   return VerifyFinal(isolate_credential, session_id, signature);
 }
 
+void ChapsAdaptor::VerifyCancel(const vector<uint8_t>& isolate_credential,
+                                const uint64_t& session_id) {
+  AutoLock lock(*lock_);
+  VLOG(1) << "CALL: " << __func__;
+  SecureBlob isolate_credential_blob(&isolate_credential.front(),
+                                     isolate_credential.size());
+  ClearVector(const_cast<vector<uint8_t>*>(&isolate_credential));
+  service_->VerifyCancel(isolate_credential_blob, session_id);
+}
+
+void ChapsAdaptor::VerifyCancel(const vector<uint8_t>& isolate_credential,
+                                const uint64_t& session_id,
+                                ::DBus::Error& /*error*/) {
+  VerifyCancel(isolate_credential, session_id);
+}
+
+
 uint32_t ChapsAdaptor::VerifyRecoverInit(
       const vector<uint8_t>& isolate_credential,
       const uint64_t& session_id,
diff --git a/chaps/chaps_adaptor.h b/chaps/chaps_adaptor.h
index 74b1cb6..2d95b59 100644
--- a/chaps/chaps_adaptor.h
+++ b/chaps/chaps_adaptor.h
@@ -257,6 +257,9 @@
                             std::vector<uint8_t>& data_out,  // NOLINT - refs
                             uint32_t& result,  // NOLINT - refs
                             ::DBus::Error& error);  // NOLINT - refs
+  virtual void EncryptCancel(const std::vector<uint8_t>& isolate_credential,
+                             const uint64_t& session_id,
+                             ::DBus::Error& error);  // NOLINT - refs
   virtual uint32_t DecryptInit(const std::vector<uint8_t>& isolate_credential,
                                const uint64_t& session_id,
                                const uint64_t& mechanism_type,
@@ -286,6 +289,9 @@
                             std::vector<uint8_t>& data_out,  // NOLINT - refs
                             uint32_t& result,  // NOLINT - refs
                             ::DBus::Error& error);  // NOLINT - refs
+  virtual void DecryptCancel(const std::vector<uint8_t>& isolate_credential,
+                             const uint64_t& session_id,
+                             ::DBus::Error& error);  // NOLINT - refs
   virtual uint32_t DigestInit(const std::vector<uint8_t>& isolate_credential,
                               const uint64_t& session_id,
                               const uint64_t& mechanism_type,
@@ -314,6 +320,9 @@
                            std::vector<uint8_t>& digest,  // NOLINT - refs
                            uint32_t& result,  // NOLINT - refs
                            ::DBus::Error& error);  // NOLINT - refs
+  virtual void DigestCancel(const std::vector<uint8_t>& isolate_credential,
+                            const uint64_t& session_id,
+                            ::DBus::Error& error);  // NOLINT - refs
   virtual uint32_t SignInit(const std::vector<uint8_t>& isolate_credential,
                             const uint64_t& session_id,
                             const uint64_t& mechanism_type,
@@ -339,6 +348,9 @@
                          std::vector<uint8_t>& signature,  // NOLINT - refs
                          uint32_t& result,  // NOLINT - refs
                          ::DBus::Error& error);  // NOLINT - refs
+  virtual void SignCancel(const std::vector<uint8_t>& isolate_credential,
+                          const uint64_t& session_id,
+                          ::DBus::Error& error);  // NOLINT - refs
   virtual uint32_t SignRecoverInit(
       const std::vector<uint8_t>& isolate_credential,
       const uint64_t& session_id,
@@ -373,6 +385,9 @@
                                const uint64_t& session_id,
                                const std::vector<uint8_t>& signature,
                                ::DBus::Error& error);  // NOLINT - refs
+  virtual void VerifyCancel(const std::vector<uint8_t>& isolate_credential,
+                            const uint64_t& session_id,
+                            ::DBus::Error& error);  // NOLINT - refs
   virtual uint32_t VerifyRecoverInit(
       const std::vector<uint8_t>& isolate_credential,
       const uint64_t& session_id,
@@ -670,6 +685,8 @@
                             uint64_t& actual_out_length,  // NOLINT - refs
                             std::vector<uint8_t>& data_out,  // NOLINT - refs
                             uint32_t& result);  // NOLINT - refs
+  virtual void EncryptCancel(const std::vector<uint8_t>& isolate_credential,
+                             const uint64_t& session_id);
   virtual uint32_t DecryptInit(const std::vector<uint8_t>& isolate_credential,
                                const uint64_t& session_id,
                                const uint64_t& mechanism_type,
@@ -695,6 +712,8 @@
                             uint64_t& actual_out_length,  // NOLINT - refs
                             std::vector<uint8_t>& data_out,  // NOLINT - refs
                             uint32_t& result);  // NOLINT - refs
+  virtual void DecryptCancel(const std::vector<uint8_t>& isolate_credential,
+                             const uint64_t& session_id);
   virtual uint32_t DigestInit(const std::vector<uint8_t>& isolate_credential,
                               const uint64_t& session_id,
                               const uint64_t& mechanism_type,
@@ -718,6 +737,8 @@
                            uint64_t& actual_out_length,  // NOLINT - refs
                            std::vector<uint8_t>& digest,  // NOLINT - refs
                            uint32_t& result);  // NOLINT - refs
+  virtual void DigestCancel(const std::vector<uint8_t>& isolate_credential,
+                            const uint64_t& session_id);
   virtual uint32_t SignInit(const std::vector<uint8_t>& isolate_credential,
                             const uint64_t& session_id,
                             const uint64_t& mechanism_type,
@@ -739,6 +760,8 @@
                          uint64_t& actual_out_length,  // NOLINT - refs
                          std::vector<uint8_t>& signature,  // NOLINT - refs
                          uint32_t& result);  // NOLINT - refs
+  virtual void SignCancel(const std::vector<uint8_t>& isolate_credential,
+                          const uint64_t& session_id);
   virtual uint32_t SignRecoverInit(
       const std::vector<uint8_t>& isolate_credential,
       const uint64_t& session_id,
@@ -767,6 +790,8 @@
   virtual uint32_t VerifyFinal(const std::vector<uint8_t>& isolate_credential,
                                const uint64_t& session_id,
                                const std::vector<uint8_t>& signature);
+  virtual void VerifyCancel(const std::vector<uint8_t>& isolate_credential,
+                            const uint64_t& session_id);
   virtual uint32_t VerifyRecoverInit(
       const std::vector<uint8_t>& isolate_credential,
       const uint64_t& session_id,
diff --git a/chaps/chaps_interface.h b/chaps/chaps_interface.h
index 8a8c527..4b8af8c 100644
--- a/chaps/chaps_interface.h
+++ b/chaps/chaps_interface.h
@@ -208,6 +208,10 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out) = 0;
+  // PKCS #11 v2.20 section 11.8 page 140,142: any errors terminate the active
+  // encryption operation.
+  virtual void EncryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id) = 0;
   // PKCS #11 v2.20 section 11.9 page 144.
   virtual uint32_t DecryptInit(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
@@ -234,6 +238,10 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out) = 0;
+  // PKCS #11 v2.20 section 11.9 page 145,146: any errors terminate the active
+  // decryption operation.
+  virtual void DecryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id) = 0;
   // PKCS #11 v2.20 section 11.10 page 148.
   virtual uint32_t DigestInit(
       const chromeos::SecureBlob& isolate_credential,
@@ -261,6 +269,10 @@
                                uint64_t max_out_length,
                                uint64_t* actual_out_length,
                                std::vector<uint8_t>* digest) = 0;
+  // PKCS #11 v2.20 section 11.10 page 149,151: any errors terminate the active
+  // digest operation.
+  virtual void DigestCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id) = 0;
   // PKCS #11 v2.20 section 11.11 page 152.
   virtual uint32_t SignInit(const chromeos::SecureBlob& isolate_credential,
                             uint64_t session_id,
@@ -284,6 +296,10 @@
                              uint64_t max_out_length,
                              uint64_t* actual_out_length,
                              std::vector<uint8_t>* signature) = 0;
+  // PKCS #11 v2.20 section 11.11 page 153,154: any errors terminate the active
+  // signing operation.
+  virtual void SignCancel(const chromeos::SecureBlob& isolate_credential,
+                          uint64_t session_id) = 0;
   // PKCS #11 v2.20 section 11.11 page 155.
   virtual uint32_t SignRecoverInit(
      const chromeos::SecureBlob& isolate_credential,
@@ -317,6 +333,10 @@
   virtual uint32_t VerifyFinal(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
                                const std::vector<uint8_t>& signature) = 0;
+  // PKCS #11 v2.20 section 11.12 page 159: any errors terminate the active
+  // verification operation.
+  virtual void VerifyCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id) = 0;
   // PKCS #11 v2.20 section 11.12 page 161.
   virtual uint32_t VerifyRecoverInit(
       const chromeos::SecureBlob& isolate_credential,
diff --git a/chaps/chaps_interface.xml b/chaps/chaps_interface.xml
index 80fc2e6..5436a62 100644
--- a/chaps/chaps_interface.xml
+++ b/chaps/chaps_interface.xml
@@ -440,6 +440,12 @@
       </arg>
     </method>
 
+    <!-- PKCS #11 v2.20 section 11.8 page 140,142 (implicit). -->
+    <method name="EncryptCancel">
+      <arg type="ay" name="isolate_credential" direction="in"/>
+      <arg type="t" name="session_id" direction="in"/>
+    </method>
+
     <!-- PKCS #11 v2.20 section 11.9 page 144. -->
     <method name="DecryptInit">
       <arg type="ay" name="isolate_credential" direction="in"/>
@@ -490,6 +496,12 @@
       </arg>
     </method>
 
+    <!-- PKCS #11 v2.20 section 11.9 page 145,146 (implicit). -->
+    <method name="DecryptCancel">
+      <arg type="ay" name="isolate_credential" direction="in"/>
+      <arg type="t" name="session_id" direction="in"/>
+    </method>
+
     <!-- PKCS #11 v2.20 section 11.10 page 148. -->
     <method name="DigestInit">
       <arg type="ay" name="isolate_credential" direction="in"/>
@@ -546,6 +558,12 @@
       </arg>
     </method>
 
+    <!-- PKCS #11 v2.20 section 11.10 page 149,151 (implicit). -->
+    <method name="DigestCancel">
+      <arg type="ay" name="isolate_credential" direction="in"/>
+      <arg type="t" name="session_id" direction="in"/>
+    </method>
+
     <!-- PKCS #11 v2.20 section 11.11 page 152. -->
     <method name="SignInit">
       <arg type="ay" name="isolate_credential" direction="in"/>
@@ -593,6 +611,12 @@
       </arg>
     </method>
 
+    <!-- PKCS #11 v2.20 section 11.11 page 153,154 (implicit). -->
+    <method name="SignCancel">
+      <arg type="ay" name="isolate_credential" direction="in"/>
+      <arg type="t" name="session_id" direction="in"/>
+    </method>
+
     <!-- PKCS #11 v2.20 section 11.11 page 155. -->
     <method name="SignRecoverInit">
       <arg type="ay" name="isolate_credential" direction="in"/>
@@ -661,6 +685,12 @@
       </arg>
     </method>
 
+    <!-- PKCS #11 v2.20 section 11.12 page 159. -->
+    <method name="VerifyCancel">
+      <arg type="ay" name="isolate_credential" direction="in"/>
+      <arg type="t" name="session_id" direction="in"/>
+    </method>
+
     <!-- PKCS #11 v2.20 section 11.12 page 161. -->
     <method name="VerifyRecoverInit">
       <arg type="ay" name="isolate_credential" direction="in"/>
diff --git a/chaps/chaps_proxy.cc b/chaps/chaps_proxy.cc
index f950234..09d4769 100644
--- a/chaps/chaps_proxy.cc
+++ b/chaps/chaps_proxy.cc
@@ -788,6 +788,18 @@
   return result;
 }
 
+void ChapsProxyImpl::EncryptCancel(const SecureBlob& isolate_credential,
+                                   uint64_t session_id) {
+  AutoLock lock(lock_);
+  if (!proxy_.get())
+    return;
+  try {
+    proxy_->EncryptCancel(isolate_credential, session_id);
+  } catch (DBus::Error err) {
+    LOG(ERROR) << "DBus::Error - " << err.what();
+  }
+}
+
 uint32_t ChapsProxyImpl::DecryptInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -883,6 +895,18 @@
   return result;
 }
 
+void ChapsProxyImpl::DecryptCancel(const SecureBlob& isolate_credential,
+                                   uint64_t session_id) {
+  AutoLock lock(lock_);
+  if (!proxy_.get())
+    return;
+  try {
+    proxy_->DecryptCancel(isolate_credential, session_id);
+  } catch (DBus::Error err) {
+    LOG(ERROR) << "DBus::Error - " << err.what();
+  }
+}
+
 uint32_t ChapsProxyImpl::DigestInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -979,6 +1003,18 @@
   return result;
 }
 
+void ChapsProxyImpl::DigestCancel(const SecureBlob& isolate_credential,
+                                  uint64_t session_id) {
+  AutoLock lock(lock_);
+  if (!proxy_.get())
+    return;
+  try {
+    proxy_->DigestCancel(isolate_credential, session_id);
+  } catch (DBus::Error err) {
+    LOG(ERROR) << "DBus::Error - " << err.what();
+  }
+}
+
 uint32_t ChapsProxyImpl::SignInit(const SecureBlob& isolate_credential,
                                   uint64_t session_id,
                                   uint64_t mechanism_type,
@@ -1061,6 +1097,18 @@
   return result;
 }
 
+void ChapsProxyImpl::SignCancel(const SecureBlob& isolate_credential,
+                                uint64_t session_id) {
+  AutoLock lock(lock_);
+  if (!proxy_.get())
+    return;
+  try {
+    proxy_->SignCancel(isolate_credential, session_id);
+  } catch (DBus::Error err) {
+    LOG(ERROR) << "DBus::Error - " << err.what();
+  }
+}
+
 uint32_t ChapsProxyImpl::SignRecoverInit(
       const SecureBlob& isolate_credential,
       uint64_t session_id,
@@ -1174,6 +1222,18 @@
   return result;
 }
 
+void ChapsProxyImpl::VerifyCancel(const SecureBlob& isolate_credential,
+                                  uint64_t session_id) {
+  AutoLock lock(lock_);
+  if (!proxy_.get())
+    return;
+  try {
+    proxy_->VerifyCancel(isolate_credential, session_id);
+  } catch (DBus::Error err) {
+    LOG(ERROR) << "DBus::Error - " << err.what();
+  }
+}
+
 uint32_t ChapsProxyImpl::VerifyRecoverInit(
       const SecureBlob& isolate_credential,
       uint64_t session_id,
diff --git a/chaps/chaps_proxy.h b/chaps/chaps_proxy.h
index 7236b40..83226f3 100644
--- a/chaps/chaps_proxy.h
+++ b/chaps/chaps_proxy.h
@@ -197,6 +197,8 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out);
+  virtual void EncryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id);
   virtual uint32_t DecryptInit(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
                                uint64_t mechanism_type,
@@ -219,6 +221,8 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out);
+  virtual void DecryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id);
   virtual uint32_t DigestInit(const chromeos::SecureBlob& isolate_credential,
                               uint64_t session_id,
                               uint64_t mechanism_type,
@@ -240,6 +244,8 @@
                                uint64_t max_out_length,
                                uint64_t* actual_out_length,
                                std::vector<uint8_t>* digest);
+  virtual void DigestCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id);
   virtual uint32_t SignInit(const chromeos::SecureBlob& isolate_credential,
                             uint64_t session_id,
                             uint64_t mechanism_type,
@@ -259,6 +265,8 @@
                              uint64_t max_out_length,
                              uint64_t* actual_out_length,
                              std::vector<uint8_t>* signature);
+  virtual void SignCancel(const chromeos::SecureBlob& isolate_credential,
+                          uint64_t session_id);
   virtual uint32_t SignRecoverInit(
       const chromeos::SecureBlob& isolate_credential,
       uint64_t session_id,
@@ -286,6 +294,8 @@
   virtual uint32_t VerifyFinal(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
                                const std::vector<uint8_t>& signature);
+  virtual void VerifyCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id);
   virtual uint32_t VerifyRecoverInit(
       const chromeos::SecureBlob& isolate_credential,
       uint64_t session_id,
diff --git a/chaps/chaps_proxy_mock.h b/chaps/chaps_proxy_mock.h
index 342e4bb..7d5ad32 100644
--- a/chaps/chaps_proxy_mock.h
+++ b/chaps/chaps_proxy_mock.h
@@ -149,6 +149,8 @@
                                       uint64_t,
                                       uint64_t*,
                                       std::vector<uint8_t>*));
+  MOCK_METHOD2(EncryptCancel, void(const chromeos::SecureBlob&,
+                                   uint64_t));
   MOCK_METHOD5(DecryptInit, uint32_t(const chromeos::SecureBlob&,
                                      uint64_t,
                                      uint64_t,
@@ -171,6 +173,8 @@
                                       uint64_t,
                                       uint64_t*,
                                       std::vector<uint8_t>*));
+  MOCK_METHOD2(DecryptCancel, void(const chromeos::SecureBlob&,
+                                   uint64_t));
   MOCK_METHOD4(DigestInit, uint32_t(const chromeos::SecureBlob&,
                                     uint64_t,
                                     uint64_t,
@@ -192,6 +196,8 @@
                                      uint64_t,
                                      uint64_t*,
                                      std::vector<uint8_t>*));
+  MOCK_METHOD2(DigestCancel, void(const chromeos::SecureBlob&,
+                                  uint64_t));
   MOCK_METHOD5(SignInit, uint32_t(const chromeos::SecureBlob&,
                                   uint64_t,
                                   uint64_t,
@@ -211,6 +217,8 @@
                                    uint64_t,
                                    uint64_t*,
                                    std::vector<uint8_t>*));
+  MOCK_METHOD2(SignCancel, void(const chromeos::SecureBlob&,
+                                uint64_t));
   MOCK_METHOD5(SignRecoverInit, uint32_t(const chromeos::SecureBlob&,
                                          uint64_t,
                                          uint64_t,
@@ -237,6 +245,8 @@
   MOCK_METHOD3(VerifyFinal, uint32_t(const chromeos::SecureBlob&,
                                      uint64_t,
                                      const std::vector<uint8_t>&));
+  MOCK_METHOD2(VerifyCancel, void(const chromeos::SecureBlob&,
+                                  uint64_t));
   MOCK_METHOD5(VerifyRecoverInit, uint32_t(const chromeos::SecureBlob&,
                                            uint64_t,
                                            uint64_t,
diff --git a/chaps/chaps_service.cc b/chaps/chaps_service.cc
index 5ba8165..027832f 100644
--- a/chaps/chaps_service.cc
+++ b/chaps/chaps_service.cc
@@ -648,6 +648,17 @@
       PreservedByteVector(data_out));
 }
 
+void ChapsServiceImpl::EncryptCancel(const SecureBlob& isolate_credential,
+                                     uint64_t session_id) {
+  Session* session = NULL;
+  if (!slot_manager_->GetSession(isolate_credential,
+                                 session_id,
+                                 &session))
+    return;
+  CHECK(session);
+  session->OperationCancel(kEncrypt);
+}
+
 uint32_t ChapsServiceImpl::DecryptInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -732,6 +743,18 @@
       PreservedByteVector(data_out));
 }
 
+void ChapsServiceImpl::DecryptCancel(const SecureBlob& isolate_credential,
+                                     uint64_t session_id) {
+  Session* session = NULL;
+  if (!slot_manager_->GetSession(isolate_credential,
+                                 session_id,
+                                 &session))
+    return;
+  CHECK(session);
+  session->OperationCancel(kDecrypt);
+}
+
+
 uint32_t ChapsServiceImpl::DigestInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -811,6 +834,17 @@
       PreservedByteVector(digest));
 }
 
+void ChapsServiceImpl::DigestCancel(const SecureBlob& isolate_credential,
+                                    uint64_t session_id) {
+  Session* session = NULL;
+  if (!slot_manager_->GetSession(isolate_credential,
+                                 session_id,
+                                 &session))
+    return;
+  CHECK(session);
+  session->OperationCancel(kDigest);
+}
+
 uint32_t ChapsServiceImpl::SignInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -888,6 +922,17 @@
       PreservedByteVector(signature));
 }
 
+void ChapsServiceImpl::SignCancel(const SecureBlob& isolate_credential,
+                                  uint64_t session_id) {
+  Session* session = NULL;
+  if (!slot_manager_->GetSession(isolate_credential,
+                                 session_id,
+                                 &session))
+    return;
+  CHECK(session);
+  session->OperationCancel(kSign);
+}
+
 uint32_t ChapsServiceImpl::SignRecoverInit(
       const SecureBlob& isolate_credential,
       uint64_t session_id,
@@ -965,6 +1010,17 @@
   return session->VerifyFinal(ConvertByteVectorToString(signature));
 }
 
+void ChapsServiceImpl::VerifyCancel(const SecureBlob& isolate_credential,
+                                    uint64_t session_id) {
+  Session* session = NULL;
+  if (!slot_manager_->GetSession(isolate_credential,
+                                 session_id,
+                                 &session))
+    return;
+  CHECK(session);
+  session->OperationCancel(kVerify);
+}
+
 uint32_t ChapsServiceImpl::VerifyRecoverInit(
       const SecureBlob& isolate_credential,
       uint64_t session_id,
diff --git a/chaps/chaps_service.h b/chaps/chaps_service.h
index 8b1dcfe..46b462c 100644
--- a/chaps/chaps_service.h
+++ b/chaps/chaps_service.h
@@ -173,6 +173,8 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out);
+  virtual void EncryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id);
   virtual uint32_t DecryptInit(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
                                uint64_t mechanism_type,
@@ -195,6 +197,8 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out);
+  virtual void DecryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id);
   virtual uint32_t DigestInit(const chromeos::SecureBlob& isolate_credential,
                               uint64_t session_id,
                               uint64_t mechanism_type,
@@ -216,6 +220,8 @@
                                uint64_t max_out_length,
                                uint64_t* actual_out_length,
                                std::vector<uint8_t>* digest);
+  virtual void DigestCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id);
   virtual uint32_t SignInit(const chromeos::SecureBlob& isolate_credential,
                             uint64_t session_id,
                             uint64_t mechanism_type,
@@ -235,6 +241,8 @@
                              uint64_t max_out_length,
                              uint64_t* actual_out_length,
                              std::vector<uint8_t>* signature);
+  virtual void SignCancel(const chromeos::SecureBlob& isolate_credential,
+                          uint64_t session_id);
   virtual uint32_t SignRecoverInit(
       const chromeos::SecureBlob& isolate_credential,
       uint64_t session_id,
@@ -262,6 +270,8 @@
   virtual uint32_t VerifyFinal(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
                                const std::vector<uint8_t>& signature);
+  virtual void VerifyCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id);
   virtual uint32_t VerifyRecoverInit(
       const chromeos::SecureBlob& isolate_credential,
       uint64_t session_id,
diff --git a/chaps/chaps_service_redirect.cc b/chaps/chaps_service_redirect.cc
index e999df9..38e8e10 100644
--- a/chaps/chaps_service_redirect.cc
+++ b/chaps/chaps_service_redirect.cc
@@ -659,6 +659,12 @@
   return CKR_OK;
 }
 
+void ChapsServiceRedirect::EncryptCancel(
+      const SecureBlob& isolate_credential,
+      uint64_t session_id) {
+  LOG(ERROR) << "Unexpected cancel operation";
+}
+
 uint32_t ChapsServiceRedirect::DecryptInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -763,6 +769,12 @@
   return CKR_OK;
 }
 
+void ChapsServiceRedirect::DecryptCancel(
+      const SecureBlob& isolate_credential,
+      uint64_t session_id) {
+  LOG(ERROR) << "Unexpected cancel operation";
+}
+
 uint32_t ChapsServiceRedirect::DigestInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -854,6 +866,12 @@
   return CKR_OK;
 }
 
+void ChapsServiceRedirect::DigestCancel(
+      const SecureBlob& isolate_credential,
+      uint64_t session_id) {
+  LOG(ERROR) << "Unexpected cancel operation";
+}
+
 uint32_t ChapsServiceRedirect::SignInit(
     const SecureBlob& isolate_credential,
     uint64_t session_id,
@@ -936,6 +954,12 @@
   return CKR_OK;
 }
 
+void ChapsServiceRedirect::SignCancel(
+      const SecureBlob& isolate_credential,
+      uint64_t session_id) {
+  LOG(ERROR) << "Unexpected cancel operation";
+}
+
 uint32_t ChapsServiceRedirect::SignRecoverInit(
       const SecureBlob& isolate_credential,
       uint64_t session_id,
@@ -1046,6 +1070,12 @@
   return CKR_OK;
 }
 
+void ChapsServiceRedirect::VerifyCancel(
+      const SecureBlob& isolate_credential,
+      uint64_t session_id) {
+  LOG(ERROR) << "Unexpected cancel operation";
+}
+
 uint32_t ChapsServiceRedirect::VerifyRecoverInit(
       const SecureBlob& isolate_credential,
       uint64_t session_id,
diff --git a/chaps/chaps_service_redirect.h b/chaps/chaps_service_redirect.h
index bbf0ec6..daedd17 100644
--- a/chaps/chaps_service_redirect.h
+++ b/chaps/chaps_service_redirect.h
@@ -175,6 +175,8 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out);
+  virtual void EncryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id);
   virtual uint32_t DecryptInit(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
                                uint64_t mechanism_type,
@@ -197,6 +199,8 @@
                                 uint64_t max_out_length,
                                 uint64_t* actual_out_length,
                                 std::vector<uint8_t>* data_out);
+  virtual void DecryptCancel(const chromeos::SecureBlob& isolate_credential,
+                             uint64_t session_id);
   virtual uint32_t DigestInit(const chromeos::SecureBlob& isolate_credential,
                               uint64_t session_id,
                               uint64_t mechanism_type,
@@ -218,6 +222,8 @@
                                uint64_t max_out_length,
                                uint64_t* actual_out_length,
                                std::vector<uint8_t>* digest);
+  virtual void DigestCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id);
   virtual uint32_t SignInit(const chromeos::SecureBlob& isolate_credential,
                             uint64_t session_id,
                             uint64_t mechanism_type,
@@ -237,6 +243,8 @@
                              uint64_t max_out_length,
                              uint64_t* actual_out_length,
                              std::vector<uint8_t>* signature);
+  virtual void SignCancel(const chromeos::SecureBlob& isolate_credential,
+                          uint64_t session_id);
   virtual uint32_t SignRecoverInit(
       const chromeos::SecureBlob& isolate_credential,
       uint64_t session_id,
@@ -264,6 +272,8 @@
   virtual uint32_t VerifyFinal(const chromeos::SecureBlob& isolate_credential,
                                uint64_t session_id,
                                const std::vector<uint8_t>& signature);
+  virtual void VerifyCancel(const chromeos::SecureBlob& isolate_credential,
+                            uint64_t session_id);
   virtual uint32_t VerifyRecoverInit(
       const chromeos::SecureBlob& isolate_credential,
       uint64_t session_id,
diff --git a/chaps/chaps_test.cc b/chaps/chaps_test.cc
index 1d1761b..a0f5932 100644
--- a/chaps/chaps_test.cc
+++ b/chaps/chaps_test.cc
@@ -1570,6 +1570,11 @@
   CK_ULONG_PTR ul = (CK_ULONG_PTR)0x1234;
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_EncryptInit(1, NULL, 3));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_DecryptInit(1, NULL, 3));
+
+  // All of the following failures should trigger an attempt to cancel the
+  // operation in progress.
+  EXPECT_CALL(proxy, EncryptCancel(_, 1)).Times(5);
+  EXPECT_CALL(proxy, DecryptCancel(_, 1)).Times(5);
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Encrypt(1, p, 3, p, NULL));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Decrypt(1, p, 3, p, NULL));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Encrypt(1, NULL, 3, p, ul));
@@ -1745,6 +1750,10 @@
   CK_BYTE_PTR p = (CK_BYTE_PTR)0x1234;
   CK_ULONG_PTR ul = (CK_ULONG_PTR)0x1234;
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_DigestInit(1, NULL));
+
+  // All of the following failures should trigger an attempt to cancel the
+  // operation in progress.
+  EXPECT_CALL(proxy, DigestCancel(_, 1)).Times(5);
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Digest(1, p, 3, p, NULL));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Digest(1, NULL, 3, p, ul));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_DigestUpdate(1, NULL, 3));
@@ -1944,6 +1953,11 @@
   CK_ULONG_PTR ul = (CK_ULONG_PTR)0x1234;
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_SignInit(1, NULL, 3));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_VerifyInit(1, NULL, 3));
+
+  // All of the following failures should trigger an attempt to cancel the
+  // operation in progress.
+  EXPECT_CALL(proxy, SignCancel(_, 1)).Times(5);
+  EXPECT_CALL(proxy, VerifyCancel(_, 1)).Times(7);
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Sign(1, p, 3, p, NULL));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Sign(1, NULL, 3, p, ul));
   EXPECT_EQ(CKR_ARGUMENTS_BAD, C_Verify(1, NULL, 3, p, 3));
diff --git a/chaps/session.h b/chaps/session.h
index dab76fb..c08caab 100644
--- a/chaps/session.h
+++ b/chaps/session.h
@@ -93,6 +93,8 @@
   virtual CK_RV OperationFinal(OperationType operation,
                                int* required_out_length,
                                std::string* data_out) = 0;
+  // Cancels an operation that is already active (like C_EncryptUpdate).
+  virtual void OperationCancel(OperationType operation) = 0;
   // Finalizes a signature verification operation (like C_VerifyFinal).
   virtual CK_RV VerifyFinal(const std::string& signature) = 0;
   // Performs an entire operation in a single step (like C_Encrypt). This is
diff --git a/chaps/session_impl.cc b/chaps/session_impl.cc
index 033fe31..cc5bc14 100644
--- a/chaps/session_impl.cc
+++ b/chaps/session_impl.cc
@@ -282,6 +282,7 @@
   }
   if (context->is_finished_) {
     LOG(ERROR) << "Operation is finished.";
+    OperationCancel(operation);
     return CKR_OPERATION_ACTIVE;
   }
   context->is_incremental_ = true;
@@ -298,7 +299,10 @@
   CHECK(operation < kNumOperationTypes);
   OperationContext* context = &operation_context_[operation];
   if (context->is_cipher_) {
-    return CipherUpdate(context, data_in, required_out_length, data_out);
+    CK_RV rv = CipherUpdate(context, data_in, required_out_length, data_out);
+    if ((rv != CKR_OK) && (rv != CKR_BUFFER_TOO_SMALL))
+      OperationCancel(operation);
+    return rv;
   } else if (context->is_digest_) {
     EVP_DigestUpdate(&context->digest_context_,
                      data_in.data(),
@@ -316,6 +320,17 @@
   return CKR_OK;
 }
 
+void SessionImpl::OperationCancel(OperationType operation) {
+  CHECK(operation < kNumOperationTypes);
+  OperationContext* context = &operation_context_[operation];
+  if (!context->is_valid_) {
+    LOG(ERROR) << "Operation is not initialized.";
+    return;
+  }
+  // Drop the context and any associated data.
+  context->Clear();
+}
+
 CK_RV SessionImpl::OperationFinal(OperationType operation,
                                   int* required_out_length,
                                   string* data_out) {
@@ -329,6 +344,7 @@
   }
   if (!context->is_incremental_ && context->is_finished_) {
     LOG(ERROR) << "Operation is not incremental.";
+    OperationCancel(operation);
     return CKR_OPERATION_ACTIVE;
   }
   context->is_incremental_ = true;
@@ -423,6 +439,7 @@
   }
   if (context->is_incremental_) {
     LOG(ERROR) << "Operation is incremental.";
+    OperationCancel(operation);
     return CKR_OPERATION_ACTIVE;
   }
   CK_RV result = CKR_OK;
diff --git a/chaps/session_impl.h b/chaps/session_impl.h
index a725831..5cb3977 100644
--- a/chaps/session_impl.h
+++ b/chaps/session_impl.h
@@ -80,6 +80,7 @@
   virtual CK_RV OperationFinal(OperationType operation,
                                int* required_out_length,
                                std::string* data_out);
+  virtual void OperationCancel(OperationType operation);
   virtual CK_RV VerifyFinal(const std::string& signature);
   virtual CK_RV OperationSinglePart(OperationType operation,
                                     const std::string& data_in,
diff --git a/chaps/session_mock.h b/chaps/session_mock.h
index 0bf6d07..d646a48 100644
--- a/chaps/session_mock.h
+++ b/chaps/session_mock.h
@@ -49,6 +49,7 @@
                                       int*,
                                       std::string*));
   MOCK_METHOD3(OperationFinal, CK_RV(OperationType, int*, std::string*));
+  MOCK_METHOD1(OperationCancel, void(OperationType));
   MOCK_METHOD1(VerifyFinal, CK_RV(const std::string&));
   MOCK_METHOD4(OperationSinglePart, CK_RV(OperationType,
                                           const std::string&,
diff --git a/chaps/session_test.cc b/chaps/session_test.cc
index 4a197b4..5fdc37c 100644
--- a/chaps/session_test.cc
+++ b/chaps/session_test.cc
@@ -464,6 +464,11 @@
                                           in.substr(10, 20),
                                           &len,
                                           &out));
+
+  // The error also terminates the operation.
+  len = 0;
+  EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED,
+            session_->OperationFinal(kDigest, &len, &out));
 }
 
 TEST_F(TestSession, SinglePartOperationPreventsUpdate) {
@@ -480,6 +485,10 @@
                                       in.substr(10, 10),
                                       NULL,
                                       NULL));
+
+  // The error also terminates the operation.
+  EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED,
+            session_->OperationSinglePart(kDigest, in, &len, &out));
 }
 
 TEST_F(TestSession, SinglePartOperationPreventsFinal) {
@@ -494,6 +503,10 @@
   len = 0;
   EXPECT_EQ(CKR_OPERATION_ACTIVE,
             session_->OperationFinal(kDigest, &len, &out));
+
+  // The error also terminates the operation.
+  EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED,
+            session_->OperationSinglePart(kDigest, in, &len, &out));
 }
 
 // Test RSA PKCS #1 encryption.