Added implementation of C_SeedRandom and C_GenerateRandom.

BUG=chromium-os:20415,chromium-os:20587
TEST=make runtests
     manual tests on x86-zgb
     > sudo /usr/sbin/chapsd libopencryptoki.so &
     > ./chapsd_test --use_dbus
     > ./chapsd_test

Change-Id: Id6b3c64f578fa5b531a33c338a7a178c705333ab
diff --git a/chaps.cc b/chaps.cc
index 853b387..c4c8665 100644
--- a/chaps.cc
+++ b/chaps.cc
@@ -161,6 +161,22 @@
   return CKR_OK;
 }
 
+// PKCS #11 v2.20 section 11.4 page 106.
+CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
+  static CK_VERSION version = {2, 20};
+  static CK_FUNCTION_LIST functionList = {
+    version,
+    // Let pkcs11f.h populate the function pointers in order.
+#define CK_PKCS11_FUNCTION_INFO(func) &func,
+    // We want only the function names, not the arguments.
+#undef CK_NEED_ARG_LIST
+#include "pkcs11/pkcs11f.h"
+#undef CK_PKCS11_FUNCTION_INFO
+  };
+  *ppFunctionList = &functionList;
+  return CKR_OK;
+}
+
 // PKCS #11 v2.20 section 11.5 page 106.
 CK_RV C_GetSlotList(CK_BBOOL tokenPresent,
                     CK_SLOT_ID_PTR pSlotList,
@@ -1369,3 +1385,43 @@
   LOG_CK_RV_AND_RETURN_IF_ERR(result);
   return CKR_OK;
 }
+
+// PKCS #11 v2.20 section 11.15 page 184.
+CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession,
+                   CK_BYTE_PTR pSeed,
+                   CK_ULONG ulSeedLen) {
+  LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
+  if (!pSeed || ulSeedLen == 0)
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  uint32_t result = g_proxy->SeedRandom(
+      hSession,
+      chaps::ConvertByteBufferToVector(pSeed, ulSeedLen));
+  LOG_CK_RV_AND_RETURN_IF_ERR(result);
+  return CKR_OK;
+}
+
+// PKCS #11 v2.20 section 11.15 page 184.
+CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
+                       CK_BYTE_PTR RandomData,
+                       CK_ULONG ulRandomLen) {
+  LOG_CK_RV_AND_RETURN_IF(!g_is_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
+  if (!RandomData || ulRandomLen == 0)
+    LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
+  vector<uint8_t> data_out;
+  uint32_t result = g_proxy->GenerateRandom(hSession, ulRandomLen, &data_out);
+  LOG_CK_RV_AND_RETURN_IF_ERR(result);
+  LOG_CK_RV_AND_RETURN_IF(data_out.size() != ulRandomLen, CKR_GENERAL_ERROR);
+  memcpy(RandomData, &data_out.front(), ulRandomLen);
+  return CKR_OK;
+}
+
+// PKCS #11 v2.20 section 11.16 page 185.
+CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession) {
+  return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+// PKCS #11 v2.20 section 11.16 page 186.
+CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession) {
+  return CKR_FUNCTION_NOT_PARALLEL;
+}
+
diff --git a/chaps_adaptor.cc b/chaps_adaptor.cc
index e13fc49..1ee0c1b 100644
--- a/chaps_adaptor.cc
+++ b/chaps_adaptor.cc
@@ -708,4 +708,18 @@
                                &key_handle);
 }
 
+uint32_t ChapsAdaptor::SeedRandom(const uint32_t& session_id,
+                              const vector<uint8_t>& seed,
+                              ::DBus::Error& /*error*/) {
+  return service_->SeedRandom(session_id, seed);
+}
+
+void ChapsAdaptor::GenerateRandom(const uint32_t& session_id,
+                              const uint32_t& num_bytes,
+                              vector<uint8_t>& random_data,
+                              uint32_t& result,
+                              ::DBus::Error& /*error*/) {
+  result = service_->GenerateRandom(session_id, num_bytes, &random_data);
+}
+
 }  // namespace
