-template <typename T, void (*Fn)(T*)>
-struct Free {
- explicit Free(T* ptr) : ptr(ptr) {}
- ~Free() {
- Fn(ptr);
- }
- Free(const Free&) = delete;
- Free& operator=(const Free&) = delete;
- T* operator*() { return ptr; }
-private:
- T* ptr;
-};
-
-void OPENSSL_free_wrapper(unsigned char* ptr)
-{
- OPENSSL_free(static_cast<void*>(ptr));
-}
-
-typedef Free<dcm_e2ee_bundle_s, dcm_e2ee_free_bundle> FreeBundle;
-typedef Free<void, free> FreeVoid;
-typedef Free<BIO, BIO_free_all> FreeBio;
-typedef Free<unsigned char, OPENSSL_free_wrapper> FreeOpenssl;
-typedef Free<X509, X509_free> FreeX509;
-typedef Free<EVP_MD_CTX, EVP_MD_CTX_free> FreeMdCtx;
-typedef Free<X509_STORE_CTX, X509_STORE_CTX_free> FreeX509StoreCtx;
-
-typedef STACK_OF(X509) X509_STACK;
-typedef std::unique_ptr<X509_STACK, decltype(&sk_X509_free)> X509StackPtr;
-
-X509StackPtr getOcfChain()
-{
- // extract OCFs root certificate
- char* ocfChain = nullptr;
- size_t ocfChainLen = 0;
-
- // OCF cert + common OCFs root cert
- e2ee_positive(ckmew_get_ocf_cert_chain, &ocfChain, &ocfChainLen);
-
- RUNNER_ASSERT_MSG(ocfChain != nullptr, "OCF cert chain is empty");
-
- FreeVoid ocfChainFree(static_cast<void*>(ocfChain));
-
- RUNNER_ASSERT_MSG(ocfChainLen > 0, "OCF cert chain has 0 length");
-
- auto bio = (BIO_new(BIO_s_mem()));
- RUNNER_ASSERT_MSG(bio != nullptr, "BIO_new failed");
- FreeBio bioFree(bio);
-
- auto written = BIO_write(bio, ocfChain, ocfChainLen);
- RUNNER_ASSERT_MSG(written >= 0, "BIO_write failed");
- RUNNER_ASSERT_MSG(static_cast<size_t>(written) == ocfChainLen, "OCF chain write is incomplete");
-
- // build a X509 chain
- X509StackPtr chainPtr(sk_X509_new_null(), sk_X509_free);
- RUNNER_ASSERT_MSG(chainPtr, "sk_X509_new_null failed");
-
- X509* cert = nullptr;
- while((cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) != nullptr)
- RUNNER_ASSERT_MSG(sk_X509_push(chainPtr.get(), cert) > 0, "Nothing was pushed to stack");
-
- // make sure the chain is long enough
- RUNNER_ASSERT_MSG(sk_X509_num(chainPtr.get()) >= 2,
- "Insufficient number of certificates in the chain");
-
- return chainPtr;
-}
-
-struct ustreambuf: public std::basic_streambuf<unsigned char> {
- ustreambuf(unsigned char* buf, size_t size) : std::basic_streambuf<unsigned char>()
- {
- pubsetbuf(buf, size);
- }
-};
-
-class Peer
-{
-public:
- Peer(const KeyAliasPair& keys, const char* derived) : ours(keys), derived(derived) {}
- ~Peer() {
- ckmc_remove_alias(derived);
- }
-
- std::string send()
- {
- ckmc_raw_buffer_s* message = nullptr;
- ckmc_raw_buffer_s* signature = nullptr;
- e2ee_positive(ckmew_sign_with_ocf, ours.pub.c_str(), &message, &signature);
-
- auto messagePtr = create_raw_buffer(message);
- auto signaturePtr = create_raw_buffer(signature);
-
- RUNNER_ASSERT_MSG(messagePtr->size > 0, "Message buffer has 0 length");
- RUNNER_ASSERT_MSG(messagePtr->data != nullptr, "Message buffer has no data");
-
- RUNNER_ASSERT_MSG(signaturePtr->size > 0, "Signature buffer has 0 length");
- RUNNER_ASSERT_MSG(signaturePtr->data != nullptr, "Signature buffer has no data");
-
- // extract OCF key certificate
- auto chainPtr = getOcfChain();
- auto ocfCertX509 = sk_X509_value(chainPtr.get(), 0);
-
- RUNNER_ASSERT_MSG(ocfCertX509 != nullptr, "OCF certificate extraction failed");
-
- // convert it to DER
- unsigned char *ocfCert = nullptr;
- size_t ocfCertLen = i2d_X509(ocfCertX509, &ocfCert);
-
- RUNNER_ASSERT_MSG(ocfCertLen > 0, "OCF certificate has 0 length");
- RUNNER_ASSERT_MSG(ocfCert != nullptr, "OCF certificate is empty");
- FreeOpenssl certFree(ocfCert);
-
- // serialize
- std::ostringstream os;
- auto serialize = [&](const unsigned char* data, size_t size){
- os.write(reinterpret_cast<const char*>(&size), sizeof(size));
- os.write(reinterpret_cast<const char*>(data), size);
- };
-
- serialize(message->data, message->size);
- serialize(signature->data, signature->size);
- serialize(ocfCert, ocfCertLen);
-
- return os.str();
- }
-
- void receive(std::string&& buffer)
- {
- // deserialize
- std::istringstream is(buffer);
- auto deserialize = [&]()
- {
- size_t size;
- is.read(reinterpret_cast<char*>(&size), sizeof(size));
- RUNNER_ASSERT_MSG(size > 0, "Deserialized 0 length vector");
- std::vector<unsigned char> data(size);
- is.read(reinterpret_cast<char*>(data.data()), size);
-
- return data;
- };
-
- auto message = deserialize();
- auto signature = deserialize();
- auto ocfCert = deserialize();
-
- // decompose message
- unsigned char* messageDup = static_cast<unsigned char*>(malloc(message.size()));
- RUNNER_ASSERT_MSG(messageDup != nullptr, "Memory allocation failed");
- memcpy(messageDup, message.data(), message.size());
-
- dcm_e2ee_bundle_h bundle = nullptr;
- e2ee_positive(dcm_e2ee_create_bundle, messageDup, message.size(), &bundle);
- RUNNER_ASSERT_MSG(bundle != nullptr, "Bundle creation failed");
- FreeBundle freeBundle(bundle);
-
- const char* platform = nullptr;
- e2ee_positive(dcm_e2ee_get_bundle_platform, bundle, &platform);
- RUNNER_ASSERT_MSG(strcmp(platform, "Tizen") == 0, "Unexpected platform:" << platform);
-
- char* label = NULL;
- ssize_t size = smack_new_label_from_self(&label);
- RUNNER_ASSERT_MSG(size > 0 && label != nullptr, "Smack label acquisition failed");
- FreeVoid freeLabel(static_cast<void*>(label));
-
- const char* pkgId = nullptr;
- e2ee_positive(dcm_e2ee_get_bundle_pkg_id, bundle, &pkgId);
- RUNNER_ASSERT_MSG(strcmp(pkgId, label) == 0, "Unexpected pkg id:" << pkgId);
-
- const unsigned char* peerPubDevKey = nullptr;
- size_t peerPubDevKeyLen = 0;
- e2ee_positive(dcm_e2ee_get_bundle_payload, bundle, &peerPubDevKey, &peerPubDevKeyLen);
- RUNNER_ASSERT_MSG(peerPubDevKey != nullptr, "Empty public key");
- RUNNER_ASSERT_MSG(peerPubDevKeyLen > 0, "Public key has zero length");
-
- // parse OCF certificate
- const unsigned char* ocfCertPtr = ocfCert.data();
- auto ocfCertX509 = d2i_X509(nullptr, &ocfCertPtr, ocfCert.size());
- RUNNER_ASSERT_MSG(ocfCertX509 != nullptr, "OCF certificate parsing failed");
- FreeX509 freeCert(ocfCertX509);
-
- // extract OCF public key from OCF certificate
- EVP_PKEY *ocfPubKey = X509_get0_pubkey(ocfCertX509);
- RUNNER_ASSERT_MSG(ocfPubKey != nullptr, "Can't get public key from OCF certificate");
-
- // verify OCF signature
- EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
- RUNNER_ASSERT_MSG(mdctx != nullptr, "EVP_MD_CTX_new failed");
- FreeMdCtx freeMd(mdctx);
-
- int ret = EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha256(), nullptr, ocfPubKey);
- RUNNER_ASSERT_MSG(ret == 1, "EVP_DigestVerifyInit failed");
-
- ret = EVP_DigestVerifyUpdate(mdctx, message.data(), message.size());
- RUNNER_ASSERT_MSG(ret == 1, "EVP_DigestVerifyUpdate failed");
-
- ret = EVP_DigestVerifyFinal(mdctx, signature.data(), signature.size());
- RUNNER_ASSERT_MSG(ret == 1, "OCF signature verification failed");
-
- // verify received cert with local certchain
- auto chainPtr = getOcfChain();
-
- // pop and free the first certificate (OCF)
- X509_free(sk_X509_shift(chainPtr.get()));
-
- // pop the last certificate (OCF root)
- auto ocfRoot = sk_X509_pop(chainPtr.get());
- RUNNER_ASSERT_MSG(ocfRoot != nullptr, "OCF root cert is NULL");
- FreeX509 freeOcfRoot(ocfRoot);
-
- X509_STORE* store = X509_STORE_new();
- FreeX509StoreCtx storeCtx(X509_STORE_CTX_new());
-
- // add OCF root as a trusted cert
- ret = X509_STORE_add_cert(store, ocfRoot);
- RUNNER_ASSERT_MSG(ret == 1, "Failed to add certificate to the store");
-
- // store becomes a member of storeCtx
- ret = X509_STORE_CTX_init(*storeCtx, store, ocfCertX509, chainPtr.get());
- RUNNER_ASSERT_MSG(ret == 1, "X509_STORE_CTX_init failed");
- ret = X509_verify_cert(*storeCtx);
- RUNNER_ASSERT_MSG(ret == 1, "OCF certificate verification failed");
-
- // derive shared key
- e2ee_positive(ckmew_key_agreement,
- ours.prv.c_str(),
- peerPubDevKey,
- peerPubDevKeyLen,
- derived);
- }
-
- RawBufferPtr encrypt(const ParamListPtr& params, const RawBufferPtr& plain)
- {
- ckmc_raw_buffer_s* encrypted = nullptr;
- e2ee_positive(ckmc_encrypt_data, params.get(), derived, "", *plain.get(), &encrypted);
- return create_raw_buffer(encrypted);
- }
-
- RawBufferPtr decrypt(const ParamListPtr& params, const RawBufferPtr& encrypted)
- {
- ckmc_raw_buffer_s* decrypted = nullptr;
- e2ee_positive(ckmc_decrypt_data, params.get(), derived, "", *encrypted.get(), &decrypted);
- return create_raw_buffer(decrypted);
- }
-
-private:
- const KeyAliasPair& ours;
- const char* derived;
-};
-