diff --git a/chaps_adaptor.h b/chaps_adaptor.h
index 7d2dc49..92badae 100644
--- a/chaps_adaptor.h
+++ b/chaps_adaptor.h
@@ -364,6 +364,14 @@
                          uint32_t& key_handle,
                          uint32_t& result,
                          ::DBus::Error& error);
+  virtual uint32_t SeedRandom(const uint32_t& session_id,
+                              const std::vector<uint8_t>& seed,
+                              ::DBus::Error& error);
+  virtual void GenerateRandom(const uint32_t& session_id,
+                              const uint32_t& num_bytes,
+                              std::vector<uint8_t>& random_data,
+                              uint32_t& result,
+                              ::DBus::Error& error);
 
 private:
   ChapsInterface* service_;
diff --git a/chaps_interface.h b/chaps_interface.h
index d8bf207..09e03e9 100644
--- a/chaps_interface.h
+++ b/chaps_interface.h
@@ -333,6 +333,13 @@
                              uint32_t base_key_handle,
                              const std::vector<uint8_t>& attributes,
                              uint32_t* key_handle) = 0;
+  // PKCS #11 v2.20 section 11.15 page 184.
+  virtual uint32_t SeedRandom(uint32_t session_id,
+                              const std::vector<uint8_t>& seed) = 0;
+  // PKCS #11 v2.20 section 11.15 page 184.
+  virtual uint32_t GenerateRandom(uint32_t session_id,
+                                  uint32_t num_bytes,
+                                  std::vector<uint8_t>* random_data) = 0;
 
 private:
   DISALLOW_COPY_AND_ASSIGN(ChapsInterface);
diff --git a/chaps_interface.xml b/chaps_interface.xml
index d2f6c1c..8b5feff 100644
--- a/chaps_interface.xml
+++ b/chaps_interface.xml
@@ -683,6 +683,25 @@
         <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/>
       </arg>
     </method>
+
+    <!-- PKCS #11 v2.20 section 11.15 page 184. -->
+    <method name="SeedRandom">
+      <arg type="u" name="session_id" direction="in"/>
+      <arg type="ay" name="seed" direction="in"/>
+      <arg type="u" name="result" direction="out">
+        <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/>
+      </arg>
+    </method>
+
+    <!-- PKCS #11 v2.20 section 11.15 page 184. -->
+    <method name="GenerateRandom">
+      <arg type="u" name="session_id" direction="in"/>
+      <arg type="u" name="num_bytes" direction="in"/>
+      <arg type="ay" name="random_data" direction="out"/>
+      <arg type="u" name="result" direction="out">
+        <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/>
+      </arg>
+    </method>
   </interface>
 </node>
 
diff --git a/chaps_proxy.cc b/chaps_proxy.cc
index 91e77ec..183dafb 100644
--- a/chaps_proxy.cc
+++ b/chaps_proxy.cc
@@ -1175,4 +1175,33 @@
   return result;
 }
 
+uint32_t ChapsProxyImpl::SeedRandom(uint32_t session_id,
+                                    const vector<uint8_t>& seed) {
+  LOG_CK_RV_AND_RETURN_IF(!proxy_.get(), CKR_CRYPTOKI_NOT_INITIALIZED);
+  LOG_CK_RV_AND_RETURN_IF(seed.size() == 0, CKR_ARGUMENTS_BAD);
+  uint32_t result = CKR_GENERAL_ERROR;
+  try {
+    result = proxy_->SeedRandom(session_id, seed);
+  } catch (DBus::Error err) {
+    result = CKR_GENERAL_ERROR;
+    LOG(ERROR) << "DBus::Error - " << err.what();
+  }
+  return result;
+}
+
+uint32_t ChapsProxyImpl::GenerateRandom(uint32_t session_id,
+                                        uint32_t num_bytes,
+                                        vector<uint8_t>* random_data) {
+  LOG_CK_RV_AND_RETURN_IF(!proxy_.get(), CKR_CRYPTOKI_NOT_INITIALIZED);
+  LOG_CK_RV_AND_RETURN_IF(!random_data || num_bytes == 0, CKR_ARGUMENTS_BAD);
+  uint32_t result = CKR_GENERAL_ERROR;
+  try {
+    proxy_->GenerateRandom(session_id, num_bytes, *random_data, result);
+  } catch (DBus::Error err) {
+    result = CKR_GENERAL_ERROR;
+    LOG(ERROR) << "DBus::Error - " << err.what();
+  }
+  return result;
+}
+
 }  // namespace
diff --git a/chaps_proxy.h b/chaps_proxy.h
index cb343a3..e106763 100644
--- a/chaps_proxy.h
+++ b/chaps_proxy.h
@@ -265,6 +265,12 @@
                              uint32_t base_key_handle,
                              const std::vector<uint8_t>& attributes,
                              uint32_t* key_handle);
+  virtual uint32_t SeedRandom(uint32_t session_id,
+                              const std::vector<uint8_t>& seed);
+  virtual uint32_t GenerateRandom(uint32_t session_id,
+                                  uint32_t num_bytes,
+                                  std::vector<uint8_t>* random_data);
+
 
 private:
   // This class provides the link to the dbus-c++ generated proxy.
diff --git a/chaps_proxy_mock.h b/chaps_proxy_mock.h
index 497dc8a..9834e7e 100644
--- a/chaps_proxy_mock.h
+++ b/chaps_proxy_mock.h
@@ -244,6 +244,10 @@
                                     uint32_t,
                                     const std::vector<uint8_t>&,
                                     uint32_t*));
+  MOCK_METHOD2(SeedRandom, uint32_t (uint32_t, const std::vector<uint8_t>&));
+  MOCK_METHOD3(GenerateRandom, uint32_t (uint32_t,
+                                         uint32_t,
+                                         std::vector<uint8_t>*));
 
 private:
   DISALLOW_COPY_AND_ASSIGN(ChapsProxyMock);
diff --git a/chaps_service_redirect.cc b/chaps_service_redirect.cc
index 6b877c7..7946f80 100644
--- a/chaps_service_redirect.cc
+++ b/chaps_service_redirect.cc
@@ -1273,4 +1273,30 @@
   return CKR_OK;
 }
 
+uint32_t ChapsServiceRedirect::SeedRandom(uint32_t session_id,
+                                          const vector<uint8_t>& seed) {
+  CHECK(functions_);
+  LOG_CK_RV_AND_RETURN_IF(seed.size() == 0, CKR_ARGUMENTS_BAD);
+  CK_BYTE_PTR in_bytes =
+      static_cast<CK_BYTE_PTR>(const_cast<uint8_t*>(&seed.front()));
+  uint32_t result = functions_->C_SeedRandom(session_id, in_bytes, seed.size());
+  LOG_CK_RV_AND_RETURN_IF_ERR(result);
+  return CKR_OK;
+}
+
+uint32_t ChapsServiceRedirect::GenerateRandom(uint32_t session_id,
+                                              uint32_t num_bytes,
+                                              vector<uint8_t>* random_data) {
+  CHECK(functions_);
+  LOG_CK_RV_AND_RETURN_IF(!random_data || num_bytes == 0, CKR_ARGUMENTS_BAD);
+  scoped_array<CK_BYTE> out_bytes(new CK_BYTE[num_bytes]);
+  CHECK(out_bytes.get());
+  uint32_t result = functions_->C_GenerateRandom(session_id,
+                                                 out_bytes.get(),
+                                                 num_bytes);
+  LOG_CK_RV_AND_RETURN_IF_ERR(result);
+  *random_data = ConvertByteBufferToVector(out_bytes.get(), num_bytes);
+  return CKR_OK;
+}
+
 }  // namespace
diff --git a/chaps_service_redirect.h b/chaps_service_redirect.h
index 6c13223..020dfbc 100644
--- a/chaps_service_redirect.h
+++ b/chaps_service_redirect.h
@@ -258,6 +258,11 @@
                              uint32_t base_key_handle,
                              const std::vector<uint8_t>& attributes,
                              uint32_t* key_handle);
+  virtual uint32_t SeedRandom(uint32_t session_id,
+                              const std::vector<uint8_t>& seed);
+  virtual uint32_t GenerateRandom(uint32_t session_id,
+                                  uint32_t num_bytes,
+                                  std::vector<uint8_t>* random_data);
 
 private:
   std::string library_path_;
diff --git a/chaps_test.cc b/chaps_test.cc
index 7e7db6f..e0c1cef 100644
--- a/chaps_test.cc
+++ b/chaps_test.cc
@@ -2333,6 +2333,55 @@
             C_DeriveKey(1, &mechanism_, 2, NULL, 0, &h));
 }
 
+TEST(TestRandom, RandomOK) {
+  ChapsProxyMock proxy(true);
+  CK_BYTE data_buffer[20];
+  CK_BYTE data_buffer2[20];
+  CK_ULONG data_length = 20;
+  memset(data_buffer, 0xAA, 20);
+  memset(data_buffer2, 0xBB, 20);
+  vector<uint8_t> data(&data_buffer[0], &data_buffer[20]);
+  EXPECT_CALL(proxy, SeedRandom(1, data))
+      .WillOnce(Return(CKR_OK));
+  EXPECT_CALL(proxy, GenerateRandom(1, data_length, _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(data), Return(CKR_OK)));
+  EXPECT_EQ(CKR_OK, C_SeedRandom(1, data_buffer, data_length));
+  EXPECT_EQ(CKR_OK, C_GenerateRandom(1, data_buffer2, data_length));
+  EXPECT_EQ(0, memcmp(data_buffer, data_buffer2, data_length));
+}
+
+TEST(TestRandom, RandomFail) {
+  ChapsProxyMock proxy(true);
+  CK_BYTE data_buffer[20];
+  CK_BYTE data_buffer2[20];
+  CK_ULONG data_length = 20;
+  memset(data_buffer, 0xAA, 20);
+  memset(data_buffer2, 0xBB, 20);
+  vector<uint8_t> data(&data_buffer[0], &data_buffer[20]);
+  EXPECT_CALL(proxy, SeedRandom(1, data))
+      .WillOnce(Return(CKR_SESSION_CLOSED));
+  EXPECT_CALL(proxy, GenerateRandom(1, data_length, _))
+      .WillOnce(Return(CKR_SESSION_CLOSED));
+  EXPECT_EQ(CKR_SESSION_CLOSED, C_SeedRandom(1, data_buffer, data_length));
+  EXPECT_EQ(CKR_SESSION_CLOSED, C_GenerateRandom(1, data_buffer2, data_length));
+}
+
+TEST(TestRandom, RandomBadArgs) {
+  ChapsProxyMock proxy(true);
+  CK_BYTE data_buffer[20] = {0};
+  EXPECT_EQ(CKR_ARGUMENTS_BAD, C_SeedRandom(1, NULL, 1));
+  EXPECT_EQ(CKR_ARGUMENTS_BAD, C_SeedRandom(1, data_buffer, 0));
+  EXPECT_EQ(CKR_ARGUMENTS_BAD, C_GenerateRandom(1, NULL, 1));
+  EXPECT_EQ(CKR_ARGUMENTS_BAD, C_GenerateRandom(1, data_buffer, 0));
+}
+
+TEST(TestRandom, RandomNotInit) {
+  ChapsProxyMock proxy(false);
+  CK_BYTE data_buffer[20] = {0};
+  EXPECT_EQ(CKR_CRYPTOKI_NOT_INITIALIZED, C_SeedRandom(1, data_buffer, 1));
+  EXPECT_EQ(CKR_CRYPTOKI_NOT_INITIALIZED, C_GenerateRandom(1, data_buffer, 1));
+}
+
 }  // namespace chaps
 
 int main(int argc, char** argv) {
diff --git a/chapsd_test.cc b/chapsd_test.cc
index 8b2a6e7..1c30af5 100644
--- a/chapsd_test.cc
+++ b/chapsd_test.cc
@@ -840,6 +840,12 @@
                                                  NULL));
 }
 
+TEST_F(TestP11PublicSession, Random) {
+  vector<uint8_t> data(8, 0xAA);
+  EXPECT_EQ(CKR_OK, chaps_->SeedRandom(session_id_, data));
+  EXPECT_EQ(CKR_OK, chaps_->GenerateRandom(session_id_, 32, &data));
+}
+
 }  // namespace chaps
 
 int main(int argc, char** argv) {