#define MBEDTLS_X509_RSASSA_PSS_SUPPORT
/**
+ * \def MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
+ *
+ * Enable parsing of all supported subtypes of the Subject Alternative Name
+ * extension. When enabled, the subject_alt_names field of mbedtls_x509_crt
+ * is defined as an mbedtls_x509_subject_alt_name_sequence, each element of
+ * which can describe a different subtype of the GeneralName choice as defined
+ * by the standard.
+ *
+ * Comment this macro to only support dNSName subtypes, and to define the
+ * subject_alt_names field as an mbedtls_x509_sequence. Any other subtypes will
+ * be ignored. This was the behavior in earlier versions.
+ */
+#define MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
+
+/**
* \def MBEDTLS_ZLIB_SUPPORT
*
* If set, the SSL/TLS module uses ZLIB to support compression and
#define CA_OPTION_URI_QUERY 15
#define CA_OPTION_ACCEPT 17
#define CA_OPTION_LOCATION_QUERY 20
+
+/**
+ * @def UUID_PREFIX
+ * @brief uuid prefix in certificate subject field
+ */
+#define UUID_PREFIX "uuid:"
+
+/**
+ * @def SUBJECT_PREFIX
+ * @brief prefix for specifying part of a cert's subject for a particular uuid
+ */
+#define SUBJECT_PREFIX "CN=" UUID_PREFIX
/**
* TODO: Move these COAP defines to CoAP lib once approved.
#endif
} CAEndpoint_t;
+#define CA_SECURE_ENDPOINT_PUBLIC_KEY_MAX_LENGTH (128)
+
/**
* Endpoint information for secure messages.
*/
CARemoteId_t identity; /**< endpoint device uuid */
CARemoteId_t userId; /**< endpoint user uuid */
uint32_t attributes;
+ uint8_t publicKey[CA_SECURE_ENDPOINT_PUBLIC_KEY_MAX_LENGTH]; /**< Peer's DER-encoded public key (if using certificate) */
+ size_t publicKeyLength; /**< Length of publicKey; zero if not using certificate */
} CASecureEndpoint_t;
/**
* API to get a secure connected peer information
*
* @param[in] peer peer information includs IP address and port.
+ * @param[out] sep copy of secure endpoint info
*
- * @return secure connected peer information on success, otherwise NULL
+ * @return CA_STATUS_OK on success; other error otherwise
*/
-const CASecureEndpoint_t *CAGetSecureEndpointData(const CAEndpoint_t *peer);
+CAResult_t CAGetSecureEndpointData(const CAEndpoint_t *peer, CASecureEndpoint_t *sep);
#endif //MULTIPLE_OWNER
/**
const uint8_t* provServerDeviceId, const size_t provServerDeviceIdLen,
uint8_t* ownerPsk, const size_t ownerPskSize);
-#ifdef MULTIPLE_OWNER
/**
- * Gets CA secure endpoint info corresponding for endpoint.
+ * Gets a copy of CA secure endpoint info corresponding for endpoint.
*
* @param[in] peer remote address
+ * @param[out] sep copy of secure endpoint data
*
- * @return CASecureEndpoint or NULL
+ * @return CA_STATUS_OK on success; other error code on failure
*/
-const CASecureEndpoint_t *GetCASecureEndpointData(const CAEndpoint_t* peer);
-#endif
+CAResult_t GetCASecureEndpointData(const CAEndpoint_t* peer, CASecureEndpoint_t *sep);
/**
* Adds a bit to the attributes field of a secure endpoint.
#include <stddef.h>
#include <stdbool.h>
+#include <assert.h>
#include "ca_adapter_net_ssl.h"
#include "cacommon.h"
#include "caipinterface.h"
#include "mbedtls/pkcs12.h"
#include "mbedtls/ssl_internal.h"
#include "mbedtls/net_sockets.h"
+#include "mbedtls/oid.h"
#ifdef __WITH_DTLS__
#include "mbedtls/timing.h"
#include "mbedtls/ssl_cookie.h"
*/
#define PERSONALIZATION_STRING "IOTIVITY_RND"
/**
- * @def UUID_PREFIX
- * @brief uuid prefix in certificate subject field
- */
-#define UUID_PREFIX "uuid:"
-/**
* @def USERID_PREFIX
* @brief userid prefix in certificate alternative subject name field
*/
return NULL;
}
-#ifdef MULTIPLE_OWNER
/**
- * Gets CA secure endpoint info corresponding for endpoint.
+ * Gets a copy of CA secure endpoint info corresponding for endpoint.
*
* @param[in] peer remote address
+ * @param[out] sep copy of secure endpoint data
*
- * @return CASecureEndpoint or NULL
+ * @return CA_STATUS_OK on success; other error code on failure
*/
-const CASecureEndpoint_t *GetCASecureEndpointData(const CAEndpoint_t* peer)
+CAResult_t GetCASecureEndpointData(const CAEndpoint_t* peer, CASecureEndpoint_t* sep)
{
OIC_LOG_V(DEBUG, NET_SSL_TAG, "In %s", __func__);
{
OIC_LOG(ERROR, NET_SSL_TAG, "Context is NULL");
oc_mutex_unlock(g_sslContextMutex);
- return NULL;
+ return CA_STATUS_NOT_INITIALIZED;
}
SslEndPoint_t* sslPeer = GetSslPeer(peer);
if(sslPeer)
{
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+ memcpy(sep, &sslPeer->sep, sizeof(sslPeer->sep));
oc_mutex_unlock(g_sslContextMutex);
- return &sslPeer->sep;
+ return CA_STATUS_OK;
}
OIC_LOG(DEBUG, NET_SSL_TAG, "Return NULL");
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
oc_mutex_unlock(g_sslContextMutex);
- return NULL;
+ return CA_STATUS_INVALID_PARAM;
}
-#endif
/**
* Adds a bit to the attributes field of a secure endpoint.
g_getCredentialTypesCallback(g_caSslContext->cipherFlag);
// Retrieve the PSK credential from SRM
- if (0 != InitPskIdentity(config))
+ if (true == g_caSslContext->cipherFlag[0] && 0 != InitPskIdentity(config))
{
OIC_LOG(ERROR, NET_SSL_TAG, "PSK identity initialization failed!");
}
if (MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 != selectedCipher &&
MBEDTLS_TLS_ECDH_ANON_WITH_AES_128_CBC_SHA256 != selectedCipher)
{
- char uuid[UUID_LENGTH * 2 + 5] = {0};
- void * uuidPos = NULL;
- void * userIdPos = NULL;
const mbedtls_x509_crt * peerCert = mbedtls_ssl_get_peer_cert(&peer->ssl);
+ const mbedtls_x509_name * name = NULL;
+ uint8_t pubKeyBuf[CA_SECURE_ENDPOINT_PUBLIC_KEY_MAX_LENGTH] = { 0 };
ret = (NULL == peerCert ? -1 : 0);
if (!checkSslOperation(peer,
ret,
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
return CA_STATUS_FAILED;
}
- uuidPos = memmem(peerCert->subject_raw.p, peerCert->subject_raw.len,
- UUID_PREFIX, sizeof(UUID_PREFIX) - 1);
- if (NULL != uuidPos)
+ /* mbedtls_pk_write_pubkey_der takes a non-const mbedtls_pk_context, but inspection
+ * shows that every place it's used internally treats it as const, so casting its
+ * constness away is safe.
+ */
+ ret = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)&peerCert->pk, pubKeyBuf, sizeof(pubKeyBuf));
+ if (ret <= 0)
{
- memcpy(uuid, (char*) uuidPos + sizeof(UUID_PREFIX) - 1, UUID_LENGTH * 2 + 4);
- OIC_LOG_V(DEBUG, NET_SSL_TAG, "certificate uuid string: %s" , uuid);
- ret = (OCConvertStringToUuid(uuid, peer->sep.identity.id)) ? 0 : -1;
- if (!checkSslOperation(peer,
- ret,
- "Failed to convert subject",
- MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT))
- {
- oc_mutex_unlock(g_sslContextMutex);
- OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
- return CA_STATUS_FAILED;
- }
+ OIC_LOG_V(ERROR, NET_SSL_TAG, "Failed to copy public key of remote peer: -0x%x", ret);
+ oc_mutex_unlock(g_sslContextMutex);
+ OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+ return CA_STATUS_FAILED;
}
- else
+ else if (ret > sizeof(peer->sep.publicKey))
{
- OIC_LOG(WARNING, NET_SSL_TAG, "uuid not found");
+ assert(!"publicKey field of CASecureEndpoint_t is too small for the public key!");
+ OIC_LOG(ERROR, NET_SSL_TAG, "Public key of remote peer was too large");
+ oc_mutex_unlock(g_sslContextMutex);
+ OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+ return CA_STATUS_FAILED;
}
+ /* DER data is written to the end of the buffer, so we have to skip ahead in it. */
+ memcpy(peer->sep.publicKey, (pubKeyBuf + sizeof(pubKeyBuf) - ret), ret);
+ peer->sep.publicKeyLength = ret;
- userIdPos = memmem(peerCert->subject_raw.p, peerCert->subject_raw.len,
- USERID_PREFIX, sizeof(USERID_PREFIX) - 1);
- if (NULL != userIdPos)
+ /* Find the CN component of the subject name. */
+ for (name = &peerCert->subject; NULL != name; name = name->next)
{
- memcpy(uuid, (char*) userIdPos + sizeof(USERID_PREFIX) - 1, UUID_LENGTH * 2 + 4);
- ret = (OCConvertStringToUuid(uuid, peer->sep.userId.id)) ? 0 : -1;
- if (!checkSslOperation(peer,
- ret,
- "Failed to convert subject alt name",
- MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT))
+ if (!name->oid.p)
{
- oc_mutex_unlock(g_sslContextMutex);
- OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
- return CA_STATUS_FAILED;
+ continue;
}
+
+ if ((name->oid.len < MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN) ||
+ (0 != memcmp(MBEDTLS_OID_AT_CN, name->oid.p, name->oid.len))))
+ {
+ continue;
+ }
+ }
+
+ if (NULL == name)
+ {
+ OIC_LOG(WARNING, NET_SSL_TAG, "no CN RDN found in subject name");
}
else
{
- OIC_LOG(WARNING, NET_SSL_TAG, "Subject alternative name not found");
+ const size_t uuidBufLen = UUID_STRING_SIZE - 1;
+ char uuid[UUID_STRING_SIZE] = { 0 };
+ const unsigned char * uuidPos = NULL;
+ const unsigned char * userIdPos = NULL;
+
+ uuidPos = (const unsigned char*)memmem(name->val.p, name->val.len,
+ UUID_PREFIX, sizeof(UUID_PREFIX) - 1);
+
+ /* If UUID_PREFIX is present, ensure there's enough data for the prefix plus an entire
+ * UUID, to make sure we don't read past the end of the buffer.
+ */
+ if ((NULL != uuidPos) &&
+ (name->val.len >= ((uuidPos - name->val.p) + (sizeof(UUID_PREFIX) - 1) + uuidBufLen)))
+ {
+ memcpy(uuid, uuidPos + sizeof(UUID_PREFIX) - 1, uuidBufLen);
+ OIC_LOG_V(DEBUG, NET_SSL_TAG, "certificate uuid string: %s", uuid);
+ ret = (OCConvertStringToUuid(uuid, peer->sep.identity.id)) ? 0 : -1;
+ if (!checkSslOperation(peer,
+ ret,
+ "Failed to convert subject",
+ MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT))
+ {
+ oc_mutex_unlock(g_sslContextMutex);
+ OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+ return CA_STATUS_FAILED;
+ }
+ }
+ else
+ {
+ OIC_LOG(WARNING, NET_SSL_TAG, "uuid not found");
+ }
+
+ /* If USERID_PREFIX is present, ensure there's enough data for the prefix plus an entire
+ * UUID, to make sure we don't read past the end of the buffer.
+ */
+ userIdPos = (const unsigned char*)memmem(name->val.p, name->val.len,
+ USERID_PREFIX, sizeof(USERID_PREFIX) - 1);
+ if ((NULL != userIdPos) &&
+ (name->val.len >= ((userIdPos - name->val.p) + (sizeof(USERID_PREFIX) - 1) + uuidBufLen)))
+ {
+ memcpy(uuid, userIdPos + sizeof(USERID_PREFIX) - 1, uuidBufLen);
+ ret = (OCConvertStringToUuid(uuid, peer->sep.userId.id)) ? 0 : -1;
+ if (!checkSslOperation(peer,
+ ret,
+ "Failed to convert subject alt name",
+ MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT))
+ {
+ oc_mutex_unlock(g_sslContextMutex);
+ OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+ return CA_STATUS_FAILED;
+ }
+ }
+ else
+ {
+ OIC_LOG(WARNING, NET_SSL_TAG, "Subject alternative name not found");
+ }
}
}
+ else
+ {
+ /* No public key information for non-certificate-using ciphersuites. */
+ memset(&peer->sep.publicKey, 0, sizeof(peer->sep.publicKey));
+ peer->sep.publicKeyLength = 0;
+ }
oc_mutex_unlock(g_sslContextMutex);
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
#ifdef MULTIPLE_OWNER
-const CASecureEndpoint_t *CAGetSecureEndpointData(const CAEndpoint_t *peer)
+CAResult_t CAGetSecureEndpointData(const CAEndpoint_t *peer, CASecureEndpoint_t *sep)
{
OIC_LOG(DEBUG, TAG, "IN CAGetSecurePeerInfo");
if (!g_isInitialized)
{
OIC_LOG(DEBUG, TAG, "CA is not initialized");
- return NULL;
+ return CA_STATUS_NOT_INITIALIZED;
}
OIC_LOG(DEBUG, TAG, "OUT CAGetSecurePeerInfo");
- return GetCASecureEndpointData(peer);
+ return GetCASecureEndpointData(peer, sep);
}
#endif //MULTIPLE_OWNER
static const char NORMAL_INFO_DATA[] =
"{\"oc\":[{\"href\":\"%s\",\"prop\":{\"rt\":[\"core.led\"],"
"\"if\":[\"oc.mi.def\"],\"obs\":1}}]}";
-
#ifdef __WITH_DTLS__
// Iotivity Device Identity.
printf("CAGetDtlsPskCredentials OUT\n");
return ret;
}
+
+const char* our_cert = "-----BEGIN CERTIFICATE-----\n"
+"MIIBhTCCASugAwIBAgIJAPZ5mB94RwYHMAoGCCqGSM49BAMCMCUxIzAhBgNVBAoM\n"
+"GklvVGl2aXR5VGVzdFNlbGZTaWduZWROYW1lMB4XDTE2MTIxNjIxMjcyMVoXDTMw\n"
+"MDgyNTIxMjcyMVowITEfMB0GA1UECgwWSW9UaXZpdHlUZXN0Q2xpZW50TmFtZTBZ\n"
+"MBMGByqGSM49AgEGCCqGSM49AwEHA0IABF8OxpJNe01ZPEFpXUUhjUV5uwJM1TF3\n"
+"ZSt0tJ71lQiRZ9cbl5z31acRpsZM+fXiR+wkR4xoP7iIyDdTHHVHtkSjSDBGMBUG\n"
+"A1UdJQQOMAwGCisGAQQBgt58AQYwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQH\n"
+"EWLwaDfA+o6U4wmQKVoK9I3B/DAKBggqhkjOPQQDAgNIADBFAiAbDHQHzSjNiDeQ\n"
+"OaJYRMLIW2dIlabiQ5pxkW/jEaRszAIhAPzuoNdrQTRbnqCy0hmS9hFt8MxDrrBh\n"
+"7jHARQm/5pko\n"
+"-----END CERTIFICATE-----\n";
+
+const char* our_key = "-----BEGIN EC PRIVATE KEY-----\n"
+"MHcCAQEEIOV0iG5CndNK6JhB8nDcqQjNjgRWe/LQWPNPua3w7nHToAoGCCqGSM49\n"
+"AwEHoUQDQgAEXw7Gkk17TVk8QWldRSGNRXm7AkzVMXdlK3S0nvWVCJFn1xuXnPfV\n"
+"pxGmxkz59eJH7CRHjGg/uIjIN1McdUe2RA==\n"
+"-----END EC PRIVATE KEY-----\n";
+
+const char* our_ca = "-----BEGIN CERTIFICATE-----\n"
+"MIIBlzCCATygAwIBAgIJALxGf3YRERn1MAoGCCqGSM49BAMCMCUxIzAhBgNVBAoM\n"
+"GklvVGl2aXR5VGVzdFNlbGZTaWduZWROYW1lMB4XDTE2MTIxNjIxMjcyMVoXDTMw\n"
+"MDgyNTIxMjcyMVowJTEjMCEGA1UECgwaSW9UaXZpdHlUZXN0U2VsZlNpZ25lZE5h\n"
+"bWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo/zp8PXaA/drJQKSG3TlerO0F\n"
+"eHkpRkmXMeLFLrImqo1w9OyLfVmrpBrCDjf83BkwLYp19bkYizL2Yk9zIQ4Do1Uw\n"
+"UzAhBgNVHSUEGjAYBgorBgEEAYLefAEGBgorBgEEAYLefAEHMA8GA1UdEwEB/wQF\n"
+"MAMBAf8wHQYDVR0OBBYEFAcRYvBoN8D6jpTjCZApWgr0jcH8MAoGCCqGSM49BAMC\n"
+"A0kAMEYCIQCuZb1LMTthWy9rPgy2FQgoFHB2LXUJlgRLJeO/gTFqgQIhANRvr1Py\n"
+"5Bp6asye5FK4VUj6tARxmRNeNLvwonLrqp2w\n"
+"-----END CERTIFICATE-----\n";
+
+// Invoked by the CA stack to retrieve credentials from this module
+void provide_x509_cert_and_key(PkiInfo_t* inf)
+{
+ /* PEM data must end in newline and be null terminated for IoTivity */
+
+ inf->crt.data = (uint8_t*) our_cert;
+ inf->crt.len = strlen(our_cert) + 1;
+ inf->key.data = (uint8_t*) our_key;
+ inf->key.len = strlen(our_key) + 1;
+ inf->ca.data = (uint8_t*) our_ca;
+ inf->ca.len = strlen(our_ca) + 1;
+
+ // CRL not provided
+ inf->crl.data = NULL;
+ inf->crl.len = 0;
+
+ return;
+}
+// Empty version, for testing.
+void badPkixInfoHandler(PkiInfo_t* inf)
+{
+ return;
+}
+
+void provide_supported_credential_types(bool* list)
+{
+ list[1] = true;
+ /*
+ * Note: there is a default implementation of this in credresource.c, exposed by
+ * pkix_interface.h, called InitManufacturerCipherSuiteList. If the cred resource
+ * has a credential of the required type, it updates list accordingly.
+ *
+ * In a separate test, we could use the cred resource and APIs (credresource.h).
+ */
+ return;
+}
+
#endif //__WITH_DTLS__
// CAInitialize TC
tempRep = NULL;
}
+#if defined(__WITH_DTLS__)
+TEST_F(CATests, DISABLED_PkiTest)
+{
+ // @todo: this test is disabled for now, it crashes with an invalid write. Cert data
+ // provided by the provide_x509_cert_and_key is stored as const char, but ParseChain()
+ // (in ca_adapter_net_ssl.c) writes to it while reading. We could change the test to
+ // provide data on the heap, but the CA stack should not be changing data provided to it
+ // by callbacks.
+
+ char* local_addr = "127.0.0.1";
+ uint16_t local_port = 5503;
+ CAEndpoint_t* serverAddr = NULL;
+
+ CARegisterHandler(request_handler, response_handler, error_handler);
+
+ EXPECT_EQ(CA_STATUS_OK, CASelectNetwork(CA_ADAPTER_IP));
+ CACreateEndpoint(CA_DEFAULT_FLAGS, CA_ADAPTER_IP, local_addr, local_port, &serverAddr);
+ ASSERT_TRUE(serverAddr != NULL);
+
+ // Register a credential types handler (tells the CA layer which creds are supported)
+ EXPECT_EQ(CA_STATUS_OK, CAregisterGetCredentialTypesHandler(provide_supported_credential_types));
+
+ // Limit ourselves to 0xC0AE : TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 since it's non-PSK
+ EXPECT_EQ(CA_STATUS_OK, CASelectCipherSuite(0xC0AE, serverAddr->adapter));
+
+ // Register an empty callback to provide the keys, expect failure when initializing the handshake.
+ EXPECT_EQ(CA_STATUS_OK, CAregisterPkixInfoHandler(badPkixInfoHandler));
+ EXPECT_EQ(CA_STATUS_FAILED, CAInitiateHandshake(serverAddr));
+
+ // Register a working callback to provide the keys, expect success.
+ EXPECT_EQ(CA_STATUS_OK, CAregisterPkixInfoHandler(provide_x509_cert_and_key));
+ EXPECT_EQ(CA_STATUS_OK, CAInitiateHandshake(serverAddr));
+
+ CADestroyEndpoint(serverAddr);
+}
+#endif /* defined(__WITH_DTLS__) */
+
// CASendRequest TC
// check return value when a NULL is passed instead of a valid CARequestInfo_t address
TEST_F(CATests, SendRequestTestWithNullAddr)
{
#ifdef __WITH_DTLS__
EXPECT_EQ(CA_STATUS_OK, CAregisterPskCredentialsHandler(CAGetDtlsPskCredentials));
+ EXPECT_EQ(CA_STATUS_OK, CAregisterPkixInfoHandler(provide_x509_cert_and_key));
+ EXPECT_EQ(CA_STATUS_OK, CAregisterGetCredentialTypesHandler(provide_supported_credential_types));
#endif
}
#define CAsetTlsCipherSuite CAsetTlsCipherSuiteTest
#define CAsslGenerateOwnerPsk CAsslGenerateOwnerPskTest
#define CAcloseSslConnectionAll CAcloseSslConnectionAllTest
-#ifdef MULTIPLE_OWNER
#define GetCASecureEndpointData GetCASecureEndpointDataTest
-#endif
#define SetCASecureEndpointAttribute SetCASecureEndpointAttributeTest
#define GetCASecureEndpointAttributes GetCASecureEndpointAttributesTest
if target_os in ['windows', 'msys_nt']:
libocsrm_env.AppendUnique(LIBPATH = [os.path.join(libocsrm_env.get('BUILD_DIR'), 'resource', 'oc_logger')])
- libocsrm_env.AppendUnique(CCFLAGS=['/W4', '/WX'])
+ # - warning C4200: nonstandard extension used: zero-sized array in struct/union
+ # - warning C4214: nonstandard extension used: bit field types other than int
+ # - warnings inherited from a header included from libcoap
+ libocsrm_env.AppendUnique(CCFLAGS=['/wd4200', '/wd4214', '/W4', '/WX'])
if target_os in ['linux', 'android', 'tizen', 'msys_nt', 'windows'] and libocsrm_env.get('SECURED') == '1':
SConscript('provisioning/SConscript', 'libocsrm_env')
libocsrm_src = libocsrm_src + [OCSRM_SRC + 'oxmpincommon.c', OCSRM_SRC + 'pbkdf2.c']
libocsrm_src = libocsrm_src + [OCSRM_SRC + 'crlresource.c', OCSRM_SRC + 'pkix_interface.c']
libocsrm_src = libocsrm_src + [OCSRM_SRC + 'oxmverifycommon.c']
+ libocsrm_src = libocsrm_src + [OCSRM_SRC + 'certhelpers.c', OCSRM_SRC + 'occertutility.c']
+ libocsrm_src = libocsrm_src + [OCSRM_SRC + 'csrresource.c']
+ libocsrm_src = libocsrm_src + [OCSRM_SRC + 'rolesresource.c']
if target_os in ['windows', 'msys_nt']:
libocsrm_src = libocsrm_src + [OCSRM_SRC + 'strptime.c']
const OicSecAce_t* GetACLResourceData(const OicUuid_t* subjectId, OicSecAce_t **savePtr);
/**
+ * This method is used by PolicyEngine to retrieve ACLs for a set of roles.
+ *
+ * @param[in] roles Array of roles to check for.
+ * @param[in] roleCount Length of roles array.
+ * @param[out] savePtr is used internally by @ref GetACLResourceDataByRoles to maintain index between
+ * successive calls for the same roles list.
+ *
+ * @note On the first call to @ref GETAclResourceDataByRoles, savePtr should point to NULL.
+ *
+ * @return reference to @ref OicSecAce_t if ACE is found, else NULL.
+ */
+const OicSecAce_t* GetACLResourceDataByRoles(const OicSecRole_t *roles, size_t roleCount, OicSecAce_t **savePtr);
+
+/**
* This function converts ACL data into CBOR format.
*
* @param acl instance of @ref OicSecAcl_t structure.
+ * @param aclVersion Version of ACL resource to produce; can be OIC_SEC_ACL_V1 or OIC_SEC_ACL_V2
* @param outPayload is the pointer to allocated memory for cbor payload.
* @param size of the cbor payload.
*
* @return ::OC_STACK_OK for Success, otherwise some error value.
*/
-OCStackResult AclToCBORPayload(const OicSecAcl_t * acl, uint8_t **outPayload, size_t *size);
+OCStackResult AclToCBORPayload(const OicSecAcl_t *acl, OicSecAclVersion_t aclVersion, uint8_t **outPayload, size_t *size);
#ifdef MULTIPLE_OWNER
/**
*
* @return ::OC_STACK_OK for Success, otherwise some error value
*/
-OCStackResult AppendACL2(const OicSecAcl_t* acl);
+OCStackResult AppendACLObject(const OicSecAcl_t* acl);
/**
* This function updates default ACE which is required for ownership transfer.
*
* @return instance of @ref OicSecAcl_t structure or NULL if error occurs
*/
-OicSecAcl_t* CBORPayloadToAcl2(const uint8_t *cborPayload, const size_t size);
+OicSecAcl_t* CBORPayloadToCloudAcl(const uint8_t *cborPayload, const size_t size);
/**
* This function prints ACL to stdin
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#ifndef IOTVT_SRM_CERTHELPERS_H_
+#define IOTVT_SRM_CERTHELPERS_H_
+
+#if defined(__WITH_TLS__) || defined(__WITH_DTLS__)
+
+#include "mbedtls/pk.h"
+#include <time.h>
+
+/**
+ * Internal certificate request function; used by CSR resource handler
+ *
+ * @param[in] subject Subject to put into the CSR
+ * @param[in] keyPair Key pair for which to generate the CSR
+ * @param[in] encoding Encoding to use; valid values are OIC_ENCODING_PEM and OIC_ENCODING_DER
+ * @param[out] csr OCByteString containing the CSR in PEM. Caller takes ownership
+ * of allocated memory and must call OICFree on csr->bytes when finished.
+ * If encoding is OIC_ENCODING_DER, csr->len is the length of the encoded CSR.
+ * If encoding is OIC_ENCODING_PEM, csr->len is the length of the PEM string including the
+ * terminating NULL.
+ *
+ * @return 0 on success, <0 on failure
+ */
+int OCInternalCSRRequest(const char *subject, mbedtls_pk_context *keyPair,
+ OicEncodingType_t encoding, OCByteString *csr);
+
+/**
+ * Internal key pair generation function; used by CSR resource handler
+ *
+ * @param[in,out] keyPair Key object to generate; caller should already have called mbedtls_pk_init on it.
+ *
+ * @return 0 on success, <0 on failure
+ */
+int OCInternalGenerateKeyPair(mbedtls_pk_context *keyPair);
+
+#endif
+
+/**
+ * Determine if a buffer contains a role certificate, and optionally, return
+ * a DER encoding of the public key. This is intended as a simple check when the
+ * certificate is presented to reject obviously invalid certificates.
+ *
+ * The following properties are checked:
+ * 1. The certificate is a valid X.509 certificate.
+ * 2. It contains the role certificate Extended Key Usage.
+ * 3. It contains at least one Subject Alternative Name extension that validly encodes a role.
+ *
+ * It does NOT validate the cryptographic signature nor check its time validity.
+ * These checks should be done when the certificate is being used as part of an access control check,
+ * as that is when the time validity check should be made, and when trusted CAs are known.
+ *
+ * @param[in] buf Buffer containing certificate as a PEM string
+ * @param[in] bufLen Length of buffer including terminating NULL
+ * @param[out] pubKey Optional pointer to receive buffer containing binary DER encoding of public key.
+ * Caller must free with OICFree when finished. Pass NULL in to not retrieve the
+ * public key.
+ * @param[out] pubKeyLen Pointer to variable to receive size of pubKey. Ignored if pubKey is NULL.
+ *
+ * @return OC_STACK_OK if certificate satisfies the properties above, and the public key is
+ * successfully extracted (if requested).
+ * OC_STACK_INVALID_PARAM if the certificate does not meet the properties above.
+ * OC_STACK_NO_MEMORY or OC_STACK_ERROR if some other error arose during the check.
+ */
+OCStackResult OCInternalIsValidRoleCertificate(const uint8_t *buf, size_t bufLen,
+ uint8_t **pubKey, size_t *pubKeyLen);
+
+/**
+ * Determine if a buffer contains a valid chain of certificates. This is intended to verify
+ * one or more intermediate CA certificates are valid.
+ *
+ * This only checks that they are valid X.509 structures; no verification of the cryptographic
+ * signature of time-validity is performed. These should be done at point of use.
+ *
+ * @param[in] buf Buffer containing certificates as a PEM string
+ * @param[in] bufLen Length of buffer including terminating NULL
+ *
+ * @return OC_STACK_OK if buf contains a valid chain of certificates
+ * OC_STACK_INVALID_PARAM if buf cannot be parsed as a chain of certificates
+ * OC_STACK_ERROR if some other error arose during the check
+ */
+OCStackResult OCInternalIsValidCertChain(const uint8_t *buf, size_t bufLen);
+
+/**
+ * Verify the validity of a role certificate chain against a set of trusted certificate
+ * authorities. If successful, a list of roles authorized by this certificate is returned.
+ *
+ * The certificate's validity is also verified against the current time.
+ *
+ * The GetPemCaCert function from credresource.h can be used to retrieve the set of trusted CAs
+ * from the /oic/sec/cred resource in a form suitable for the trustedCaCerts and trustedCaCertsLength
+ * parameters.
+ *
+ * @param[in] certificate OicSecKey_t containing the leaf certificate
+ * @param[in] optData Optional OicSecOpt_t containing intermediate CAs and revocation status
+ * @param[in] trustedCaCerts PEM string containing the trusted CAs certificates
+ * @param[in] trustedCaCertsLength Length of trustedCaCerts (including terminating NULL)
+ * @param[out] roles Pointer to receive array of OicSecRole_t objects listing roles
+ * Caller must call OICFree to release this memory when finished
+ * @param[out] rolesLength Length of returned roles array
+ * @param[out] notValidAfter struct tm to receive notValidAfter time of the certificate
+ *
+ * @return OC_STACK_OK if the certificate is valid.
+ * OC_STACK_INVALID_PARAM if the certificate is not valid.
+ * OC_STACK_NO_MEMORY or OC_STACK_ERROR if some other error arose during validation.
+ */
+OCStackResult OCInternalVerifyRoleCertificate(const OicSecKey_t *certificate, const OicSecOpt_t *optData,
+ const uint8_t *trustedCaCerts, size_t trustedCaCertsLength,
+ OicSecRole_t **roles, size_t *rolesLength,
+ struct tm *notValidAfter);
+#endif
#include "cainterface.h"
#include "securevirtualresourcetypes.h"
#include "octypes.h"
+#include <cbor.h>
#ifdef __cplusplus
extern "C" {
#if defined(__WITH_TLS__) || defined(__WITH_DTLS__)
/**
- * Used by mbedTLS to retrieve trusted CA certificates
+ * Used by role certificate validator to get CA certificates as PEM
+ *
+ * @param[out] crt certificates to be filled.
+ * @param[in] usage credential usage string.
+ */
+OCStackResult GetPemCaCert(ByteArray_t * crt, const char * usage);
+/**
+ * Used by mbedTLS to retrieve trusted CA certificates as DER
*
* @param[out] crt certificates to be filled.
* @param[in] usage credential usage string.
*/
void InitCipherSuiteListInternal(bool *list, const char * usage);
#endif // __WITH_TLS__
+
+// Helpers shared by cred and roles resources
+CborError SerializeEncodingToCbor(CborEncoder *rootMap, const char *tag, const OicSecKey_t *value);
+CborError SerializeSecOptToCbor(CborEncoder *rootMap, const char *tag, const OicSecOpt_t *value);
+CborError DeserializeEncodingFromCbor(CborValue *rootMap, OicSecKey_t *value);
+CborError DeserializeSecOptFromCbor(CborValue *rootMap, OicSecOpt_t *value);
+
#ifdef __cplusplus
}
#endif
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#ifndef IOTVT_SRM_CSRR_H
+#define IOTVT_SRM_CSRR_H
+
+#include "cainterface.h"
+#include "securevirtualresourcetypes.h"
+#include "octypes.h"
+
+#if defined(__WITH_TLS__) || defined(__WITH_DTLS__)
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * Initialize the CSR resource.
+ *
+ * @return OC_STACK_OK if successful; error otherwise.
+ */
+OCStackResult InitCSRResource();
+
+/**
+ * De-initialize the CSR resource.
+ *
+ * @return OC_STACK_OK if successful; error otherwise.
+ */
+OCStackResult DeInitCSRResource();
+
+/**
+ * This function converts a CBOR payload into a CSR.
+ * Caller needs to call 'OICFree' on *csr after use.
+ *
+ * @param[in] cborPayload Received CBOR payload to extract the CSR from
+ * @param[in] size Size of cborPayload
+ * @param[out] csr Buffer containing the CSR
+ * @param[out] csrLen Length of csr
+ * @param[out] encoding Encoding of CSR (OIC_ENCODING_PEM or OIC_ENCODING_DER)
+ */
+OCStackResult CBORPayloadToCSR(const uint8_t *cborPayload, size_t size,
+ uint8_t **csr, size_t *csrLen,
+ OicEncodingType_t *encoding);
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+#endif
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#ifndef IOTVT_SRM_ROLESR_H
+#define IOTVT_SRM_ROLESR_H
+
+#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
+
+#include "cainterface.h"
+#include "securevirtualresourcetypes.h"
+#include "octypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct RoleCertChain {
+ uint32_t credId; /**< locally assigned ID number for use with DELETE */
+ OicSecKey_t certificate; /**< leaf certificate data */
+ OicSecOpt_t optData; /**< intermediate CA certificates */
+
+ struct RoleCertChain *next; /**< next chain in list */
+} RoleCertChain_t;
+
+/**
+ * Initialize the roles resource.
+ *
+ * @return OC_STACK_OK if successful; error otherwise
+ */
+OCStackResult InitRolesResource();
+
+/**
+ * De-initialize the roles resource.
+ *
+ * @return OC_STACK_OK if successful; error otherwise
+ */
+OCStackResult DeInitRolesResource();
+
+/**
+ * Retrieve the roles asserted by a given endpoint with certificates.
+ *
+ * @param[in] endpoint Endpoint to retrieve roles for
+ * @param[out] roles Pointer to receive array of OicSecRole_t objects containing roles for this endpoint
+ * On success, caller must free the received array with OICFree when finished
+ * @param[out] roleCount Variable to receive length of roles array.
+ *
+ * @note If the endpoint is found but has not asserted any roles with certificates,
+ * OC_STACK_OK will be returned, but NULL will be returned in roles and 0 in roleCount.
+ *
+ * @return OC_STACK_OK if list of roles is successfully populated; error otherwise.
+ */
+OCStackResult GetEndpointRoles(const CAEndpoint_t *endpoint, OicSecRole_t **roles, size_t *roleCount);
+
+/**
+ * This function converts a CBOR payload into a list of role certificates.
+ * Caller needs to call 'OICFree' on *roleCertList after use.
+ *
+ * @param[in] cborPayload Received CBOR payload to extract the role cert list from
+ * @param[in] size Size of cborPayload
+ * @param[out] roleCertList Pointer to receive linked list of RoleCertChain_t objects
+ * On success, caller must call FreeRoleCertChainList on *roleCertList when finished
+ * @return OC_STACK_OK if payload is successfully converted; error code otherwise
+ */
+OCStackResult CBORPayloadToRoles(const uint8_t *cborPayload, size_t size, RoleCertChain_t **roleCertList);
+
+/**
+ * Free the memory used by a list of RoleCertChain_t objects created by CBORPayloadToRoles.
+ *
+ * @param[in] roleCertList List received from CBORPayloadToRoles
+ */
+void FreeRoleCertChainList(RoleCertChain_t *roleCertList);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* defined(__WITH_DTLS__) || defined(__WITH_TLS__) */
+
+#endif /* IOTVT_SRM_ROLESR_H */
extern const char * OIC_JSON_ACLIST_NAME;
extern const char * OIC_JSON_ACES_NAME;
+extern const char * OIC_RSRC_TYPE_SEC_ACL2;
+extern const char * OIC_RSRC_ACL2_URI;
+extern const char * OIC_JSON_ACL2_NAME;
+
//PSTAT
extern const char * OIC_RSRC_TYPE_SEC_PSTAT;
extern const char * OIC_RSRC_PSTAT_URI;
extern const char * OIC_JSON_CRED_NAME;
extern const char * OIC_JSON_CREDS_NAME;
+//csr
+extern const char * OIC_RSRC_TYPE_SEC_CSR;
+extern const char * OIC_RSRC_CSR_URI;
+extern const char * OIC_JSON_CSR_NAME;
+
+//roles
+extern const char * OIC_RSRC_TYPE_SEC_ROLES;
+extern const char * OIC_RSRC_ROLES_URI;
+extern const char * OIC_JSON_ROLES_NAME;
+
//CRL
extern const char * OIC_RSRC_TYPE_SEC_CRL;
extern const char * OIC_RSRC_CRL_URI;
//reset profile
extern const char * OIC_JSON_RESET_PF_NAME;
-
extern const char * OIC_JSON_SUBJECTID_NAME;
extern const char * OIC_JSON_RESOURCES_NAME;
extern const char * OIC_JSON_AMSS_NAME;
extern const char * OIC_JSON_DEVICE_ID_FORMAT_NAME;
extern const char * OIC_JSON_CREDID_NAME;
extern const char * OIC_JSON_ROLEIDS_NAME;
+extern const char * OIC_JSON_AUTHORITY_NAME;
extern const char * OIC_JSON_CREDTYPE_NAME;
extern const char * OIC_JSON_PUBLICDATA_NAME;
extern const char * OIC_JSON_PRIVATEDATA_NAME;
extern const char * OIC_JSON_EMPTY_STRING;
-// Certificates provided by Cloud
+// Certificates provided by Cloud or OBT/CMS
extern const char * TRUST_CA;
extern const char * PRIMARY_CERT;
+extern const char * PRIMARY_KEY;
// Certificates provided by manufacturer
extern const char * MF_TRUST_CA;
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#if defined(__WITH_TLS__) || defined(__WITH_DTLS__)
+
+#ifndef OCCERTUTILITY_H_
+#define OCCERTUTILITY_H_
+
+#include "securevirtualresourcetypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Generate a random serial number suitable for use with a certificate.
+ * Callers must record used serial numbers and ensure serial numbers do not
+ * get repeated!
+ *
+ * @param[OUT] serial Pointer to a string that will be set to the location of the ASCII serial number.
+ * Memory for this buffer will be allocated internally; caller must call OICFree on serial
+ * when finished to free its memory.
+ * @param[OUT] serialLen Variable to receive size of serial, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if generated successfully; error otherwise.
+ */
+OCStackResult OCGenerateRandomSerialNumber(char **serial, size_t *serialLen);
+
+/**
+ * Generate a new NIST P-256 elliptic curve key pair and return the public and private
+ * keys as PEM encoded strings.
+ *
+ * @param[OUT] publicKey Pointer to receive PEM encoded public key. Memory is allocated internally
+ * and must be freed by caller with OICFree.
+ * @param[OUT] publicKeyLen Variable to receive length of publicKey, which will include terminating NULL.
+ * @param[OUT] privateKey Pointer to receive PEM encoded private key. Memory is allocated internally
+ * and, when finished, caller should first call OICClearMemory to securely erase
+ * its contents and then freed with OICFree. Caller is responsible for ensuring
+ * this key remains protected and secret!
+ * @param[OUT] privateKeyLen Variable to receive length of privateKey, which will include terminating NULL.
+ */
+OCStackResult OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen,
+ char **privateKey, size_t *privateKeyLen);
+
+/**
+ * Generate a certificate to act as a Certificate Authority (CA).
+ *
+ * @param subject Comma-separated list of RDN types and values:
+ * e.g. "C=US, O=Open Connectivity Foundation, CN=Main CA"
+ * @param subjectPublicKey Subject's public key in PEM format
+ * @param issuerCert Issuer's certificate in PEM format
+ * If this certificate will be self-signed, pass in NULL.
+ * @param issuerPrivateKey Issuer's private key in PEM format
+ * If self-signing (issuerCert is NULL), this must be the private
+ * key corresponding to subjectPublicKey.
+ * @param serial String containing the serial number in ASCII numerals:
+ * e.g., "12345". Caller must ensure each certificate issued by a
+ * CA has a unique serial number, and it is recommended to generate
+ * them randomly by producing 19 random bytes and converting them to
+ * a numerical value. See GenerateRandomSerialNumber().
+ * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20131231235959" for December 31st 2013 at 23:59:59
+ * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20140101010203" for January 1st 2014 at 01:02:03
+ * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be
+ * allocated internally; caller must call OICFree on certificate when finished to free
+ * its memory.
+ * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if successful, error code otherwise
+ */
+OCStackResult OCGenerateCACertificate(
+ const char *subject,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen);
+
+/**
+ * Generate a certificate for a device's identity.
+ *
+ * @param subjectUuid UUID for the device to use the certificate.
+ * @param subjectPublicKey Subject's public key in PEM format
+ * @param issuerCert Issuer's certificate in PEM format
+ * @param issuerPrivateKey Issuer's private key in PEM format
+ * @param serial String containing the serial number in ASCII numerals:
+ * e.g., "12345". Caller must ensure each certificate issued by a
+ * CA has a unique serial number, and it is recommended to generate
+ * them randomly by producing 19 random bytes and converting them to
+ * a numerical value. See GenerateRandomSerialNumber().
+ * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20131231235959" for December 31st 2013 at 23:59:59
+ * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20140101010203" for January 1st 2014 at 01:02:03
+ * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be
+ * allocated internally; caller must call OICFree on certificate when finished to free
+ * its memory.
+ * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if successful, error code otherwise
+ */
+OCStackResult OCGenerateIdentityCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen);
+
+/**
+ * Generate a certificate for a device's role.
+ *
+ * @param subjectUuid UUID for the device to use the certificate.
+ * @param subjectPublicKey Subject's public key in PEM format
+ * @param issuerCert Issuer's certificate in PEM format
+ * @param issuerPrivateKey Issuer's private key in PEM format
+ * @param serial String containing the serial number in ASCII numerals:
+ * e.g., "12345". Caller must ensure each certificate issued by a
+ * CA has a unique serial number, and it is recommended to generate
+ * them randomly by producing 19 random bytes and converting them to
+ * a numerical value. See GenerateRandomSerialNumber().
+ * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20131231235959" for December 31st 2013 at 23:59:59
+ * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20140101010203" for January 1st 2014 at 01:02:03
+ * @param role The role to encode in the certificate.
+ * @param authority The role authority to encode in the certificate. This field is optional;
+ * to omit, use NULL, and authority is construed to be the certificate issuer.
+ * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be
+ * allocated internally; caller must call OICFree on certificate when finished to free
+ * its memory.
+ * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if successful, error code otherwise
+ */
+OCStackResult OCGenerateRoleCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ const char *role,
+ const char *authority,
+ char **certificate,
+ size_t *certificateLen);
+
+/**
+ * Extract a UUID from a CSR.
+ *
+ * @param[in] csr The CSR containing the UUID as null-terminated PEM.
+ * @param[out] uuid The UUID in the CSR
+ *
+ * @return 0 on success, nonzero otherwise
+ */
+OCStackResult OCGetUuidFromCSR(const char* csr, OicUuid_t* uuid);
+
+/**
+ * Extract a public key from a CSR.
+ *
+ * @param[in] csr The CSR containing the public key, as null-terminated PEM.
+ * @param[out] publicKey The public key is output here as null-terminated PEM.
+ * Callers must call OICFree when finished.
+ *
+ * @return 0 on success, nonzero otherwise
+ */
+OCStackResult OCGetPublicKeyFromCSR(const char* csr, char** publicKey);
+
+/**
+ * Verify the signature in a CSR is valid.
+ *
+ * @param[in] csr The CSR to check, as null-terminated PEM.
+ *
+ * @returns 0 on success, nonzero otherwise
+ *
+ * @remark Requires that ECDSA with SHA-256 be used for the signature.
+ */
+OCStackResult OCVerifyCSRSignature(const char* csr);
+
+/**
+ * Convert a CSR from DER encoding to PEM encoding.
+ *
+ * @param[in] derCSR The CSR to convert, encoded as DER
+ * @param[in] derCSRLen Then number of bytes in derCSR
+ * @param[out] pemCSR The output, PEM encoded, null-terminated CSR. Callers
+ * call OICFree when finished.
+ *
+ * @returns 0 on success, nonzero otherwise
+*/
+OCStackResult OCConvertDerCSRToPem(const char* derCSR, size_t derCSRLen, char** pemCSR);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OCCERTUTILITY_H_ */
+
+#endif /* defined(__WITH_TLS__) || defined(__WITH_DTLS__) */
\ No newline at end of file
OIC_R_AMACL_TYPE,
OIC_R_CRED_TYPE,
OIC_R_CRL_TYPE,
+ OIC_R_CSR_TYPE,
OIC_R_DOXM_TYPE,
OIC_R_DPAIRING_TYPE,
OIC_R_PCONF_TYPE,
OIC_R_PSTAT_TYPE,
+ OIC_R_ROLES_TYPE,
OIC_R_SACL_TYPE,
OIC_R_SVC_TYPE,
OIC_SEC_SVR_TYPE_COUNT, //define the value to number of SVR
*/
#define UUID_LENGTH 128/8 // 128-bit GUID length
//TODO: Confirm the length and type of ROLEID.
-#define ROLEID_LENGTH 128/8 // 128-bit ROLEID length
+#define ROLEID_LENGTH 64 // 64-byte authority max length
+#define ROLEAUTHORITY_LENGTH 64 // 64-byte authority max length
#define OWNER_PSK_LENGTH_128 128/8 //byte size of 128-bit key size
#define OWNER_PSK_LENGTH_256 256/8 //byte size of 256-bit key size
OicSecValidity_t *next;
};
+typedef enum
+{
+ OIC_SEC_ACL_UNKNOWN = 0,
+ OIC_SEC_ACL_V1 = 1,
+ OIC_SEC_ACL_V2 = 2
+} OicSecAclVersion_t;
+
+#define OIC_SEC_ACL_LATEST OIC_SEC_ACL_V2
+
+typedef enum
+{
+ OicSecAceUuidSubject = 0, /* Default to this type. */
+ OicSecAceRoleSubject
+} OicSecAceSubjectType;
+
+/**
+ * /oic/sec/role (Role) data type.
+ * Derived from OIC Security Spec; see Spec for details.
+ */
+struct OicSecRole
+{
+ // <Attribute ID>:<Read/Write>:<Multiple/Single>:<Mandatory?>:<Type>
+ char id[ROLEID_LENGTH]; // 0:R:S:Y:String
+ char authority[ROLEAUTHORITY_LENGTH]; // 1:R:S:N:String
+};
+
struct OicSecAce
{
// <Attribute ID>:<Read/Write>:<Multiple/Single>:<Mandatory?>:<Type>
- OicUuid_t subjectuuid; // 0:R:S:Y:uuid
+ OicSecAceSubjectType subjectType;
+ union // 0:R:S:Y:{roletype|didtype|"*"}
+ {
+ OicUuid_t subjectuuid; // Only valid for subjectType == OicSecAceUuidSubject
+ OicSecRole_t subjectRole; // Only valid for subjectType == OicSecAceRoleSubject
+ };
OicSecRsrc_t *resources; // 1:R:M:Y:Resource
uint16_t permission; // 2:R:S:Y:UINT16
OicSecValidity_t *validities; // 3:R:M:N:Time-interval
};
/**
- * /oic/sec/role (Role) data type.
- * Derived from OIC Security Spec; see Spec for details.
- */
-struct OicSecRole
-{
- // <Attribute ID>:<Read/Write>:<Multiple/Single>:<Mandatory?>:<Type>
- //TODO fill in with Role definition
- uint8_t id[ROLEID_LENGTH];
-};
-
-/**
* /oic/sec/sacl (Signed Access Control List) data type.
* Derived from OIC Security Spec; see Spec for details.
*/
/**
* API to send ACL information to resource.
*
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
* @param[in] selectedDeviceInfo Selected target device.
+ * @param[in] aclVersion Version of the ACL resource to access
* @param[in] acl ACL to provision.
* @param[in] resultCallback callback provided by API user, callback will be called when
* provisioning request recieves a response from resource server.
* @return OC_STACK_OK in case of success and other value otherwise.
*/
OCStackResult SRPProvisionACL(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
- OicSecAcl_t *acl, OCProvisionResultCB resultCallback);
+ OicSecAcl_t *acl, OicSecAclVersion_t aclVersion, OCProvisionResultCB resultCallback);
/**
* API to save ACL which has several ACE into Acl of SVR.
/**
* API to request CRED information to resource.
*
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
* @param[in] selectedDeviceInfo Selected target device.
* @param[in] resultCallback callback provided by API user, callback will be called when
* provisioning request recieves a response from resource server.
/**
* API to request ACL information to resource.
*
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
* @param[in] selectedDeviceInfo Selected target device.
+ * @param[in] aclVersion Version of ACL resource to query
* @param[in] resultCallback callback provided by API user, callback will be called when
* provisioning request recieves a response from resource server.
* @return OC_STACK_OK in case of success and other value otherwise.
*/
OCStackResult SRPGetACLResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
- OCProvisionResultCB resultCallback);
+ OicSecAclVersion_t aclVersion, OCProvisionResultCB resultCallback);
+
+/**
+ * API to request the Certificate Signing Request (CSR) resource.
+ *
+ * @param[in] ctx Application context to be returned in result callback.
+ * @param[in] selectedDeviceInfo Selected target device.
+ * @param[in] resultCallback callback provided by API user, callback will be called when
+ * provisioning request recieves a response from resource server.
+ * @return OC_STACK_OK in case of success and other value otherwise.
+ */
+OCStackResult SRPGetCSRResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCGetCSRResultCB resultCallback);
+
+/**
+ * API to request the Roles resource.
+ *
+ * @param[in] ctx Application context to be returned in result callback.
+ * @param[in] selectedDeviceInfo Selected target device.
+ * @param[in] resultCallback Callback provided by API user. Callback will be called when
+ * provisioning request receives a response from resource server.
+ * @return OC_STACK_OK in case of success or error value otherwise.
+ */
+OCStackResult SRPGetRolesResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCGetRolesResultCB resultCallback);
+
+/**
+ * This function requests the device delete a particular role certificate by credId.
+ *
+ * @param[in] ctx Application context that is returned in the result callback.
+ * @param[in] selectedDeviceInfo Selected target device.
+ * @param[in] resultCallback callback provided by the API user. Callback will be called when request receives
+ * a response from the resource server.
+ * @param[in] credId credId to request be deleted.
+ *
+ * @return OC_STACK_OK in case of success, and error value otherwise.
+ */
+OCStackResult SRPDeleteRoleCertificateByCredId(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCProvisionResultCB resultCallback, uint32_t credId);
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
/**
* function to provision Trust certificate chain to devices.
*
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
* @param[in] type Type of credentials to be provisioned to the device.
* @param[in] credId CredId of trust certificate chain to be provisioned to the device.
* @param[in] selectedDeviceInfo Pointer to OCProvisionDev_t instance,respresenting resource to be provsioned.
/**
* API to send Direct-Pairing Configuration to a device.
*
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
* @param[in] selectedDeviceInfo Selected target device.
* @param[in] pconf PCONF pointer.
* @param[in] resultCallback callback provided by API user, callback will be called when
/**
* API to provision credential to devices.
*
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
* @param[in] type Type of credentials to be provisioned to the device.
* @param[in] keySize size of key
- * @param[in] pDev1 Pointer to PMOwnedDeviceInfo_t instance,respresenting resource to be provsioned.
- @param[in] pDev2 Pointer to PMOwnedDeviceInfo_t instance,respresenting resource to be provsioned.
+ * @param[in] pDev1 Pointer to PMOwnedDeviceInfo_t instance, respresenting resource to be provsioned.
+ * @param[in] pDev2 Pointer to PMOwnedDeviceInfo_t instance, respresenting resource to be provsioned.
+ * @param[in] pemCert When provisioning a certificate (type is SIGNED_ASYMMETRIC_KEY), this is the
+ * certificate, encoded as PEM.
* @param[in] resultCallback callback provided by API user, callback will be called when
* provisioning request recieves a response from first resource server.
* @return OC_STACK_OK in case of success and other value otherwise.
OCStackResult SRPProvisionCredentials(void *ctx,OicSecCredType_t type, size_t keySize,
const OCProvisionDev_t *pDev1,
const OCProvisionDev_t *pDev2,
+ const char* pemCert,
OCProvisionResultCB resultCallback);
/**
* Function to unlink devices.
* This function will remove the credential & relationship between the two devices.
*
- * @param[in] ctx Application context would be returned in result callback
+ * @param[in] ctx Application context to be returned in result callback
* @param[in] pTargetDev1 first device information to be unlinked.
* @param[in] pTargetDev2 second device information to be unlinked.
* @param[in] resultCallback callback provided by API user, callback will be called when
* Function to device revocation.
* This function will remove credential of target device from all devices in subnet.
*
- * @param[in] ctx Application context would be returned in result callback
+ * @param[in] ctx Application context to be returned in result callback
* @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)
* @param[in] pTargetDev Device information to be revoked.
* @param[in] resultCallback callback provided by API user, callback will be called when
OCProvisionResultCB resultCallback);
/**
-* Function to device revocation
-* This function will remove credential of target device from all devices in subnet.
-*
-* @param[in] ctx Application context would be returned in result callback
-* @param[in] pOwnedDevList List of owned devices
-* @param[in] pTargetDev Device information to be revoked.
-* @param[in] resultCallback callback provided by API user, callback will be called when
-* credential revocation is finished.
-* @return OC_STACK_OK in case of success and other value otherwise.
-* If OC_STACK_OK is returned, the caller of this API should wait for callback.
-* OC_STACK_CONTINUE means operation is success but no request is need to be initiated.
-*/
+ * Function to device revocation
+ * This function will remove credential of target device from all devices in subnet.
+ *
+ * @param[in] ctx Application context to be returned in result callback
+ * @param[in] pOwnedDevList List of owned devices
+ * @param[in] pTargetDev Device information to be revoked.
+ * @param[in] resultCallback callback provided by API user, callback will be called when
+ * credential revocation is finished.
+ * @return OC_STACK_OK in case of success and other value otherwise.
+ * If OC_STACK_OK is returned, the caller of this API should wait for callback.
+ * OC_STACK_CONTINUE means operation is success but no request is need to be initiated.
+ */
OCStackResult SRPRemoveDeviceWithoutDiscovery(void* ctx, const OCProvisionDev_t* pOwnedDevList,
const OCProvisionDev_t* pTargetDev, OCProvisionResultCB resultCallback);
* Function to sync-up credential and ACL of the target device.
* This function will remove credential and ACL of target device from all devices in subnet.
*
- * @param[in] ctx Application context would be returned in result callback
+ * @param[in] ctx Application context to be returned in result callback
* @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)
* @param[in] pTargetDev Device information to be revoked.
* @param[in] resultCallback callback provided by API user, callback will be called when
/**\r
* API to provision credentials between two devices and ACLs for the devices who act as a server.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] type Type of credentials to be provisioned to the device.\r
* @param[in] keySize size of key
* @param[in] pDev1 Pointer to OCProvisionDev_t instance,respresenting device to be provisioned.\r
OCProvisionResultCB resultCallback);\r
\r
/**\r
- * API to send ACL information to device.\r
+ * API to send version 1 ACL information to device.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] selectedDeviceInfo Selected target device.\r
* @param[in] acl ACL to provision.\r
* @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
OCProvisionResultCB resultCallback);\r
\r
/**\r
+ * API to send version 2 ACL information to device.\r
+ *\r
+ * @param[in] ctx Application context returned in the result callback.\r
+ * @param[in] selectedDeviceInfo Selected target device.\r
+ * @param[in] acl ACL to provision.\r
+ * @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
+ request recieves a response from resource server.\r
+ * @return OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCProvisionACL2(void *ctx, const OCProvisionDev_t *selectedDeviceInfo, OicSecAcl_t *acl,
+ OCProvisionResultCB resultCallback);\r
+\r
+/**\r
* function to save ACL which has several ACE into Acl of SVR.\r
*\r
* @param acl ACL to be saved in Acl of SVR.\r
/**\r
* this function requests CRED information to resource.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] selectedDeviceInfo Selected target device.\r
* @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
request recieves a response from resource server.\r
OCProvisionResultCB resultCallback);\r
\r
/**\r
- * this function requests ACL information to resource.\r
+ * this function requests version 1 ACL information to resource.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] selectedDeviceInfo Selected target device.\r
* @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
request recieves a response from resource server.\r
OCProvisionResultCB resultCallback);\r
\r
/**\r
+ * this function requests version 2 ACL information to resource.\r
+ *\r
+ * @param[in] ctx Application context returned in the result callback.\r
+ * @param[in] selectedDeviceInfo Selected target device.\r
+ * @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
+ request recieves a response from resource server.\r
+ * @return OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCGetACL2Resource(void* ctx, const OCProvisionDev_t *selectedDeviceInfo,\r
+ OCProvisionResultCB resultCallback);\r
+\r
+/**\r
+ * This function requests the device provide its Certificate Signing Request (CSR).\r
+ *\r
+ * @param[in] ctx Application context returned in the result callback.\r
+ * @param[in] selectedDeviceInfo Selected target device.\r
+ * @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
+ request recieves a response from resource server.\r
+ * @return OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCGetCSRResource(void* ctx, const OCProvisionDev_t *selectedDeviceInfo,\r
+ OCGetCSRResultCB resultCallback);\r
+\r
+/**\r
+ * This function requests the device provide its roles resource, listing the role certificates\r
+ * it has for the local requestor.\r
+ *\r
+ * @param[in] ctx Application context that is returned in the result callback.\r
+ * @param[in] selectedDeviceInfo Selected target device.\r
+ * @param[in] resultCallback callback provided by the API user. Callback will be called when provisioning\r
+ * request receives a response from the resource server.\r
+ * @return OC_STACK_OK in case of success, and error value otherwise.\r
+ */\r
+OCStackResult OCGetRolesResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,\r
+ OCGetRolesResultCB resultCallback);\r
+\r
+/**\r
+ * This function requests the device delete a particular role certificate by credId.\r
+ *\r
+ * @param[in] ctx Application context that is returned in the result callback.\r
+ * @param[in] selectedDeviceInfo Selected target device.\r
+ * @param[in] resultCallback callback provided by the API user. Callback will be called when request receives\r
+ * a response from the resource server.\r
+ * @param[in] credId credId to request be deleted. If 0, delete all role certificates for this peer.\r
+ *\r
+ * @return OC_STACK_OK in case of success, and error value otherwise.\r
+ */\r
+OCStackResult OCDeleteRoleCertificateByCredId(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,\r
+ OCProvisionResultCB resultCallback, uint32_t credId);\r
+\r
+/**\r
* this function sends Direct-Pairing Configuration to a device.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] selectedDeviceInfo Selected target device.\r
* @param[in] pconf PCONF pointer.\r
* @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
/**\r
* API to provision credential to devices.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] type Type of credentials to be provisioned to the device.\r
* @param[in] keySize size of key
* @param[in] pDev1 Pointer to OCProvisionDev_t instance,respresenting resource to be provsioned.\r
const OCProvisionDev_t *pDev2,\r
OCProvisionResultCB resultCallback);\r
\r
+/**\r
+ * API to provision a certificate to a device.\r
+ *\r
+ * @param[in] ctx Application context returned in result callback.\r
+ * @param[in] pDev Pointer to OCProvisionDev_t instance, respresenting the device to be provsioned.\r
+ * @param[in] pemCert Certificate to provision, encoded as PEM\r
+ * @param[in] resultCallback callback provided by API user, callback will be called when\r
+ * provisioning request receives a response from first resource server.\r
+ * @return OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCProvisionCertificate(void *ctx,\r
+ const OCProvisionDev_t *pDev,\r
+ const char* pemCert,\r
+ OCProvisionResultCB resultCallback);\r
+\r
#ifdef MULTIPLE_OWNER\r
/**\r
* API to provision preconfigured PIN to device(NOT LIST).\r
* OCProvisionPreconfigPin API will update the device's Doxm\r
* and then try preconfigured PIN provisioning once again.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] targetDeviceInfo Selected target device.\r
* @param[in] preconfigPin string of preconfigured PIN.\r
* @param[in] preconfigPinLen string length of 'preconfigPin'.\r
OCProvisionResultCB resultCallback);\r
\r
/**\r
-* Function to device revocation\r
-* This function will remove credential of target device from all devices in subnet.\r
-*\r
-* @param[in] ctx Application context would be returned in result callback\r
-* @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)\r
-* @param[in] pTargetUuid Device information to be revoked.\r
-* @param[in] resultCallback callback provided by API user, callback will be called when\r
-* credential revocation is finished.\r
+ * Function to device revocation\r
+ * This function will remove credential of target device from all devices in subnet.\r
+ *\r
+ * @param[in] ctx Application context would be returned in result callback\r
+ * @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)\r
+ * @param[in] pTargetUuid Device information to be revoked.\r
+ * @param[in] resultCallback callback provided by API user, callback will be called when\r
+ * credential revocation is finished.\r
* @return OC_STACK_OK in case of success and other value otherwise.\r
-*/\r
+ */\r
OCStackResult OCRemoveDeviceWithUuid(void* ctx,\r
unsigned short waitTimeForOwnedDeviceDiscovery,\r
const OicUuid_t* pTargetUuid,\r
/**\r
* this function sends CRL information to resource.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] selectedDeviceInfo Selected target device.\r
* @param[in] crl CRL to provision.\r
* @param[in] resultCallback callback provided by API user, callback will be called when provisioning\r
/**\r
* function to provision Trust certificate chain to devices.\r
*\r
- * @param[in] ctx Application context would be returned in result callback.\r
+ * @param[in] ctx Application context returned in the result callback.\r
* @param[in] type Type of credentials to be provisioned to the device.\r
* @param[in] credId CredId of trust certificate chain to be provisioned to the device.\r
* @param[in] selectedDeviceInfo Pointer to OCProvisionDev_t instance,respresenting resource to be provsioned.\r
*/\r
OCStackResult OCSaveTrustCertChain(uint8_t *trustCertChain, size_t chainSize,\r
OicEncodingType_t encodingType, uint16_t *credId);\r
+\r
+/**\r
+ * Function to save an identity certificate chain into Cred of SVR.\r
+ *\r
+ * @param[in] cert Certificate chain to be saved in Cred of SVR, PEM encoded, null terminated\r
+ * @param[in] key private key corresponding to the certificate, PEM encoded, null terminated\r
+ * @param[out] credId CredId of saved certificate chain in Cred of SVR.\r
+ * @return OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCSaveOwnCertChain(char* cert, char* key, uint16_t *credId);\r
+\r
/**\r
* function to register callback, for getting notification for TrustCertChain change.\r
*\r
OCStackResult res;
}OCProvisionResult_t;
+typedef struct OCPMGetCsrResult
+{
+ OicUuid_t deviceId;
+ OCStackResult res;
+ uint8_t *csr;
+ size_t csrLen;
+ OicEncodingType_t encoding; /* Determines contents of csr; either OIC_ENCODING_DER or OIC_ENCODING_PEM */
+} OCPMGetCsrResult_t;
+
+typedef struct OCPMRoleCertChain
+{
+ uint64_t credId; /**< credential ID */
+ OicSecKey_t certificate; /**< leaf certificate */
+ OicSecOpt_t optData; /**< intermediate CA certificates (if any) */
+} OCPMRoleCertChain_t;
+
+typedef struct OCPMGetRolesResult
+{
+ OicUuid_t deviceId; /**< responding device ID */
+ OCStackResult res; /**< result for this device */
+ OCPMRoleCertChain_t *chains; /**< cert chains (if res is OC_STACK_OK) */
+ size_t chainsLength; /**< length of chains array (if res is OC_STACK_OK */
+} OCPMGetRolesResult_t;
+
/**
* Owner device type
*/
*/
typedef void (*OCProvisionResultCB)(void* ctx, size_t nOfRes, OCProvisionResult_t *arr, bool hasError);
+/**
+ * Callback function definition of CSR retrieve API
+ *
+ * @param[OUT] ctx - If user set a context, it will be returned here.
+ * @param[OUT] nOfRes - total number of results
+ * @param[OUT] arr - Array of OCPMGetCsrResult_t, containing one entry for each target device. If an entry's res
+ * member is OC_STACK_OK, then csr and csrLen are valid; otherwise they should not be used.
+ * This memory is only valid while the callback is executing; callers must make copies if the data
+ * needs to be kept longer.
+ * @param[OUT] hasError - If all calls succeded, this will be false. One or more errors, and this will
+ * be true. Examine the elements of arr to discover which failed.
+ */
+typedef void (*OCGetCSRResultCB)(void* ctx, size_t nOfRes, OCPMGetCsrResult_t *arr, bool hasError);
+
+/**
+ * Callback function definition of roles retrieve API
+ *
+ * @param[OUT] ctx - If user set a context, it will be returned here.
+ * @param[OUT] nOfRes - total number of results
+ * @param[OUT] arr - Array of OCPMGetRolesResult_t, containing one entry for each target device. If an entry's res
+ * member is OC_STACK_OK, then chains and chainsLength are valid; otherwise they should not be used.
+ * This memory is only valid while the callback is executing; callers must make copies if the data
+ * needs to be kept longer.
+ * @param[OUT] hasError - If all calls succeeded, this will be false. One or more errors, and this will
+ * be true. Examine the elements of arr to discover which failed.
+ */
+typedef void (*OCGetRolesResultCB)(void* ctx, size_t nOfRes, OCPMGetRolesResult_t *arr, bool hasError);
/**
* Callback function definition of direct-pairing
######################################################################
provisioningclient = provisioning_sample_env.Program('provisioningclient', 'provisioningclient.c')
+autoprovisioningclient = provisioning_sample_env.Program('autoprovisioningclient', 'autoprovisioningclient.c')
sampleserver_justworks = provisioning_sample_env.Program('sampleserver_justworks', 'sampleserver_justworks.cpp')
sampleserver_randompin = provisioning_sample_env.Program('sampleserver_randompin', 'sampleserver_randompin.cpp')
sampleserver_mfg = provisioning_sample_env.Program('sampleserver_mfg', 'sampleserver_mfg.cpp')
sampleserver_mvjustworks = provisioning_sample_env.Program('sampleserver_mvjustworks', 'sampleserver_mvjustworks.cpp')
+certgenerator = provisioning_sample_env.Program('certgenerator', 'certgenerator.cpp')
if provisioning_sample_env.get('MULTIPLE_OWNER') == '1':
subownerclient = provisioning_sample_env.Program('subownerclient', 'subownerclient.c')
if provisioning_sample_env.get('MULTIPLE_OWNER') == '1':
Alias("samples", [
- provisioningclient, subownerclient,
+ provisioningclient, subownerclient, autoprovisioningclient,
sampleserver_justworks, sampleserver_randompin, sampleserver_mfg,
sampleserver_preconfpin,
clientdat, subownerclientdat, preconfserverdat,
])
else:
Alias("samples", [
- provisioningclient,
+ provisioningclient, autoprovisioningclient,
sampleserver_justworks, sampleserver_randompin, sampleserver_mfg,
clientdat,
justworksdat, randompindat, mfgdat, randompin_with_emptyuuid_dat,
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2017 Microsoft. All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************/
+
+/*
+ * This file is derived from iotivity\resource\csdk\security\provisioning\sample\provisioningclient.c
+ * That program is interactive, and requires user input to perform provisioning
+ * steps, while this version takes input on the command line and executes a
+ * sequence of provisioning operations automatically, then exists, reporting
+ * whether the sequence succeeded. This is so that we can script provisioning
+ * for testing.
+ */
+
+#include "iotivity_config.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "payload_logging.h"
+#include "utlist.h"
+#include "logger.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "ocprovisioningmanager.h"
+#include "oxmjustworks.h"
+#include "oxmrandompin.h"
+#include "securevirtualresourcetypes.h"
+#include "srmutility.h"
+#include "pmtypes.h"
+#include "oxmverifycommon.h"
+#include "mbedtls/config.h"
+#include "mbedtls/pem.h"
+#include "mbedtls/x509_csr.h"
+#include "certhelpers.h"
+
+#ifdef _MSC_VER
+#include <io.h>
+
+#define F_OK 0
+#define access _access_s
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif //__cplusplus
+
+#define DISCOVERY_TIMEOUT 3 // 3 sec
+#define CALLBACK_TIMEOUT 60 // 1 min
+#define TAG "provisioningclient"
+
+static const char* SVR_DB_FILE_NAME = "oic_svr_db_client.dat";
+ // '_' for separaing from the same constant variable in |srmresourcestrings.c|
+static const char* PRVN_DB_FILE_NAME = "oic_autoprvn_mng.db";
+
+// |g_ctx| means provision manager application context and
+// the following, includes |un/own_list|, could be variables, which |g_ctx| has,
+// for accessing all function(s) for these, they are declared on global domain
+static char* g_ctx = "Provision Manager Client Application Context";
+static char* g_svr_fname;
+static char* g_prvn_fname;
+static OCProvisionDev_t* g_own_list;
+static OCProvisionDev_t* g_unown_list;
+static int g_own_cnt;
+static int g_unown_cnt;
+mbedtls_x509_csr g_csr;
+
+static volatile bool g_doneCB; /* Set to true by the callback to indicate it completed. */
+static bool g_successCB; /* Set to true by the callback to indicate success. */
+
+// function declaration(s) for calling them before implementing
+
+static OCProvisionDev_t* getDevInst(const OCProvisionDev_t*, const int);
+static int printDevList(const OCProvisionDev_t*);
+static size_t printUuidList(const OCUuidList_t*);
+static size_t printResultList(const OCProvisionResult_t*, const size_t);
+static void printUuid(const OicUuid_t*);
+static FILE* fopen_prvnMng(const char*, const char*);
+static int waitCallbackRet(void);
+
+/* At a few places in this file, warning 4028 is incorrectly produced, disable it for the whole file. */\r
+#ifdef _MSC_VER\r
+#pragma warning( disable : 4028)\r
+#endif
+
+// callback function(s) for provisioning client using C-level provisioning API
+static void ownershipTransferCB(void* ctx, size_t nOfRes, OCProvisionResult_t* arr, bool hasError)
+{
+ if(!hasError)
+ {
+ OIC_LOG_V(INFO, TAG, "Ownership Transfer SUCCEEDED - ctx: %s", (char*) ctx);
+ g_successCB = true;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Ownership Transfer FAILED - ctx: %s", (char*) ctx);
+ printResultList((const OCProvisionResult_t*) arr, nOfRes);
+ g_successCB = false;
+ }
+ g_doneCB = true;
+}
+
+static void provisionCredCB(void* ctx, size_t nOfRes, OCProvisionResult_t* arr, bool hasError)
+{
+ if(!hasError)
+ {
+ OIC_LOG_V(INFO, TAG, "Provision Credential SUCCEEDED - ctx: %s", (char*) ctx);
+ g_successCB = true;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Provision Credential FAILED - ctx: %s", (char*) ctx);
+ printResultList((const OCProvisionResult_t*) arr, nOfRes);
+ g_successCB = false;
+ }
+ g_doneCB = true;
+}
+
+/* Function of type OCProvisionResultCB from \resource\csdk\security\provisioning\include\pmtypes.h */
+void provisionTrustChainCB(void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
+{
+ if(!hasError)
+ {
+ OIC_LOG_V(INFO, TAG, "Provision Credential SUCCEEDED - ctx: %s", (char*)ctx);
+ g_successCB = true;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Provision Credential FAILED - ctx: %s", (char*)ctx);
+ printResultList((const OCProvisionResult_t*)arr, nOfRes);
+ g_successCB = false;
+ }
+ g_doneCB = true;
+}
+
+static void getCsrForCertProvCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool hasError)
+{
+ g_successCB = false;
+
+ if(!hasError)
+ {
+ if(nOfRes != 1)
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED - ctx: %s", (char*)ctx);
+ goto exit;
+ }
+
+ mbedtls_x509_csr_init(&g_csr);
+ int ret = mbedtls_x509_csr_parse(&g_csr, arr[0].csr, arr[0].csrLen);
+ if(ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+ mbedtls_x509_csr_free(&g_csr);
+
+ goto exit;
+ }
+
+ OIC_LOG(INFO, TAG, "getCsrForCertProvCB success");
+ g_successCB = true;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED - ctx: %s", (char*)ctx);
+ }
+
+exit:
+ g_doneCB = true;
+}
+
+// function(s) for provisioning client using C-level provisioning API
+static int initProvisionClient(void)
+{
+ // initialize persistent storage for SVR DB
+ static OCPersistentStorage pstStr =
+ {
+ .open = fopen_prvnMng,
+ .read = fread,
+ .write = fwrite,
+ .close = fclose,
+ .unlink = remove
+ };
+ if (OC_STACK_OK != OCRegisterPersistentStorageHandler(&pstStr))
+ {
+ OIC_LOG(ERROR, TAG, "OCRegisterPersistentStorageHandler error");
+ return -1;
+ }
+
+ // initialize OC stack and provisioning manager
+ if (OC_STACK_OK != OCInit(NULL, 0, OC_CLIENT_SERVER))
+ {
+ OIC_LOG(ERROR, TAG, "OCStack init error");
+ return -1;
+ }
+
+ if (access(PRVN_DB_FILE_NAME, F_OK) == 0)
+ {
+ printf("************************************************************\n");
+ printf("************Provisioning DB file already exists.************\n");
+ printf("************************************************************\n");
+ }
+ else
+ {
+ printf("*************************************************************\n");
+ printf("************No provisioning DB file, creating new************\n");
+ printf("*************************************************************\n");
+ }
+
+ if (OC_STACK_OK != OCInitPM(PRVN_DB_FILE_NAME))
+ {
+ OIC_LOG(ERROR, TAG, "OC_PM init error");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int discoverAllDevices(void)
+{
+ // delete un/owned device lists before updating them
+ if(g_own_list)
+ {
+ OCDeleteDiscoveredDevices(g_own_list);
+ g_own_list = NULL;
+ }
+ if(g_unown_list)
+ {
+ OCDeleteDiscoveredDevices(g_unown_list);
+ g_unown_list = NULL;
+ }
+
+ // call |OCGetDevInfoFromNetwork| API
+ printf(" Discovering All Un/Owned Devices on Network..\n");
+ if(OC_STACK_OK != OCGetDevInfoFromNetwork(DISCOVERY_TIMEOUT, &g_own_list, &g_unown_list))
+ {
+ OIC_LOG(ERROR, TAG, "OCGetDevInfoFromNetwork API error");
+ return -1;
+ }
+
+ // display the discovered un/owned lists
+ printf(" > Discovered Owned Devices\n");
+ g_own_cnt = printDevList(g_own_list);
+ printf(" > Discovered Unowned Devices\n");
+ g_unown_cnt = printDevList(g_unown_list);
+
+ return 0;
+}
+
+
+static int discoverUnownedDevices(void)
+{
+ // delete unowned device list before updating it
+ if(g_unown_list)
+ {
+ OCDeleteDiscoveredDevices(g_unown_list);
+ g_unown_list = NULL;
+ }
+
+ // call |OCDiscoverUnownedDevices| API
+ printf(" Discovering Only Unowned Devices on Network..\n");
+ if(OC_STACK_OK != OCDiscoverUnownedDevices(DISCOVERY_TIMEOUT, &g_unown_list))
+ {
+ OIC_LOG(ERROR, TAG, "OCDiscoverUnownedDevices API error");
+ return -1;
+ }
+
+ // display the discovered unowned list
+ printf(" > Discovered Unowned Devices\n");
+ g_unown_cnt = printDevList(g_unown_list);
+
+ return 0;
+}
+
+static int discoverOwnedDevices(void)
+{
+ // delete owned device list before updating it
+ if(g_own_list)
+ {
+ OCDeleteDiscoveredDevices(g_own_list);
+ g_own_list = NULL;
+ }
+
+ // call |OCDiscoverOwnedDevices| API
+ printf(" Discovering Only Owned Devices on Network..\n");
+ if(OC_STACK_OK != OCDiscoverOwnedDevices(DISCOVERY_TIMEOUT, &g_own_list))
+ {
+ OIC_LOG(ERROR, TAG, "OCDiscoverOwnedDevices API error");
+ return -1;
+ }
+
+ // display the discovered owned list
+ printf(" > Discovered Owned Devices\n");
+ g_own_cnt = printDevList(g_own_list);
+
+ return 0;
+}
+
+static int registerDevices(void)
+{
+ // check |unown_list| for registering devices
+ if(!g_unown_list || 0>=g_unown_cnt)
+ {
+ OIC_LOG(ERROR, TAG, "Unowned device list empty, must discover unowned devices first");
+ return -1; // Error, we should have registered unowned devices already
+ }
+
+ // call |OCDoOwnershipTransfer| API
+ // calling this API with callback actually acts like blocking
+ // for error checking, the return value saved and printed
+ g_doneCB = false;
+ printf(" Registering All Discovered Unowned Devices..\n");
+ OCStackResult rst = OCDoOwnershipTransfer((void*) g_ctx, g_unown_list, ownershipTransferCB);
+ if(OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCDoOwnershipTransfer API error: %d", rst);
+ return -1;
+ }
+ if(waitCallbackRet()) // input |g_doneCB| flag implicitly
+ {
+ OIC_LOG(ERROR, TAG, "OCDoOwnershipTransfer callback error");
+ return -1;
+ }
+
+ if(!g_successCB)
+ {
+ OIC_LOG(ERROR, TAG, "OCDoOwnershipTransfer callback failed");
+ return -1;
+ }
+
+ // display the registered result
+ printf(" > Registered Discovered Unowned Devices\n");
+ printf(" > Please Discover Owned Devices for the Registered Result, with [10|12] Menu\n");
+
+ return 0;
+}
+
+/*
+* Test CA key and certificate created with
+* iotivity/resource/csdk/security/scripts/test_cert_generation.sh
+*/
+const char* caCertPem = "-----BEGIN CERTIFICATE-----\n"
+"MIIBfzCCASSgAwIBAgIJAOMHgsOiVyXpMAoGCCqGSM49BAMCMBkxFzAVBgNVBAoM\n"
+"DklvVGl2aXR5VGVzdENBMB4XDTE3MDEwNjAwNDA1M1oXDTMwMDkxNTAwNDA1M1ow\n"
+"GTEXMBUGA1UECgwOSW9UaXZpdHlUZXN0Q0EwWTATBgcqhkjOPQIBBggqhkjOPQMB\n"
+"BwNCAAQMaZlQqz2C2DFRh3xcATSAyEIp9RIjYMU8/x6cZ0OTKA0ihZCDXmMksLk8\n"
+"zWhYI/Sr7wpYxBaQh5yvaiP5zFl2o1UwUzAhBgNVHSUEGjAYBgorBgEEAYLefAEG\n"
+"BgorBgEEAYLefAEHMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHnMVojc5HNr\n"
+"fuLOz3iNV3u3vG3kMAoGCCqGSM49BAMCA0kAMEYCIQC7+/pCT8hEupk3nhStU3pF\n"
+"QmJg6VCbcFKMY8MmqyBx2QIhAIwM63/OPXh9hpbRoVp5Z90KxUvbjZ/XstqULmYl\n"
+"LzEE\n"
+"-----END CERTIFICATE-----\n";
+
+const char* caKeyPem = "-----BEGIN EC PRIVATE KEY-----\n"
+"MHcCAQEEID8CoB1N6UCnGYZPAKWu376GtJxW7KlI2BXzS3ISQrEnoAoGCCqGSM49\n"
+"AwEHoUQDQgAEUjKmL4wS485e3FIl3s07qvN+Auw0ra6+/y2phVu/qA/D92rd+HJK\n"
+"Tnzf9FpTBRvWkiUh8rAWVuwoQxVkLmr9WA==\n"
+"-----END EC PRIVATE KEY-----\n";
+
+static int provisionTrustAnchor(int dev_num)
+{
+ // make sure we own at least one device to provision
+ if(!g_own_list || g_own_cnt == 0)
+ {
+ OIC_LOG(ERROR, TAG, "Unowned device list empty, must discover unowned devices first");
+ return -1; // Error, we should have registered unowned devices already
+ }
+
+ OCProvisionDev_t* targetDevice = getDevInst((const OCProvisionDev_t*)g_own_list, dev_num);
+ if(targetDevice == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Error, invalid device %d");
+ return -1;
+ }
+
+ // Install the CA trust anchor
+ uint16_t caCredId = 0;
+ OCStackResult rst = OCSaveTrustCertChain((uint8_t*)caCertPem, strlen(caCertPem) + 1,
+ OIC_ENCODING_PEM, &caCredId);
+ if (OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCSaveTrustCertChain error: %d", rst);
+ return -1;
+ }
+
+ // Provision the CA root cert to the target device
+ printf(" > Saving root certificate (trust anchor) to selected device..\n");
+ g_doneCB = false;
+ OicSecCredType_t type = SIGNED_ASYMMETRIC_KEY;
+
+ rst = OCProvisionTrustCertChain((void*)g_ctx, type, caCredId, targetDevice, &provisionTrustChainCB);
+ if(OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCProvisionTrustCertChain returned error: %d", rst);
+ return -1;
+ }
+
+ if(waitCallbackRet()) // input |g_doneCB| flag implicitly
+ {
+ OIC_LOG(ERROR, TAG, "OCProvisionTrustCertChain callback error");
+ return -1;
+ }
+ if(!g_successCB)
+ {
+ return -1;
+ }
+
+ printf(" > Provisioned certificate trust anchor\n");
+
+ return 0;
+
+}
+
+static int getCsr(int dev_num)
+{
+ // check |own_list| for retrieving CSR
+ if(!g_own_list || 1 > g_own_cnt)
+ {
+ OIC_LOG(ERROR, TAG, "Unowned device list empty, must discover unowned devices first");
+ return -1; // Error, we should have registered unowned devices already
+ }
+
+ // call |getDevInst| API
+ // calling this API with callback actually acts like blocking
+ // for error checking, the return value saved and printed
+ g_doneCB = false;
+ OCProvisionDev_t* dev = getDevInst((const OCProvisionDev_t*) g_own_list, dev_num);
+ if(!dev)
+ {
+ OIC_LOG(ERROR, TAG, "getDevInst: device instance empty");
+ goto GETCSR_ERROR;
+ }
+ OCStackResult rst = OCGetCSRResource((void*) g_ctx, dev, getCsrForCertProvCB);
+ if(OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGetCSRResource API error: %d", rst);
+
+ goto GETCSR_ERROR;
+ }
+ if(waitCallbackRet()) // input |g_doneCB| flag implicitly
+ {
+ OIC_LOG(ERROR, TAG, "OCGetCSRResource callback error");
+ goto GETCSR_ERROR;
+ }
+ if(!g_successCB)
+ {
+ return -1;
+ }
+
+ /* @todo: once change https://gerrit.iotivity.org/gerrit/#/c/17509 merges, we'll have this function */
+#if 0
+ int ret = OCInternalVerifyCSRSignature(&g_csr);
+ if(ret != 0)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to validate CSR signature");
+ mbedtls_x509_csr_free(&g_csr);
+ return -1;
+ }
+#endif
+ mbedtls_x509_csr_free(&g_csr);
+
+ printf(" > Get CSR SUCCEEDED\n");
+
+ return 0;
+
+GETCSR_ERROR:
+ printf(" > Get CSR FAILED\n");
+ return -1;
+}
+
+static OCProvisionDev_t* getDevInst(const OCProvisionDev_t* dev_lst, const int dev_num)
+{
+ if (!dev_lst || 0 >= dev_num)
+ {
+ printf(" Device List is Empty..\n");
+ return NULL;
+ }
+
+ OCProvisionDev_t* lst = (OCProvisionDev_t*)dev_lst;
+ for (int i = 0; lst; )
+ {
+ if (dev_num == ++i)
+ {
+ return lst;
+ }
+ lst = lst->next;
+ }
+
+ return NULL; // in here |lst| is always |NULL|
+}
+
+static int printDevList(const OCProvisionDev_t* dev_lst)
+{
+ if (!dev_lst)
+ {
+ printf(" Device List is Empty..\n\n");
+ return 0;
+ }
+
+ OCProvisionDev_t* lst = (OCProvisionDev_t*)dev_lst;
+ int lst_cnt = 0;
+ for (; lst; )
+ {
+ printf(" [%d] ", ++lst_cnt);
+ printUuid((const OicUuid_t*)&lst->doxm->deviceID);
+ printf("\n");
+ lst = lst->next;
+ }
+ printf("\n");
+
+ return lst_cnt;
+}
+
+static size_t printUuidList(const OCUuidList_t* uid_lst)
+{
+ if (!uid_lst)
+ {
+ printf(" Device List is Empty..\n\n");
+ return 0;
+ }
+
+ OCUuidList_t* lst = (OCUuidList_t*)uid_lst;
+ size_t lst_cnt = 0;
+ for (; lst; )
+ {
+ printf(" [%zu] ", ++lst_cnt);
+ printUuid((const OicUuid_t*)&lst->dev);
+ printf("\n");
+ lst = lst->next;
+ }
+ printf("\n");
+
+ return lst_cnt;
+}
+
+static size_t printResultList(const OCProvisionResult_t* rslt_lst, const size_t rslt_cnt)
+{
+ if (!rslt_lst || (0 == rslt_cnt))
+ {
+ printf(" Device List is Empty..\n\n");
+ return 0;
+ }
+
+ size_t lst_cnt = 0;
+ for (; rslt_cnt > lst_cnt; ++lst_cnt)
+ {
+ printf(" [%" PRIuPTR "] ", lst_cnt + 1);
+ printUuid((const OicUuid_t*)&rslt_lst[lst_cnt].deviceId);
+ printf(" - result: %d\n", rslt_lst[lst_cnt].res);
+ }
+ printf("\n");
+
+ return lst_cnt;
+}
+
+static void printUuid(const OicUuid_t* uid)
+{
+ for (int i = 0; i<UUID_LENGTH; )
+ {
+ printf("%02X", (*uid).id[i++]);
+ if (i == 4 || i == 6 || i == 8 || i == 10) // canonical format for UUID has '8-4-4-4-12'
+ {
+ printf("-");
+ }
+ }
+}
+
+static FILE* fopen_prvnMng(const char* path, const char* mode)
+{
+ if (0 == strncmp(path, OC_SECURITY_DB_DAT_FILE_NAME, strlen(OC_SECURITY_DB_DAT_FILE_NAME)))
+ {
+ // input |g_svr_db_fname| internally by force, not using |path| parameter
+ // because |OCPersistentStorage::open| is called |OCPersistentStorage| internally
+ // with its own |SVR_DB_FILE_NAME|
+ return fopen(SVR_DB_FILE_NAME, mode);
+ }
+ else
+ {
+ return fopen(path, mode);
+ }
+}
+
+static int waitCallbackRet(void)
+{
+ for(int i = 0; !g_doneCB; ++i)
+ {
+ if(OC_STACK_OK != OCProcess())
+ {
+ OIC_LOG(ERROR, TAG, "OCStack process error");
+ return -1;
+ }
+
+ if (i == CALLBACK_TIMEOUT)
+ {
+ return -1;
+ }
+
+ sleep(1);
+ }
+
+ return 0;
+}
+
+void shutdownProvisionClient()
+{
+ if(OC_STACK_OK != OCStop())
+ {
+ OIC_LOG(ERROR, TAG, "OCStack stop error");
+ }
+ OCDeleteDiscoveredDevices(g_own_list); // after here |g_own_list| points to nothing
+ OCDeleteDiscoveredDevices(g_unown_list); // after here |g_unown_list| points to nothing
+
+ OICFreeAndSetToNull(&g_svr_fname);
+ OICFreeAndSetToNull(&g_prvn_fname);
+}
+
+static int initDiscoverRegisterAllDevices()
+{
+ if(initProvisionClient())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: ProvisionClient init error", __func__);
+ return -1;
+ }
+
+ if(discoverAllDevices())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Discovery failed", __func__);
+ return -1;
+ }
+
+ if(registerDevices())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Failed to onboard devices", __func__);
+ return -1;
+ }
+
+ if(discoverAllDevices())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Re-discovery failed after registerDevices", __func__);
+ return -1;
+ }
+
+ return 0;
+
+}
+
+int TestTrustAnchorProvisioning()
+{
+ int ret = -1;
+
+ OIC_LOG_V(ERROR, TAG, "Running %s", __func__);
+
+ if(initDiscoverRegisterAllDevices())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Failed discover and provision devices", __func__);
+ goto exit;
+ }
+
+ /* There should be one owned device with number 1. */
+ if(provisionTrustAnchor(1))
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Failed to provision trust anchor to device", __func__);
+ goto exit;
+ }
+
+ ret = 0;
+
+exit:
+
+ shutdownProvisionClient();
+
+ return ret;
+}
+
+int TestCSRResource()
+{
+ int ret = -1;
+
+ OIC_LOG_V(ERROR, TAG, "Running %s", __func__);
+
+ if(initDiscoverRegisterAllDevices())
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Failed discover and provision devices", __func__);
+ goto exit;
+ }
+
+ /* There should be one owned device with number 1. */
+ if(getCsr(1))
+ {
+ OIC_LOG(ERROR, TAG, "Test1: Failed to get CSR from device");
+ goto exit;
+ }
+
+ ret = 0;
+
+exit:
+
+ shutdownProvisionClient();
+
+ return ret;
+}
+
+// main function for provisioning client using C-level provisioning API
+int main(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ printf("%s: No test number provided (argc = %d)\n", argv[0], argc);
+ return 1;
+ }
+
+ unsigned int testNumber = strtoul(argv[1], NULL, 10);
+ switch (testNumber)
+ {
+ case 1:
+ return TestTrustAnchorProvisioning();
+ case 2:
+ return TestCSRResource();
+ default:
+ printf("%s: Invalid test number\n", argv[0]);
+ return 1;
+ }
+
+}
+
+#ifdef __cplusplus
+}
+#endif //__cplusplus
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <vector>
+
+#include "ocstack.h"
+#include "oic_malloc.h"
+#include "ocrandom.h"
+#include "occertutility.h"
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+/** @todo stop-gap for naming issue. Windows.h does not like us to use ERROR */
+#ifdef ERROR
+#undef ERROR
+#endif //ERROR
+#endif //HAVE_WINDOWS_H
+
+static long GetFileSize(FILE *f)
+{
+ long offset, size;
+
+ offset = ftell(f);
+
+ if (offset < 0)
+ {
+ return offset;
+ }
+
+ if (fseek(f, 0, SEEK_END) < 0)
+ {
+ return -1;
+ }
+
+ size = ftell(f);
+
+ if (fseek(f, 0, SEEK_SET) < 0)
+ {
+ return -1;
+ }
+
+ return size;
+}
+
+static char *ReadLine(char *buf, size_t len)
+{
+ char *p;
+ int lenAsInt;
+
+ if (len > 0xFFFFFFFF)
+ {
+ return NULL;
+ }
+ else
+ {
+ lenAsInt = (int)len;
+ }
+
+ if (NULL == fgets(buf, lenAsInt, stdin))
+ {
+ return NULL;
+ }
+
+ /* Trim newline that fgets stores in the buffer. */
+ for (p = buf; ((p - buf) < lenAsInt) && (*p != '\r') && (*p != '\n'); p++);
+
+ *p = '\0';
+
+ return buf;
+}
+
+typedef enum {
+ CA_CERT,
+ IDENTITY_CERT,
+ ROLE_CERT
+} CertType;
+
+static void DoGenCertificate(CertType certType)
+{
+ OCStackResult res;
+ char subjKeyPairName[50];
+ char issKeyPairName[50];
+ char filename[70];
+ char subject[100];
+ char authority[100];
+ OicUuid_t subjectUuid;
+ char notValidBefore[50];
+ char notValidAfter[50];
+ char *serial = NULL;
+ size_t serialLen = 0;
+ long fileLen = 0;
+ size_t bytesProcessed = 0;
+ std::vector<char> publicKey;
+ std::vector<char> privateKey;
+ std::vector<char> issuerCert;
+ FILE *f = NULL;
+ time_t nowTimeT;
+ struct tm *now;
+ char *certificate = NULL;
+ size_t certificateLen = 0;
+
+ printf("Subject key pair name (do not include .pub or .prv): ");
+ if (NULL == ReadLine(subjKeyPairName, sizeof(subjKeyPairName)))
+ {
+ printf("Failed to get key pair name!");
+ goto exit;
+ }
+
+ /* This sample only supports self-signed CAs. Intermediates can be created with
+ * the helper functions, though, by providing a different issuer private key and
+ * certificate.
+ */
+ if (CA_CERT != certType)
+ {
+ printf("Issuer cert/key pair name (do not include .crt, .pub, or .prv): ");
+ if (NULL == ReadLine(issKeyPairName, sizeof(issKeyPairName)))
+ {
+ printf("Failed to get key pair name!");
+ goto exit;
+ }
+ }
+ else
+ {
+ strcpy(issKeyPairName, subjKeyPairName);
+ }
+
+ // -- Load public key --
+
+ sprintf(filename, "%s.pub", subjKeyPairName);
+ f = fopen(filename, "rb");
+ if (NULL == f)
+ {
+ printf("Could not open public key!\n");
+ goto exit;
+ }
+
+ fileLen = GetFileSize(f);
+ if (fileLen < 0)
+ {
+ printf("Could not get size of public key!\n");
+ goto exit;
+ }
+
+ publicKey.resize(fileLen);
+ bytesProcessed = fread(publicKey.data(), 1, publicKey.size(), f);
+ if (bytesProcessed < publicKey.size())
+ {
+ printf("Could not read public key! Got %zu, expected %zu\n", bytesProcessed, publicKey.size());
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: could not fclose public key\n");
+ }
+
+ // -- Load private key --
+ sprintf(filename, "%s.prv", issKeyPairName);
+ f = fopen(filename, "rb");
+ if (NULL == f)
+ {
+ printf("Could not open private key!\n");
+ goto exit;
+ }
+
+ fileLen = GetFileSize(f);
+ if (fileLen < 0)
+ {
+ printf("Could not get size of private key!\n");
+ goto exit;
+ }
+
+ privateKey.resize(fileLen);
+ bytesProcessed = fread(privateKey.data(), 1, privateKey.size(), f);
+ if (bytesProcessed < privateKey.size())
+ {
+ printf("Could not read private key! Got %zu, expected %zu\n", bytesProcessed, privateKey.size());
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: could not fclose private key\n");
+ }
+
+ f = NULL;
+
+ // -- Load issuer cert if applicable --
+ if (CA_CERT != certType)
+ {
+ sprintf(filename, "%s.crt", issKeyPairName);
+ f = fopen(filename, "rb");
+ if (NULL == f)
+ {
+ printf("Could not open issuer certificate file!\n");
+ goto exit;
+ }
+
+ fileLen = GetFileSize(f);
+ if (fileLen < 0)
+ {
+ printf("Could not get size of issuer certificate!\n");
+ goto exit;
+ }
+
+ issuerCert.resize(fileLen);
+ bytesProcessed = fread(issuerCert.data(), 1, issuerCert.size(), f);
+ if (bytesProcessed < issuerCert.size())
+ {
+ printf("Could not read issuer certificate! Got %zu, expected %zu\n", bytesProcessed, issuerCert.size());
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: could not fclose issuer certificate\n");
+ }
+
+ f = NULL;
+ }
+
+ // -- Prompt user for subject name --
+
+ if (CA_CERT == certType)
+ {
+ printf("Subject name as comma-separated list of RDN types and values\n");
+ printf("e.g.: C=US, O=Open Connectivity Foundation, CN=Main CA : ");
+ if (NULL == ReadLine(subject, sizeof(subject)))
+ {
+ printf("Failed to get subject!\n");
+ goto exit;
+ }
+ }
+ else
+ {
+ printf("Subject UUID in the RFC 4122 form (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX): ");
+ if (NULL == ReadLine(subject, sizeof(subject)))
+ {
+ printf("Failed to get subject UUID!");
+ goto exit;
+ }
+
+ if (!OCConvertStringToUuid(subject, subjectUuid.id))
+ {
+ printf("Failed to parse input UUID!\n");
+ goto exit;
+ }
+ }
+
+ // -- Compute validity to be ten years from current time --
+ nowTimeT = time(NULL);
+ now = gmtime(&nowTimeT);
+
+ if (0 == strftime(notValidBefore, sizeof(notValidBefore), "%Y%m%d%H%M%S", now))
+ {
+ printf("Failed to format notValidBefore time!");
+ goto exit;
+ }
+
+ now->tm_year += 10;
+
+ if (0 == strftime(notValidAfter, sizeof(notValidAfter), "%Y%m%d%H%M%S", now))
+ {
+ printf("Failed to format notValidAfter time!");
+ goto exit;
+ }
+
+ // -- Generate random serial number. A real CA should record serial numbers as they are used, and --
+ // -- make sure none are ever repeated! --
+
+ res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+ if (OC_STACK_OK != res)
+ {
+ printf("Failed to generate a serial number!\n");
+ goto exit;
+ }
+
+ switch (certType)
+ {
+ case CA_CERT:
+ res = OCGenerateCACertificate(
+ subject,
+ publicKey.data(),
+ NULL,
+ privateKey.data(),
+ serial,
+ notValidBefore,
+ notValidAfter,
+ &certificate,
+ &certificateLen);
+ break;
+
+ case IDENTITY_CERT:
+ res = OCGenerateIdentityCertificate(
+ &subjectUuid,
+ publicKey.data(),
+ issuerCert.data(),
+ privateKey.data(),
+ serial,
+ notValidBefore,
+ notValidAfter,
+ &certificate,
+ &certificateLen);
+ break;
+
+ case ROLE_CERT:
+ printf("Role name: ");
+ /* We don't need subject's contents now, so reuse the buffer.*/
+ if (NULL == ReadLine(subject, sizeof(subject)))
+ {
+ printf("Failed to read role name!\n");
+ goto exit;
+ }
+ printf("Authority name (press ENTER for none): ");
+ if (NULL == ReadLine(authority, sizeof(authority)))
+ {
+ printf("Failed to read authority name!\n");
+ goto exit;
+ }
+ res = OCGenerateRoleCertificate(
+ &subjectUuid,
+ publicKey.data(),
+ issuerCert.data(),
+ privateKey.data(),
+ serial,
+ notValidBefore,
+ notValidAfter,
+ subject,
+ ('\0' != *authority)?authority:NULL,
+ &certificate,
+ &certificateLen);
+ break;
+ }
+
+ if (OC_STACK_OK != res)
+ {
+ printf("Failed to generate certificate!\n");
+ goto exit;
+ }
+
+ sprintf(filename, "%s.crt", subjKeyPairName);
+ f = fopen(filename, "wb");
+ if (NULL == f)
+ {
+ printf("Failed to open certificate file for writing!\n");
+ goto exit;
+ }
+
+ bytesProcessed = fwrite(certificate, 1, certificateLen, f);
+ if (bytesProcessed < certificateLen)
+ {
+ printf("Failed to write certificate! Had %zu bytes, wrote %zu\n", certificateLen, bytesProcessed);
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to close certificate file\n");
+ goto exit;
+ }
+
+ f = NULL;
+
+ printf("Wrote certificate file.\n");
+
+exit:
+
+ OICFree(serial);
+ OICFree(certificate);
+ if (NULL != f)
+ {
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose\n");
+ }
+ }
+}
+
+static void DoGenKeyPair()
+{
+ OCStackResult res = OC_STACK_OK;
+ char nameBuf[100];
+ char *publicKey = NULL;
+ char *privateKey = NULL;
+ size_t publicKeyLen = 0;
+ size_t privateKeyLen = 0;
+ FILE *f = NULL;
+
+ printf("Name the key pair: ");
+ if (NULL == ReadLine(nameBuf, sizeof(nameBuf)))
+ {
+ printf("Failed to read name!");
+ return;
+ }
+
+ res = OCGenerateKeyPair(&publicKey, &publicKeyLen, &privateKey, &privateKeyLen);
+ if (OC_STACK_OK == res)
+ {
+ char filename[120];
+ size_t written;
+
+ sprintf(filename, "%s.pub", nameBuf);
+ f = fopen(filename, "wb");
+ if (NULL == f)
+ {
+ printf("Couldn't open %s to write public key!\n", filename);
+ goto exit;
+ }
+
+ written = fwrite(publicKey, 1, publicKeyLen, f);
+ if (written < publicKeyLen)
+ {
+ printf("Failed to write public key! Had %zu, wrote %zu\n", publicKeyLen, written);
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose. Errno: %d\n", errno);
+ }
+
+ printf("Wrote public key.\n");
+
+ sprintf(filename, "%s.prv", nameBuf);
+ f = fopen(filename, "wb");
+ if (NULL == f)
+ {
+ printf("Couldn't open %s to write private key!\n", filename);
+ goto exit;
+ }
+ written = fwrite(privateKey, 1, privateKeyLen, f);
+ if (written < privateKeyLen)
+ {
+ printf("Failed to write private key! Had %zu, wrote %zu\n", privateKeyLen, written);
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose. Errno: %d\n", errno);
+ }
+ f = NULL;
+
+ printf("Wrote private key.\n");
+ }
+ else
+ {
+ printf("Failed!\n");
+ }
+
+exit:
+
+ OICFree(publicKey);
+ OICClearMemory(privateKey, privateKeyLen);
+ OICFree(privateKey);
+ if (NULL != f)
+ {
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose. errno: %d\n", errno);
+ }
+ }
+}
+
+int main()
+{
+ for (;;)
+ {
+ int option;
+
+ printf("-- Certificate Generator Sample Utility --\n\n");
+
+ printf(" 1. Generate a new key pair\n");
+ printf(" 2. Generate a self-signed CA certificate (requires a key pair for the CA)\n");
+ printf(" 3. Generate an identity certificate for a particular device UUID\n");
+ printf(" (requires the CA's private key and certificate, and the device's public key)\n");
+ printf(" 4. Generate a role certificate for a particular device UUID and role\n");
+ printf(" (requires the CA's private key and certificate, and the device's public key)\n");
+
+ printf("\n");
+ printf(" 0. Exit\n");
+ printf("\n");
+ printf("Select: ");
+
+ if (scanf("%d", &option) < 1)
+ {
+ printf("Failed to read the option! Exiting.\n");
+ return 0;
+ }
+
+ while ('\n' != getchar()); /* Consume the rest of the line so it doesn't get fed to the next input. */
+
+ switch (option)
+ {
+ case 1:
+ DoGenKeyPair();
+ break;
+ case 2:
+ DoGenCertificate(CA_CERT);
+ break;
+ case 3:
+ DoGenCertificate(IDENTITY_CERT);
+ break;
+ case 4:
+ DoGenCertificate(ROLE_CERT);
+ break;
+ case 0:
+ return 0;
+ default:
+ printf("Invalid option %d\n\n", option);
+ break;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+#!/usr/bin/python -W
+
+import subprocess
+import os
+import time
+import sys
+import shutil
+import platform
+import argparse
+import textwrap
+
+# Note: to see the return value of a process
+# - in Windows type: echo %errorlevel%
+# - in Linux type: echo $?
+
+# Resets the state of sampleserver_justworks and autoprovisioningclient so that
+# state from one test is not present during subsequent tests
+def cleanup(iotivity_base_path, exe_path):
+
+ # Copy fresh oic_svr_db_server_justworks.dat
+ dat_file_src = os.path.join(iotivity_base_path, 'resource', 'csdk', 'security', 'provisioning', 'sample', 'oic_svr_db_server_justworks.dat' )
+ dat_file_dest = exe_path
+ shutil.copy(dat_file_src, dat_file_dest)
+\r
+ # Copy fresh oic_svr_db_client.dat \r
+ dat_file_src = os.path.join(iotivity_base_path, 'resource', 'examples', 'oic_svr_db_client.dat' )\r
+ dat_file_dest = exe_path\r
+ shutil.copy(dat_file_src, dat_file_dest)\r
+\r
+ #Delete provisioning DB file\r
+ db_file = os.path.join(exe_path, 'oic_autoprvn_mng.db')\r
+ if os.path.isfile(db_file):\r
+ os.unlink(db_file)\r
+\r
+# Print the environment variables (useful for debugging)
+def print_environment():
+ for key in os.environ.keys():\r
+ print "%30s %s \n" % (key,os.environ[key])\r
+ print 'current PATH is %s' % sys.path
+
+
+### main ###
+
+usage = '''
+ Run end-to-end certificate tests between autoprovisioningclient and sampleserver_justworks
+ Usage Notes
+ - script assumes it's being run from the root of iotivity, e.g.:
+ t:\\iotivity\\resource\\csdk\\security\\provisioning\\sample\\provisioningTest.py
+ - I have added
+ t:\\iotivity\\out\\windows\\amd64\\debug\\resource\\csdk
+ t:\\iotivity\\out\\windows\\amd64\\debug
+ to my PATH
+ - The discovery timeout in autoprovisioning client may be a bit agressive, 3 seconds
+'''
+
+parser = argparse.ArgumentParser(
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ description=usage
+)
+
+parser.add_argument('--arch', nargs='?', choices = ['amd64', 'x86', 'arm'], help= 'Architecture, one of x86, amd64 or arm. Defaults to amd64', default='amd64')
+parser.add_argument('--build', nargs='?', choices = ['debug', 'release'], help= 'Build type, one of debug or release. Defaults to debug', default='debug')
+
+args = parser.parse_args()
+
+# Number of unit tests in autoprovisioningclient
+NUM_TESTS = 2
+
+iotivity_base_path = os.getcwd()
+os_name = platform.system()
+if os_name == 'Windows':
+ os_name = 'windows'
+elif os_name == 'Linux':
+ os_name = 'linux'
+
+exe_path = os.path.join(iotivity_base_path, 'out', os_name, args.arch, args.build, 'resource', 'csdk', 'security', 'provisioning', 'sample')
+
+# Set PATH so octbstack.dll is found
+cwd = os.getcwd()
+sys.path.append(os.path.join(cwd, exe_path))
+sys.path.append(os.path.join(cwd, 'out', os_name, args.arch, args.build))
+
+# Work in the output dir with the test binaries
+os.chdir(exe_path)
+
+output_text = ""
+num_failures = 0
+for i in range(1, NUM_TESTS + 1):\r
+ print '\nRunning test %d...\n' % i\r
+\r
+ # Clear state from previous test\r
+ cleanup(iotivity_base_path, exe_path)\r
+\r
+ # Start the device/server with a non-blocking call\r
+ #note: Popen can take file descriptors to redirect the processes stdin, stdout, stderr\r
+ try:\r
+ server_process = subprocess.Popen('sampleserver_justworks')\r
+ except:\r
+ print 'Failed to start sampleserver_justworks', sys.exc_info()[0]\r
+ sys.exit(-1)\r
+\r
+ # Run the auto provisioning client with the test number as argument, block until it returns\r
+ return_code = subprocess.call(["autoprovisioningclient", str(i)])\r
+\r
+ if return_code != 0:\r
+ num_failures += 1\r
+ print "Test %d failed" % i\r
+ output_text += "Test %d failed\n" % i\r
+ else:\r
+ print "Test %d passed" % i\r
+\r
+\r
+ server_process.kill()\r
+
+
+print "\n------------------------------------"
+print " Test Results: %d of %d tests passed" % (NUM_TESTS - num_failures, NUM_TESTS)
+print "------------------------------------"
+print output_text
+print '\n'
+
+
#include "srmutility.h"
#include "pmtypes.h"
#include "oxmverifycommon.h"
+#include "mbedtls/config.h"
+#include "mbedtls/pem.h"
+#include "mbedtls/x509_csr.h"
+#include "occertutility.h"
#ifdef _MSC_VER
#include <io.h>
#define _33_PROVIS_DP_ 33
#define _34_CHECK_LINK_STATUS_ 34
#define _35_SAVE_ACL_ 35
+#define _36_PROVIS_CERT_ 36
#define _40_UNLINK_PAIR_DEVS_ 40
#define _50_REMOVE_SELEC_DEV_ 50
#define _51_REMOVE_DEV_WITH_UUID_ 51
#define _53_RESET_SVR_DB_ 53
#define _60_GET_CRED_ 60
#define _61_GET_ACL_ 61
+#define _62_GET_CSR_ 62
#ifdef MULTIPLE_OWNER
#define _70_MOT_CHANGE_MOM_ 70
#define _71_MOT_PROV_PRECONF_PIN_ 71
PRM_PRE_CONFIGURED,
};
+static char* TEST_CERT_NOT_BEFORE = "20170101000000"; // Not before field for certificates, in format YYYYMMDDhhmmss
+static char* TEST_CERT_NOT_AFTER = "20270101000000"; // + ten years
+
// |g_ctx| means provision manager application context and
// the following, includes |un/own_list|, could be variables, which |g_ctx| has,
// for accessing all function(s) for these, they are declared on global domain
static OCProvisionDev_t* g_mot_enable_list;
static int g_mot_enable_cnt;
#endif //MULTIPLE_OWNER
+char* g_caKeyPem; /* Test CA private key */
+char* g_caCertPem; /* Test CA certificate */
+uint16_t g_caCredId = 0; /* ID of CA's OCF identity cert */
+char* g_csr; /* Certificate signing request from device */
-static bool g_doneCB;
+static bool g_doneCB; /* Set to true by the callback to indicate it completed. */
+static bool g_successCB; /* Set to true by the callback to indicate success. */
#ifdef __WITH_TLS__
static int secure_protocol = 1;
static void setDevProtocol(OCProvisionDev_t* dev_lst);
static int waitCallbackRet(void);
static int selectTwoDiffNum(int*, int*, const int, const char*);
+/* At a few places in this file, warning 4028 is incorrectly produced, disable it for the whole file. */
+#ifdef _MSC_VER
+#pragma warning( disable : 4028)
+#endif
+
// callback function(s) for provisioning client using C-level provisioning API
static void ownershipTransferCB(void* ctx, size_t nOfRes, OCProvisionResult_t* arr, bool hasError)
{
if(!hasError)
{
OIC_LOG_V(INFO, TAG, "Provision Credential SUCCEEDED - ctx: %s", (char*) ctx);
+ g_successCB = true;
}
else
{
OIC_LOG_V(ERROR, TAG, "Provision Credential FAILED - ctx: %s", (char*) ctx);
printResultList((const OCProvisionResult_t*) arr, nOfRes);
+ g_successCB = false;
+ }
+ g_doneCB = true;
+}
+
+/* Function of type OCProvisionResultCB from \resource\csdk\security\provisioning\include\pmtypes.h */
+void provisionTrustChainCB(void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
+{
+ if (!hasError)
+ {
+ OIC_LOG_V(INFO, TAG, "Provision Credential SUCCEEDED - ctx: %s", (char*)ctx);
+ g_successCB = true;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Provision Credential FAILED - ctx: %s", (char*)ctx);
+ printResultList((const OCProvisionResult_t*)arr, nOfRes);
+ g_successCB = false;
}
g_doneCB = true;
}
g_doneCB = true;
}
-static void provisionDPCB(void* ctx, size_t nOfRes, OCProvisionResult_t* arr, bool hasError)
+static void getCsrCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool hasError)
+{
+ if (!hasError)
+ {
+ size_t i;
+ mbedtls_x509_csr csr;
+ OIC_LOG_V(INFO, TAG, "getCsrCB SUCCEEDED - ctx: %s", (char*)ctx);
+ for (i = 0; i < nOfRes; i++)
+ {
+ char pemBuffer[2048] = { 0 };
+ char infoBuffer[2048] = { 0 };
+ size_t olen;
+ int ret;
+
+ if (arr[i].encoding == OIC_ENCODING_DER)
+ {
+ ret = mbedtls_pem_write_buffer("-----BEGIN CERTIFICATE REQUEST-----",
+ "-----END CERTIFICATE REQUEST-----",
+ arr[i].csr,
+ arr[i].csrLen,
+ (unsigned char *)pemBuffer,
+ sizeof(pemBuffer),
+ &olen);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't convert CSR into PEM: %d", ret);
+ pemBuffer[0] = '\0';
+ }
+ }
+ else
+ {
+ OICStrcpyPartial(pemBuffer, sizeof(pemBuffer), (const char *)arr[i].csr, arr[i].csrLen);
+ }
+
+ mbedtls_x509_csr_init(&csr);
+ ret = mbedtls_x509_csr_parse_der(&csr, arr[i].csr, arr[i].csrLen);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+ infoBuffer[0] = '\0';
+ }
+ else
+ {
+ ret = mbedtls_x509_csr_info(infoBuffer, sizeof(infoBuffer), "", &csr);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't get CSR info buffer: %d", ret);
+ infoBuffer[0] = '\0';
+ }
+ }
+ mbedtls_x509_csr_free(&csr);
+
+ OIC_LOG(INFO, TAG, "getCsrCB success");
+ // OIC_LOG_V truncates strings, and the entirety of the CSR PEM gets cut off if we use it
+ printf("getCsrCB: csr[%" PRIuPTR "]:\n%s\n", i, pemBuffer);
+ printf("getCsrCB: csr info[%" PRIuPTR "]:\n%s\n", i, infoBuffer);
+ }
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrCB FAILED - ctx: %s", (char*)ctx);
+
+ if (!arr || (0 == nOfRes))
+ {
+ printf(" Device List is Empty..\n\n");
+ }
+ else
+ {
+ size_t lst_cnt;\r
+ for (lst_cnt = 0; nOfRes > lst_cnt; ++lst_cnt)
+ {
+ printf(" [%" PRIuPTR "] ", lst_cnt + 1);
+ printUuid((const OicUuid_t*)&arr[lst_cnt].deviceId);
+ printf(" - result: %d\n", arr[lst_cnt].res);
+ }
+ printf("\n");
+ }
+ }
+ g_doneCB = true;
+}
+
+static void getCsrForCertProvCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool hasError)
+{
+ g_successCB = false;
+
+ if (!hasError)
+ {
+ if (nOfRes != 1)
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED - ctx: %s", (char*)ctx);
+ goto exit;
+ }
+
+ if (arr[0].encoding == OIC_ENCODING_DER)
+ {
+ OCStackResult res = OCConvertDerCSRToPem((char*)arr[0].csr, arr[0].csrLen, &g_csr);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED (CSR re-encoding failed) - error: %d, ctx: %s", res, (char*)ctx);
+ goto exit;
+ }
+ g_successCB = true;
+ }
+ else if(arr[0].encoding == OIC_ENCODING_PEM)
+ {
+ g_csr = (char*)OICCalloc(1, arr[0].csrLen);
+ if (g_csr == NULL)
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED (memory allocation) - ctx: %s", (char*)ctx);
+ goto exit;
+ }
+
+ memcpy(g_csr, arr[0].csr, arr[0].csrLen);
+
+ OIC_LOG(INFO, TAG, "getCsrForCertProvCB success");
+ g_successCB = true;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED (unknown encoding) - ctx: %s", (char*)ctx);
+ goto exit;
+ }
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED - ctx: %s", (char*)ctx);
+ }
+
+exit:
+ g_doneCB = true;
+}
+
+static void provisionDPCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
{
if(!hasError)
{
return -1;
}
- if (access(PRVN_DB_FILE_NAME, F_OK) != -1)
+ if (access(PRVN_DB_FILE_NAME, F_OK) == 0)
{
printf("************************************************************\n");
printf("************Provisioning DB file already exists.************\n");
return 0;
}
+/*
+ * Initialize the provisioning client for certificate provisioning.
+ * This function:
+ * 1. Generates a root key pair for a CA.
+ * 2. Generates a self-signed root certificate for the CA public key.
+ * 3. Saves this root as a trust anchor locally.
+ * 4. Generate and store an IoTivity key and cert (issued from the CA root cert).
+ * This is an EE cert the CA/OBT will use in DTLS.
+ *
+ * @param[out] credid parameter for the ID of the CA credential
+ */
+static int setupCA()
+{
+ char* publicKey = NULL;
+ size_t publicKeyLen = 0;
+ size_t caKeyLen = 0;
+ char* serial = NULL;
+ size_t serialLen = 0;
+ size_t caCertLen = 0;
+ char* idPublicKey = NULL;
+ char* idKey = NULL;
+ char* idCert = NULL;
+ size_t idCertLen = 0;
+ size_t idKeyLen = 0;
+
+ if (g_caCredId == 0)
+ {
+ printf("Setting up CA for certificate provisioning\n");
+ }
+ else
+ {
+ printf("Skipping CA setup, already done\n");
+ return 0;
+ }
+
+ /* Create CA keypair, serial number */
+ OCStackResult res = OCGenerateKeyPair(&publicKey, &publicKeyLen, &g_caKeyPem, &caKeyLen);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGenerateKeyPair failed, error: %d", res);
+ goto exit;
+ }
+
+ res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGenerateRandomSerialNumber failed, error: %d", res);
+ goto exit;
+ }
+
+ /* Create a CA certificate */
+ res = OCGenerateCACertificate(
+ "C=US, O=Open Connectivity Foundation, CN=IoTivity test code CA", // subject
+ publicKey,
+ NULL, // Issuer private key is null
+ g_caKeyPem, // use CA's own key to create self-signed cert
+ serial,
+ TEST_CERT_NOT_BEFORE,
+ TEST_CERT_NOT_AFTER,
+ &g_caCertPem,
+ &caCertLen);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGenerateCACertificate failed, error: %d", res);
+ goto exit;
+ }
+
+ /* Set our own trust anchor so that we trust certs we've issued. */\r
+ res = OCSaveTrustCertChain((uint8_t*) g_caCertPem, caCertLen, OIC_ENCODING_PEM, &g_caCredId);
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCSaveTrustCertChain error: %d", res);
+ goto exit;
+ }
+
+ /* Create identity certificate for use by the CA. */
+ res = OCGenerateKeyPair(&idPublicKey, &publicKeyLen, &idKey, &idKeyLen);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGenerateKeyPair failed, error: %d", res);
+ goto exit;
+ }
+
+ res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGenerateRandomSerialNumber failed, error: %d", res);
+ goto exit;
+ }
+
+ OCUUIdentity deviceId = { 0 };
+ res = OCGetDeviceId(&deviceId);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to get own UUID, error: %d", res);
+ goto exit;
+ }
+
+ OicUuid_t uuid = { 0 };
+ memcpy(uuid.id, deviceId.id, sizeof(uuid.id));
+
+ res = OCGenerateIdentityCertificate(
+ &uuid,
+ idPublicKey,
+ g_caCertPem,
+ g_caKeyPem,
+ serial,
+ TEST_CERT_NOT_BEFORE,
+ TEST_CERT_NOT_AFTER,
+ &idCert,
+ &idCertLen);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to create identity cert for CA, error: %d", res);
+ goto exit;
+ }
+
+ uint16_t idCertCredId = 0;
+ res = OCSaveOwnCertChain(idCert, idKey, &idCertCredId);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to save CA's identity cert & key, error: %d", res);
+ goto exit;
+ }
+
+exit:
+ OICFree(publicKey);
+ OICFree(serial);
+ OICFree(idPublicKey);
+ if (idKey != NULL)
+ {
+ OICClearMemory(idKey, idKeyLen);
+ OICFree(idKey);
+ }
+ OICFree(idCert);
+
+ if (res != OC_STACK_OK)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Create an identity certificate for a device, based on the information in its CSR.
+ * Assumes the csr has already been validated wtih OCVerifyCSRSignature.
+ */
+static int createIdentityCertFromCSR(const char* caKeyPem, const char* caCertPem, char* csr,
+ char** deviceCert)
+{
+ char* publicKey = NULL;
+ char* serial = NULL;
+ size_t serialLen;
+ OicUuid_t uuid = { 0 };
+ OCStackResult res = OC_STACK_ERROR;
+
+ res = OCGetUuidFromCSR(csr, &uuid);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to get UUID from CSR, error: %d", res);
+ goto exit;
+ }
+ /* Note: a real OBT must make sure the UUID isn't already in use on the network. */
+
+ res = OCGetPublicKeyFromCSR(csr, &publicKey);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to get public key from CSR, error: %d", res);
+ goto exit;
+ }
+
+ res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+ if (res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGenerateRandomSerialNumber failed, error: %d", res);
+ goto exit;
+ }
+
+ size_t deviceCertLen;
+ res = OCGenerateIdentityCertificate(
+ &uuid,
+ publicKey,
+ caCertPem,
+ caKeyPem,
+ serial,
+ TEST_CERT_NOT_BEFORE,
+ TEST_CERT_NOT_AFTER,
+ deviceCert,
+ &deviceCertLen);
+
+ if(res != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGenerateIdentityCertificate failed, error: %d", res);
+ goto exit;
+ }
+
+exit:
+ OICFree(publicKey);
+ OICFree(serial);
+
+ if (res != OC_STACK_OK)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int provisionCert(void)
+{
+ // make sure we own at least one device to provision
+ if (!g_own_list || g_own_cnt == 0)
+ {
+ printf(" > Owned Device List, to Provision Credentials, is Empty\n");
+ printf(" > Please Register Unowned Devices first, with [20] Menu\n");
+ return 0; // normal case
+ }
+
+ // select device for provisioning certificate
+ int dev_num = 0;
+ for (; ; )
+ {
+ printf(" > Enter Device Number, for certificate provisioning: ");
+ for (int ret = 0; 1 != ret; )
+ {
+ ret = scanf("%d", &dev_num);
+ for (; 0x20 <= getchar(); ); // for removing overflow garbages
+ // '0x20<=code' is character region
+ }
+ if (0<dev_num && g_own_cnt >= dev_num)
+ {
+ break;
+ }
+ printf(" Entered Wrong Number. Please Enter Again\n");
+ }
+
+ OCProvisionDev_t* targetDevice = getDevInst((const OCProvisionDev_t*)g_own_list, dev_num);
+ if (targetDevice == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Error, invalid device %d");
+ return -1;
+ }
+
+ // Install the CA trust anchor
+ if (setupCA() != 0)
+ {
+ printf(" Failed to setup CA\n");
+ return -1;
+ }
+
+ // Provision the CA root cert to the target device
+ printf(" > Saving root certificate (trust anchor) to selected device..\n");
+ g_doneCB = false;
+ OicSecCredType_t type = SIGNED_ASYMMETRIC_KEY;
+
+ OCStackResult rst = OCProvisionTrustCertChain((void*)g_ctx, type, g_caCredId, targetDevice, &provisionTrustChainCB);
+ if (OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCProvisionTrustCertChain returned error: %d", rst);
+ return -1;
+ }
+
+ if (waitCallbackRet()) // input |g_doneCB| flag implicitly
+ {
+ OIC_LOG(ERROR, TAG, "OCProvisionTrustCertChain callback error");
+ return -1;
+ }
+ if (!g_successCB)
+ {
+ return -1;
+ }
+
+ // Request a CSR from the device, check the CSR signature
+ printf(" > Getting CSR from device..\n");
+ g_doneCB = false;
+ rst = OCGetCSRResource((void*)g_ctx, targetDevice, getCsrForCertProvCB);
+ if (OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGetCSRResource API error: %d", rst);
+ return -1;
+ }
+ if (waitCallbackRet()) // input |g_doneCB| flag implicitly
+ {
+ OIC_LOG(ERROR, TAG, "OCGetCSRResource callback error");
+ return -1;
+ }
+ if (!g_successCB)
+ {
+ return -1;
+ }
+
+ rst = OCVerifyCSRSignature(g_csr);
+ if (OC_STACK_OK != rst)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to validate CSR signature");
+ OICFreeAndSetToNull(&g_csr);
+ return -1;
+ }
+
+ printf(" > Creating a certificate for the device..\n");
+ char* deviceCert;
+ int ret = createIdentityCertFromCSR(g_caKeyPem, g_caCertPem, g_csr, &deviceCert);
+ OICFreeAndSetToNull(&g_csr);
+ if (ret != 0)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to generate certificate");
+ OICFree(deviceCert);
+ return -1;
+ }
+
+ //Provision the new cert
+ printf(" > Provisioning certificate credential to selected device..\n");
+ g_doneCB = false;
+ rst = OCProvisionCertificate((void *)g_ctx, targetDevice, deviceCert, provisionCredCB);
+ if (OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCProvisionCertificate returned error: %d", rst);
+ OICFree(deviceCert);
+ return -1;
+ }
+ if (waitCallbackRet()) // input |g_doneCB| flag implicitly
+ {
+ OIC_LOG(ERROR, TAG, "OCProvisionCertificate callback error");
+ OICFree(deviceCert);
+ return -1;
+ }
+ if (!g_successCB)
+ {
+ OICFree(deviceCert);
+ return -1;
+ }
+
+ printf(" > Provisioned certificate crendentials\n");
+ OICFree(deviceCert);
+
+ return 0;
+}
+
static int provisionAcl(void)
{
// check |own_list| for provisioning access control list
return -1;
}
+static int getCsr(void)
+{
+ // check |own_list| for retrieving CSR
+ if(!g_own_list || 1>g_own_cnt)
+ {
+ printf(" > Owned Device List, to retrieve CSR, is Empty\n");
+ printf(" > Please Register Unowned Devices first, with [20] Menu\n");
+ return 0; // normal case
+ }
+
+ // select device for retrieving CSR
+ int dev_num = 0;
+ for( ; ; )
+ {
+ printf(" > Enter Device Number, for retrieving CSR: ");
+ for(int ret=0; 1!=ret; )
+ {
+ ret = scanf("%d", &dev_num);
+ for( ; 0x20<=getchar(); ); // for removing overflow garbages
+ // '0x20<=code' is character region
+ }
+ if(0<dev_num && g_own_cnt>=dev_num)
+ {
+ break;
+ }
+ printf(" Entered Wrong Number. Please Enter Again\n");
+ }
+
+ // call |getDevInst| API
+ // calling this API with callback actually acts like blocking
+ // for error checking, the return value saved and printed
+ g_doneCB = false;
+ OCProvisionDev_t* dev = getDevInst((const OCProvisionDev_t*) g_own_list, dev_num);
+ if(!dev)
+ {
+ OIC_LOG(ERROR, TAG, "getDevInst: device instance empty");
+ goto GETCSR_ERROR;
+ }
+ OCStackResult rst = OCGetCSRResource((void*) g_ctx, dev, getCsrCB);
+ if(OC_STACK_OK != rst)
+ {
+ OIC_LOG_V(ERROR, TAG, "OCGetCSRResource API error: %d", rst);
+
+ goto GETCSR_ERROR;
+ }
+ if(waitCallbackRet()) // input |g_doneCB| flag implicitly
+ {
+ OIC_LOG(ERROR, TAG, "OCGetCSRResource callback error");
+ goto GETCSR_ERROR;
+ }
+
+ // display the result of get credential
+ printf(" > Get CSR SUCCEEDED\n");
+
+ return 0;
+
+GETCSR_ERROR:
+ return -1;
+}
+
static int unlinkPairwise(void)
{
// check |own_list| for unlinking pairwise devices
OIC_LOG(ERROR, TAG, "createAcl: device instance empty");
goto CRACL_ERROR;
}
+ ace->subjectType = OicSecAceUuidSubject;
memcpy(&ace->subjectuuid, &dev->doxm->deviceID, UUID_LENGTH);
// enter number of |resources| in 'accessed' device
printf("** 32. Provision the Selected Access Control List(ACL)\n");
printf("** 33. Provision Direct-Pairing Configuration\n");
printf("** 34. Check Linked Status of the Selected Device on PRVN DB\n");
- printf("** 35. Save the Selected Access Control List(ACL) into local SVR DB\n\n");
+ printf("** 35. Save the Selected Access Control List(ACL) into local SVR DB\n");
+ printf("** 36. Provision certificate credential\n\n");
+
printf("** [D] UNLINK PAIRWISE THINGS\n");
printf("** 40. Unlink Pairwise Things\n\n");
printf("** [F] GET SECURITY RESOURCE FOR DEBUGGING ONLY\n");
printf("** 60. Get the Credential resources of the Selected Device\n");
- printf("** 61. Get the ACL resources of the Selected Device\n\n");
+ printf("** 61. Get the ACL resources of the Selected Device\n");
+ printf("** 62. Get the CSR of the Selected Device\n\n");
#ifdef MULTIPLE_OWNER
printf("** [G] UPDATE THE MULTIPLE OWNERSHIP TRANSFER RELATED VALUE\n");
OIC_LOG(ERROR, TAG, "_35_SAVE_ACL_: error");
}
break;
+ case _36_PROVIS_CERT_:
+ if (provisionCert())
+ {
+ OIC_LOG(ERROR, TAG, "_36_PROVIS_CERT_: error");
+ }
+ break;
case _40_UNLINK_PAIR_DEVS_:
if(unlinkPairwise())
{
OIC_LOG(ERROR, TAG, "_61_GET_ACL_: error");
}
break;
+ case _62_GET_CSR_:
+ if(getCsr())
+ {
+ OIC_LOG(ERROR, TAG, "_62_GET_CSR_: error");
+ }
+ break;
#ifdef MULTIPLE_OWNER
case _70_MOT_CHANGE_MOM_:
if(changeMultipleOwnershipTrnasferMode())
return NULL; // not need to 'goto' |ERROR| before allocating |acl|
}
LL_APPEND(acl->aces, ace);
+ ace->subjectType = OicSecAceUuidSubject;
memcpy(ace->subjectuuid.id, subject->id, sizeof(subject->id));
// fill the href
goto exit;
}
- OicSecAcl_t* acl = CBORPayloadToAcl2(cbor, size);
+ OicSecAcl_t* acl = CBORPayloadToCloudAcl(cbor, size);
if (NULL == acl)
{
OIC_LOG(ERROR, TAG, "Can't parse CBOR payload");
OCStackResult OCProvisionACL(void* ctx, const OCProvisionDev_t *selectedDeviceInfo, OicSecAcl_t *acl,
OCProvisionResultCB resultCallback)
{
- return SRPProvisionACL(ctx, selectedDeviceInfo, acl, resultCallback);
+ return SRPProvisionACL(ctx, selectedDeviceInfo, acl, OIC_SEC_ACL_V1, resultCallback);
+}
+
+/**
+ * this function sends ACL information to resource.
+ *
+ * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] selectedDeviceInfo Selected target device.
+ * @param[in] acl ACL to provision.
+ * @param[in] resultCallback callback provided by API user, callback will be called when provisioning
+ request recieves a response from resource server.
+ * @return OC_STACK_OK in case of success and other value otherwise.
+ */
+OCStackResult OCProvisionACL2(void* ctx, const OCProvisionDev_t *selectedDeviceInfo, OicSecAcl_t *acl,
+ OCProvisionResultCB resultCallback)
+{
+ return SRPProvisionACL(ctx, selectedDeviceInfo, acl, OIC_SEC_ACL_V2, resultCallback);
}
/**
OCStackResult OCGetACLResource(void* ctx, const OCProvisionDev_t *selectedDeviceInfo,
OCProvisionResultCB resultCallback)
{
- return SRPGetACLResource(ctx, selectedDeviceInfo, resultCallback);
+ return SRPGetACLResource(ctx, selectedDeviceInfo, OIC_SEC_ACL_V1, resultCallback);
+}
+
+OCStackResult OCGetACL2Resource(void* ctx, const OCProvisionDev_t *selectedDeviceInfo,\r
+ OCProvisionResultCB resultCallback)
+{
+ return SRPGetACLResource(ctx, selectedDeviceInfo, OIC_SEC_ACL_V2, resultCallback);
+}
+
+OCStackResult OCGetCSRResource(void* ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCGetCSRResultCB resultCallback)
+{
+ return SRPGetCSRResource(ctx, selectedDeviceInfo, resultCallback);
+}
+
+OCStackResult OCGetRolesResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCGetRolesResultCB resultCallback)
+{
+ return SRPGetRolesResource(ctx, selectedDeviceInfo, resultCallback);
}
+OCStackResult OCDeleteRoleCertificateByCredId(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCProvisionResultCB resultCallback, uint32_t credId)
+{
+ return SRPDeleteRoleCertificateByCredId(ctx, selectedDeviceInfo, resultCallback, credId);
+}
OCStackResult OCReadTrustCertChain(uint16_t credId, uint8_t **trustCertChain,
size_t *chainSize)
OCProvisionResultCB resultCallback)
{
return SRPProvisionCredentials(ctx, type, keySize,
- pDev1, pDev2, resultCallback);
+ pDev1, pDev2, NULL, resultCallback);
}
+#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
+/**
+* API to provision a certificate to a device.
+*
+* @param[in] ctx Application context returned in result callback.
+* @param[in] pDev Pointer to OCProvisionDev_t instance, respresenting the device to be provsioned.
+* @param[in] pemCert Certificate to provision, encoded as PEM
+* @param[in] resultCallback callback provided by API user, callback will be called when
+* provisioning request receives a response from first resource server.
+* @return OC_STACK_OK in case of success and other value otherwise.
+*/
+OCStackResult OCProvisionCertificate(void *ctx,
+ const OCProvisionDev_t *pDev,
+ const char* pemCert,
+ OCProvisionResultCB resultCallback)
+{
+ return SRPProvisionCredentials(ctx, SIGNED_ASYMMETRIC_KEY, 0,
+ pDev, NULL, pemCert, resultCallback);
+}
+#endif
+
/**
* this function sends Direct-Pairing Configuration to a device.
*
UpdateLinkResults(link, 1, arr[0].res);
if (NULL != link->pDev2Acl)
{
- OCStackResult res = SRPProvisionACL(ctx, link->pDev2, link->pDev2Acl, &AclProv2CB);
+ OCStackResult res = SRPProvisionACL(ctx, link->pDev2, link->pDev2Acl, OIC_SEC_ACL_V1, &AclProv2CB);
if (OC_STACK_OK!=res)
{
UpdateLinkResults(link, 2, res);
if (NULL != link->pDev1Acl)
{
- OCStackResult res = SRPProvisionACL(ctx, link->pDev1, link->pDev1Acl, &AclProv1CB);
+ OCStackResult res = SRPProvisionACL(ctx, link->pDev1, link->pDev1Acl, OIC_SEC_ACL_V1, &AclProv1CB);
if (OC_STACK_OK!=res)
{
OIC_LOG(ERROR, TAG, "Error while provisioning ACL for device 1");
else if (NULL!=link->pDev2Acl)
{
OIC_LOG(ERROR, TAG, "ACL for device 1 is NULL");
- OCStackResult res = SRPProvisionACL(ctx, link->pDev2, link->pDev2Acl, &AclProv2CB);
+ OCStackResult res = SRPProvisionACL(ctx, link->pDev2, link->pDev2Acl, OIC_SEC_ACL_V1, &AclProv2CB);
if (OC_STACK_OK!=res)
{
OIC_LOG(ERROR, TAG, "Error while provisioning ACL for device 2");
link->currentCountResults = 0;
link->resArr = (OCProvisionResult_t*) OICMalloc(sizeof(OCProvisionResult_t)*noOfResults);
res = SRPProvisionCredentials(link, type, keySize,
- pDev1, pDev2, &ProvisionCredsCB);
+ pDev1, pDev2, NULL, &ProvisionCredsCB);
if (res != OC_STACK_OK)
{
OICFree(link->resArr);
}
/**
+ * Function to save an identity certificate chain into Cred of SVR.
+ *
+ * @param[in] cert Certificate chain to be saved in Cred of SVR, PEM encoded, null terminated
+ * @param[in] key key corresponding to the certificate, PEM encoded, null terminated
+ * @param[out] credId CredId of saved certificate chain in Cred of SVR.
+ * @return OC_STACK_OK in case of success and other value otherwise.
+ */
+OCStackResult OCSaveOwnCertChain(char* cert, char* key, uint16_t *credId)
+{
+ OicSecKey_t ownCert = { 0 };
+ ownCert.data = (uint8_t*) cert;
+ ownCert.len = strlen(cert) + 1;
+ ownCert.encoding = OIC_ENCODING_PEM;
+
+ OicSecKey_t ownKey = { 0 };
+ ownKey.data = (uint8_t*) key;
+ ownKey.len = strlen(key) + 1;
+ ownKey.encoding = OIC_ENCODING_PEM;
+
+ return SRPSaveOwnCertChain(&ownCert, &ownKey, credId);
+}
+
+/**
* function to register notifier for Trustcertchain change.
*
* @param[in] ctx user context.
ownerAce->permission = PERMISSION_FULL_CONTROL;
//Set subject as PT's UUID
+ ownerAce->subjectType = OicSecAceUuidSubject;
memcpy(ownerAce->subjectuuid.id, owner->id, sizeof(owner->id));
wildcardRsrc->href = OICStrdup(WILDCARD_RESOURCE_URI);
goto error;
}
- res = AclToCBORPayload(ownerAcl, &secPayload->securityData, &secPayload->payloadSize);
+ res = AclToCBORPayload(ownerAcl, OIC_SEC_ACL_V1, &secPayload->securityData, &secPayload->payloadSize);
if (OC_STACK_OK != res)
{
OICFree(secPayload);
#include "pstatresource.h"
#include "srmresourcestrings.h"
#include "credresource.h"
+#include "csrresource.h"
+#include "rolesresource.h"
#include "doxmresource.h"
#include "pconfresource.h"
#include "credentialgenerator.h"
int numOfResults; /**< Number of results in result array.**/
};
+typedef struct GetCsrData GetCsrData_t;
+struct GetCsrData {
+ void *ctx;
+ const OCProvisionDev_t *deviceInfo; /**< Pointer to PMDevInfo_t.**/
+ OCGetCSRResultCB resultCallback; /**< Pointer to result callback.**/
+ OCPMGetCsrResult_t *resArr; /**< Result array.**/
+ size_t numOfResults; /**< Number of results in result array.**/
+};
+
+typedef struct GetRolesData GetRolesData_t;
+struct GetRolesData {
+ void *ctx; /**< User-provided context **/
+ const OCProvisionDev_t *deviceInfo; /**< Pointer to PMDevInfo_t.**/
+ OCGetRolesResultCB resultCallback; /**< Pointer to result callback.**/
+ OCPMGetRolesResult_t *resArr; /**< Result array.**/
+ size_t numOfResults; /**< Number of results in result array.**/
+};
+
/**
* Structure to carry PCONF provision API data to callback.
*/
return OC_STACK_DELETE_TRANSACTION;
}
-
/**
* Internal function for handling credential generation and sending credential to resource server.
*
return OC_STACK_DELETE_TRANSACTION;
}
+static OCStackApplicationResult provisionIdentityCertCB(void *ctx, OCDoHandle UNUSED,
+ OCClientResponse *clientResponse)
+{
+ // Just call the callback provided to SRProvisionCredentials
+ VERIFY_NOT_NULL_RETURN(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
+ CredentialData_t* credData = (CredentialData_t *)ctx;
+ (void)UNUSED;
+ bool hasError;
+
+ // We expect OC_STACK_RESOURCE_CHANGED, anything else is an error
+ if (clientResponse && (OC_STACK_RESOURCE_CHANGED == clientResponse->result))
+ {
+ hasError = false;
+ }
+ else
+ {
+ hasError = true;
+ }
+
+ OCProvisionResultCB resultCallback = credData->resultCallback;
+ VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_DELETE_TRANSACTION);
+
+ ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
+ credData->resArr, hasError);
+
+ OICFree(credData);
+
+ return OC_STACK_DELETE_TRANSACTION;
+
+}
+
OCStackResult SRPProvisionTrustCertChain(void *ctx, OicSecCredType_t type, uint16_t credId,
const OCProvisionDev_t *selectedDeviceInfo, OCProvisionResultCB resultCallback)
{
- OIC_LOG(INFO, TAG, "In SRPProvisionTrustCertChain");
+ OIC_LOG(INFO, TAG, "IN SRPProvisionTrustCertChain");
VERIFY_NOT_NULL_RETURN(TAG, selectedDeviceInfo, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_INVALID_CALLBACK);
if (SIGNED_ASYMMETRIC_KEY != type)
return OC_STACK_NO_MEMORY;
}
secPayload->base.type = PAYLOAD_TYPE_SECURITY;
- int secureFlag = 0;
+ int secureFlag = 1; /* Don't send the private key to the device, if it happens to be present */
if(OC_STACK_OK != CredToCBORPayload(trustCertChainCred, &secPayload->securityData, &secPayload->payloadSize, secureFlag))
{
DeleteCredList(trustCertChainCred);
}
VERIFY_SUCCESS_RETURN(TAG, (OC_STACK_OK == ret), ERROR, OC_STACK_ERROR);
+
+ OIC_LOG(INFO, TAG, "OUT SRPProvisionTrustCertChain");
+
return OC_STACK_OK;
}
res = GetDoxmDeviceID(&cred->subject);
if (OC_STACK_OK != res)
{
- OIC_LOG(ERROR, TAG, "Cann't get the device id(GetDoxmDeviceID)");
+ OIC_LOG(ERROR, TAG, "Can't get the device id(GetDoxmDeviceID)");
DeleteCredList(cred);
return res;
}
- cred->credUsage= (char *)OICCalloc(1, strlen(TRUST_CA)+1 );
+ cred->credUsage= (char *)OICCalloc(1, strlen(TRUST_CA) + 1);
VERIFY_NOT_NULL_RETURN(TAG, cred->credUsage, ERROR, OC_STACK_NO_MEMORY);
OICStrcpy(cred->credUsage, strlen(TRUST_CA) + 1, TRUST_CA);
VERIFY_NOT_NULL_RETURN(TAG, cred->optionalData.data, ERROR, OC_STACK_NO_MEMORY);
cred->optionalData.len = chainSize + 1;
}
- else
+ else if (encodingType == OIC_ENCODING_DER)
{
cred->optionalData.data = (uint8_t *)OICCalloc(1, chainSize);
VERIFY_NOT_NULL_RETURN(TAG, cred->optionalData.data, ERROR, OC_STACK_NO_MEMORY);
cred->optionalData.len = chainSize;
}
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Unknown encoding in %s", __func__);
+ DeleteCredList(cred);
+ return OC_STACK_INVALID_PARAM;
+ }
memcpy(cred->optionalData.data, trustCertChain, chainSize);
cred->optionalData.encoding = encodingType;
cred->optionalData.revstat = false;
return res;
}
-OCStackResult SRPSaveOwnCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_t *credId)
+static OCStackResult saveCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_t *credId, const char* usage)
{
- OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+ OIC_LOG_V(DEBUG, TAG, "IN %s", __func__);
VERIFY_NOT_NULL_RETURN(TAG, cert, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, cert->data, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, key, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, key->data, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, credId, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, usage, ERROR, OC_STACK_INVALID_PARAM);
OCStackResult res = OC_STACK_ERROR;
return res;
}
- cred->credUsage= (char *)OICCalloc(1, strlen(PRIMARY_CERT)+1 );
+ cred->credUsage= (char *)OICCalloc(1, strlen(usage) + 1);
VERIFY_NOT_NULL_RETURN(TAG, cred->credUsage, ERROR, OC_STACK_NO_MEMORY);
- OICStrcpy(cred->credUsage, strlen(PRIMARY_CERT) + 1, PRIMARY_CERT) ;
+ OICStrcpy(cred->credUsage, strlen(usage) + 1, usage);
cred->credType = SIGNED_ASYMMETRIC_KEY;
}
*credId = cred->credId;
- OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ OIC_LOG_V(DEBUG, TAG, "OUT %s", __func__);
return res;
}
+
+OCStackResult SRPSaveOwnCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_t *credId)
+{
+ return saveCertChain(cert, key, credId, PRIMARY_CERT);
+}
+
#endif // __WITH_DTLS__ || __WITH_TLS__
OCStackResult SRPProvisionCredentials(void *ctx, OicSecCredType_t type, size_t keySize,
const OCProvisionDev_t *pDev1,
const OCProvisionDev_t *pDev2,
+ const char* pemCert,
OCProvisionResultCB resultCallback)
{
VERIFY_NOT_NULL_RETURN(TAG, pDev1, ERROR, OC_STACK_INVALID_PARAM);
}
if (!resultCallback)
{
- OIC_LOG(INFO, TAG, "SRPUnlinkDevices : NULL Callback");
+ OIC_LOG(INFO, TAG, "SRPProvisionCredentials: NULL Callback");
return OC_STACK_INVALID_CALLBACK;
}
if (SYMMETRIC_PAIR_WISE_KEY == type &&
0 == memcmp(&pDev1->doxm->deviceID, &pDev2->doxm->deviceID, sizeof(OicUuid_t)))
{
- OIC_LOG(INFO, TAG, "SRPUnlinkDevices : Same device ID");
+ OIC_LOG(INFO, TAG, "SRPProvisionCredentials : Same device ID");
return OC_STACK_INVALID_PARAM;
}
VERIFY_SUCCESS_RETURN(TAG, (res==OC_STACK_OK), ERROR, OC_STACK_ERROR);
return res;
}
+ case SIGNED_ASYMMETRIC_KEY:
+ {
+ /* pDev1 is the device to be provisioned, checked non-null above */
+ /* pDev2 is not used, should be NULL */
+ /* size param is not used. */
+ /* pemCert is the cerficiate to be provisioned */
+ VERIFY_NOT_NULL_RETURN(TAG, pemCert, ERROR, OC_STACK_INVALID_PARAM);
+
+ OicSecKey_t deviceCert = { 0 };
+ deviceCert.data = (uint8_t*) pemCert; /* Casting away const is OK here */
+ deviceCert.len = strlen(pemCert) + 1;
+ deviceCert.encoding = OIC_ENCODING_PEM;
+
+ /* Create a credential object */
+ OicSecCred_t* cred = GenerateCredential(&pDev1->doxm->deviceID, SIGNED_ASYMMETRIC_KEY,
+ &deviceCert, NULL, // oic.sec.cred.publicdata = deviceCert, .privatedata = NULL
+ &provTooldeviceID, NULL); // rowner is the provisioning tool and no eowner
+ VERIFY_NOT_NULL_RETURN(TAG, cred, ERROR, OC_STACK_ERROR);
+
+ cred->publicData.encoding = OIC_ENCODING_PEM;
+ cred->credUsage = OICStrdup(PRIMARY_CERT);
+
+ /* Create credential data (used by the response handler provisionIdentityCertCB and freed there) */
+ CredentialData_t *credData = (CredentialData_t *)OICCalloc(1, sizeof(CredentialData_t));
+ if (NULL == credData)
+ {
+ DeleteCredList(cred);
+ OIC_LOG(ERROR, TAG, "Memory allocation problem");
+ return OC_STACK_NO_MEMORY;
+ }
+ credData->deviceInfo1 = pDev1;
+ credData->deviceInfo2 = NULL;
+ credData->credInfo = cred;
+ credData->ctx = ctx;
+ credData->credInfoFirst = cred;
+ credData->numOfResults = 0;
+ credData->resultCallback = resultCallback;
+ credData->resArr = NULL;
+
+ /* Note: the callback of type OCClientResponseHandler, thin wrapper that calls resultCallback */
+ OCStackResult res = provisionCredentials(cred, pDev1, credData, &provisionIdentityCertCB);
+ if (res != OC_STACK_OK)
+ {
+ OICFree(credData);
+ }
+
+ DeleteCredList(cred);
+ return OC_STACK_OK;
+ }
default:
{
OIC_LOG(ERROR, TAG, "Invalid option.");
}
OCStackResult SRPProvisionACL(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
- OicSecAcl_t *acl, OCProvisionResultCB resultCallback)
+ OicSecAcl_t *acl, OicSecAclVersion_t aclVersion, OCProvisionResultCB resultCallback)
{
VERIFY_NOT_NULL_RETURN(TAG, selectedDeviceInfo, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, acl, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_INVALID_CALLBACK);
+ const char *uri = NULL;
+
+ switch (aclVersion)
+ {
+ case OIC_SEC_ACL_V1:
+ uri = OIC_RSRC_ACL_URI;
+ break;
+ case OIC_SEC_ACL_V2:
+ uri = OIC_RSRC_ACL2_URI;
+ break;
+ default:
+ return OC_STACK_INVALID_PARAM;
+ }
+
// if rowneruuid is empty, set it to device ID
OicUuid_t emptyOwner = {.id = {0} };
if (memcmp(&(acl->rownerID.id), &emptyOwner, UUID_IDENTITY_SIZE) == 0)
return OC_STACK_NO_MEMORY;
}
secPayload->base.type = PAYLOAD_TYPE_SECURITY;
- if(OC_STACK_OK != AclToCBORPayload(acl, &secPayload->securityData, &secPayload->payloadSize))
+ if(OC_STACK_OK != AclToCBORPayload(acl, aclVersion, &secPayload->securityData, &secPayload->payloadSize))
{
OCPayloadDestroy((OCPayload *)secPayload);
OIC_LOG(ERROR, TAG, "Failed to AclToCBORPayload");
selectedDeviceInfo->endpoint.addr,
selectedDeviceInfo->securePort,
selectedDeviceInfo->connType,
- query, sizeof(query), OIC_RSRC_ACL_URI))
+ query, sizeof(query), uri))
{
OIC_LOG(ERROR, TAG, "DeviceDiscoveryHandler : Failed to generate query");
return OC_STACK_ERROR;
}
OCStackResult SRPGetACLResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
- OCProvisionResultCB resultCallback)
+ OicSecAclVersion_t aclVersion, OCProvisionResultCB resultCallback)
{
VERIFY_NOT_NULL_RETURN(TAG, selectedDeviceInfo, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_INVALID_CALLBACK);
+ const char *uri = NULL;
+
+ switch (aclVersion)
+ {
+ case OIC_SEC_ACL_V1:
+ uri = OIC_RSRC_ACL_URI;
+ break;
+ case OIC_SEC_ACL_V2:
+ uri = OIC_RSRC_ACL2_URI;
+ break;
+ default:
+ return OC_STACK_INVALID_PARAM;
+ }
+
char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
if(!PMGenerateQuery(true,
selectedDeviceInfo->endpoint.addr,
selectedDeviceInfo->securePort,
selectedDeviceInfo->connType,
- query, sizeof(query), OIC_RSRC_ACL_URI))
+ query, sizeof(query), uri))
{
OIC_LOG(ERROR, TAG, "SRPGetACLResource : Failed to generate query");
return OC_STACK_ERROR;
return OC_STACK_OK;
}
+/**
+ * Internal Function to store results in result array during GetCSRResourceCB.
+ */
+static void registerResultForGetCSRResourceCB(GetCsrData_t *getCsrData,
+ OCStackResult stackresult,
+ const uint8_t *payload,
+ size_t payloadSize)
+{
+ /* SRPGetCSRResource allocates the memory for getCsrData. When it calls this callback,
+ * numOfResults points to the current entry we're filling out. Later when this structure
+ * gets returned to the caller, that's when it actually reflects the number of
+ * results returned.
+ */
+ OCPMGetCsrResult_t* currentEntry = &getCsrData->resArr[getCsrData->numOfResults];
+ OIC_LOG_V(INFO, TAG, "Inside registerResultForGetCSRResourceCB "
+ "getCsrData->numOfResults is %d\n", getCsrData->numOfResults);
+ memcpy(currentEntry->deviceId.id,
+ getCsrData->deviceInfo->doxm->deviceID.id, UUID_LENGTH);
+ currentEntry->res = stackresult;
+
+ if (OC_STACK_OK == stackresult)
+ {
+ OCStackResult res = CBORPayloadToCSR(payload, payloadSize,
+ ¤tEntry->csr,
+ ¤tEntry->csrLen,
+ ¤tEntry->encoding);
+ if (OC_STACK_OK != res)
+ {
+ currentEntry->res = res;
+ currentEntry->csr = NULL;
+ currentEntry->csrLen = 0;
+ currentEntry->encoding = OIC_ENCODING_UNKNOW;
+ }
+ }
+
+ ++(getCsrData->numOfResults);
+}
+
+/**
+ * Callback handler of SRPGetCSRResource.
+ *
+ * @param[in] ctx ctx value passed to callback from calling function.
+ * @param[in] UNUSED handle to an invocation
+ * @param[in] clientResponse Response from queries to remote servers.
+ * @return OC_STACK_DELETE_TRANSACTION to delete the transaction
+ * and OC_STACK_KEEP_TRANSACTION to keep it.
+ */
+static OCStackApplicationResult SRPGetCSRResourceCB(void *ctx, OCDoHandle UNUSED,
+ OCClientResponse *clientResponse)
+{
+ size_t i = 0;
+ OIC_LOG_V(INFO, TAG, "IN %s", __func__);
+ OC_UNUSED(UNUSED);
+ VERIFY_NOT_NULL_RETURN(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
+ GetCsrData_t *getCsrData = (GetCsrData_t*)ctx;
+ OCGetCSRResultCB resultCallback = getCsrData->resultCallback;
+
+ if (clientResponse)
+ {
+ if (OC_STACK_OK == clientResponse->result)
+ {
+ uint8_t *payload = ((OCSecurityPayload*)clientResponse->payload)->securityData;
+ size_t size = ((OCSecurityPayload*)clientResponse->payload)->payloadSize;
+
+ OIC_LOG_BUFFER(DEBUG, TAG, payload, size);
+
+ registerResultForGetCSRResourceCB(getCsrData, OC_STACK_OK, payload, size);
+ }
+ }
+ else
+ {
+ registerResultForGetCSRResourceCB(getCsrData, OC_STACK_ERROR, NULL, 0);
+ }
+
+ ((OCGetCSRResultCB)(resultCallback))(getCsrData->ctx, getCsrData->numOfResults,
+ getCsrData->resArr,
+ false);
+ OIC_LOG_V(ERROR, TAG, "%s: received Null clientResponse", __func__);
+ for (i = 0; i < getCsrData->numOfResults; i++)
+ {
+ OICFree(getCsrData->resArr[i].csr);
+ }
+ OICFree(getCsrData->resArr);
+ OICFree(getCsrData);
+ OIC_LOG_V(INFO, TAG, "OUT %s", __func__);
+
+ return OC_STACK_DELETE_TRANSACTION;
+}
+
+OCStackResult SRPGetCSRResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCGetCSRResultCB resultCallback)
+{
+ VERIFY_NOT_NULL_RETURN(TAG, selectedDeviceInfo, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_INVALID_CALLBACK);
+
+ char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
+ if (!PMGenerateQuery(true,
+ selectedDeviceInfo->endpoint.addr,
+ selectedDeviceInfo->securePort,
+ selectedDeviceInfo->connType,
+ query, sizeof(query), OIC_RSRC_CSR_URI))
+ {
+ OIC_LOG(ERROR, TAG, "SRPGetCSRResource : Failed to generate query");
+ return OC_STACK_ERROR;
+ }
+ OIC_LOG_V(DEBUG, TAG, "Query=%s", query);
+
+ OCCallbackData cbData = {.context=NULL, .cb=NULL, .cd=NULL};
+ cbData.cb = &SRPGetCSRResourceCB;
+ GetCsrData_t* getCsrData = (GetCsrData_t*)OICCalloc(1, sizeof(GetCsrData_t));
+ if (NULL == getCsrData)
+ {
+ OIC_LOG(ERROR, TAG, "Unable to allocate memory");
+ return OC_STACK_NO_MEMORY;
+ }
+ getCsrData->deviceInfo = selectedDeviceInfo;
+ getCsrData->resultCallback = resultCallback;
+ getCsrData->numOfResults=0;
+ getCsrData->ctx = ctx;
+
+ int noOfRiCalls = 1;
+ getCsrData->resArr = (OCPMGetCsrResult_t*)OICCalloc(noOfRiCalls, sizeof(OCPMGetCsrResult_t));
+ if (NULL == getCsrData->resArr)
+ {
+ OICFree(getCsrData);
+ OIC_LOG(ERROR, TAG, "Unable to allocate memory");
+ return OC_STACK_NO_MEMORY;
+ }
+ cbData.context = (void *)getCsrData;
+ OCMethod method = OC_REST_GET;
+ OCDoHandle handle = NULL;
+ OIC_LOG(DEBUG, TAG, "Sending Get CSR to resource server");
+ OCStackResult ret = OCDoResource(&handle, method, query, NULL, NULL,
+ selectedDeviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
+ if (OC_STACK_OK != ret)
+ {
+ OIC_LOG(ERROR, TAG, "OCStack resource error");
+ OICFree(getCsrData->resArr);
+ OICFree(getCsrData);
+ }
+ OIC_LOG(DEBUG, TAG, "OUT SRPGetCSRResource");
+
+ return ret;
+}
+
+/**
+ * Internal Function to store results in result array during GetRolesResourceCB.
+ */
+static void registerResultForGetRolesResourceCB(GetRolesData_t *getRolesData,
+ OCStackResult stackresult,
+ const uint8_t *payload,
+ size_t payloadSize)
+{
+ /* SRPGetRolesResource allocates the memory for getRolesData. When it calls this callback,
+ * numOfResults points to the current entry we're filling out. Later when this structure
+ * gets returned to the caller, that's when it actually reflects the number of
+ * results returned.
+ */
+ OCPMGetRolesResult_t* currentEntry = &getRolesData->resArr[getRolesData->numOfResults];
+ OIC_LOG_V(INFO, TAG, "Inside registerResultForGetCSRResourceCB "
+ "getRolesData->numOfResults is %d\n", getRolesData->numOfResults);
+ memcpy(currentEntry->deviceId.id,
+ getRolesData->deviceInfo->doxm->deviceID.id, UUID_LENGTH);
+ currentEntry->res = stackresult;
+ currentEntry->chainsLength = 0;
+
+ if (OC_STACK_OK == stackresult)
+ {
+ RoleCertChain_t *chains = NULL;
+ OCStackResult res = CBORPayloadToRoles(payload, payloadSize, &chains);
+ if (OC_STACK_OK != res)
+ {
+ currentEntry->res = res;
+ currentEntry->chains = NULL;
+ }
+ else
+ {
+ RoleCertChain_t *curr = NULL;
+ for (curr = chains; NULL != curr; curr = curr->next)
+ {
+ currentEntry->chainsLength++;
+ }
+ currentEntry->chains = (OCPMRoleCertChain_t *)OICCalloc(currentEntry->chainsLength, sizeof(OCPMRoleCertChain_t));
+ if (NULL == currentEntry->chains)
+ {
+ OIC_LOG(ERROR, TAG, "No memory allocating role chains");
+ currentEntry->chainsLength = 0;
+ currentEntry->res = OC_STACK_NO_MEMORY;
+ }
+ else
+ {
+ size_t i;
+ for (i = 0, curr = chains; NULL != curr; curr = curr->next, i++)
+ {
+ currentEntry->chains[i].credId = curr->credId;
+ /* Take ownership of the buffers from certificate and optData, rather than copy. */
+ currentEntry->chains[i].certificate = curr->certificate;
+ currentEntry->chains[i].optData = curr->optData;
+
+ curr->certificate.data = NULL;
+ curr->certificate.len = 0;
+ curr->optData.data = NULL;
+ curr->optData.len = 0;
+ }
+ }
+ FreeRoleCertChainList(chains);
+ }
+ }
+
+ ++(getRolesData->numOfResults);
+}
+
+/**
+ * Callback handler of SRPGetRolesResource.
+ *
+ * @param[in] ctx ctx value passed to callback from calling function.
+ * @param[in] UNUSED handle to an invocation
+ * @param[in] clientResponse Response from queries to remote servers.
+ * @return OC_STACK_DELETE_TRANSACTION to delete the transaction
+ * and OC_STACK_KEEP_TRANSACTION to keep it.
+ */
+static OCStackApplicationResult SRPGetRolesResourceCB(void *ctx, OCDoHandle handle,
+ OCClientResponse *clientResponse)
+{
+ OIC_LOG(INFO, TAG, "Inside SRPGetRolesResourceCB.");
+ OC_UNUSED(handle);
+ VERIFY_NOT_NULL_RETURN(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
+ GetRolesData_t *getRolesData = (GetRolesData_t*)ctx;
+ OCGetRolesResultCB resultCallback = getRolesData->resultCallback;
+
+ if (clientResponse)
+ {
+ if (OC_STACK_OK == clientResponse->result)
+ {
+ uint8_t *payload = ((OCSecurityPayload*)clientResponse->payload)->securityData;
+ size_t size = ((OCSecurityPayload*)clientResponse->payload)->payloadSize;
+
+ OIC_LOG_BUFFER(DEBUG, TAG, payload, size);
+
+ registerResultForGetRolesResourceCB(getRolesData, OC_STACK_OK, payload, size);
+ }
+ }
+ else
+ {
+ OIC_LOG(ERROR, TAG, "SRPGetRolesResourceCB received Null clientResponse");
+ registerResultForGetRolesResourceCB(getRolesData, OC_STACK_ERROR, NULL, 0);
+ }
+
+ resultCallback(getRolesData->ctx, getRolesData->numOfResults,
+ getRolesData->resArr,
+ false);
+ for (size_t i = 0; i < getRolesData->numOfResults; i++)
+ {
+ /* We took ownership of certificate.data and optData.data, so we must free them.
+ * These are allocated internally by tinycbor, which uses malloc and free, so we call
+ * free directly for those.
+ */
+ for (size_t j = 0; j < getRolesData->resArr[i].chainsLength; j++)
+ {
+ free(getRolesData->resArr[i].chains[j].certificate.data);
+ free(getRolesData->resArr[i].chains[j].optData.data);
+ }
+ OICFree(getRolesData->resArr[i].chains);
+ }
+ OICFree(getRolesData->resArr);
+ OICFree(getRolesData);
+
+ return OC_STACK_DELETE_TRANSACTION;
+}
+
+OCStackResult SRPGetRolesResource(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCGetRolesResultCB resultCallback)
+{
+ VERIFY_NOT_NULL_RETURN(TAG, selectedDeviceInfo, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_INVALID_CALLBACK);
+
+ char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
+ if (!PMGenerateQuery(true,
+ selectedDeviceInfo->endpoint.addr,
+ selectedDeviceInfo->securePort,
+ selectedDeviceInfo->connType,
+ query, sizeof(query), OIC_RSRC_ROLES_URI))
+ {
+ OIC_LOG(ERROR, TAG, "SRPGetRolesResource : Failed to generate query");
+ return OC_STACK_ERROR;
+ }
+ OIC_LOG_V(DEBUG, TAG, "Query=%s", query);
+
+ OCCallbackData cbData = {.context=NULL, .cb=NULL, .cd=NULL};
+ cbData.cb = &SRPGetRolesResourceCB;
+ GetRolesData_t *getRolesData = (GetRolesData_t*)OICCalloc(1, sizeof(GetRolesData_t));
+ if (NULL == getRolesData)
+ {
+ OIC_LOG(ERROR, TAG, "Unable to allocate memory");
+ return OC_STACK_NO_MEMORY;
+ }
+ getRolesData->deviceInfo = selectedDeviceInfo;
+ getRolesData->resultCallback = resultCallback;
+ getRolesData->numOfResults = 0;
+ getRolesData->ctx = ctx;
+
+ getRolesData->resArr = (OCPMGetRolesResult_t*)OICCalloc(1, sizeof(OCPMGetRolesResult_t));
+ if (NULL == getRolesData->resArr)
+ {
+ OICFree(getRolesData);
+ OIC_LOG(ERROR, TAG, "Unable to allocate memory");
+ return OC_STACK_NO_MEMORY;
+ }
+ cbData.context = (void *)getRolesData;
+ OCDoHandle handle = NULL;
+ OIC_LOG(DEBUG, TAG, "Sending Get Roles to resource server");
+ OCStackResult ret = OCDoResource(&handle, OC_REST_GET, query, NULL, NULL,
+ selectedDeviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
+ if (OC_STACK_OK != ret)
+ {
+ OIC_LOG(ERROR, TAG, "OCStack resource error");
+ OICFree(getRolesData->resArr);
+ OICFree(getRolesData);
+ }
+ OIC_LOG(DEBUG, TAG, "OUT SRPGetRolesResource");
+
+ return ret;
+}
+
+/**
+ * Callback handler of SRPDeleteRoleCertificateByCredId.
+ *
+ * @param[in] ctx ctx value passed to callback from calling function.
+ * @param[in] UNUSED handle to an invocation
+ * @param[in] clientResponse Response from queries to remote servers.
+ * @return OC_STACK_DELETE_TRANSACTION to delete the transaction
+ * and OC_STACK_KEEP_TRANSACTION to keep it.
+ */
+static OCStackApplicationResult SRPDeleteRoleCertificateCB(void *ctx, OCDoHandle UNUSED,
+ OCClientResponse *clientResponse)
+{
+ OC_UNUSED(UNUSED);
+ GetSecData_t *getSecData = (GetSecData_t *)ctx;
+
+ OIC_LOG(DEBUG, TAG, "SRPDeleteRoleCertificateCB IN");
+
+ if (NULL != clientResponse)
+ {
+ memcpy(getSecData->resArr[(getSecData->numOfResults)].deviceId.id,
+ getSecData->deviceInfo->doxm->deviceID.id, UUID_LENGTH);
+ getSecData->resArr[(getSecData->numOfResults)].res = clientResponse->result;
+ ++(getSecData->numOfResults);
+ }
+
+ OIC_LOG(DEBUG, TAG, "SRPDeleteRoleCertificateCB OUT");
+
+ return OC_STACK_DELETE_TRANSACTION;
+}
+
+OCStackResult SRPDeleteRoleCertificateByCredId(void* ctx, const OCProvisionDev_t *selectedDeviceInfo,
+ OCProvisionResultCB resultCallback, uint32_t credId)
+{
+ VERIFY_NOT_NULL_RETURN(TAG, selectedDeviceInfo, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_INVALID_CALLBACK);
+
+ char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
+ if (!PMGenerateQuery(true,
+ selectedDeviceInfo->endpoint.addr,
+ selectedDeviceInfo->securePort,
+ selectedDeviceInfo->connType,
+ query, sizeof(query), OIC_RSRC_ROLES_URI))
+ {
+ OIC_LOG(ERROR, TAG, "SRPDeleteRoleCertificateByCredId : Failed to generate query");
+ return OC_STACK_ERROR;
+ }
+ size_t queryLen = strlen(query);
+ int snRet = snprintf(query + queryLen, sizeof(query) - queryLen, "?credId=%u", credId);
+
+ if (0 > snRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "snprintf returned error: %d", snRet);
+ return OC_STACK_ERROR;
+ }
+ else if ((size_t)snRet >= (sizeof(query) - queryLen))
+ {
+ OIC_LOG_V(ERROR, TAG, "snprintf truncated");
+ return OC_STACK_ERROR;
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "Query=%s", query);
+
+ OCCallbackData cbData = {.context=NULL, .cb=NULL, .cd=NULL};
+ cbData.cb = &SRPDeleteRoleCertificateCB;
+ GetSecData_t *getSecData = (GetSecData_t*)OICCalloc(1, sizeof(GetSecData_t));
+ if (NULL == getSecData)
+ {
+ OIC_LOG(ERROR, TAG, "Unable to allocate memory");
+ return OC_STACK_NO_MEMORY;
+ }
+ getSecData->deviceInfo = selectedDeviceInfo;
+ getSecData->resultCallback = resultCallback;
+ getSecData->numOfResults = 0;
+ getSecData->ctx = ctx;
+
+ getSecData->resArr = (OCProvisionResult_t*)OICCalloc(1, sizeof(OCProvisionResult_t));
+ if (NULL == getSecData->resArr)
+ {
+ OICFree(getSecData);
+ OIC_LOG(ERROR, TAG, "Unable to allocate memory");
+ return OC_STACK_NO_MEMORY;
+ }
+ cbData.context = (void *)getSecData;
+ OCMethod method = OC_REST_DELETE;
+ OCDoHandle handle = NULL;
+ OIC_LOG(DEBUG, TAG, "Sending Delete Roles to resource server");
+ OCStackResult ret = OCDoResource(&handle, method, query, NULL, NULL,
+ selectedDeviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
+ if (OC_STACK_OK != ret)
+ {
+ OIC_LOG(ERROR, TAG, "OCStack resource error");
+ OICFree(getSecData->resArr);
+ OICFree(getSecData);
+ }
+ OIC_LOG(DEBUG, TAG, "OUT SRPGetRolesResource");
+
+ return ret;
+}
+
OCStackResult SRPReadTrustCertChain(uint16_t credId, uint8_t **trustCertChain,
size_t *chainSize)
{
uint8_t deviceId2[] = {0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x63};
memcpy(pDev2.doxm->deviceID.id, deviceId2, sizeof(deviceId2));
- EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionACL(NULL, NULL, &acl, &provisioningCB));
+ EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionACL(NULL, NULL, &acl, OIC_SEC_ACL_V2, &provisioningCB));
}
TEST(SRPProvisionACLTest, NullCallback)
{
- EXPECT_EQ(OC_STACK_INVALID_CALLBACK, SRPProvisionACL(NULL, &pDev1, &acl, NULL));
+ EXPECT_EQ(OC_STACK_INVALID_CALLBACK, SRPProvisionACL(NULL, &pDev1, &acl, OIC_SEC_ACL_V2, NULL));
}
TEST(SRPProvisionACLTest, NullACL)
{
- EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionACL(NULL, &pDev1, NULL, &provisioningCB));
+ EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionACL(NULL, &pDev1, NULL, OIC_SEC_ACL_V2, &provisioningCB));
+}
+
+TEST(SRPProvisionACLTest, InvalidVersion)
+{
+ EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionACL(NULL, &pDev1, &acl, OIC_SEC_ACL_UNKNOWN, &provisioningCB));
}
TEST(SRPProvisionCredentialsTest, NullDevice1)
{
EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionCredentials(NULL, credType,
OWNER_PSK_LENGTH_128, NULL,
- &pDev2, &provisioningCB));
+ &pDev2, NULL, &provisioningCB));
}
TEST(SRPProvisionCredentialsTest, SamelDeviceId)
{
EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionCredentials(NULL, credType,
OWNER_PSK_LENGTH_128, &pDev1,
- &pDev1, &provisioningCB));
+ &pDev1, NULL, &provisioningCB));
}
TEST(SRPProvisionCredentialsTest, NullCallback)
{
EXPECT_EQ(OC_STACK_INVALID_CALLBACK, SRPProvisionCredentials(NULL, credType,
OWNER_PSK_LENGTH_128,
- &pDev1, &pDev2, NULL));
+ &pDev1, &pDev2, NULL, NULL));
}
TEST(SRPProvisionCredentialsTest, InvalidKeySize)
{
EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionCredentials(NULL, credType,
- 0, &pDev1, &pDev2,
+ 0, &pDev1, &pDev2, NULL,
&provisioningCB));
}
TEST(SRPProvisionTrustCertChainTest, SRPGetACLResourceNoCallback)
{
- EXPECT_EQ(OC_STACK_INVALID_CALLBACK, SRPGetACLResource(NULL, &pDev1, NULL));
+ EXPECT_EQ(OC_STACK_INVALID_CALLBACK, SRPGetACLResource(NULL, &pDev1, OIC_SEC_ACL_V2, NULL));
}
TEST(SRPProvisionTrustCertChainTest, SRPGetACLResourceNoDevice)
{
- EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPGetACLResource(NULL, NULL, provisioningCB));
+ EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPGetACLResource(NULL, NULL, OIC_SEC_ACL_V2, provisioningCB));
}
TEST(SRPProvisionTrustCertChainTest, SRPGetCredResourceNoCallback)
static const uint8_t ACL_ACLIST_MAP_SIZE = 1;
static const uint8_t ACL_ACES_MAP_SIZE = 3;
static const uint8_t ACL_RESOURCE_MAP_SIZE = 3;
+static const uint8_t ACE_ROLE_MAP_SIZE = 2;
// CborSize is the default cbor payload size being used.
static OicSecAcl_t *gAcl = NULL;
static OCResourceHandle gAclHandle = NULL;
+static OCResourceHandle gAcl2Handle = NULL;
void FreeRsrc(OicSecRsrc_t *rsrc)
{
newAce = (OicSecAce_t*)OICCalloc(1, sizeof(OicSecAce_t));
VERIFY_NOT_NULL(TAG, newAce, ERROR);
- //Subject uuid
- memcpy(&newAce->subjectuuid, &ace->subjectuuid, sizeof(OicUuid_t));
+ //Subject
+ newAce->subjectType = ace->subjectType;
+ switch (newAce->subjectType)
+ {
+ case OicSecAceUuidSubject:
+ memcpy(&newAce->subjectuuid, &ace->subjectuuid, sizeof(OicUuid_t));
+ break;
+ case OicSecAceRoleSubject:
+ memcpy(&newAce->subjectRole, &ace->subjectRole, sizeof(ace->subjectRole));
+ break;
+ default:
+ assert(!"Unsupported ACE type");
+ OICFree(newAce);
+ return NULL;
+ }
OicSecRsrc_t* rsrc = NULL;
LL_FOREACH(ace->resources, rsrc)
return size;
}
-OCStackResult AclToCBORPayload(const OicSecAcl_t *secAcl, uint8_t **payload, size_t *size)
+OCStackResult AclToCBORPayload(const OicSecAcl_t *secAcl, OicSecAclVersion_t aclVersion, uint8_t **payload, size_t *size)
{
- if (NULL == secAcl || NULL == payload || NULL != *payload || NULL == size)
+ if (NULL == secAcl || NULL == payload || NULL != *payload || NULL == size)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ /* aclVersion parameter validation. */
+ switch (aclVersion)
{
+ case OIC_SEC_ACL_V1:
+ case OIC_SEC_ACL_V2:
+ break;
+ default:
return OC_STACK_INVALID_PARAM;
}
uint8_t aclMapSize = ACL_ACES_MAP_SIZE;
size_t inLen = 0;
+ // Version 1 doesn't support role subjects. If we have any, we can't comply with the request.
+ if ((OIC_SEC_ACL_V2 > aclVersion) && (OicSecAceRoleSubject == ace->subjectType))
+ {
+ cborEncoderResult = CborUnknownError;
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "ACE has a role subject; can't create a V1 ACL payload for this ACL.");
+ }
+
OicSecValidity_t* validityElts = ace->validities;
while(validityElts)
{
cborEncoderResult = cbor_encode_text_string(&oicSecAclMap, OIC_JSON_SUBJECTID_NAME,
strlen(OIC_JSON_SUBJECTID_NAME));
VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding Subject Name Tag.");
- inLen = (memcmp(&(ace->subjectuuid), &WILDCARD_SUBJECT_ID, sizeof(OicUuid_t)) == 0) ?
- WILDCARD_SUBJECT_ID_LEN : sizeof(OicUuid_t);
- if(inLen == WILDCARD_SUBJECT_ID_LEN)
+ if (OicSecAceUuidSubject == ace->subjectType)
{
- cborEncoderResult = cbor_encode_text_string(&oicSecAclMap, WILDCARD_RESOURCE_URI,
- strlen(WILDCARD_RESOURCE_URI));
- VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding Subject Id wildcard Value.");
+ inLen = (memcmp(&(ace->subjectuuid), &WILDCARD_SUBJECT_ID, sizeof(OicUuid_t)) == 0) ?
+ WILDCARD_SUBJECT_ID_LEN : sizeof(OicUuid_t);
+ if (inLen == WILDCARD_SUBJECT_ID_LEN)
+ {
+ cborEncoderResult = cbor_encode_text_string(&oicSecAclMap, WILDCARD_RESOURCE_URI,
+ strlen(WILDCARD_RESOURCE_URI));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding Subject Id wildcard Value.");
+ }
+ else
+ {
+ char *subject = NULL;
+ ret = ConvertUuidToStr(&ace->subjectuuid, &subject);
+ cborEncoderResult = (OC_STACK_OK == ret) ? CborNoError : CborUnknownError;
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed to convert subject UUID to string");
+ cborEncoderResult = cbor_encode_text_string(&oicSecAclMap, subject, strlen(subject));
+ OICFree(subject);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding Subject UUID Value");
+ }
+ }
+ else if (OicSecAceRoleSubject == ace->subjectType)
+ {
+ assert(OIC_SEC_ACL_V2 <= aclVersion);
+ CborEncoder roleMap;
+ cborEncoderResult = cbor_encoder_create_map(&oicSecAclMap, &roleMap, ACE_ROLE_MAP_SIZE);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed creating role map");
+
+ cborEncoderResult = cbor_encode_text_string(&roleMap, OIC_JSON_ROLEIDS_NAME, strlen(OIC_JSON_ROLEIDS_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding roleid tag");
+
+ cborEncoderResult = cbor_encode_text_string(&roleMap, ace->subjectRole.id, strlen(ace->subjectRole.id));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding roleid value");
+
+ cborEncoderResult = cbor_encode_text_string(&roleMap, OIC_JSON_AUTHORITY_NAME, strlen(OIC_JSON_AUTHORITY_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding authority tag");
+
+ cborEncoderResult = cbor_encode_text_string(&roleMap, ace->subjectRole.authority, strlen(ace->subjectRole.authority));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding authority value");
+
+ cborEncoderResult = cbor_encoder_close_container(&oicSecAclMap, &roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed closing role map");
}
else
{
- char *subject = NULL;
- ret = ConvertUuidToStr(&ace->subjectuuid, &subject);
- VERIFY_SUCCESS(TAG, ret == OC_STACK_OK, ERROR);
- cborEncoderResult = cbor_encode_text_string(&oicSecAclMap, subject, strlen(subject));
- VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding Subject UUID Value.");
- OICFree(subject);
+ assert(!"Unknown ACE subject type");
+ cborEncoderResult = CborUnknownError;
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Unknown ACE subject type");
}
// Resources
// Since the allocated initial memory failed, double the memory.
cborLen += cbor_encoder_get_buffer_size(&encoder, encoder.end);
cborEncoderResult = CborNoError;
- ret = AclToCBORPayload(secAcl, payload, &cborLen);
+ ret = AclToCBORPayload(secAcl, aclVersion, payload, &cborLen);
*size = cborLen;
}
else if (cborEncoderResult != CborNoError)
// This function converts CBOR format to ACL data.
// Caller needs to invoke 'free' when done using
// It parses { "aclist" : [ { ... } ] } instead of { "aclist" : { "aces" : [ ] } }
-OicSecAcl_t* CBORPayloadToAcl2(const uint8_t *cborPayload, const size_t size)
+OicSecAcl_t* CBORPayloadToCloudAcl(const uint8_t *cborPayload, const size_t size)
{
if (NULL == cborPayload || 0 == size)
{
if(strcmp(subject, WILDCARD_RESOURCE_URI) == 0)
{
ace->subjectuuid.id[0] = '*';
+ free(subject);
}
else
{
OIC_LOG_V(DEBUG, TAG, "Converting subjectuuid = %s to uuid...", subject);
ret = ConvertStrToUuid(subject, &ace->subjectuuid);
+ free(subject);
VERIFY_SUCCESS(TAG, ret == OC_STACK_OK, ERROR);
}
- OICFree(subject);
}
// Resources -- Mandatory
VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding Rownerid Value.");
OIC_LOG_V(DEBUG, TAG, "Converting rownerid = %s to uuid...", stRowner);
ret = ConvertStrToUuid(stRowner, &acl->rownerID);
+ free(stRowner);
VERIFY_SUCCESS(TAG, ret == OC_STACK_OK, ERROR);
- OICFree(stRowner);
}
- OICFree(tagName);
+ // Strings allocated with cbor_value_dup_text_string must be freed with free, not OICFree.
+ free(tagName);
+ tagName = NULL;
}
if (cbor_value_is_valid(&aclMap))
{
}
// This function converts CBOR format to ACL data.
-// Caller needs to invoke 'free' when done using
+// Caller needs to invoke 'OICFree' on returned value when done using
// note: This function is used in unit test hence not declared static,
OicSecAcl_t* CBORPayloadToAcl(const uint8_t *cborPayload, const size_t size)
{
CborValue aclCbor = { .parser = NULL };
CborParser parser = { .end = NULL };
CborError cborFindResult = CborNoError;
+ char *tagName = NULL;
+ char *roleTagName = NULL;
cbor_parser_init(cborPayload, size, 0, &parser, &aclCbor);
while (cbor_value_is_valid(&aclMap))
{
- char* tagName = NULL;
size_t len = 0;
CborType type = cbor_value_get_type(&aclMap);
if (type == CborTextStringType)
if (name)
{
// Subject -- Mandatory
- if (strcmp(name, OIC_JSON_SUBJECTID_NAME) == 0)
+ if (strcmp(name, OIC_JSON_SUBJECTID_NAME) == 0)
{
- char *subject = NULL;
- cborFindResult = cbor_value_dup_text_string(&aceMap, &subject, &tempLen, NULL);
- VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding subject Value.");
- if(strcmp(subject, WILDCARD_RESOURCE_URI) == 0)
+ if (cbor_value_is_text_string(&aceMap))
{
- ace->subjectuuid.id[0] = '*';
+ /* UUID-type subject */
+ char *subject = NULL;
+ cborFindResult = cbor_value_dup_text_string(&aceMap, &subject, &tempLen, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding subject Value.");
+ if (strcmp(subject, WILDCARD_RESOURCE_URI) == 0)
+ {
+ ace->subjectuuid.id[0] = '*';
+ ace->subjectType = OicSecAceUuidSubject;
+ }
+ else
+ {
+ ret = ConvertStrToUuid(subject, &ace->subjectuuid);
+ if (OC_STACK_OK != ret)
+ {
+ cborFindResult = CborUnknownError;
+ free(subject);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed converting subject UUID");
+ }
+ ace->subjectType = OicSecAceUuidSubject;
+ }
+ free(subject);
+ }
+ else if (cbor_value_is_container(&aceMap))
+ {
+ /* Role subject */
+ size_t unusedLen = 0;
+ CborValue roleMap;
+ memset(&roleMap, 0, sizeof(roleMap));
+
+ cborFindResult = cbor_value_enter_container(&aceMap, &roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed entering role map");
+
+ while (cbor_value_is_valid(&roleMap) && cbor_value_is_text_string(&roleMap))
+ {
+ cborFindResult = cbor_value_dup_text_string(&roleMap, &roleTagName, &unusedLen, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed getting role map tag name");
+ cborFindResult = cbor_value_advance(&roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing role map");
+
+ if (NULL != roleTagName)
+ {
+ if (strcmp(roleTagName, OIC_JSON_ROLEIDS_NAME) == 0)
+ {
+ char *roleId = NULL;
+ cborFindResult = cbor_value_dup_text_string(&roleMap, &roleId, &unusedLen, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed getting role id value");
+ if (strlen(roleId) >= sizeof(ace->subjectRole.id))
+ {
+ cborFindResult = CborUnknownError;
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Role ID is too long");
+ }
+ OICStrcpy(ace->subjectRole.id, sizeof(ace->subjectRole.id), roleId);
+ free(roleId);
+ }
+ else if (strcmp(roleTagName, OIC_JSON_AUTHORITY_NAME) == 0)
+ {
+ char *authorityName = NULL;
+ cborFindResult = cbor_value_dup_text_string(&roleMap, &authorityName, &unusedLen, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed getting role authority value");
+ if (strlen(authorityName) >= sizeof(ace->subjectRole.authority))
+ {
+ cborFindResult = CborUnknownError;
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Authority name is too long");
+ }
+ OICStrcpy(ace->subjectRole.authority, sizeof(ace->subjectRole.authority), authorityName);
+ free(authorityName);
+ }
+ else
+ {
+ OIC_LOG_V(WARNING, TAG, "Unknown tag name in role map: %s", roleTagName);
+ }
+
+ free(roleTagName);
+ roleTagName = NULL;
+ }
+
+ if (cbor_value_is_valid(&roleMap))
+ {
+ cborFindResult = cbor_value_advance(&roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing role map");
+ }
+ }
+
+ /* Make sure at least the id is present. */
+ if ('\0' == ace->subjectRole.id[0])
+ {
+ cborFindResult = CborUnknownError;
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "ID for role was not present in role map");
+ }
+
+ ace->subjectType = OicSecAceRoleSubject;
}
else
{
- ret = ConvertStrToUuid(subject, &ace->subjectuuid);
- VERIFY_SUCCESS(TAG, ret == OC_STACK_OK, ERROR);
+ cborFindResult = CborUnknownError;
+ OIC_LOG_V(ERROR, TAG, "Unknown subject value type: %d", cbor_value_get_type(&aceMap));
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Type of subject value was not expected");
}
- OICFree(subject);
}
// Resources -- Mandatory
cborFindResult = cbor_value_dup_text_string(&aclMap, &stRowner, &len, NULL);
VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding Rownerid Value.");
ret = ConvertStrToUuid(stRowner, &acl->rownerID);
+ free(stRowner);
VERIFY_SUCCESS(TAG, ret == OC_STACK_OK, ERROR);
- OICFree(stRowner);
}
else if (NULL != gAcl)
{
memcpy(&(acl->rownerID), &(gAcl->rownerID), sizeof(OicUuid_t));
}
- OICFree(tagName);
+ free(tagName);
+ tagName = NULL;
}
if (cbor_value_is_valid(&aclMap))
{
acl = NULL;
}
+ free(tagName);
+ free(roleTagName);
+
return acl;
}
{
uint8_t *payload = NULL;
size_t size = 0;
- if (OC_STACK_OK == AclToCBORPayload(gAcl, &payload, &size))
+ if (OC_STACK_OK == AclToCBORPayload(gAcl, OIC_SEC_ACL_V2, &payload, &size))
{
if (OC_STACK_OK == UpdateSecureResourceInPS(OIC_JSON_ACL_NAME, payload, size))
{
OIC_LOG(INFO, TAG, "IN RemoveAllAce");
//Backup the current ACL
- ret = AclToCBORPayload(gAcl, &aclBackup, &backupSize);
+ ret = AclToCBORPayload(gAcl, OIC_SEC_ACL_LATEST, &aclBackup, &backupSize);
if(OC_STACK_OK == ret)
{
// Remove all ACE from ACL
}
//Generate empty ACL payload
- ret = AclToCBORPayload(gAcl, &payload, &size);
+ ret = AclToCBORPayload(gAcl, OIC_SEC_ACL_LATEST, &payload, &size);
if (OC_STACK_OK == ret )
{
//Update the PS.
return (OC_STACK_OK == ret ? OC_STACK_RESOURCE_DELETED : ret);
}
-static OCEntityHandlerResult HandleACLGetRequest(const OCEntityHandlerRequest *ehRequest)
+static OCEntityHandlerResult HandleACLGetRequest(const OCEntityHandlerRequest *ehRequest, OicSecAclVersion_t aclVersion)
{
OIC_LOG(INFO, TAG, "HandleACLGetRequest processing the request");
uint8_t* payload = NULL;
0 == strcmp(WILDCARD_RESOURCE_URI, rsrc->href))
{
// Convert ACL data into CBOR format for transmission
- if (OC_STACK_OK != AclToCBORPayload(&targetAcl, &payload, &size))
+ if (OC_STACK_OK != AclToCBORPayload(&targetAcl, aclVersion, &payload, &size))
{
ehRet = OC_EH_ERROR;
}
else
{
// Convert ACL data into CBOR format for transmission
- if (OC_STACK_OK != AclToCBORPayload(&targetAcl, &payload, &size))
+ if (OC_STACK_OK != AclToCBORPayload(&targetAcl, aclVersion, &payload, &size))
{
ehRet = OC_EH_ERROR;
}
{
OIC_LOG(DEBUG,TAG,"'subject' field is not inculded in REST request.");
// Convert ACL data into CBOR format for transmission.
- if (OC_STACK_OK != AclToCBORPayload(gAcl, &payload, &size))
+ if (OC_STACK_OK != AclToCBORPayload(gAcl, aclVersion, &payload, &size))
{
ehRet = OC_EH_ERROR;
}
size_t cborSize = 0;
uint8_t *cborPayload = NULL;
- if (OC_STACK_OK == AclToCBORPayload(gAcl, &cborPayload, &cborSize))
+ if (OC_STACK_OK == AclToCBORPayload(gAcl, OIC_SEC_ACL_LATEST, &cborPayload, &cborSize))
{
if (UpdateSecureResourceInPS(OIC_JSON_ACL_NAME, cborPayload, cborSize) == OC_STACK_OK)
{
switch (ehRequest->method)
{
case OC_REST_GET:
- ehRet = HandleACLGetRequest(ehRequest);
+ ehRet = HandleACLGetRequest(ehRequest, OIC_SEC_ACL_V1);
+ break;
+
+ case OC_REST_POST:
+ ehRet = HandleACLPostRequest(ehRequest);
+ break;
+
+ case OC_REST_DELETE:
+ ehRet = HandleACLDeleteRequest(ehRequest);
+ break;
+
+ default:
+ ehRet = ((SendSRMResponse(ehRequest, ehRet, NULL, 0)) == OC_STACK_OK) ?
+ OC_EH_OK : OC_EH_ERROR;
+ }
+ }
+
+ return ehRet;
+}
+
+OCEntityHandlerResult ACL2EntityHandler(OCEntityHandlerFlag flag, OCEntityHandlerRequest * ehRequest,
+ void* callbackParameter)
+{
+ OIC_LOG(DEBUG, TAG, "Received request ACL2EntityHandler");
+ OC_UNUSED(callbackParameter);
+ OCEntityHandlerResult ehRet = OC_EH_ERROR;
+
+ if (!ehRequest)
+ {
+ return ehRet;
+ }
+
+ if (flag & OC_REQUEST_FLAG)
+ {
+ // TODO : Handle PUT method
+ OIC_LOG(DEBUG, TAG, "Flag includes OC_REQUEST_FLAG");
+ switch (ehRequest->method)
+ {
+ case OC_REST_GET:
+ ehRet = HandleACLGetRequest(ehRequest, OIC_SEC_ACL_V2);
break;
case OC_REST_POST:
}
/**
- * This internal method is used to create '/oic/sec/acl' resource.
+ * This internal method is used to create the '/oic/sec/acl' and '/oic/sec/acl2' resources.
*/
static OCStackResult CreateACLResource()
{
OIC_LOG(FATAL, TAG, "Unable to instantiate ACL resource");
DeInitACLResource();
}
+
+ ret = OCCreateResource(&gAcl2Handle,
+ OIC_RSRC_TYPE_SEC_ACL2,
+ OC_RSRVD_INTERFACE_DEFAULT,
+ OIC_RSRC_ACL2_URI,
+ ACL2EntityHandler,
+ NULL,
+ OC_SECURE);
+
+ if (OC_STACK_OK != ret)
+ {
+ OIC_LOG(FATAL, TAG, "Unable to instantiate ACL2 resource");
+ DeInitACLResource();
+ }
+
return ret;
}
VERIFY_NOT_NULL(TAG, readOnlyAce, ERROR);
// Subject -- Mandatory
+ readOnlyAce->subjectType = OicSecAceUuidSubject;
memcpy(&readOnlyAce->subjectuuid, &WILDCARD_SUBJECT_ID, sizeof(readOnlyAce->subjectuuid));
// Resources -- Mandatory
VERIFY_NOT_NULL(TAG, readWriteAce, ERROR);
// Subject -- Mandatory
+ readWriteAce->subjectType = OicSecAceUuidSubject;
memcpy(&readWriteAce->subjectuuid, &WILDCARD_SUBJECT_ID, sizeof(readWriteAce->subjectuuid));
// Resources -- Mandatory
{
OCStackResult ret = OCDeleteResource(gAclHandle);
gAclHandle = NULL;
+ OCStackResult ret2 = OCDeleteResource(gAcl2Handle);
+ gAcl2Handle = NULL;
if (gAcl)
{
DeleteACLList(gAcl);
gAcl = NULL;
}
- return ret;
+ return (OC_STACK_OK != ret) ? ret : ret2;
}
const OicSecAce_t* GetACLResourceData(const OicUuid_t* subjectId, OicSecAce_t **savePtr)
// Find the next ACL corresponding to the 'subjectID' and return it.
LL_FOREACH(begin, ace)
{
- if (memcmp(&(ace->subjectuuid), subjectId, sizeof(OicUuid_t)) == 0)
+ if ((OicSecAceUuidSubject == ace->subjectType) &&
+ (0 == memcmp(&(ace->subjectuuid), subjectId, sizeof(OicUuid_t))))
{
OIC_LOG(DEBUG, TAG, "GetACLResourceData: found matching ACE:");
printACE(ace);
return NULL;
}
-OCStackResult AppendACL2(const OicSecAcl_t* acl)
+const OicSecAce_t* GetACLResourceDataByRoles(const OicSecRole_t *roles, size_t roleCount, OicSecAce_t **savePtr)
+{
+ OicSecAce_t *ace = NULL;
+ OicSecAce_t *begin = NULL;
+
+ if ((NULL == savePtr) || (NULL == gAcl))
+ {
+ OIC_LOG(ERROR, TAG, "Invalid parameters to GetACLResourceDataByRoles");
+ return NULL;
+ }
+
+ if ((NULL == roles) || (0 == roleCount))
+ {
+ /* Not an error; just nothing to do. */
+ return NULL;
+ }
+
+ /*
+ * savePtr MUST point to NULL if this is the 'first' call to retrieve ACL for
+ * subjectID.
+ */
+ if (NULL == *savePtr)
+ {
+ begin = gAcl->aces;
+ }
+ else
+ {
+ /*
+ * If this is a 'successive' call, search for location pointed by
+ * savePtr and assign 'begin' to the next ACL after it in the linked
+ * list and start searching from there.
+ */
+ LL_FOREACH(gAcl->aces, ace)
+ {
+ if (ace == *savePtr)
+ {
+ begin = ace->next;
+ }
+ }
+ }
+
+ // Find the next ACL corresponding to the 'roleID' and return it.
+ LL_FOREACH(begin, ace)
+ {
+ if (OicSecAceRoleSubject == ace->subjectType)
+ {
+ for (size_t i = 0; i < roleCount; i++)
+ {
+ if ((0 == strcmp(ace->subjectRole.id, roles[i].id) &&
+ (0 == strcmp(ace->subjectRole.authority, roles[i].authority))))
+ {
+ *savePtr = ace;
+ return ace;
+ }
+ }
+ }
+ }
+
+ // Cleanup in case no ACL is found
+ *savePtr = NULL;
+ return NULL;
+}
+
+OCStackResult AppendACLObject(const OicSecAcl_t* acl)
{
OCStackResult ret = OC_STACK_ERROR;
size_t size = 0;
uint8_t *payload = NULL;
- ret = AclToCBORPayload(gAcl, &payload, &size);
+ ret = AclToCBORPayload(gAcl, OIC_SEC_ACL_LATEST, &payload, &size);
if (OC_STACK_OK == ret)
{
ret = UpdateSecureResourceInPS(OIC_JSON_ACL_NAME, payload, size);
// Convert CBOR format to ACL data. This will also validate the ACL data received.
OicSecAcl_t* newAcl = CBORPayloadToAcl(cborPayload, size);
- return AppendACL2(newAcl);
+ return AppendACLObject(newAcl);
}
OCStackResult InstallACL(const OicSecAcl_t* acl)
if (newInstallAcl)
{
- ret = AppendACL2(newInstallAcl);
+ ret = AppendACLObject(newInstallAcl);
if (OC_STACK_OK != ret)
{
OIC_LOG(ERROR, TAG, "Failed to append ACL");
VERIFY_NOT_NULL(TAG, newAce, ERROR);
// Subject -- Mandatory
+ newAce->subjectType = OicSecAceUuidSubject;
memcpy(&newAce->subjectuuid, &WILDCARD_SUBJECT_ID, WILDCARD_SUBJECT_ID_LEN);
//Resources -- Mandatory
LL_FOREACH_SAFE(gAcl->aces, ace, tempAce)
{
//Find default security resource ACL
- if(memcmp(&ace->subjectuuid, &WILDCARD_SUBJECT_ID, sizeof(OicUuid_t)) == 0 &&
+ if(OicSecAceUuidSubject == ace->subjectType &&
+ memcmp(&ace->subjectuuid, &WILDCARD_SUBJECT_ID, sizeof(OicUuid_t)) == 0 &&
((PERMISSION_READ | PERMISSION_WRITE) == ace->permission))
{
matchedRsrc = 0;
size_t size = 0;
uint8_t *payload = NULL;
- if (OC_STACK_OK == AclToCBORPayload(gAcl, &payload, &size))
+ if (OC_STACK_OK == AclToCBORPayload(gAcl, OIC_SEC_ACL_LATEST, &payload, &size))
{
if (UpdateSecureResourceInPS(OIC_JSON_ACL_NAME, payload, size) == OC_STACK_OK)
{
memcpy(prevId.id, gAcl->rownerID.id, sizeof(prevId.id));
memcpy(gAcl->rownerID.id, newROwner->id, sizeof(newROwner->id));
- ret = AclToCBORPayload(gAcl, &cborPayload, &size);
+ ret = AclToCBORPayload(gAcl, OIC_SEC_ACL_LATEST, &cborPayload, &size);
VERIFY_SUCCESS(TAG, OC_STACK_OK == ret, ERROR);
ret = UpdateSecureResourceInPS(OIC_JSON_ACL_NAME, cborPayload, size);
--- /dev/null
+/* *****************************************************************
+ *
+ * Copyright 2017 Microsoft. All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * *****************************************************************/
+
+#include "iotivity_config.h"
+
+#include "logger.h"
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "cacommon.h"
+#include "ocrandom.h"
+#include "cacommonutil.h"
+
+#include "ocpayload.h"
+#include "payload_logging.h"
+#include "pmutility.h"
+#include "srmutility.h"
+
+// headers required for mbed TLS
+#include "mbedtls/config.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/x509_csr.h"
+#include "mbedtls/x509_crt.h"
+#include "mbedtls/oid.h"
+
+#ifndef NDEBUG
+#include "mbedtls/debug.h"
+#include "mbedtls/version.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "certhelpers.h"
+
+#define TAG "OIC_CERTHELPERS"
+
+/**
+ * @def PERSONALIZATION_STRING
+ * @brief Personalization string for the mbedtls RNG
+ */
+#define PERSONALIZATION_STRING "IOTIVITY_RND"
+
+#define MAX_URI_QUERY MAX_URI_LENGTH + MAX_QUERY_LENGTH
+
+#define MAX_STRING_LEN 254
+
+#define MAX_ROLES_PER_CERT 10
+
+/* OID for role certificates (1.3.6.1.4.1.44924.1.7) suitable for mbedTLS check */
+static const char s_ekuRoleOid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x06\x01\x04\x01\x82\xDE\x7C\x01\x07";
+
+/**
+ * Generates elliptic curve keypair.
+ *
+ * @param[out] pk mbedtls public key container
+ *
+ * @return 0 on success or <0 on error
+ */
+static int GenerateEccKeyPair(mbedtls_pk_context *pk)
+{
+ int ret = 0;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+
+ OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+ VERIFY_NON_NULL_RET(pk, TAG, "Param pk is NULL", -1);
+
+ // Initialize the DRBG context
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &entropy, (const unsigned char *)PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING));
+
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "Seed initialization failed! %d", ret);
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+ mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON);
+ ret = mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "mbedtls_pk_setup error %d", ret);
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+ ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*pk), mbedtls_ctr_drbg_random, &ctr_drbg);
+ if (0 > ret)
+ {
+ OIC_LOG(ERROR, TAG, "mbedtls_ecp_gen_keypair error");
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+
+exit:
+
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ return 0;
+}
+
+/**
+ * Generates certificate signing request for given key pair.
+ *
+ * @param[in] subject CSR Subject names should contain a comma-separated list of OID types and values:
+ * e.g. "C=UA,O=ABC,CN=uuid:32323232-3232-3232-3232-323232323232"
+ * @param[in] key Public/private key pair to generate for.
+ * @param[in] encoding Encoding to use; valid values are OIC_ENCODING_PEM and OIC_ENCODING_DER
+ * @param[out] csr certificate signing request in the requested encoding
+ *
+ * @return 0 on success or <0 on error
+ */
+static int GenerateCSRForKey(const char *subject, mbedtls_pk_context* key, OicEncodingType_t encoding, OCByteString *csr)
+{
+ int ret = 0;
+ unsigned char buf[2048];
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_x509write_csr req;
+
+ OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+ VERIFY_NON_NULL_RET(subject, TAG, "Param subject is NULL", -1);
+ VERIFY_NON_NULL_RET(csr, TAG, "Param csr is NULL", -1);
+
+ if ((OIC_ENCODING_PEM != encoding) && (OIC_ENCODING_DER != encoding))
+ {
+ OIC_LOG(ERROR, TAG, "Unsupported encoding requested");
+ return -1;
+ }
+
+ // Initialize all data structures up front so the error path can always free them safely, no
+ // matter where the error exit point.
+ mbedtls_x509write_csr_init(&req);
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+ csr->bytes = NULL;
+
+ // Initialize CSR context
+ // Set up MD algorithm, key and subject to CSR
+ mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256);
+ mbedtls_x509write_csr_set_key(&req, key);
+ ret = mbedtls_x509write_csr_set_subject_name(&req, subject);
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "mbedtls_x509write_csr_set_subject_name error %d", ret);
+ goto exit;
+ }
+
+ // Initialize the DRBG context
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &entropy, (const unsigned char *)PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING));
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "Seed initialization failed! %d", ret);
+ goto exit;
+ }
+ mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON);
+
+ // Create CSR
+ if (OIC_ENCODING_PEM == encoding)
+ {
+ ret = mbedtls_x509write_csr_pem(&req, buf, sizeof(buf),
+ mbedtls_ctr_drbg_random, &ctr_drbg);
+
+ // In the PEM case, success is always denoted by a zero return. We have to compute the length.
+ if (0 != ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "mbedtls_x509write_csr_pem error %d", ret);
+ goto exit;
+ }
+ else
+ {
+ csr->len = strlen((const char *)buf) + 1;
+ csr->bytes = (uint8_t *)OICMalloc(csr->len);
+ if (NULL == csr->bytes)
+ {
+ OIC_LOG(ERROR, TAG, "OICMalloc returned NULL on csr allocation");
+ ret = MBEDTLS_ERR_X509_ALLOC_FAILED;
+ goto exit;
+ }
+ memcpy(csr->bytes, buf, csr->len);
+ }
+ }
+ else // encoding == OIC_ENCODING_DER
+ {
+ ret = mbedtls_x509write_csr_der(&req, buf, sizeof(buf),
+ mbedtls_ctr_drbg_random, &ctr_drbg);
+
+ // In the DER case, ret is the number of bytes written.
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "mbedtls_x509write_csr_der error %d", ret);
+ goto exit;
+ }
+ else
+ {
+ csr->len = ret;
+ csr->bytes = (uint8_t *)OICMalloc(csr->len);
+ if (NULL == csr->bytes)
+ {
+ OIC_LOG(ERROR, TAG, "OICMalloc returned NULL on csr allocation");
+ ret = MBEDTLS_ERR_X509_ALLOC_FAILED;
+ goto exit;
+ }
+ // DER data gets written to the end of the buffer.
+ memcpy(csr->bytes, buf + sizeof(buf) - csr->len, csr->len);
+ ret = 0;
+ }
+ }
+
+
+
+exit:
+
+ if (0 > ret)
+ {
+ OICFree(csr->bytes);
+ csr->bytes = NULL;
+ csr->len = 0;
+ }
+
+ mbedtls_entropy_free(&entropy);
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_x509write_csr_free(&req);
+
+ OICClearMemory(buf, sizeof(buf));
+
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ return ret;
+}
+
+int OCInternalCSRRequest(const char *subject, mbedtls_pk_context *keyPair, OicEncodingType_t encoding, OCByteString *csr)
+{
+ return GenerateCSRForKey(subject, keyPair, encoding, csr);
+}
+
+int OCInternalGenerateKeyPair(mbedtls_pk_context *keyPair)
+{
+ return GenerateEccKeyPair(keyPair);
+}
+
+OCStackResult OCInternalIsValidRoleCertificate(const uint8_t *buf, size_t bufLen,
+ uint8_t **pubKey, size_t *pubKeyLen)
+{
+ OCStackResult res = OC_STACK_ERROR;
+ mbedtls_x509_crt parsedCert;
+
+ OIC_LOG(DEBUG, TAG, "OCInternalIsValidRoleCertificate IN");
+
+ mbedtls_x509_crt_init(&parsedCert);
+ int mbedRet = mbedtls_x509_crt_parse(&parsedCert, buf, bufLen);
+
+ if (0 > mbedRet)
+ {
+ OIC_LOG(ERROR, TAG, "Could not parse cert chain");
+ goto exit;
+ }
+
+ bool valid = false;
+ /* There should only be one certificate. */
+ if (NULL != parsedCert.next)
+ {
+ OIC_LOG(ERROR, TAG, "Expected only one certificate, but buffer contained more than one.");
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ /* We opt to require an EKU extension to be present; all-purposes certs are not allowed.
+ * mbedtls_x509_crt_check_extended_key_usage will return success if the EKU extension is absent,
+ * so we check this separately first.
+ */
+ if ((parsedCert.ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE) == 0)
+ {
+ OIC_LOG(ERROR, TAG, "EKU extension is absent. We require it to be present.");
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ /* The subject alternative name extension must also be present. */
+ if ((parsedCert.ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) == 0)
+ {
+ OIC_LOG(ERROR, TAG, "Subject alternative name is absent, and is required in a role certificate.");
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ mbedRet = mbedtls_x509_crt_check_extended_key_usage(&parsedCert, s_ekuRoleOid, MBEDTLS_OID_SIZE(s_ekuRoleOid));
+ if (0 > mbedRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "Role EKU is absent: %d", mbedRet);
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ valid = false;
+ /* Check for at least one subjAltName with a role in it. */
+ for (const mbedtls_x509_general_names *nameCur = &parsedCert.subject_alt_names;
+ NULL != nameCur;
+ nameCur = nameCur->next)
+ {
+ if (MBEDTLS_X509_GENERALNAME_DIRECTORYNAME == nameCur->general_name.name_type)
+ {
+ /* Name must contain a CN component. OU is optional. Anything else is ignored
+ * but not grounds for rejection.
+ */
+ for (const mbedtls_x509_name *dirName = nameCur->general_name.directory_name;
+ NULL != dirName;
+ dirName = dirName->next)
+ {
+ if ((MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN) == dirName->oid.len) &&
+ (0 == memcmp(MBEDTLS_OID_AT_CN, dirName->oid.p, MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN))))
+ {
+ if (dirName->val.len >= ROLEID_LENGTH)
+ {
+ OIC_LOG_V(ERROR, TAG, "Certificate has role id that is too long: %" PRIuPTR, dirName->val.len);
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ valid = true;
+ }
+ else if ((MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_ORG_UNIT) == dirName->oid.len) &&
+ (0 == memcmp(MBEDTLS_OID_AT_ORG_UNIT, dirName->oid.p, MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_ORG_UNIT))))
+ {
+ if (dirName->val.len >= ROLEAUTHORITY_LENGTH)
+ {
+ OIC_LOG_V(ERROR, TAG, "Certificate has role authority that is too long: %" PRIuPTR, dirName->val.len);
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+ /* Presence of OU does not affect validity, so don't set valid flag. */
+ }
+ else
+ {
+ OIC_LOG(WARNING, TAG, "Directory name has attribute that isn't CN or OU; ignoring");
+ }
+ }
+ }
+
+ if (valid)
+ {
+ break;
+ }
+ }
+
+ if (!valid)
+ {
+ OIC_LOG(ERROR, TAG, "Could not find valid role encoded in a subject alternative name");
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ /* If the public key is requested, extract it. */
+ if (NULL != pubKey)
+ {
+ unsigned char tmp[1024] = { 0 };
+ mbedRet = mbedtls_pk_write_pubkey_der(&parsedCert.pk, tmp, sizeof(tmp));
+ if (0 > mbedRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not write public key as DER: %d", mbedRet);
+ res = OC_STACK_ERROR;
+ goto exit;
+ }
+
+ /* mbedRet is the amount of data written, and it's written at the END of the buffer. */
+ *pubKey = (uint8_t *)OICCalloc(1, mbedRet);
+ if (NULL == *pubKey)
+ {
+ OIC_LOG(ERROR, TAG, "No memory allocating pubKey");
+ res = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+ memcpy(*pubKey, tmp + sizeof(tmp) - mbedRet, mbedRet);
+ *pubKeyLen = mbedRet;
+ }
+
+ res = OC_STACK_OK;
+
+exit:
+
+ mbedtls_x509_crt_free(&parsedCert);
+
+ OIC_LOG_V(DEBUG, TAG, "OCInternalIsValidRoleCertificate OUT; returning %d", res);
+ return res;
+}
+
+OCStackResult OCInternalIsValidCertChain(const uint8_t *buf, size_t bufLen)
+{
+ OCStackResult res = OC_STACK_ERROR;
+ int mbedRet;
+ mbedtls_x509_crt parsedCert;
+
+ OIC_LOG(DEBUG, TAG, "OCInternalIsValidCertChain IN");
+
+ mbedtls_x509_crt_init(&parsedCert);
+ mbedRet = mbedtls_x509_crt_parse(&parsedCert, buf, bufLen);
+ if (0 > mbedRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to parse certificate chain: %d", mbedRet);
+ res = OC_STACK_INVALID_PARAM;
+ }
+ else
+ {
+ res = OC_STACK_OK;
+ if (0 < mbedRet)
+ {
+ OIC_LOG_V(WARNING, TAG, "Partly successful: mbedTLS returned %d", mbedRet);
+ }
+ }
+
+ mbedtls_x509_crt_free(&parsedCert);
+
+ OIC_LOG_V(DEBUG, TAG, "OCInternalIsValidCertChain OUT; returning %d", res);
+
+ return res;
+}
+
+static const mbedtls_x509_crt_profile s_certProfile = {
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256), /* MD algorithms */
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA), /* Signature algorithms */
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1), /* EC curves */
+ 0 /* RSA minimum key length - not used because we only use EC key pairs */
+};
+
+OCStackResult OCInternalVerifyRoleCertificate(const OicSecKey_t *certificate, const OicSecOpt_t *optData,
+ const uint8_t *trustedCaCerts, size_t trustedCaCertsLength,
+ OicSecRole_t **roles, size_t *rolesLength,
+ struct tm *notValidAfter)
+{
+ VERIFY_NOT_NULL_RETURN(TAG, certificate, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, trustedCaCerts, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, roles, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, rolesLength, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, notValidAfter, ERROR, OC_STACK_INVALID_PARAM);
+
+ OCStackResult res = OC_STACK_ERROR;
+ int mbedRet;
+ uint32_t flags = 0;
+ mbedtls_x509_crt certChain, trustedCas;
+
+ OicSecRole_t rolesTmp[MAX_ROLES_PER_CERT];
+ memset(rolesTmp, 0, sizeof(rolesTmp));
+ size_t rolesTmpCount = 0;
+
+ OIC_LOG(DEBUG, TAG, "OCInternalVerifyRoleCertificate IN");
+
+ mbedtls_x509_crt_init(&certChain);
+ mbedtls_x509_crt_init(&trustedCas);
+
+ res = OCInternalIsValidRoleCertificate(certificate->data, certificate->len, NULL, NULL);
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Certificate is not valid as a role certificate: %d", res);
+ goto exit;
+ }
+
+ mbedRet = mbedtls_x509_crt_parse(&certChain, certificate->data, certificate->len);
+ if (0 > mbedRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not parse certificate: %d", mbedRet);
+ res = OC_STACK_ERROR;
+ goto exit;
+ }
+
+ if (NULL != optData)
+ {
+ mbedRet = mbedtls_x509_crt_parse(&certChain, optData->data, optData->len);
+ if (0 > mbedRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not parse optional data: %d", mbedRet);
+ res = OC_STACK_ERROR;
+ goto exit;
+ }
+ }
+
+ mbedRet = mbedtls_x509_crt_parse(&trustedCas, trustedCaCerts, trustedCaCertsLength);
+ if (0 > mbedRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not parse trusted CA certs: %d", mbedRet);
+ res = OC_STACK_ERROR;
+ goto exit;
+ }
+
+ mbedRet = mbedtls_x509_crt_verify_with_profile(
+ &certChain,
+ &trustedCas,
+ NULL,
+ &s_certProfile,
+ NULL,
+ &flags,
+ NULL,
+ NULL);
+ if (0 > mbedRet)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to verify certificate: ret = %d, flags = %u", mbedRet, flags);
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ /* Certificate is verified. If requested, extract the list of roles and return.
+ * The first certificate in the certChain list is the leaf, since we parsed it first.
+ */
+ if (NULL != roles)
+ {
+ for (const mbedtls_x509_general_names *nameCur = &certChain.subject_alt_names;
+ (NULL != nameCur) && (rolesTmpCount < MAX_ROLES_PER_CERT);
+ nameCur = nameCur->next)
+ {
+ if (MBEDTLS_X509_GENERALNAME_DIRECTORYNAME == nameCur->general_name.name_type)
+ {
+ bool advanceCount = false;
+ for (const mbedtls_x509_name *dirName = nameCur->general_name.directory_name;
+ NULL != dirName;
+ dirName = dirName->next)
+ {
+ if ((MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN) == dirName->oid.len) &&
+ (0 == memcmp(MBEDTLS_OID_AT_CN, dirName->oid.p, MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN))))
+ {
+ /* When checking validity above, we made sure the role ID and authority were not too
+ * long to fit in an OicSecRole_t. Here we only assert, but don't check again in release code.
+ * id was also initialized as all zeroes, so string will automatically be null-terminated.
+ */
+ assert(dirName->val.len < ROLEID_LENGTH);
+ memcpy(rolesTmp[rolesTmpCount].id, dirName->val.p, dirName->val.len);
+ advanceCount = true;
+ }
+ else if ((MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_ORG_UNIT) == dirName->oid.len) &&
+ (0 == memcmp(MBEDTLS_OID_AT_ORG_UNIT, dirName->oid.p, MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_ORG_UNIT))))
+ {
+ assert(dirName->val.len < ROLEID_LENGTH);
+ memcpy(rolesTmp[rolesTmpCount].authority, dirName->val.p, dirName->val.len);
+ }
+
+ }
+
+ if (advanceCount)
+ {
+ rolesTmpCount++;
+ }
+ }
+ }
+
+ if (MAX_ROLES_PER_CERT <= rolesTmpCount)
+ {
+ OIC_LOG(ERROR, TAG, "More roles than supported in a single certificate");
+ res = OC_STACK_ERROR;
+ goto exit;
+ }
+ else if (0 == rolesTmpCount)
+ {
+ OIC_LOG(ERROR, TAG, "No roles in the certificate");
+ res = OC_STACK_ERROR;
+ goto exit;
+ }
+
+ *roles = (OicSecRole_t *)OICCalloc(1, sizeof(OicSecRole_t) * rolesTmpCount);
+ if (NULL == *roles)
+ {
+ OIC_LOG(ERROR, TAG, "No memory allocating roles array");
+ res = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+
+ memcpy(*roles, rolesTmp, sizeof(rolesTmp[0]) * rolesTmpCount);
+ *rolesLength = rolesTmpCount;
+ }
+
+ memset(notValidAfter, 0, sizeof(*notValidAfter));
+
+ notValidAfter->tm_year = certChain.valid_to.year - 1900;
+ notValidAfter->tm_mon = certChain.valid_to.mon - 1;
+ notValidAfter->tm_mday = certChain.valid_to.day;
+ notValidAfter->tm_hour = certChain.valid_to.hour;
+ notValidAfter->tm_min = certChain.valid_to.min;
+ notValidAfter->tm_sec = certChain.valid_to.sec;
+
+ res = OC_STACK_OK;
+
+exit:
+
+ mbedtls_x509_crt_free(&trustedCas);
+ mbedtls_x509_crt_free(&certChain);
+
+ OIC_LOG_V(DEBUG, TAG, "OCInternalVerifyRoleCertificate out: %d", res);
+
+ return res;
+}
\ No newline at end of file
#include "srmutility.h"
#include "psinterface.h"
#include "pinoxmcommon.h"
+#include "certhelpers.h"
+#include "cacommon.h"
#ifdef __unix__
#include <sys/types.h>
#if defined(__WITH_DTLS__) || defined (__WITH_TLS__)
#include <mbedtls/ssl_ciphersuites.h>
+#include "mbedtls/pk.h"
+#include "mbedtls/base64.h"
+#include "mbedtls/pem.h"
#endif
#define TAG "OIC_SRM_CREDL"
#include <intsafe.h>
#endif
-
/** Max credential types number used for TLS */
#define MAX_TYPE 2
/** Default cbor payload size. This value is increased in case of CborErrorOutOfMemory.
return cborEncoderResult;
}
-static CborError SerializeEncodingToCbor(CborEncoder *rootMap, const char* tag, const OicSecKey_t *value)
+CborError SerializeEncodingToCbor(CborEncoder *rootMap, const char* tag, const OicSecKey_t *value)
{
CborError cborEncoderResult = CborNoError;
CborEncoder map;
return cborEncoderResult;
}
-static CborError SerializeSecOptToCbor(CborEncoder *rootMap, const char* tag, const OicSecOpt_t *value)
+CborError SerializeSecOptToCbor(CborEncoder *rootMap, const char* tag, const OicSecOpt_t *value)
{
CborError cborEncoderResult = CborNoError;
CborEncoder map;
return cborFindResult;
}
-static CborError DeserializeEncodingFromCbor(CborValue *rootMap, OicSecKey_t *value)
+CborError DeserializeEncodingFromCbor(CborValue *rootMap, OicSecKey_t *value)
{
CborValue map = { .parser = NULL };
CborError cborFindResult = cbor_value_enter_container(rootMap, &map);
return cborFindResult;
}
-static CborError DeserializeSecOptFromCbor(CborValue *rootMap, OicSecOpt_t *value)
+CborError DeserializeSecOptFromCbor(CborValue *rootMap, OicSecOpt_t *value)
{
CborValue map = { .parser = NULL };
CborError cborFindResult = cbor_value_enter_container(rootMap, &map);
return cborFindResult;
}
+/* Produce debugging output for all credentials, output metadata. */
+static void logCredMetadata()
+{
+#if defined(TB_LOG)
+ OicSecCred_t * temp = NULL;
+ size_t count = 0;
+ char uuidString[UUID_STRING_SIZE];
+ OicUuid_t ownUuid;
+
+ OIC_LOG_V(DEBUG, TAG, "IN %s:", __func__);
+
+ if (GetDoxmDeviceID(&ownUuid) == OC_STACK_OK && OCConvertUuidToString(ownUuid.id, uuidString))
+ {
+ OIC_LOG_V(DEBUG, TAG, "Own UUID: %s", uuidString);
+ }
+
+ LL_FOREACH(gCred, temp)
+ {
+ count++;
+ OIC_LOG(DEBUG, TAG, " ");
+ OIC_LOG_V(DEBUG, TAG, "Cred ID: %d", temp->credId);
+ if (OCConvertUuidToString(temp->subject.id, uuidString))
+ {
+ OIC_LOG_V(DEBUG, TAG, "Subject UUID: %s", uuidString);
+ }
+ OIC_LOG_V(DEBUG, TAG, "Cred Type: %d", temp->credType);
+ OIC_LOG_V(DEBUG, TAG, "privateData length: %d, encoding: %d", temp->privateData.len, temp->privateData.encoding);
+
+#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
+ OIC_LOG_V(DEBUG, TAG, "publicData length: %d, encoding: %d", temp->publicData.len, temp->publicData.encoding);
+ if (temp->credUsage)
+ {
+ OIC_LOG_V(DEBUG, TAG, "credUsage: %s", temp->credUsage);
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "optionalData length: %d, encoding: %d", temp->optionalData.len, temp->optionalData.encoding);
+#endif
+
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "Found %d credentials.", count);
+
+ OIC_LOG_V(DEBUG, TAG, "OUT %s:", __func__);
+#endif
+}
+
+
OCStackResult CredToCBORPayload(const OicSecCred_t *credS, uint8_t **cborPayload,
size_t *cborSize, int secureFlag)
{
+ OIC_LOG_V(DEBUG, TAG, "IN %s:", __func__);
if (NULL == credS || NULL == cborPayload || NULL != *cborPayload || NULL == cborSize)
{
return OC_STACK_INVALID_PARAM;
}
#endif //MULTIPLE_OWNER
- if (SIGNED_ASYMMETRIC_KEY == cred->credType && cred->publicData.data)
+ if ((SIGNED_ASYMMETRIC_KEY == cred->credType || ASYMMETRIC_KEY == cred->credType)
+ && cred->publicData.data)
{
mapSize++;
}
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
//PublicData -- Not Mandatory
- if (SIGNED_ASYMMETRIC_KEY == cred->credType && cred->publicData.data)
+ if ((SIGNED_ASYMMETRIC_KEY == cred->credType || ASYMMETRIC_KEY == cred->credType)
+ && cred->publicData.data)
{
cborEncoderResult = SerializeEncodingToCbor(&credMap,
OIC_JSON_PUBLICDATA_NAME, &cred->publicData);
ret = OC_STACK_ERROR;
}
+ OIC_LOG_V(DEBUG, TAG, "OUT %s:", __func__);
+
return ret;
}
{
cborFindResult = DeserializeEncodingFromCbor(&credMap, &cred->privateData);
VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed to read privateData structure");
-
- OicEncodingType_t encoding = cred->privateData.encoding;
- if (OIC_ENCODING_DER == encoding || OIC_ENCODING_PEM == encoding)
- {
- //For unit test
- cred->privateData.encoding = OIC_ENCODING_RAW;
- OIC_LOG(WARNING, TAG, "Unknown encoding type detected for private data.");
- }
}
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
//PublicData -- Not Mandatory
VERIFY_NOT_NULL(TAG, cred->publicData.data, ERROR);
memcpy(cred->publicData.data, publicData->data, publicData->len);
cred->publicData.len = publicData->len;
+ cred->publicData.encoding = publicData->encoding;
}
#endif // __WITH_DTLS__
VERIFY_NOT_NULL(TAG, cred->privateData.data, ERROR);
memcpy(cred->privateData.data, privateData->data, privateData->len);
cred->privateData.len = privateData->len;
- cred->privateData.encoding = OIC_ENCODING_RAW;
+ cred->privateData.encoding = privateData->encoding;
}
VERIFY_NOT_NULL(TAG, rownerID, ERROR);
}
OIC_LOG(DEBUG, TAG, "OUT Cred UpdatePersistentStorage");
+
+ logCredMetadata();
+
return ret;
}
#endif//__WITH_DTLS__
}
- if (OC_EH_CHANGED != ret)
+ if (OC_EH_CHANGED != ret && cred != NULL)
{
if(OC_STACK_OK != RemoveCredential(&cred->subject))
{
cred->privateData.encoding = tmpCred->privateData.encoding;
}
#if defined(__WITH_X509__) || defined(__WITH_TLS__)
- else if (tmpCred->publicData.data)
+ if (tmpCred->publicData.data)
{
cred->publicData.data = (uint8_t *)OICCalloc(1, tmpCred->publicData.len);
VERIFY_NOT_NULL(TAG, cred->publicData.data, ERROR);
memcpy(cred->publicData.data, tmpCred->publicData.data, tmpCred->publicData.len);
cred->publicData.len = tmpCred->publicData.len;
+ cred->publicData.encoding = tmpCred->publicData.encoding;
}
- else if (tmpCred->optionalData.data)
+ if (tmpCred->optionalData.data)
{
cred->optionalData.data = (uint8_t *)OICCalloc(1, tmpCred->optionalData.len);
VERIFY_NOT_NULL(TAG, cred->optionalData.data, ERROR);
cred->optionalData.encoding = tmpCred->optionalData.encoding;
cred->optionalData.revstat= tmpCred->optionalData.revstat;
}
-
if (tmpCred->credUsage)
{
cred->credUsage = OICStrdup(tmpCred->credUsage);
}
#if defined (__WITH_TLS__) || defined(__WITH_DTLS__)
-void GetDerCaCert(ByteArray_t * crt, const char * usage)
+/* Caller must call OICFree on *der when finished. */
+static int ConvertPemCertToDer(const char *pem, size_t pemLen, uint8_t** der, size_t* derLen)
+{
+ size_t bufSize = B64DECODE_OUT_SAFESIZE(pemLen + 1);
+ uint8_t *buf = OICCalloc(1, bufSize);
+ if (NULL == buf)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to allocate memory");
+ return -1;
+ }
+ size_t outSize = 0;
+ if (B64_OK != b64Decode(pem, pemLen, buf, bufSize, &outSize))
+ {
+ OICFree(buf);
+ OIC_LOG(ERROR, TAG, "Failed to decode base64 data");
+ return -1;
+ }
+
+ *der = buf;
+ *derLen = outSize;
+
+ return 0;
+}
+
+/* Caller must call OICFree on *pem when finished. */
+static int ConvertDerCertToPem(const uint8_t* der, size_t derLen, uint8_t** pem)
+{
+ const char* pemHeader = "-----BEGIN CERTIFICATE-----\n";
+ const char* pemFooter = "-----END CERTIFICATE-----\n";
+
+ /* Get the length required for output*/
+ size_t pemLen;
+ int ret = mbedtls_pem_write_buffer(pemHeader,
+ pemFooter,
+ der,
+ derLen,
+ NULL,
+ 0,
+ &pemLen);
+ if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't convert cert into PEM, failed getting required length: %d", ret);
+ return ret;
+ }
+
+ *pem = OICCalloc(1, pemLen + 1);
+ if (*pem == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to allocate memory for PEM cert");
+ return ret;
+ }
+
+ /* Try the conversion */
+ ret = mbedtls_pem_write_buffer(pemHeader, pemFooter,
+ der,
+ derLen,
+ *pem,
+ pemLen,
+ &pemLen);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't convert cert into PEM, failed getting required length: %d", ret);
+ OICFreeAndSetToNull(pem);
+ return ret;
+ }
+
+ return 0;
+}
+
+static OCStackResult GetCaCert(ByteArray_t * crt, const char * usage, OicEncodingType_t desiredEncoding)
{
OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
if (NULL == crt || NULL == usage)
{
OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
- return;
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ switch (desiredEncoding)
+ {
+ case OIC_ENCODING_PEM:
+ case OIC_ENCODING_DER:
+ case OIC_ENCODING_BASE64:
+ break;
+ default:
+ OIC_LOG_V(ERROR, TAG, "%s: Unsupported encoding %d", __func__, desiredEncoding);
+ return OC_STACK_INVALID_PARAM;
}
+
crt->len = 0;
OicSecCred_t* temp = NULL;
LL_FOREACH(gCred, temp)
{
if ((SIGNED_ASYMMETRIC_KEY == temp->credType) &&
+ (temp->credUsage != NULL) &&
(0 == strcmp(temp->credUsage, usage)) && (false == temp->optionalData.revstat))
{
- if(OIC_ENCODING_BASE64 == temp->optionalData.encoding)
+ if (OIC_ENCODING_DER == desiredEncoding)
{
- size_t bufSize = B64DECODE_OUT_SAFESIZE((temp->optionalData.len + 1));
- uint8_t * buf = OICCalloc(1, bufSize);
- if(NULL == buf)
+ if ((OIC_ENCODING_BASE64 == temp->optionalData.encoding) ||
+ (OIC_ENCODING_PEM == temp->optionalData.encoding))
{
- OIC_LOG(ERROR, TAG, "Failed to allocate memory");
- return;
+ uint8_t *buf = NULL;
+ size_t outSize = 0;
+ int ret = ConvertPemCertToDer((const char *)temp->optionalData.data, temp->optionalData.len, &buf, &outSize);
+ if (0 > ret)
+ {
+ OIC_LOG(ERROR, TAG, "Could not convert PEM cert to DER");
+ return OC_STACK_ERROR;
+ }
+
+ uint8_t *savePtr = crt->data;
+ crt->data = OICRealloc(crt->data, crt->len + outSize);
+ if (NULL == crt->data)
+ {
+ OIC_LOG(ERROR, TAG, "No memory reallocating crt->data");
+ OICFree(savePtr);
+ OICFree(buf);
+ return OC_STACK_NO_MEMORY;
+ }
+ memcpy(crt->data + crt->len, buf, outSize);
+ crt->len += outSize;
+ OICFree(buf);
}
- size_t outSize;
- if(B64_OK != b64Decode((char*)(temp->optionalData.data),
- temp->optionalData.len, buf, bufSize, &outSize))
+ else
{
- OICFree(buf);
- OIC_LOG(ERROR, TAG, "Failed to decode base64 data");
- return;
+ uint8_t *savePtr = crt->data;
+ crt->data = OICRealloc(crt->data, crt->len + temp->optionalData.len);
+ if (NULL == crt->data)
+ {
+ OIC_LOG(ERROR, TAG, "No memory reallocating crt->data");
+ OICFree(savePtr);
+ return OC_STACK_NO_MEMORY;
+ }
+ memcpy(crt->data + crt->len, temp->optionalData.data, temp->optionalData.len);
+ crt->len += temp->optionalData.len;
}
- crt->data = OICRealloc(crt->data, crt->len + outSize);
- memcpy(crt->data + crt->len, buf, outSize);
- crt->len += outSize;
- OICFree(buf);
+ OIC_LOG_V(DEBUG, TAG, "%s found", usage);
}
else
{
+ /* PEM/Base64 */
+ uint8_t *pem = NULL;
+ size_t pemLen = 0;
+ if ((OIC_ENCODING_BASE64 == temp->optionalData.encoding) ||
+ (OIC_ENCODING_PEM == temp->optionalData.encoding))
+ {
+ pem = temp->optionalData.data;
+ pemLen = temp->optionalData.len;
+ }
+ else
+ {
+ int ret = ConvertDerCertToPem(temp->optionalData.data, temp->optionalData.len, &pem);
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed converting DER cert to PEM: %d", ret);
+ return OC_STACK_ERROR;
+ }
+ pemLen = strlen((char *)pem) + 1;
+ }
+
+ uint8_t *oldData = crt->data;
crt->data = OICRealloc(crt->data, crt->len + temp->optionalData.len);
+ if (NULL == crt->data)
+ {
+ OIC_LOG(ERROR, TAG, "No memory reallocating crt->data");
+ OICFree(oldData);
+ return OC_STACK_NO_MEMORY;
+ }
memcpy(crt->data + crt->len, temp->optionalData.data, temp->optionalData.len);
crt->len += temp->optionalData.len;
}
- OIC_LOG_V(DEBUG, TAG, "%s found", usage);
}
}
if(0 == crt->len)
{
OIC_LOG_V(WARNING, TAG, "%s not found", usage);
+ return OC_STACK_NO_RESOURCE;
}
OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
- return;
+ return OC_STACK_OK;
+}
+
+void GetDerCaCert(ByteArray_t * crt, const char * usage)
+{
+ (void)GetCaCert(crt, usage, OIC_ENCODING_DER);
+}
+
+OCStackResult GetPemCaCert(ByteArray_t * crt, const char * usage)
+{
+ return GetCaCert(crt, usage, OIC_ENCODING_PEM);
}
void GetDerOwnCert(ByteArray_t * crt, const char * usage)
LL_FOREACH(gCred, temp)
{
if (SIGNED_ASYMMETRIC_KEY == temp->credType &&
+ temp->credUsage != NULL &&
0 == strcmp(temp->credUsage, usage))
{
crt->data = OICRealloc(crt->data, crt->len + temp->publicData.len);
key->len = 0;
LL_FOREACH(gCred, temp)
{
- if (SIGNED_ASYMMETRIC_KEY == temp->credType &&
+ if ((SIGNED_ASYMMETRIC_KEY == temp->credType || ASYMMETRIC_KEY == temp->credType) &&
+ NULL != temp->credUsage &&
0 == strcmp(temp->credUsage, usage))
{
- key->data = OICRealloc(key->data, key->len + temp->privateData.len);
- memcpy(key->data + key->len, temp->privateData.data, temp->privateData.len);
- key->len += temp->privateData.len;
- OIC_LOG_V(DEBUG, TAG, "Key for %s found", usage);
+
+ if (temp->privateData.encoding == OIC_ENCODING_PEM)
+ {
+ /* Convert PEM to DER */
+ mbedtls_pk_context ctx;
+ mbedtls_pk_init(&ctx);
+
+ int ret = mbedtls_pk_parse_key(&ctx, temp->privateData.data, temp->privateData.len, NULL, 0);
+ if (ret != 0)
+ {
+ mbedtls_pk_free(&ctx);
+ OIC_LOG_V(ERROR, TAG, "Key for %s found, but failed to convert from PEM to DER (while reading PEM)", usage);
+ return;
+ }
+
+ key->data = OICRealloc(key->data, key->len + temp->privateData.len);
+ if (key->data == NULL)
+ {
+ mbedtls_pk_free(&ctx);
+ OIC_LOG(ERROR, TAG, "Realloc failed to increase key->data length");
+ return;
+ }
+
+ key->len += temp->privateData.len;
+ ret = mbedtls_pk_write_key_der(&ctx, key->data, key->len);
+ if (ret < 1) /* return value is the number of bytes written, or error */
+ {
+ mbedtls_pk_free(&ctx);
+ key->len = 0;
+ OIC_LOG_V(ERROR, TAG, "Key for %s found, but failed to convert from PEM to DER (while writing DER)", usage);
+ return;
+ }
+ key->data = OICRealloc(key->data, ret);
+ key->len = ret;
+ break;
+
+ }
+ else if(temp->privateData.encoding == OIC_ENCODING_DER)
+ {
+ key->data = OICRealloc(key->data, key->len + temp->privateData.len);
+ memcpy(key->data + key->len, temp->privateData.data, temp->privateData.len);
+ key->len += temp->privateData.len;
+ OIC_LOG_V(DEBUG, TAG, "Key for %s found", usage);
+ break;
+ }
+ else
+ {
+ OIC_LOG_V(WARNING, TAG, "Key for %s found, but it has an unknown encoding", usage);
+ }
}
}
if(0 == key->len)
}
case SIGNED_ASYMMETRIC_KEY:
{
- if (0 == strcmp(temp->credUsage, usage))
+ if (NULL != temp->credUsage && 0 == strcmp(temp->credUsage, usage))
{
list[1] = true;
OIC_LOG_V(DEBUG, TAG, "SIGNED_ASYMMETRIC_KEY found for %s", usage);
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#include "iotivity_config.h"
+#include <stdlib.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include "oic_string.h"
+#include "cainterface.h"
+#include "payload_logging.h"
+#include "ocstack.h"
+#include "ocrandom.h"
+#include "cacommon.h"
+#include "srmresourcestrings.h"
+#include "ocpayload.h"
+#include "ocpayloadcbor.h"
+#include "credresource.h"
+#include "doxmresource.h"
+#include "srmutility.h"
+#include "certhelpers.h"
+#include "csrresource.h"
+#include "resourcemanager.h"
+
+#define TAG "OIC_SRM_CSR"
+
+static OCResourceHandle gCsrHandle = NULL;
+
+/** Default cbor payload size. This value is increased in case of CborErrorOutOfMemory.
+ * The value of payload size is increased until reaching belox max cbor size.
+ */
+static const uint16_t CBOR_SIZE = 2048;
+
+static const char* EncodingValueToString(OicEncodingType_t encoding)
+{
+ switch (encoding)
+ {
+ case OIC_ENCODING_RAW: return OIC_SEC_ENCODING_RAW;
+ case OIC_ENCODING_BASE64: return OIC_SEC_ENCODING_BASE64;
+ case OIC_ENCODING_DER: return OIC_SEC_ENCODING_DER;
+ case OIC_ENCODING_PEM: return OIC_SEC_ENCODING_PEM;
+ default: return NULL;
+ }
+}
+
+
+static OCStackResult StoreKeyPair(mbedtls_pk_context *keyPair, const OicUuid_t *myUuid)
+{
+ int ret = 0;
+ OicSecCred_t *cred = NULL;
+ OicSecKey_t publicData;
+ OicSecKey_t privateData;
+ uint8_t publicBuf[1024];
+ uint8_t privateBuf[1024];
+
+ /* These DER writing APIs write at the END of the buffers, hence the pointer arithmetic. See the API
+ * documentation for mbedTLS.
+ */
+
+ ret = mbedtls_pk_write_pubkey_der(keyPair, publicBuf, sizeof(publicBuf));
+ VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
+ publicData.data = publicBuf + sizeof(publicBuf) - ret;
+ publicData.len = ret;
+ publicData.encoding = OIC_ENCODING_DER;
+
+ ret = mbedtls_pk_write_key_der(keyPair, privateBuf, sizeof(privateBuf));
+ VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
+ privateData.data = privateBuf + sizeof(privateBuf) - ret;
+ privateData.len = ret;
+ privateData.encoding = OIC_ENCODING_DER;
+
+ cred = GenerateCredential(myUuid, ASYMMETRIC_KEY, &publicData, &privateData, myUuid, NULL);
+ VERIFY_NOT_NULL(TAG, cred, ERROR);
+ cred->credUsage = OICStrdup(PRIMARY_KEY); // @todo: we may be able to use PRIMARY_CERT here too; need to investigate
+ VERIFY_NOT_NULL(TAG, cred->credUsage, ERROR);
+
+ VERIFY_SUCCESS(TAG, OC_STACK_OK == AddCredential(cred), ERROR);
+
+ return OC_STACK_OK;
+
+exit:
+
+ if (NULL != cred)
+ {
+ DeleteCredList(cred);
+ }
+
+ OICClearMemory(privateBuf, sizeof(privateBuf));
+
+ return OC_STACK_ERROR;
+}
+
+static OCStackResult CSRToCBORPayload(const uint8_t *csr, size_t csrLen, OicEncodingType_t encoding, uint8_t **cborPayload, size_t *cborSize)
+{
+ OCStackResult ret = OC_STACK_ERROR;
+
+ CborError cborEncoderResult = CborNoError;
+ uint8_t *outPayload = NULL;
+ size_t cborLen = *cborSize;
+ CborEncoder encoder;
+ CborEncoder csrRootMap;
+ const char *strEncoding = NULL;
+
+ if ((NULL == csr) || (0 == csrLen) || (NULL == cborPayload) || (NULL == cborSize))
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if ((OIC_ENCODING_DER != encoding) && (OIC_ENCODING_PEM != encoding))
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ *cborSize = 0;
+ *cborPayload = NULL;
+
+ if (0 == cborLen)
+ {
+ cborLen = CBOR_SIZE;
+ }
+
+ outPayload = (uint8_t *)OICCalloc(1, cborLen);
+ VERIFY_NOT_NULL_RETURN(TAG, outPayload, ERROR, OC_STACK_NO_MEMORY);
+ cbor_encoder_init(&encoder, outPayload, cborLen, 0);
+
+ // Create CSR Root Map (csr)
+ cborEncoderResult = cbor_encoder_create_map(&encoder, &csrRootMap, 4);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding CSR Root Map");
+
+ // Create CSR string entry
+ cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_CSR_NAME, strlen(OIC_JSON_CSR_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding CSR name.");
+ cborEncoderResult = cbor_encode_byte_string(&csrRootMap, csr, csrLen);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding CSR value.");
+
+ // Create encoding entry
+ cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_ENCODING_NAME, strlen(OIC_JSON_ENCODING_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding encoding name.");
+ strEncoding = EncodingValueToString(encoding);
+ assert(strEncoding != NULL);
+ cborEncoderResult = cbor_encode_text_string(&csrRootMap, strEncoding, strlen(strEncoding));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding encoding value.");
+
+ // Rownerid
+ {
+ char *rowner = NULL;
+ OicUuid_t myUuid;
+ cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_ROWNERID_NAME,
+ strlen(OIC_JSON_ROWNERID_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding rownerid Name.");
+ ret = GetDoxmDeviceID(&myUuid);
+ VERIFY_SUCCESS(TAG, OC_STACK_OK == ret, ERROR);
+ ret = ConvertUuidToStr(&myUuid, &rowner);
+ VERIFY_SUCCESS(TAG, OC_STACK_OK == ret, ERROR);
+ cborEncoderResult = cbor_encode_text_string(&csrRootMap, rowner, strlen(rowner));
+ OICFree(rowner);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding rownerid Value.");
+ }
+
+ //RT -- Mandatory
+ CborEncoder rtArray;
+ cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_RT_NAME,
+ strlen(OIC_JSON_RT_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding RT Name Tag.");
+ cborEncoderResult = cbor_encoder_create_array(&csrRootMap, &rtArray, 1);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding RT Value.");
+ for (size_t i = 0; i < 1; i++)
+ {
+ cborEncoderResult = cbor_encode_text_string(&rtArray, OIC_RSRC_TYPE_SEC_CSR,
+ strlen(OIC_RSRC_TYPE_SEC_CSR));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding RT Value.");
+ }
+ cborEncoderResult = cbor_encoder_close_container(&csrRootMap, &rtArray);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Closing RT.");
+
+ //IF-- Mandatory
+ CborEncoder ifArray;
+ cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_IF_NAME,
+ strlen(OIC_JSON_IF_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding IF Name Tag.");
+ cborEncoderResult = cbor_encoder_create_array(&csrRootMap, &ifArray, 1);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding IF Value.");
+ for (size_t i = 0; i < 1; i++)
+ {
+ cborEncoderResult = cbor_encode_text_string(&ifArray, OC_RSRVD_INTERFACE_DEFAULT,
+ strlen(OC_RSRVD_INTERFACE_DEFAULT));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding IF Value.");
+ }
+ cborEncoderResult = cbor_encoder_close_container(&csrRootMap, &ifArray);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Closing IF.");
+
+ // Close CSR Map
+ cborEncoderResult = cbor_encoder_close_container(&encoder, &csrRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Closing CSR root Map.");
+
+ if (CborNoError == cborEncoderResult)
+ {
+ OIC_LOG(DEBUG, TAG, "CSRToCBORPayload Succeeded");
+ *cborSize = cbor_encoder_get_buffer_size(&encoder, outPayload);
+ *cborPayload = outPayload;
+ ret = OC_STACK_OK;
+ }
+exit:
+ if (CborErrorOutOfMemory == cborEncoderResult)
+ {
+ OIC_LOG(DEBUG, TAG, "CSRToCBORPayload:CborErrorOutOfMemory : retry with more memory");
+
+ // reallocate and try again!
+ OICFree(outPayload);
+ // Since the allocated initial memory failed, double the memory.
+ cborLen += cbor_encoder_get_buffer_size(&encoder, encoder.end);
+ cborEncoderResult = CborNoError;
+ ret = CSRToCBORPayload(csr, csrLen, encoding, cborPayload, &cborLen);
+ *cborSize = cborLen;
+ }
+ else if (cborEncoderResult != CborNoError)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to CSRToCBORPayload");
+ OICFree(outPayload);
+ outPayload = NULL;
+ *cborSize = 0;
+ *cborPayload = NULL;
+ ret = OC_STACK_ERROR;
+ }
+
+ return ret;
+}
+
+OCStackResult CBORPayloadToCSR(const uint8_t *cborPayload, size_t size, uint8_t **csr, size_t *csrLen, OicEncodingType_t *encoding)
+{
+ if (NULL == cborPayload || 0 == size || NULL == csr || NULL == csrLen)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ OCStackResult ret = OC_STACK_ERROR;
+ CborValue csrCbor = { .parser = NULL };
+ CborParser parser = { .end = NULL };
+ CborError cborFindResult = CborNoError;
+ uint8_t* cborCsr = NULL;
+ size_t cborCsrLen = 0;
+ char* tagName = NULL;
+ size_t len = 0;
+
+ cborFindResult = cbor_parser_init(cborPayload, size, 0, &parser, &csrCbor);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed to initialize parser.");
+
+ if (!cbor_value_is_container(&csrCbor))
+ {
+ return OC_STACK_ERROR;
+ }
+
+ // Enter CSR Root Map
+ CborValue csrRootMap = { .parser = NULL, .ptr = NULL, .remaining = 0, .extra = 0, .type = 0, .flags = 0 };
+ cborFindResult = cbor_value_enter_container(&csrCbor, &csrRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Entering CSR Root Map.");
+
+ while (cbor_value_is_valid(&csrRootMap))
+ {
+ if (NULL != tagName)
+ {
+ free(tagName);
+ tagName = NULL;
+ }
+ len = 0;
+ CborType type = cbor_value_get_type(&csrRootMap);
+ if (type == CborTextStringType && cbor_value_is_text_string(&csrRootMap))
+ {
+ cborFindResult = cbor_value_dup_text_string(&csrRootMap, &tagName, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed finding name in CSR Root Map.");
+ cborFindResult = cbor_value_advance(&csrRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing value in CSR Root Map.");
+ }
+ if (NULL != tagName)
+ {
+ if (strcmp(tagName, OIC_JSON_CSR_NAME) == 0 && cbor_value_is_byte_string(&csrRootMap))
+ {
+ cborFindResult = cbor_value_dup_byte_string(&csrRootMap, &cborCsr, &cborCsrLen, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed finding name in CSR Map");
+ // Use our own heap allocator and copy the result in so callers can use OICFree.
+ *csr = (uint8_t *)OICCalloc(1, cborCsrLen);
+ VERIFY_NOT_NULL(TAG, *csr, ERROR);
+ memcpy(*csr, cborCsr, cborCsrLen);
+ free(cborCsr);
+ cborCsr = NULL;
+ *csrLen = cborCsrLen;
+ cborCsrLen = 0;
+ }
+ else if (strcmp(tagName, OIC_JSON_ENCODING_NAME) == 0 && cbor_value_is_text_string(&csrRootMap))
+ {
+ char *strEncoding = NULL;
+ cborFindResult = cbor_value_dup_text_string(&csrRootMap, &strEncoding, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed finding encoding type");
+
+ if (strcmp(strEncoding, OIC_SEC_ENCODING_DER) == 0)
+ {
+ *encoding = OIC_ENCODING_DER;
+ }
+ else if (strcmp(strEncoding, OIC_SEC_ENCODING_PEM) == 0)
+ {
+ *encoding = OIC_ENCODING_PEM;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Invalid encoding type %s", strEncoding);
+ ret = OC_STACK_ERROR;
+ free(strEncoding);
+ goto exit;
+ }
+
+ free(strEncoding);
+ }
+ else
+ {
+ // Ignore any other tag type for now.
+ OIC_LOG_V(WARNING, TAG, "Unknown tag %s", tagName);
+ }
+
+ }
+ if (cbor_value_is_valid(&csrRootMap))
+ {
+ cborFindResult = cbor_value_advance(&csrRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing CSR Root Map");
+ }
+ }
+
+ ret = OC_STACK_OK;
+
+exit:
+ if (CborNoError != cborFindResult)
+ {
+ if (NULL != *csr)
+ {
+ OICFreeAndSetToNull((void **)csr);
+ }
+
+ *csrLen = 0;
+
+ ret = OC_STACK_ERROR;
+ }
+
+ // tagName and cborCsr are both allocated internally by tinycbor, which uses malloc, not OICMalloc.
+ // Therefore, they must be freed with free, not OICFree.
+ if (NULL != tagName)
+ {
+ free(tagName);
+ }
+ if (NULL != cborCsr)
+ {
+ free(cborCsr);
+ }
+
+ return ret;
+
+}
+
+static OCEntityHandlerResult HandleCsrGetRequest(OCEntityHandlerRequest * ehRequest)
+{
+ OCStackResult res = OC_STACK_OK;
+ OCEntityHandlerResult ehRet = OC_EH_OK;
+ int ret = 0;
+ mbedtls_pk_context keyPair;
+ OicUuid_t myUuid;
+ char *myUuidStr = NULL;
+ char *myDNStr = NULL;
+ size_t myDNStrLen = 0;
+ ByteArray_t keyData = { .data = NULL, .len = 0 };
+ OCByteString csr = { .bytes = NULL, .len = 0 };
+ size_t size = 0;
+ uint8_t *payload = NULL;
+
+ OIC_LOG(INFO, TAG, "HandleCsrGetRequest processing GET request");
+
+ mbedtls_pk_init(&keyPair);
+
+ // Retrieve our current certificate, if we have one, and use that key
+ GetDerKey(&keyData, PRIMARY_CERT);
+
+ if (0 == keyData.len)
+ {
+ // No cert? Get our primary key pair, or generate it if absent.
+ GetDerKey(&keyData, PRIMARY_KEY);
+ }
+
+ res = GetDoxmDeviceID(&myUuid);
+ VERIFY_SUCCESS(TAG, OC_STACK_OK == res, ERROR);
+ res = ConvertUuidToStr(&myUuid, &myUuidStr);
+ VERIFY_SUCCESS(TAG, OC_STACK_OK == res, ERROR);
+
+ if (0 < keyData.len)
+ {
+ ret = mbedtls_pk_parse_key(&keyPair, keyData.data, keyData.len, NULL, 0);
+ if (0 > ret)
+ {
+ res = OC_STACK_ERROR;
+ // Because we need to set res before failing out, this is a redundant check of ret, but gives better logging.
+ VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
+ }
+ }
+ else
+ {
+ ret = OCInternalGenerateKeyPair(&keyPair);
+ if (0 > ret)
+ {
+ res = OC_STACK_ERROR;
+ VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
+ }
+ VERIFY_SUCCESS(TAG, OC_STACK_OK == (res = StoreKeyPair(&keyPair, &myUuid)), ERROR);
+ }
+
+ // Generate CSR
+ myDNStrLen = strlen(myUuidStr) + sizeof(SUBJECT_PREFIX); // sizeof(SUBJECT_PREFIX) will also count the byte for terminating null.
+ myDNStr = (char *)OICMalloc(myDNStrLen);
+ VERIFY_NOT_NULL(TAG, myDNStr, ERROR);
+ ret = snprintf(myDNStr, myDNStrLen, SUBJECT_PREFIX "%s", myUuidStr);
+ VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
+ /* ret >= myDNStrLen means there was truncation; assert because this means a code bug. */
+ assert((size_t)ret < myDNStrLen);
+ ret = OCInternalCSRRequest(myDNStr, &keyPair, OIC_ENCODING_DER, &csr);
+ if (0 > ret)
+ {
+ res = OC_STACK_ERROR;
+ VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
+ }
+
+ // Convert CSR data into CBOR for transmission
+ res = CSRToCBORPayload(csr.bytes, csr.len, OIC_ENCODING_DER, &payload, &size);
+
+exit:
+
+ ehRet = (OC_STACK_OK == res) ? OC_EH_OK : OC_EH_ERROR;
+
+ //Send payload to request originator
+ ehRet = ((SendSRMResponse(ehRequest, ehRet, payload, size)) == OC_STACK_OK) ?
+ OC_EH_OK : OC_EH_ERROR;
+
+ mbedtls_pk_free(&keyPair);
+ OICFree(myDNStr);
+ OICFree(myUuidStr);
+ OICFree(keyData.data);
+ OICFree(csr.bytes);
+ OICFree(payload);
+ return ehRet;
+}
+
+static OCEntityHandlerResult CredCsrEntityHandler(OCEntityHandlerFlag flag,
+ OCEntityHandlerRequest * ehRequest,
+ void* callbackParameter)
+{
+ OC_UNUSED(callbackParameter);
+ OCEntityHandlerResult ret = OC_EH_ERROR;
+
+ if (!ehRequest)
+ {
+ return OC_EH_ERROR;
+ }
+
+ if (flag & OC_REQUEST_FLAG)
+ {
+ OIC_LOG(DEBUG, TAG, "Flag includes OC_REQUEST_FLAG");
+ if (ehRequest->method == OC_REST_GET)
+ {
+ ret = HandleCsrGetRequest(ehRequest);
+ }
+ else
+ {
+ /* Only GET supported on CSR resource. */
+ ret = OC_EH_ERROR;
+ }
+ }
+
+ return ret;
+}
+
+OCStackResult InitCSRResource()
+{
+ OCStackResult ret = OCCreateResource(&gCsrHandle,
+ OIC_RSRC_TYPE_SEC_CSR,
+ OC_RSRVD_INTERFACE_DEFAULT,
+ OIC_RSRC_CSR_URI,
+ CredCsrEntityHandler,
+ NULL,
+ OC_SECURE);
+
+ if (OC_STACK_OK != ret)
+ {
+ OIC_LOG(FATAL, TAG, "Unable to instantiate CSR resource");
+ }
+
+ return ret;
+}
+
+OCStackResult DeInitCSRResource()
+{
+ OCStackResult res = OCDeleteResource(gCsrHandle);
+
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to delete CSR resource: %d", res);
+ }
+
+ gCsrHandle = NULL;
+
+ return res;
+}
\ No newline at end of file
if(CA_STATUS_OK == errorInfo->result)
{
- const CASecureEndpoint_t* authenticatedSubOwnerInfo = CAGetSecureEndpointData(object);
- if(authenticatedSubOwnerInfo)
+ CASecureEndpoint_t authenticationSubOwnerInfo;
+ CAResult_t caRes = CAGetSecureEndpointData(object, &authenticationSubOwnerInfo);
+ if (CA_STATUS_OK == caRes)
{
- if (0 == memcmp(authenticatedSubOwnerInfo->identity.id, gDoxm->owner.id,
- authenticatedSubOwnerInfo->identity.id_length))
+ if (0 == memcmp(authenticationSubOwnerInfo.identity.id, gDoxm->owner.id,
+ authenticationSubOwnerInfo.identity.id_length))
{
OIC_LOG(WARNING, TAG, "Super owner tried MOT, this request will be ignored.");
return;
LL_FOREACH(gDoxm->subOwners, subOwnerInst)
{
if(0 == memcmp(subOwnerInst->uuid.id,
- authenticatedSubOwnerInfo->identity.id,
- authenticatedSubOwnerInfo->identity.id_length))
+ authenticationSubOwnerInfo.identity.id,
+ authenticationSubOwnerInfo.identity.id_length))
{
break;
}
if(subOwnerInst)
{
char* strUuid = NULL;
- memcpy(subOwnerInst->uuid.id, authenticatedSubOwnerInfo->identity.id,
- authenticatedSubOwnerInfo->identity.id_length);
+ memcpy(subOwnerInst->uuid.id, authenticationSubOwnerInfo.identity.id,
+ authenticationSubOwnerInfo.identity.id_length);
if(OC_STACK_OK != ConvertUuidToStr(&subOwnerInst->uuid, &strUuid))
{
OIC_LOG(ERROR, TAG, "Failed to allocate memory.");
}
}
}
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not CAGetSecureEndpointData: %d", caRes);
+ }
}
if(CA_STATUS_OK != CAregisterPskCredentialsHandler(GetDtlsPskCredentials))
LL_APPEND(acl->aces, ace);
+ ace->subjectType = OicSecAceUuidSubject;
memcpy(&ace->subjectuuid, &gDpair->pdeviceID, sizeof(OicUuid_t));
for(size_t i = 0; i < pdAcl->resourcesLen; i++)
size_t size = 0;
uint8_t *payload = NULL;
- if (OC_STACK_OK == AclToCBORPayload(acl, &payload, &size))
+ if (OC_STACK_OK == AclToCBORPayload(acl, OIC_SEC_ACL_V1, &payload, &size))
{
AppendACL(payload, size);
OICFree(payload);
--- /dev/null
+/* *****************************************************************
+ *
+ * Copyright 2017 Microsoft. All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * *****************************************************************/
+
+#if defined(__WITH_TLS__) || defined(__WITH_DTLS__)
+
+#include "iotivity_config.h"
+
+#include "logger.h"
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "cacommon.h"
+#include "ocrandom.h"
+#include "cacommonutil.h"
+
+#include "ocpayload.h"
+#include "payload_logging.h"
+#include "pmutility.h"
+#include "srmutility.h"
+
+// headers required for mbed TLS
+#include "mbedtls/config.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/x509_csr.h"
+#include "mbedtls/oid.h"
+#include "mbedtls/x509_crt.h"
+#include "mbedtls/oid.h"
+#include "mbedtls/pem.h"
+#include "mbedtls/base64.h"
+
+#ifndef NDEBUG
+#include "mbedtls/debug.h"
+#include "mbedtls/version.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "certhelpers.h"
+#include "occertutility.h"
+
+#define TAG "OIC_OCCERTUTILITY"
+
+/**
+ * @def PERSONALIZATION_STRING
+ * @brief Personalization string for the mbedtls RNG
+ */
+#define PERSONALIZATION_STRING "IOTIVITY_RND"
+
+#define MAX_URI_QUERY MAX_URI_LENGTH + MAX_QUERY_LENGTH
+
+#define MAX_STRING_LEN 254
+
+/* ASN.1 DER encoding of the EKU for identity certificates (1.3.6.1.4.1.44924.1.6) */
+static const unsigned char s_ekuIdentity[] = { 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06 };
+
+/* ASN.1 DER encoding of the EKU for role certificates (1.3.6.1.4.1.44924.1.7) */
+static const unsigned char s_ekuRole[] = { 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x07 };
+
+/* ASN.1 DER encoding of the EKU for both identity and roles (for use by CAs) */
+static const unsigned char s_ekuCA[] = { 0x30, 0x18, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x07 };
+
+/**
+ * Generates elliptic curve keypair.
+ *
+ * @param[out] pk mbedtls public key container
+ *
+ * @return 0 on success or <0 on error
+ */
+static int GenerateEccKeyPair(mbedtls_pk_context *pk)
+{
+ int ret = 0;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+
+ OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+ VERIFY_NON_NULL_RET(pk, TAG, "Param pk is NULL", -1);
+
+ // Initialize the DRBG context
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &entropy, (const unsigned char *)PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING));
+
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "Seed initialization failed! %d", ret);
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+ mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON);
+ ret = mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "mbedtls_pk_setup error %d", ret);
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+ ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*pk), mbedtls_ctr_drbg_random, &ctr_drbg);
+ if (0 > ret)
+ {
+ OIC_LOG(ERROR, TAG, "mbedtls_ecp_gen_keypair error");
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+
+exit:
+
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ return 0;
+}
+
+OCStackResult OCGenerateRandomSerialNumber(char **serial, size_t *serialLen)
+{
+ int ret = 0;
+ OCStackResult res = OC_STACK_ERROR;
+ unsigned char random[20]; /* Per RFC 5280, 20 octets is the maximum length of a serial number. */
+ mbedtls_mpi serialMpi;
+
+ VERIFY_NOT_NULL_RETURN(TAG, serial, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, serialLen, ERROR, OC_STACK_INVALID_PARAM);
+
+ mbedtls_mpi_init(&serialMpi);
+ memset(serial, 0, sizeof(*serial));
+
+ VERIFY_SUCCESS(TAG, OCGetRandomBytes(random, sizeof(random)), ERROR);
+
+ /* Per RFC 5280, 20 octets is the maximum length of a serial number. In ASN.1, if the highest-order
+ * bit is set it causes a padding octet to be written, which would be 21 and non-compliant.
+ * Therefore, always clear the highest-order bit. Integers in ASN.1 are always big-Endian.
+ */
+ random[0] &= 0x7F;
+
+ /* Import into a large integer object and then output as a string. */
+ ret = mbedtls_mpi_read_binary(&serialMpi, random, sizeof(random));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ /* Get the needed string length and allocate. */
+ ret = mbedtls_mpi_write_string(&serialMpi, 10, NULL, 0, serialLen);
+ VERIFY_SUCCESS(TAG, ret == MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL, ERROR);
+ *serial = OICCalloc(1, *serialLen);
+ VERIFY_NOT_NULL(TAG, *serial, ERROR);
+
+ /* Do the write for real. */
+ ret = mbedtls_mpi_write_string(&serialMpi, 10, *serial, *serialLen, serialLen);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ res = OC_STACK_OK;
+
+exit:
+
+ if (OC_STACK_OK != res)
+ {
+ OICFree(*serial);
+ *serial = NULL;
+ *serialLen = 0;
+ }
+ mbedtls_mpi_free(&serialMpi);
+
+ return res;
+}
+
+OCStackResult OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen,
+ char **privateKey, size_t *privateKeyLen)
+{
+ int ret = 0;
+ mbedtls_pk_context keyPair;
+ unsigned char buf[2048];
+
+ mbedtls_pk_init(&keyPair);
+
+ VERIFY_NOT_NULL_RETURN(TAG, publicKey, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, publicKeyLen, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, privateKey, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, privateKeyLen, ERROR, OC_STACK_INVALID_PARAM);
+
+ *publicKey = NULL;
+ *publicKeyLen = 0;
+ *privateKey = NULL;
+ *privateKeyLen = 0;
+
+ ret = OCInternalGenerateKeyPair(&keyPair);
+ if (ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to generate key pair: %d", ret);
+ goto exit;
+ }
+
+ ret = mbedtls_pk_write_pubkey_pem(&keyPair, buf, sizeof(buf));
+ if (ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to export public key as PEM: %d", ret);
+ goto exit;
+ }
+
+ *publicKeyLen = strlen((char *)buf) + 1;
+ *publicKey = OICCalloc(1, *publicKeyLen);
+ if (NULL == *publicKey)
+ {
+ OIC_LOG(ERROR, TAG, "Could not allocate memory for public key");
+ ret = -1;
+ goto exit;
+ }
+ memcpy(*publicKey, buf, *publicKeyLen);
+
+ ret = mbedtls_pk_write_key_pem(&keyPair, buf, sizeof(buf));
+ if (ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to export private key as PEM: %d", ret);
+ goto exit;
+ }
+
+ *privateKeyLen = strlen((char *)buf) + 1;
+ *privateKey = OICCalloc(1, *privateKeyLen);
+ if (NULL == *privateKey)
+ {
+ OIC_LOG(ERROR, TAG, "Could not allocate memory for private key");
+ ret = -1;
+ goto exit;
+ }
+ memcpy(*privateKey, buf, *privateKeyLen);
+
+exit:
+
+ mbedtls_pk_free(&keyPair);
+
+ OICClearMemory(buf, sizeof(buf));
+
+ if (ret != 0)
+ {
+ OICFree(*publicKey);
+ OICClearMemory(*privateKey, *privateKeyLen);
+ OICFree(*privateKey);
+
+ *publicKey = NULL;
+ *publicKeyLen = 0;
+ *privateKey = NULL;
+ *privateKeyLen = 0;
+
+ return OC_STACK_ERROR;
+ }
+ else
+ {
+ return OC_STACK_OK;
+ }
+}
+
+typedef enum {
+ CERT_TYPE_CA,
+ CERT_TYPE_IDENTITY,
+ CERT_TYPE_ROLE
+} CertificateType_t;
+
+static OCStackResult GenerateCertificate(
+ CertificateType_t certType,
+ const char *subject,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ const char *role,
+ const char *authority,
+ OCByteString *certificate)
+{
+ OCStackResult res = OC_STACK_INVALID_PARAM;
+ int ret = 0;
+ mbedtls_x509write_cert outCertCtx;
+ mbedtls_pk_context subjKeyCtx;
+ mbedtls_pk_context issKeyCtx;
+ mbedtls_x509_crt issCertCtx;
+ mbedtls_mpi serialMpi;
+ mbedtls_x509_general_names names;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+
+ char buf[2048];
+
+ if (NULL == subjectPublicKey || NULL == issuerPrivateKey || NULL == subject || NULL == serial ||
+ NULL == notValidBefore || NULL == notValidAfter)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ mbedtls_x509write_crt_init(&outCertCtx);
+ mbedtls_pk_init(&subjKeyCtx);
+ mbedtls_pk_init(&issKeyCtx);
+ mbedtls_x509_crt_init(&issCertCtx);
+ mbedtls_mpi_init(&serialMpi);
+ memset(&names, 0, sizeof(names));
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+ memset(certificate, 0, sizeof(*certificate));
+
+ ret = mbedtls_mpi_read_string(&serialMpi, 10, serial);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_pk_parse_public_key(&subjKeyCtx, (const uint8_t *)subjectPublicKey, strlen(subjectPublicKey) + 1);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_pk_parse_key(&issKeyCtx, (const uint8_t *)issuerPrivateKey, strlen(issuerPrivateKey) + 1, NULL, 0);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ /* If issuerCert is NULL, then the cert will be self-signed. */
+ if (NULL != issuerCert)
+ {
+ ret = mbedtls_x509_crt_parse(&issCertCtx, (const uint8_t *)issuerCert, strlen(issuerCert) + 1);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ }
+
+ ret = mbedtls_x509write_crt_set_validity(&outCertCtx, notValidBefore, notValidAfter);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ mbedtls_x509write_crt_set_version(&outCertCtx, MBEDTLS_X509_CRT_VERSION_3);
+ mbedtls_x509write_crt_set_md_alg(&outCertCtx, MBEDTLS_MD_SHA256);
+
+ res = OC_STACK_ERROR;
+
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &entropy, (const uint8_t *)PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON);
+
+ ret = mbedtls_x509write_crt_set_serial(&outCertCtx, &serialMpi);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_x509write_crt_set_subject_name(&outCertCtx, subject);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ if (NULL != issuerCert)
+ {
+ // mbedtls_x509_dn_gets returns the number of bytes written to buf.
+ ret = mbedtls_x509_dn_gets(buf, sizeof(buf), &issCertCtx.subject);
+ VERIFY_SUCCESS(TAG, 0 < ret, ERROR);
+ ret = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, buf);
+ }
+ else
+ {
+ /* If self-signed, use the same contents of subject for the issuer name. */
+ ret = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, subject);
+ }
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ mbedtls_x509write_crt_set_subject_key(&outCertCtx, &subjKeyCtx);
+
+ mbedtls_x509write_crt_set_issuer_key(&outCertCtx, &issKeyCtx);
+
+ if (certType == CERT_TYPE_CA)
+ {
+ ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 1, -1);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ ret = mbedtls_x509write_crt_set_key_usage(&outCertCtx,
+ MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ }
+ else
+ {
+ ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 0, 0);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ ret = mbedtls_x509write_crt_set_key_usage(&outCertCtx,
+ MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
+ MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
+ MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
+ MBEDTLS_X509_KU_KEY_AGREEMENT);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ }
+
+ switch (certType)
+ {
+ case CERT_TYPE_ROLE:
+ ret = mbedtls_x509write_crt_set_extension(&outCertCtx,
+ MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE),
+ 0,
+ s_ekuRole, sizeof(s_ekuRole));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ ret = snprintf(buf, sizeof(buf), "CN=%s%s%s", role, (NULL != authority) ? ",OU=" : "", (NULL != authority) ? authority : "");
+ VERIFY_SUCCESS(TAG, ret < sizeof(buf), ERROR);
+ names.next = NULL;
+ names.general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME;
+ ret = mbedtls_x509_string_to_names(&names.general_name.directory_name, buf);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_x509write_crt_set_subject_alt_names(&outCertCtx, &names);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ break;
+
+ case CERT_TYPE_IDENTITY:
+ ret = mbedtls_x509write_crt_set_extension(&outCertCtx,
+ MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE),
+ 0,
+ s_ekuIdentity, sizeof(s_ekuIdentity));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ break;
+
+ case CERT_TYPE_CA:
+ ret = mbedtls_x509write_crt_set_extension(&outCertCtx,
+ MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE),
+ 0,
+ s_ekuCA, sizeof(s_ekuCA));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ break;
+
+ default:
+ assert(false);
+ VERIFY_SUCCESS(TAG, false, ERROR);
+ }
+
+ ret = mbedtls_x509write_crt_pem(&outCertCtx, (uint8_t *)buf, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ certificate->len = strlen(buf) + 1;
+ certificate->bytes = (uint8_t *)OICCalloc(1, certificate->len);
+ VERIFY_NOT_NULL(TAG, certificate->bytes, ERROR);
+ memcpy(certificate->bytes, buf, certificate->len);
+
+ res = OC_STACK_OK;
+
+exit:
+
+ if (OC_STACK_OK != res)
+ {
+ OICFree(certificate->bytes);
+ certificate->bytes = NULL;
+ certificate->len = 0;
+ }
+
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+ mbedtls_asn1_free_named_data_list(&names.general_name.directory_name);
+ mbedtls_mpi_free(&serialMpi);
+ mbedtls_x509_crt_free(&issCertCtx);
+ mbedtls_pk_free(&issKeyCtx);
+ mbedtls_pk_free(&subjKeyCtx);
+ mbedtls_x509write_crt_free(&outCertCtx);
+
+ return res;
+}
+
+OCStackResult OCGenerateCACertificate(
+ const char *subject,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen)
+{
+ OCStackResult res = OC_STACK_OK;
+ OCByteString byteStr = { 0 };
+
+ res = GenerateCertificate(
+ CERT_TYPE_CA,
+ subject,
+ subjectPublicKey,
+ issuerCert,
+ issuerPrivateKey,
+ serial,
+ notValidBefore,
+ notValidAfter,
+ NULL,
+ NULL,
+ &byteStr);
+
+ if (OC_STACK_OK == res)
+ {
+ *certificate = (char *)byteStr.bytes;
+ *certificateLen = byteStr.len;
+ }
+
+ return res;
+}
+
+OCStackResult OCGenerateIdentityCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen)
+{
+ OCStackResult res = OC_STACK_OK;
+ OCByteString byteStr = { 0 };
+ char uuidStr[UUID_STRING_SIZE] = { 0 } ;
+ char subject[sizeof(uuidStr) + sizeof(SUBJECT_PREFIX)] = { 0 } ;
+
+ if (NULL == issuerCert)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (!OCConvertUuidToString(subjectUuid->id, uuidStr))
+ {
+ OIC_LOG(ERROR, TAG, "Could not convert UUID");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (snprintf(subject, sizeof(subject), "%s%s", SUBJECT_PREFIX, uuidStr) == sizeof(subject))
+ {
+ OIC_LOG(ERROR, TAG, "Could not write subject string");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ res = GenerateCertificate(
+ CERT_TYPE_IDENTITY,
+ subject,
+ subjectPublicKey,
+ issuerCert,
+ issuerPrivateKey,
+ serial,
+ notValidBefore,
+ notValidAfter,
+ NULL,
+ NULL,
+ &byteStr);
+
+ if (OC_STACK_OK == res)
+ {
+ *certificate = (char *)byteStr.bytes;
+ *certificateLen = byteStr.len;
+ }
+
+ return res;
+}
+
+OCStackResult OCGenerateRoleCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ const char *role,
+ const char *authority,
+ char **certificate,
+ size_t *certificateLen)
+{
+ OCStackResult res = OC_STACK_ERROR;
+ OCByteString byteStr;
+ char uuidStr[UUID_STRING_SIZE] = { 0 };
+ char subject[sizeof(uuidStr) + sizeof(SUBJECT_PREFIX)] = { 0 };
+
+ memset(&byteStr, 0, sizeof(byteStr));
+
+ if (NULL == role || NULL == issuerCert)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (!OCConvertUuidToString(subjectUuid->id, uuidStr))
+ {
+ OIC_LOG(ERROR, TAG, "Could not convert UUID");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (snprintf(subject, sizeof(subject), "%s%s", SUBJECT_PREFIX, uuidStr) == sizeof(subject))
+ {
+ OIC_LOG(ERROR, TAG, "Could not write subject string");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ res = GenerateCertificate(
+ CERT_TYPE_ROLE,
+ subject,
+ subjectPublicKey,
+ issuerCert,
+ issuerPrivateKey,
+ serial,
+ notValidBefore,
+ notValidAfter,
+ role,
+ authority,
+ &byteStr);
+
+ if (OC_STACK_OK == res)
+ {
+ *certificate = (char *)byteStr.bytes;
+ *certificateLen = byteStr.len;
+ }
+
+ return res;
+}
+
+
+/* Verify the signature in a CSR is valid. */
+static int VerifyCSRSignature(mbedtls_x509_csr* csr)
+{
+ unsigned char hash[MBEDTLS_MD_MAX_SIZE];
+
+ if (csr->sig_md != MBEDTLS_MD_SHA256)
+ {
+ OIC_LOG(ERROR, TAG, "Unexpected digest used in CSR\n");
+ return -1;
+ }
+
+ if ((csr->cri.len == 0) || (csr->cri.p == NULL))
+ {
+ OIC_LOG(ERROR, TAG, "Missing CertificateRequestInfo field in CSR\n");
+ return -1;
+ }
+
+ if ((csr->sig.len == 0) || (csr->sig.p == NULL))
+ {
+ OIC_LOG(ERROR, TAG, "Missing signature field in CSR\n");
+ return -1;
+ }
+
+ if (MBEDTLS_OID_CMP(MBEDTLS_OID_ECDSA_SHA256, &csr->sig_oid) != 0)
+ {
+ char buf[256];
+ if (mbedtls_oid_get_numeric_string(buf, sizeof(buf), &csr->sig_oid) > 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Unexpected signature OID in CSR (got %s)\n", buf);
+ }
+ else
+ {
+ OIC_LOG(ERROR, TAG, "Unexpected signature OID in CSR\n");
+ }
+ return -1;
+ }
+
+ if (mbedtls_pk_get_type(&csr->pk) != MBEDTLS_PK_ECKEY)
+ {
+ OIC_LOG(ERROR, TAG, "Unexpected public key type in CSR\n");
+ return -1;
+ }
+
+ /* mbedtls_pk_get_bitlen returns the bit length of the curve */
+ if (mbedtls_pk_get_bitlen(&csr->pk) != 256)
+ {
+ OIC_LOG(ERROR, TAG, "Unexpected public length in CSR\n");
+ return -1;
+ }
+
+ mbedtls_ecp_keypair* ecKey = mbedtls_pk_ec(csr->pk);
+ if ((ecKey != NULL) && (ecKey->grp.id != MBEDTLS_ECP_DP_SECP256R1))
+ {
+ OIC_LOG(ERROR, TAG, "Unexpected curve parameters in CSR\n");
+ return -1;
+ }
+
+ /* Hash the CertificateRequestInfoField (https://tools.ietf.org/html/rfc2986#section-3) */
+ int ret = mbedtls_md(mbedtls_md_info_from_type(csr->sig_md), csr->cri.p, csr->cri.len, hash);
+ if (ret != 0)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to hash CertificateRequestInfoField\n");
+ return ret;
+ }
+
+ /* the length of hash is determined from csr->sig_md*/
+ ret = mbedtls_pk_verify(&csr->pk, csr->sig_md, hash, 0, csr->sig.p, csr->sig.len);
+
+ return ret;
+}
+
+OCStackResult OCVerifyCSRSignature(const char* csr)
+{
+ mbedtls_x509_csr csrObj;
+
+ mbedtls_x509_csr_init(&csrObj);
+ int ret = mbedtls_x509_csr_parse(&csrObj, (const unsigned char*)csr, strlen(csr) + 1);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_ERROR;
+ }
+
+ ret = VerifyCSRSignature(&csrObj);
+
+ mbedtls_x509_csr_free(&csrObj);
+
+ if (ret != 0)
+ {
+ return OC_STACK_ERROR;
+ }
+
+ return OC_STACK_OK;
+}
+
+OCStackResult OCGetUuidFromCSR(const char* csr, OicUuid_t* uuid)
+{
+ mbedtls_x509_csr csrObj;
+
+ mbedtls_x509_csr_init(&csrObj);
+ int ret = mbedtls_x509_csr_parse(&csrObj, (const unsigned char*)csr, strlen(csr) + 1);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_ERROR;
+ }
+
+ char uuidStr[UUID_STRING_SIZE + sizeof(SUBJECT_PREFIX) - 1] = { 0 }; // Both constants count NULL, subtract one
+ ret = mbedtls_x509_dn_gets(uuidStr, sizeof(uuidStr), &csrObj.subject);
+ if (ret != (sizeof(uuidStr) - 1))
+ {
+ OIC_LOG_V(ERROR, TAG, "mbedtls_x509_dn_gets returned length or error: %d, expected %d", ret, sizeof(uuidStr) - 1);
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_ERROR;
+ }
+
+ if (!OCConvertStringToUuid(uuidStr + sizeof(SUBJECT_PREFIX) - 1, uuid->id))
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to convert UUID: '%s'", uuidStr);
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_ERROR;
+ }
+
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_OK;
+}
+
+OCStackResult OCGetPublicKeyFromCSR(const char* csr, char** publicKey)
+{
+ mbedtls_x509_csr csrObj;
+
+ mbedtls_x509_csr_init(&csrObj);
+ int ret = mbedtls_x509_csr_parse(&csrObj, (const unsigned char*)csr, strlen(csr) + 1);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_ERROR;
+ }
+
+ char subjectPublicKey[500] = { 0 };
+ ret = mbedtls_pk_write_pubkey_pem(&csrObj.pk, (unsigned char*)subjectPublicKey, sizeof(subjectPublicKey));
+ if (ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to write subject public key as PEM: %d", ret);
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_ERROR;
+ }
+
+ size_t pkLen = strlen(subjectPublicKey) + 1;
+ *publicKey = (char*) OICCalloc(1, pkLen);
+ if (*publicKey == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to allocate memory for public key");
+ mbedtls_x509_csr_free(&csrObj);
+ return OC_STACK_ERROR;
+ }
+
+ memcpy(*publicKey, subjectPublicKey, pkLen);
+ mbedtls_x509_csr_free(&csrObj);
+
+ return OC_STACK_OK;
+}
+
+OCStackResult OCConvertDerCSRToPem(const char* derCSR, size_t derCSRLen, char** pemCSR)
+{
+ const char* pemHeader = "-----BEGIN CERTIFICATE REQUEST-----\n";
+ const char* pemFooter = "-----END CERTIFICATE REQUEST-----\n";
+
+ /* Get the length required for output*/
+ size_t pemCSRLen;
+ int ret = mbedtls_pem_write_buffer(pemHeader,
+ pemFooter,
+ (const unsigned char*)derCSR,
+ derCSRLen,
+ NULL,
+ 0,
+ &pemCSRLen);
+ if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't convert CSR into PEM, failed getting required length: %d", ret);
+ return OC_STACK_ERROR;
+ }
+
+ *pemCSR = OICCalloc(1, pemCSRLen + 1);
+ if (*pemCSR == NULL)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to allocate memory for PEM CSR");
+ return OC_STACK_ERROR;
+ }
+
+ /* Try the conversion */
+ ret = mbedtls_pem_write_buffer(pemHeader, pemFooter,
+ (const unsigned char *)derCSR,
+ derCSRLen,
+ (unsigned char*) *pemCSR,
+ pemCSRLen,
+ &pemCSRLen);
+ if (ret < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Couldn't convert CSR into PEM, failed getting required length: %d", ret);
+ OICFree(*pemCSR);
+ *pemCSR = NULL;
+ return OC_STACK_ERROR;
+ }
+
+ return OC_STACK_OK;
+}
+
+#endif /* defined(__WITH_TLS__) || defined(__WITH_DTLS__) */
\ No newline at end of file
#include "pconfresource.h"
#include "amaclresource.h"
#include "credresource.h"
+#include "rolesresource.h"
#define TAG "OIC_SRM_PE"
return false;
}
+static void ProcessMatchingACE(SRMRequestContext_t *context, const OicSecAce_t *currentAce)
+{
+ // Found the subject, so how about resource?
+ OIC_LOG_V(DEBUG, TAG, "%s:found ACE matching subject", __func__);
+
+ // Subject was found, so err changes to Rsrc not found for now.
+ context->responseVal = ACCESS_DENIED_RESOURCE_NOT_FOUND;
+ OIC_LOG_V(DEBUG, TAG, "%s:Searching for resource...", __func__);
+ if (IsResourceInAce(context->resourceUri, currentAce))
+ {
+ OIC_LOG_V(INFO, TAG, "%s:found matching resource in ACE", __func__);
+
+ // Found the resource, so it's down to valid period & permission.
+ context->responseVal = ACCESS_DENIED_INVALID_PERIOD;
+ if (IsAccessWithinValidTime(currentAce))
+ {
+ context->responseVal = ACCESS_DENIED_INSUFFICIENT_PERMISSION;
+ if (IsPermissionAllowingRequest(currentAce->permission,
+ context->requestedPermission))
+ {
+ context->responseVal = ACCESS_GRANTED;
+ }
+ }
+ }
+}
/**
* Find ACLs containing context->subject.
if (NULL != currentAce)
{
- // Found the subject, so how about resource?
- OIC_LOG_V(DEBUG, TAG, "%s:found ACE matching subject" ,__func__);
+ ProcessMatchingACE(context, currentAce);
+ }
+ else
+ {
+ OIC_LOG_V(INFO, TAG, "%s:no ACL found matching subject for resource %s",
+ __func__, context->resourceUri);
+ }
+ } while ((NULL != currentAce) && !IsAccessGranted(context->responseVal));
- // Subject was found, so err changes to Rsrc not found for now.
- context->responseVal = ACCESS_DENIED_RESOURCE_NOT_FOUND;
- OIC_LOG_V(DEBUG, TAG, "%s:Searching for resource..." ,__func__);
- if (IsResourceInAce(context->resourceUri, currentAce))
+#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
+ // If no subject ACE granted access, try role ACEs.
+ if (!IsAccessGranted(context->responseVal))
+ {
+ currentAce = NULL;
+ aceSavePtr = NULL;
+ OicSecRole_t *roles = NULL;
+ size_t roleCount = 0;
+ OCStackResult res = GetEndpointRoles(context->endPoint, &roles, &roleCount);
+ if (OC_STACK_OK != res)
{
- OIC_LOG_V(INFO, TAG, "%s:found matching resource in ACE" ,__func__);
-
- // Found the resource, so it's down to valid period & permission.
- context->responseVal = ACCESS_DENIED_INVALID_PERIOD;
- if (IsAccessWithinValidTime(currentAce))
+ OIC_LOG_V(ERROR, TAG, "Could not locate any roles for endpoint: %d", res);
+ }
+ else
+ {
+ do
{
- context->responseVal = ACCESS_DENIED_INSUFFICIENT_PERMISSION;
- if (IsPermissionAllowingRequest(currentAce->permission,
- context->requestedPermission))
+ currentAce = GetACLResourceDataByRoles(roles, roleCount, &aceSavePtr);
+ if (NULL != currentAce)
{
- context->responseVal = ACCESS_GRANTED;
+ ProcessMatchingACE(context, currentAce);
}
- }
+ else
+ {
+ OIC_LOG_V(INFO, TAG, "%s:no ACL found matching roles for resource %s",
+ __func__, context->resourceUri);
+ }
+ } while ((NULL != currentAce) && !IsAccessGranted(context->responseVal));
+
+ OICFree(roles);
}
}
- else
- {
- OIC_LOG_V(INFO, TAG, "%s:no ACE found matching subject for resource %s",
- __func__, context->resourceUri);
- }
- } while ((NULL != currentAce)
- && (false == IsAccessGranted(context->responseVal)));
+#endif /* defined(__WITH_DTLS__) || defined(__WITH_TLS__) */
OIC_LOG_V(INFO, TAG, "%s:Leaving with responseVal = %s", __func__,
IsAccessGranted(context->responseVal) ? "ACCESS_GRANTED" : "ACCESS_DENIED");
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
#include "crlresource.h"
+#include "csrresource.h"
+#include "rolesresource.h"
#endif // __WITH_DTLS__ || __WITH_TLS__
OCStackResult SendSRMResponse(const OCEntityHandlerRequest *ehRequest,
{
ret = InitCRLResource();
}
+ if(OC_STACK_OK == ret)
+ {
+ ret = InitCSRResource();
+ }
+ if(OC_STACK_OK == ret)
+ {
+ ret = InitRolesResource();
+ }
#endif // __WITH_DTLS__ || __WITH_TLS__
if(OC_STACK_OK == ret)
{
DeInitPstatResource();
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
DeInitCRLResource();
+ DeInitCSRResource();
+ DeInitRolesResource();
#endif // __WITH_DTLS__ || __WITH_TLS__
DeInitAmaclResource();
//#ifdef DIRECT_PAIRING
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
+#include "iotivity_config.h"
+#include <stdlib.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include "oic_string.h"
+#include "cainterface.h"
+#include "payload_logging.h"
+#include "ocstack.h"
+#include "ocrandom.h"
+#include "cacommon.h"
+#include "srmresourcestrings.h"
+#include "ocpayload.h"
+#include "ocpayloadcbor.h"
+#include "credresource.h"
+#include "doxmresource.h"
+#include "srmutility.h"
+#include "certhelpers.h"
+#include "resourcemanager.h"
+#include "utlist.h"
+#include "ca_adapter_net_ssl.h"
+#include "ocstackinternal.h"
+#include "rolesresource.h"
+
+#define TAG "OIC_SRM_ROLES"
+
+typedef struct RolesEntry {
+ uint8_t *publicKey; /**< DER-encoded public key */
+ size_t publicKeyLength; /**< length of publicKey */
+ RoleCertChain_t *chains; /**< stored certificates */
+
+ struct tm cacheValidUntil; /**< Cache valid until time; use 0 if cache is not yet set */
+ OicSecRole_t *cachedRoles; /**< Cached roles; must free with OICFree */
+ size_t cachedRolesLength; /**< Length of cachedRoles array */
+
+ struct RolesEntry *next;
+} RolesEntry_t;
+
+static OCResourceHandle gRolesHandle = NULL;
+static RolesEntry_t *gRoles = NULL;
+static uint32_t gIdCounter = 1;
+
+/**
+ * Default cbor payload size. This value is increased in case of CborErrorOutOfMemory.
+ * The value of payload size is increased until reaching max cbor size.
+ */
+static const uint16_t CBOR_SIZE = 2048;
+
+/**
+ * Zero subject UUID.
+ */
+static const char EMPTY_UUID[] = "00000000-0000-0000-0000-000000000000";
+
+/**
+ * Mandatory parts of a role map cred entry: 4 key/value pairs: credId, subject, publicData, and credType.
+ */
+static const uint8_t ROLE_MAP_SIZE = 4;
+
+static void InvalidateRoleCache(RolesEntry_t *entry)
+{
+ memset(&entry->cacheValidUntil, 0, sizeof(entry->cacheValidUntil));
+ OICFree(entry->cachedRoles);
+ entry->cachedRoles = NULL;
+ entry->cachedRolesLength = 0;
+}
+
+/* Caller must call OICFree on publicKey when finished. */
+static OCStackResult GetPeerPublicKeyFromEndpoint(const CAEndpoint_t *endpoint,
+ uint8_t **publicKey,
+ size_t *publicKeyLength)
+{
+ CASecureEndpoint_t sep;
+ CAResult_t res = GetCASecureEndpointData(endpoint, &sep);
+ if (CA_STATUS_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Failed to GetCASecureEndpointData: %d", __func__, res);
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if ((NULL == sep.publicKey) || (0 == sep.publicKeyLength))
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: Peer did not have a public key", __func__);
+ return OC_STACK_ERROR;
+ }
+
+ *publicKey = OICCalloc(1, sep.publicKeyLength);
+ if (NULL == *publicKey)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s: No memory for publicKey", __func__);
+ return OC_STACK_NO_MEMORY;
+ }
+
+ memcpy(*publicKey, sep.publicKey, sep.publicKeyLength);
+ *publicKeyLength = sep.publicKeyLength;
+
+ return OC_STACK_OK;
+}
+
+/* Caller must call OICFree on publicKey when finished. */
+static OCStackResult GetPeerPublicKey(const OCDevAddr *peer, uint8_t **publicKey, size_t *publicKeyLength)
+{
+ CAEndpoint_t endpoint;
+ CopyDevAddrToEndpoint(peer, &endpoint);
+
+ return GetPeerPublicKeyFromEndpoint(&endpoint, publicKey, publicKeyLength);
+}
+
+static void FreeRoleCertChain(RoleCertChain_t *roleCert)
+{
+ if (NULL == roleCert)
+ {
+ return;
+ }
+
+ OICFree(roleCert->optData.data);
+ OICFree(roleCert->certificate.data);
+ OICFree(roleCert);
+}
+
+void FreeRoleCertChainList(RoleCertChain_t *roleCertList)
+{
+ if (NULL == roleCertList)
+ {
+ return;
+ }
+
+ RoleCertChain_t *certTmp1 = NULL;
+ RoleCertChain_t *certTmp2 = NULL;
+ LL_FOREACH_SAFE(roleCertList, certTmp1, certTmp2)
+ {
+ LL_DELETE(roleCertList, certTmp1);
+ FreeRoleCertChain(certTmp1);
+ }
+}
+
+static void FreeRolesEntry(RolesEntry_t *rolesEntry)
+{
+ if (NULL == rolesEntry)
+ {
+ return;
+ }
+
+ FreeRoleCertChainList(rolesEntry->chains);
+ OICFree(rolesEntry->chains);
+ OICFree(rolesEntry->publicKey);
+ OICFree(rolesEntry->cachedRoles);
+ OICFree(rolesEntry);
+}
+
+static void FreeRolesList(RolesEntry_t *roles)
+{
+ if (NULL != roles)
+ {
+ RolesEntry_t *entryTmp1 = NULL;
+ RolesEntry_t *entryTmp2 = NULL;
+ LL_FOREACH_SAFE(roles, entryTmp1, entryTmp2)
+ {
+ LL_DELETE(roles, entryTmp1);
+ FreeRolesEntry(entryTmp1);
+ }
+ }
+}
+
+static OCStackResult DuplicateRoleCertChain(const RoleCertChain_t *roleCert, RoleCertChain_t **duplicate)
+{
+ OIC_LOG(DEBUG, TAG, "DuplicateRoleCertChain IN");
+
+ OCStackResult res = OC_STACK_ERROR;
+ RoleCertChain_t *tmp = NULL;
+
+ if ((NULL == roleCert) || (NULL == duplicate))
+ {
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ tmp = (RoleCertChain_t *)OICCalloc(1, sizeof(RoleCertChain_t));
+ if (NULL == tmp)
+ {
+ OIC_LOG(ERROR, TAG, "No memory for tmp");
+ res = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+
+ tmp->certificate.data = (uint8_t *)OICCalloc(1, roleCert->certificate.len);
+ if (NULL == tmp->certificate.data)
+ {
+ OIC_LOG(ERROR, TAG, "No memory for certificate data");
+ res = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+ tmp->credId = roleCert->credId;
+ tmp->certificate.len = roleCert->certificate.len;
+ tmp->certificate.encoding = roleCert->certificate.encoding;
+ memcpy(tmp->certificate.data, roleCert->certificate.data, roleCert->certificate.len);
+
+ if (NULL != roleCert->optData.data)
+ {
+ tmp->optData.data = (uint8_t *)OICCalloc(1, roleCert->optData.len);
+ if (NULL == tmp->optData.data)
+ {
+ OIC_LOG(ERROR, TAG, "No memory for optional data");
+ res = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+ tmp->optData.len = roleCert->optData.len;
+ tmp->optData.encoding = roleCert->optData.encoding;
+ tmp->optData.revstat = roleCert->optData.revstat;
+ memcpy(tmp->optData.data, roleCert->optData.data, roleCert->optData.len);
+ }
+
+ *duplicate = tmp;
+ res = OC_STACK_OK;
+
+exit:
+
+ OIC_LOG_V(DEBUG, TAG, "DuplicateRoleCertChain OUT: %d", res);
+
+ if (OC_STACK_OK != res)
+ {
+ FreeRoleCertChain(tmp);
+ }
+
+ return res;
+}
+
+static OCStackResult AddRoleCertificate(const RoleCertChain_t *roleCert, const uint8_t *pubKey, size_t pubKeyLength)
+{
+ OCStackResult res = OC_STACK_ERROR;
+ RolesEntry_t *targetEntry = NULL;
+ RoleCertChain_t *copy = NULL;
+
+ OIC_LOG(DEBUG, TAG, "AddRoleCertificate IN");
+
+ if ((NULL == pubKey) || (0 == pubKeyLength))
+ {
+ assert(!"AddRoleCertificate called with no public key");
+ res = OC_STACK_INVALID_PARAM;
+ goto exit;
+ }
+
+ for (targetEntry = gRoles; NULL != targetEntry; targetEntry = targetEntry->next)
+ {
+ if ((targetEntry->publicKeyLength == pubKeyLength) &&
+ (0 == memcmp(targetEntry->publicKey, pubKey, pubKeyLength)))
+ {
+ break;
+ }
+ }
+
+ if (NULL != targetEntry)
+ {
+ InvalidateRoleCache(targetEntry);
+ }
+ else
+ {
+ /* We haven't seen this public key before and need a new entry. */
+ targetEntry = (RolesEntry_t *)OICCalloc(1, sizeof(RolesEntry_t *));
+ if (NULL == targetEntry)
+ {
+ OIC_LOG(ERROR, TAG, "No memory for new targetEntry");
+ res = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+ targetEntry->publicKey = (uint8_t *)OICCalloc(1, pubKeyLength);
+ if (NULL == targetEntry->publicKey)
+ {
+ OIC_LOG(ERROR, TAG, "No memory for new targetEntry public key");
+ OICFree(targetEntry);
+ res = OC_STACK_NO_MEMORY;
+ goto exit;
+ }
+ targetEntry->publicKeyLength = pubKeyLength;
+ memcpy(targetEntry->publicKey, pubKey, pubKeyLength);
+
+ LL_PREPEND(gRoles, targetEntry);
+ }
+
+ // @todo: Detect duplicates and don't add them again
+ res = DuplicateRoleCertChain(roleCert, ©);
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not duplicate role cert chain: %d", res);
+ goto exit;
+ }
+
+ // Assign our own credId.
+ copy->credId = gIdCounter++;
+
+ LL_APPEND(targetEntry->chains, copy);
+
+ res = OC_STACK_OK;
+
+exit:
+
+ if (OC_STACK_OK != res)
+ {
+ FreeRoleCertChain(copy);
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "AddRoleCertificate IN: %d", res);
+
+ return res;
+}
+
+static OCStackResult RolesToCBORPayload(const RoleCertChain_t *roles, uint8_t **cborPayload,
+ size_t *cborSize)
+{
+ OCStackResult ret = OC_STACK_OK;
+
+ CborError cborEncoderResult = CborNoError;
+ uint8_t *outPayload = NULL;
+ CborEncoder encoder;
+ CborEncoder rolesRootMap;
+ CborEncoder rolesArray;
+ size_t roleCount = 0;
+ const RoleCertChain_t *currChain = NULL;
+
+ VERIFY_NOT_NULL_RETURN(TAG, roles, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, cborPayload, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, cborSize, ERROR, OC_STACK_INVALID_PARAM);
+
+ size_t cborLen = *cborSize;
+ *cborSize = 0;
+ *cborPayload = NULL;
+
+ if (0 == cborLen)
+ {
+ cborLen = CBOR_SIZE;
+ }
+
+ outPayload = (uint8_t *)OICCalloc(1, cborLen);
+ VERIFY_NOT_NULL_RETURN(TAG, outPayload, ERROR, OC_STACK_NO_MEMORY);
+ cbor_encoder_init(&encoder, outPayload, cborLen, 0);
+
+ // Create roles root map (roles, rt, if)
+ cborEncoderResult = cbor_encoder_create_map(&encoder, &rolesRootMap, 3);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding roles root map");
+
+ // Roles array
+ cborEncoderResult = cbor_encode_text_string(&rolesRootMap, OIC_JSON_ROLES_NAME, strlen(OIC_JSON_ROLES_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding roles name tag");
+
+ for (currChain = roles; NULL != currChain; currChain = currChain->next)
+ {
+ roleCount++;
+ }
+
+ cborEncoderResult = cbor_encoder_create_array(&rolesRootMap, &rolesArray, roleCount);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding roles array");
+
+ for (currChain = roles; NULL != currChain; currChain = currChain->next)
+ {
+ CborEncoder roleMap;
+ size_t mapSize = ROLE_MAP_SIZE;
+
+ if (NULL != currChain->optData.data)
+ {
+ mapSize++;
+ }
+
+ cborEncoderResult = cbor_encoder_create_map(&rolesArray, &roleMap, mapSize);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding role map");
+
+ // credId - mandatory
+ cborEncoderResult = cbor_encode_text_string(&roleMap, OIC_JSON_CREDID_NAME, strlen(OIC_JSON_CREDID_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding credId tag");
+ cborEncoderResult = cbor_encode_int(&roleMap, currChain->credId);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding credId value");
+
+ // subjectuuid - mandatory - always zero for role certificates
+ cborEncoderResult = cbor_encode_text_string(&roleMap, OIC_JSON_SUBJECTID_NAME, strlen(OIC_JSON_SUBJECTID_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding subject tag");
+ cborEncoderResult = cbor_encode_text_string(&roleMap, EMPTY_UUID, sizeof(EMPTY_UUID) - 1);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding subject value");
+
+ // publicData - mandatory
+ cborEncoderResult = SerializeEncodingToCbor(&roleMap, OIC_JSON_PUBLICDATA_NAME, &currChain->certificate);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding publicData");
+
+ // optionalData
+ if (NULL != currChain->optData.data)
+ {
+ cborEncoderResult = SerializeSecOptToCbor(&roleMap, OIC_JSON_OPTDATA_NAME, &currChain->optData);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding optional data");
+ }
+
+ // credType - mandatory
+ cborEncoderResult = cbor_encode_text_string(&roleMap, OIC_JSON_CREDTYPE_NAME, strlen(OIC_JSON_CREDTYPE_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding credType tag");
+ // Per security spec, only SIGNED_ASYMMETRIC_KEY is supported here.
+ cborEncoderResult = cbor_encode_int(&roleMap, SIGNED_ASYMMETRIC_KEY);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding credType value");
+
+ cborEncoderResult = cbor_encoder_close_container(&rolesArray, &roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed closing role map");
+ }
+
+ cborEncoderResult = cbor_encoder_close_container(&rolesRootMap, &rolesArray);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed closing roles array");
+
+ // RT -- Mandatory
+ CborEncoder rtArray;
+ cborEncoderResult = cbor_encode_text_string(&rolesRootMap, OIC_JSON_RT_NAME,
+ strlen(OIC_JSON_RT_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding RT Name Tag.");
+ cborEncoderResult = cbor_encoder_create_array(&rolesRootMap, &rtArray, 1);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding RT Value.");
+ for (size_t i = 0; i < 1; i++)
+ {
+ cborEncoderResult = cbor_encode_text_string(&rtArray, OIC_RSRC_TYPE_SEC_ROLES,
+ strlen(OIC_RSRC_TYPE_SEC_ROLES));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding RT Value.");
+ }
+ cborEncoderResult = cbor_encoder_close_container(&rolesRootMap, &rtArray);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Closing RT.");
+
+ // IF-- Mandatory
+ CborEncoder ifArray;
+ cborEncoderResult = cbor_encode_text_string(&rolesRootMap, OIC_JSON_IF_NAME,
+ strlen(OIC_JSON_IF_NAME));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding IF Name Tag.");
+ cborEncoderResult = cbor_encoder_create_array(&rolesRootMap, &ifArray, 1);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Addding IF Value.");
+ for (size_t i = 0; i < 1; i++)
+ {
+ cborEncoderResult = cbor_encode_text_string(&ifArray, OC_RSRVD_INTERFACE_DEFAULT,
+ strlen(OC_RSRVD_INTERFACE_DEFAULT));
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding IF Value.");
+ }
+ cborEncoderResult = cbor_encoder_close_container(&rolesRootMap, &ifArray);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Closing IF.");
+
+ // Close roles root map
+ cborEncoderResult = cbor_encoder_close_container(&encoder, &rolesRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed closing roles root map");
+
+ *cborSize = cbor_encoder_get_buffer_size(&encoder, outPayload);
+ *cborPayload = outPayload;
+
+exit:
+ if (CborErrorOutOfMemory == cborEncoderResult)
+ {
+ OIC_LOG(DEBUG, TAG, "RolesToCBORPayload:CborErrorOutOfMemory : retry with more memory");
+
+ // reallocate and try again!
+ OICFree(outPayload);
+ // Since the initially-allocated memory failed, double the memory.
+ cborLen += cbor_encoder_get_buffer_size(&encoder, encoder.end);
+ ret = RolesToCBORPayload(roles, cborPayload, cborSize);
+ *cborSize = cborLen;
+ }
+ else if (cborEncoderResult != CborNoError)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to RolesToCBORPayload");
+ OICFree(outPayload);
+ *cborSize = 0;
+ *cborPayload = NULL;
+ ret = OC_STACK_ERROR;
+ }
+
+ return ret;
+}
+
+/* Caller must call DeleteRoleCredentials on roleEntries when finished. */
+OCStackResult CBORPayloadToRoles(const uint8_t *cborPayload, size_t size, RoleCertChain_t **roleEntries)
+{
+ if (NULL == cborPayload || 0 == size || NULL == roleEntries)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ CborValue rolesCbor = { .parser = NULL };
+ CborParser parser = { .end = NULL };
+ CborError cborFindResult = CborNoError;
+ RoleCertChain_t *headRoleCertChain = NULL;
+ char* tagName = NULL;
+ size_t len = 0;
+
+ cborFindResult = cbor_parser_init(cborPayload, size, 0, &parser, &rolesCbor);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed to initialize parser.");
+
+ if (!cbor_value_is_container(&rolesCbor))
+ {
+ return OC_STACK_ERROR;
+ }
+
+ // Enter roles Root Map
+ CborValue rolesRootMap;
+ memset(&rolesRootMap, 0, sizeof(rolesRootMap));
+ cborFindResult = cbor_value_enter_container(&rolesCbor, &rolesRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Entering roles Root Map.");
+
+ while (cbor_value_is_valid(&rolesRootMap))
+ {
+ if (NULL != tagName)
+ {
+ free(tagName);
+ tagName = NULL;
+ }
+ len = 0;
+ CborType type = cbor_value_get_type(&rolesRootMap);
+ if (type == CborTextStringType && cbor_value_is_text_string(&rolesRootMap))
+ {
+ cborFindResult = cbor_value_dup_text_string(&rolesRootMap, &tagName, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed finding name in roles Root Map.");
+ cborFindResult = cbor_value_advance(&rolesRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing value in roles Root Map.");
+ }
+ else
+ {
+ OIC_LOG_V(WARNING, TAG, "Value is not of type string, but of type %x", type);
+ }
+
+ if (NULL != tagName)
+ {
+ if (strcmp(tagName, OIC_JSON_ROLES_NAME) == 0)
+ {
+ // Enter role array
+ int roleCount = 0;
+ RoleCertChain_t *currEntry = NULL;
+ CborValue roleArray;
+ memset(&roleArray, 0, sizeof(roleArray));
+
+ cborFindResult = cbor_value_enter_container(&rolesRootMap, &roleArray);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed entering role array.");
+
+ while (cbor_value_is_valid(&roleArray))
+ {
+ roleCount++;
+ CborValue roleMap;
+ memset(&roleMap, 0, sizeof(roleMap));
+ cborFindResult = cbor_value_enter_container(&roleArray, &roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed entering role map.");
+
+ if (NULL == currEntry)
+ {
+ assert(NULL == headRoleCertChain);
+ headRoleCertChain = currEntry = (RoleCertChain_t *)OICCalloc(1, sizeof(RoleCertChain_t));
+ VERIFY_NOT_NULL(TAG, currEntry, ERROR);
+ }
+ else
+ {
+ assert(NULL != headRoleCertChain);
+ currEntry->next = (RoleCertChain_t *)OICCalloc(1, sizeof(RoleCertChain_t));
+ VERIFY_NOT_NULL(TAG, currEntry->next, ERROR);
+ currEntry = currEntry->next;
+ }
+
+ while (cbor_value_is_valid(&roleMap))
+ {
+ free(tagName);
+ tagName = NULL;
+ CborType innerType = cbor_value_get_type(&roleMap);
+ if (innerType == CborTextStringType)
+ {
+ cborFindResult = cbor_value_dup_text_string(&roleMap, &tagName, &len, NULL);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed finding name in role map.");
+ cborFindResult = cbor_value_advance(&roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing value in role map.");
+ }
+ if (NULL != tagName)
+ {
+ //credid
+ if (strcmp(tagName, OIC_JSON_CREDID_NAME) == 0)
+ {
+ uint64_t credId64 = 0;
+ cborFindResult = cbor_value_get_uint64(&roleMap, &credId64);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed retrieving credId.");
+ if (UINT32_MAX < credId64)
+ {
+ cborFindResult = CborUnknownError;
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "credId was too large.");
+ }
+ currEntry->credId = (uint32_t)credId64;
+ }
+ else if (strcmp(tagName, OIC_JSON_PUBLICDATA_NAME) == 0)
+ {
+ cborFindResult = DeserializeEncodingFromCbor(&roleMap, &currEntry->certificate);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed to read publicData");
+ }
+ else if (strcmp(tagName, OIC_JSON_OPTDATA_NAME) == 0)
+ {
+ cborFindResult = DeserializeSecOptFromCbor(&roleMap, &currEntry->optData);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed to read optionalData");
+ }
+ else if (strcmp(tagName, OIC_JSON_CREDTYPE_NAME) == 0)
+ {
+ uint64_t credType = 0;
+ cborFindResult = cbor_value_get_uint64(&roleMap, &credType);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed retrieving credType.");
+ /* Only SIGNED_ASYMMETRIC_KEY is supported. */
+ if (SIGNED_ASYMMETRIC_KEY != (OicSecCredType_t)credType)
+ {
+ OIC_LOG_V(ERROR, TAG, "Unsupported role credential type: %llu", credType);
+ goto exit;
+ }
+ }
+ // Silently ignore subject field; log anything else.
+ else if (strcmp(tagName, OIC_JSON_SUBJECTID_NAME) != 0)
+ {
+ OIC_LOG_V(WARNING, TAG, "Unknown role map tag: %s", tagName);
+ }
+ }
+
+ if (cbor_value_is_valid(&roleMap))
+ {
+ cborFindResult = cbor_value_advance(&roleMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing role map.");
+ }
+ }
+
+ if (NULL == currEntry->certificate.data)
+ {
+ OIC_LOG(ERROR, TAG, "Role credential did not have publicData");
+ goto exit;
+ }
+
+ if (cbor_value_is_valid(&roleArray))
+ {
+ cborFindResult = cbor_value_advance(&roleArray);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing role array.");
+ }
+ }
+ }
+ else
+ {
+ // Ignore any other tag type for now.
+ OIC_LOG_V(WARNING, TAG, "Unknown role root map tag %s", tagName);
+ }
+
+ }
+ if (cbor_value_is_valid(&rolesRootMap))
+ {
+ cborFindResult = cbor_value_advance(&rolesRootMap);
+ VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed advancing CSR Root Map");
+ }
+ }
+
+ *roleEntries = headRoleCertChain;
+
+exit:
+ if (NULL != tagName)
+ {
+ free(tagName);
+ }
+
+ if (CborNoError != cborFindResult)
+ {
+ if (NULL != headRoleCertChain)
+ {
+ FreeRoleCertChainList(headRoleCertChain);
+ }
+ return OC_STACK_ERROR;
+ }
+ else
+ {
+ return OC_STACK_OK;
+ }
+}
+
+static OCEntityHandlerResult HandleGetRequest(OCEntityHandlerRequest *ehRequest)
+{
+ OIC_LOG(DEBUG, TAG, "Roles HandleGetRequest IN");
+
+ OCStackResult res = OC_STACK_ERROR;
+ OCEntityHandlerResult ehRet = OC_EH_ERROR;
+ size_t size = 0;
+ uint8_t *payload = NULL;
+ const RoleCertChain_t *roles = NULL;
+ uint8_t *publicKey = NULL;
+ size_t publicKeyLength = 0;
+
+ res = GetPeerPublicKey(&ehRequest->devAddr, &publicKey, &publicKeyLength);
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not get remote peer's public key: %d", res);
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+
+ for (const RolesEntry_t *entry = gRoles; NULL != entry; entry = entry->next)
+ {
+ if ((entry->publicKeyLength == publicKeyLength) &&
+ (0 == memcmp(entry->publicKey, publicKey, publicKeyLength)))
+ {
+ roles = entry->chains;
+ break;
+ }
+ }
+
+ if (NULL == roles)
+ {
+ OIC_LOG(ERROR, TAG, "Could not find a roles list for this peer");
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+
+ res = RolesToCBORPayload(roles, &payload, &size);
+ ehRet = (OC_STACK_OK == res) ? OC_EH_OK : OC_EH_ERROR;
+
+exit:
+
+ ehRet = ((SendSRMResponse(ehRequest, ehRet, payload, size)) == OC_STACK_OK) ? OC_EH_OK : OC_EH_ERROR;
+
+ OICFree(payload);
+ OICFree(publicKey);
+
+ OIC_LOG(DEBUG, TAG, "Roles HandleGetRequest OUT");
+ return ehRet;
+}
+
+static OCEntityHandlerResult HandlePostRequest(OCEntityHandlerRequest *ehRequest)
+{
+ OCEntityHandlerResult ehRet = OC_EH_ERROR;
+ uint8_t *pubKey = NULL;
+ size_t pubKeyLength = 0;
+ uint8_t *peerPubKey = NULL;
+ size_t peerPubKeyLen = 0;
+
+ OIC_LOG(DEBUG, TAG, "Roles HandlePostRequest IN");
+
+ RoleCertChain_t *chains = NULL;
+ uint8_t *payload = (((OCSecurityPayload*)ehRequest->payload)->securityData);
+ size_t size = (((OCSecurityPayload*)ehRequest->payload)->payloadSize);
+
+ OCStackResult res = GetPeerPublicKey(&ehRequest->devAddr, &peerPubKey, &peerPubKeyLen);
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not get peer's public key: %d", res);
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+
+ res = CBORPayloadToRoles(payload, size, &chains);
+ if (OC_STACK_OK == res)
+ {
+ RoleCertChain_t *curr;
+
+ for (curr = chains; NULL != curr; curr = curr->next)
+ {
+ if (NULL != curr->optData.data)
+ {
+ if (OC_STACK_OK != OCInternalIsValidCertChain(curr->optData.data, curr->optData.len))
+ {
+ OIC_LOG(ERROR, TAG, "Optional data is not a valid cert chain");
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+ }
+
+ if (OC_STACK_OK != OCInternalIsValidRoleCertificate(curr->certificate.data, curr->certificate.len,
+ &pubKey, &pubKeyLength))
+ {
+ OIC_LOG(ERROR, TAG, "Could not verify certificate is a valid role certificate");
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+
+ if ((pubKeyLength != peerPubKeyLen) ||
+ (0 != memcmp(pubKey, peerPubKey, pubKeyLength)))
+ {
+ OIC_LOG(ERROR, TAG, "Peer sent us certificate not for its public key");
+ continue;
+ }
+
+ if (OC_STACK_OK != AddRoleCertificate(curr, pubKey, pubKeyLength))
+ {
+ OIC_LOG(ERROR, TAG, "Could not AddRoleCertificate");
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+ }
+
+ ehRet = OC_EH_OK;
+ }
+
+exit:
+
+ ehRet = (SendSRMResponse(ehRequest, ehRet, NULL, 0) == OC_STACK_OK) ? OC_EH_OK : OC_EH_ERROR;
+
+ FreeRoleCertChainList(chains);
+ OICFree(pubKey);
+ OICFree(peerPubKey);
+
+ OIC_LOG(DEBUG, TAG, "Roles HandlePostRequest OUT");
+
+ return ehRet;
+}
+
+static OCEntityHandlerResult HandleDeleteRequest(OCEntityHandlerRequest *ehRequest)
+{
+ OIC_LOG(DEBUG, TAG, "Roles HandleDeleteRequest IN");
+
+ OCEntityHandlerResult ehRet = OC_EH_ERROR;
+ uint8_t *peerPubKey = NULL;
+ size_t peerPubKeyLen = 0;
+ OicParseQueryIter_t parseIter = { .attrPos = NULL };
+ uint32_t credId = 0;
+
+ if (NULL == ehRequest->query)
+ {
+ return ehRet;
+ }
+
+ // Parsing REST query to get the credId
+ ParseQueryIterInit((unsigned char *)ehRequest->query, &parseIter);
+ while (GetNextQuery(&parseIter))
+ {
+ if (strncasecmp((const char *)parseIter.attrPos, OIC_JSON_CREDID_NAME,
+ parseIter.attrLen) == 0)
+ {
+ int ret = sscanf((const char *)parseIter.valPos, "%lu", &credId);
+ if (1 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "credId was not valid: %s", parseIter.valPos);
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+ }
+ else
+ {
+ OIC_LOG_V(DEBUG, TAG, "Unexpected in query string: %s=%s", parseIter.attrPos, parseIter.valPos);
+ }
+ }
+
+ OCStackResult res = GetPeerPublicKey(&ehRequest->devAddr, &peerPubKey, &peerPubKeyLen);
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not get peer's public key: %d", res);
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+
+ RolesEntry_t *entry = NULL;
+ for (entry = gRoles; NULL != entry; entry = entry->next)
+ {
+ assert((0 < entry->publicKeyLength) && (NULL != entry->publicKey));
+ if ((entry->publicKeyLength == peerPubKeyLen) &&
+ (0 == memcmp(entry->publicKey, peerPubKey, peerPubKeyLen)))
+ {
+ break;
+ }
+ }
+
+ if (NULL == entry)
+ {
+ /* No entry for this peer. */
+ OIC_LOG(ERROR, TAG, "No entry for this peer's public key");
+ ehRet = OC_EH_ERROR;
+ goto exit;
+ }
+
+ InvalidateRoleCache(entry);
+
+ RoleCertChain_t *curr1 = NULL;
+ RoleCertChain_t *curr2 = NULL;
+ LL_FOREACH_SAFE(entry->chains, curr1, curr2)
+ {
+ // credId of zero means all creds; we never assign zero as a credId.
+ if ((0 == credId) || (curr1->credId == credId))
+ {
+ LL_DELETE(entry->chains, curr1);
+ FreeRoleCertChain(curr1);
+ ehRet = OC_EH_OK;
+ break;
+ }
+ }
+
+exit:
+
+ ehRet = ((SendSRMResponse(ehRequest, ehRet, NULL, 0)) == OC_STACK_OK) ? OC_EH_OK : OC_EH_ERROR;
+
+ OIC_LOG(DEBUG, TAG, "Roles HandleDeleteRequest OUT");
+ OICFree(peerPubKey);
+
+ return ehRet;
+}
+
+static OCEntityHandlerResult RolesEntityHandler(OCEntityHandlerFlag flag,
+ OCEntityHandlerRequest * ehRequest,
+ void* callbackParameter)
+{
+ OCEntityHandlerResult ehRet = OC_EH_ERROR;
+
+ OC_UNUSED(callbackParameter);
+
+ if (!ehRequest)
+ {
+ return OC_EH_ERROR;
+ }
+
+ if (flag & OC_REQUEST_FLAG)
+ {
+ OIC_LOG(DEBUG, TAG, "Flag includes OC_REQUEST_FLAG");
+ switch (ehRequest->method)
+ {
+ case OC_REST_GET:
+ ehRet = HandleGetRequest(ehRequest);
+ break;
+ case OC_REST_PUT:
+ case OC_REST_POST:
+ ehRet = HandlePostRequest(ehRequest);
+ break;
+ case OC_REST_DELETE:
+ ehRet = HandleDeleteRequest(ehRequest);
+ break;
+ default:
+ ehRet = ((SendSRMResponse(ehRequest, OC_EH_ERROR, NULL, 0)) == OC_STACK_OK) ?
+ OC_EH_OK : OC_EH_ERROR;
+ break;
+ }
+ }
+
+ return ehRet;
+}
+
+OCStackResult InitRolesResource()
+{
+ OCStackResult res = OCCreateResource(&gRolesHandle,
+ OIC_RSRC_TYPE_SEC_ROLES,
+ OC_RSRVD_INTERFACE_DEFAULT,
+ OIC_RSRC_ROLES_URI,
+ RolesEntityHandler,
+ NULL,
+ OC_SECURE);
+
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(FATAL, TAG, "Unable to instantiate roles resource: %d", res);
+ }
+
+ return res;
+}
+
+OCStackResult DeInitRolesResource()
+{
+ OCStackResult res = OCDeleteResource(gRolesHandle);
+
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to delete roles resource: %d", res);
+ }
+
+ gRolesHandle = NULL;
+
+ FreeRolesList(gRoles);
+
+ gRoles = NULL;
+
+ return res;
+}
+
+/*
+ * true if before < after, false if before >= after
+ */
+static bool IsBefore(const struct tm *before, const struct tm *after)
+{
+ if (before->tm_year > after->tm_year)
+ {
+ return false;
+ }
+
+ if (before->tm_year == after->tm_year)
+ {
+ if (before->tm_mon > after->tm_mon)
+ {
+ return false;
+ }
+
+ if (before->tm_mon == after->tm_mon)
+ {
+ if (before->tm_mday > after->tm_mday)
+ {
+ return false;
+ }
+
+ if (before->tm_mday == after->tm_mday)
+ {
+ if (before->tm_hour > after->tm_hour)
+ {
+ return false;
+ }
+
+ if (before->tm_hour == after->tm_hour)
+ {
+ if (before->tm_min > after->tm_min)
+ {
+ return false;
+ }
+
+ if (before->tm_min == after->tm_min)
+ {
+ return (before->tm_sec < after->tm_sec);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+OCStackResult GetEndpointRoles(const CAEndpoint_t *endpoint, OicSecRole_t **roles, size_t *roleCount)
+{
+ VERIFY_NOT_NULL_RETURN(TAG, endpoint, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, roles, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, roleCount, ERROR, OC_STACK_INVALID_PARAM);
+
+ uint8_t *publicKey = NULL;
+ size_t publicKeyLength = 0;
+ RolesEntry_t *targetEntry = NULL;
+ OicSecRole_t *rolesToReturn = NULL;
+ size_t rolesToReturnCount = 0;
+ ByteArray_t trustedCaCerts;
+ memset(&trustedCaCerts, 0, sizeof(trustedCaCerts));
+
+ OCStackResult res = GetPeerPublicKeyFromEndpoint(endpoint, &publicKey, &publicKeyLength);
+ if (OC_STACK_INVALID_PARAM == res)
+ {
+ /*
+ * OC_STACK_INVALID_PARAM means the endpoint didn't authenticate with a certificate.
+ * Succeed and return no roles.
+ */
+ *roles = NULL;
+ *roleCount = 0;
+ return OC_STACK_OK;
+ }
+ else if (OC_STACK_OK != res)
+ {
+ /* Any other error is a fatal error. */
+ OIC_LOG_V(ERROR, TAG, "Could not GetPeerPublicKeyFromEndpoint: %d", res);
+ return res;
+ }
+
+ for (targetEntry = gRoles; NULL != targetEntry; targetEntry = targetEntry->next)
+ {
+ if ((targetEntry->publicKeyLength == publicKeyLength) &&
+ (0 == memcmp(targetEntry->publicKey, publicKey, publicKeyLength)))
+ {
+ break;
+ }
+ }
+
+ if (NULL == targetEntry)
+ {
+ /* No roles for this peer. */
+ *roles = NULL;
+ *roleCount = 0;
+ OICFree(publicKey);
+ return OC_STACK_OK;
+ }
+
+ /* Is the cache still valid? */
+ struct tm now;
+ memset(&now, 0, sizeof(now));
+#ifndef WITH_ARDUINO /* No reliable time on Arduino, so assume the cache is valid if present. */
+ time_t nowTimeT = 0;
+ nowTimeT = time(NULL);
+ /* If we failed to get the current time, assume the cache is valid if present. */
+ if ((time_t)-1 != nowTimeT)
+ {
+ /* gmtime_{s,r} should not ever fail. */
+#ifdef HAVE_WINDOWS_H
+ if (0 != gmtime_s(&now, &nowTimeT))
+ {
+ assert(!"gmtime_s failed");
+ OIC_LOG(WARNING, TAG, "gmtime_s failed; assuming role cache is valid");
+ memset(&now, 0, sizeof(now));
+ }
+#else
+ if (NULL == gmtime_r(&nowTimeT, &now))
+ {
+ assert(!"gmtime_r failed");
+ OIC_LOG(WARNING, TAG, "gmtime_r failed; assuming role cache is valid");
+ memset(&now, 0, sizeof(now));
+ }
+#endif
+ }
+#endif /* WITH_ARDUINO */
+
+ if (IsBefore(&now, &targetEntry->cacheValidUntil))
+ {
+ /* now < cacheValidUntil: provide caller with a copy of the cached roles */
+ *roles = (OicSecRole_t *)OICCalloc(targetEntry->cachedRolesLength, sizeof(OicSecRole_t));
+ if (NULL == *roles)
+ {
+ OICFree(publicKey);
+ return OC_STACK_NO_MEMORY;
+ }
+ memcpy(*roles, targetEntry->cachedRoles, (targetEntry->cachedRolesLength * sizeof(OicSecRole_t)));
+ *roleCount = targetEntry->cachedRolesLength;
+
+ OICFree(publicKey);
+ return OC_STACK_OK;
+ }
+
+ InvalidateRoleCache(targetEntry);
+
+ /* Retrieve the current set of trusted CAs from the cred resource. */
+ res = GetPemCaCert(&trustedCaCerts, TRUST_CA);
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Could not get CA certs: %d", res);
+ OICFree(publicKey);
+ return res;
+ }
+
+ for (const RoleCertChain_t *chain = targetEntry->chains; NULL != chain; chain = chain->next)
+ {
+ OicSecRole_t *currCertRoles = NULL;
+ size_t currCertRolesCount = 0;
+ struct tm notValidAfter;
+ memset(¬ValidAfter, 0, sizeof(notValidAfter));
+
+ res = OCInternalVerifyRoleCertificate(&chain->certificate, &chain->optData,
+ trustedCaCerts.data, trustedCaCerts.len,
+ &currCertRoles, &currCertRolesCount,
+ ¬ValidAfter);
+
+ if (OC_STACK_OK != res)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to verify a role certificate: %d", res);
+ /* Don't exit; try all certificates presented. */
+ }
+ else
+ {
+ /* Add returned roles to current list. */
+ OicSecRole_t *savePtr = rolesToReturn;
+ rolesToReturn = (OicSecRole_t *)OICRealloc(rolesToReturn,
+ sizeof(rolesToReturn[0]) * (rolesToReturnCount + currCertRolesCount));
+ if (NULL == rolesToReturn)
+ {
+ OIC_LOG(ERROR, TAG, "No memory reallocating rolesToReturn");
+ memset(&targetEntry->cacheValidUntil, 0, sizeof(targetEntry->cacheValidUntil));
+ OICFree(trustedCaCerts.data);
+ OICFree(savePtr);
+ OICFree(currCertRoles);
+ OICFree(publicKey);
+ return OC_STACK_NO_MEMORY;
+ }
+ memcpy(rolesToReturn + (rolesToReturnCount * sizeof(rolesToReturn[0])),
+ currCertRoles,
+ currCertRolesCount * sizeof(currCertRoles[0]));
+ rolesToReturnCount += currCertRolesCount;
+ OICFree(currCertRoles);
+ }
+
+ /*
+ * Set the cacheValidUntil value to be the earliest notValidUntil date amongst
+ * all the certificates.
+ *
+ * Assumption is that if tm_year is zero, the cacheValidUntil is all zero, and so the
+ * cacheValidUntil value hasn't yet been set. tm_year of 0 means the year 1900, and we
+ * should never see this value in a certificate.
+ */
+ if ((0 == targetEntry->cacheValidUntil.tm_year) ||
+ IsBefore(¬ValidAfter, &targetEntry->cacheValidUntil))
+ {
+ memcpy(&targetEntry->cacheValidUntil, ¬ValidAfter, sizeof(targetEntry->cacheValidUntil));
+ }
+ }
+
+ targetEntry->cachedRoles = rolesToReturn;
+ targetEntry->cachedRolesLength = rolesToReturnCount;
+
+ /* Make a copy for the caller. */
+ *roles = (OicSecRole_t *)OICCalloc(targetEntry->cachedRolesLength, sizeof(OicSecRole_t));
+ if (NULL == *roles)
+ {
+ OICFree(publicKey);
+ OICFree(trustedCaCerts.data);
+ return OC_STACK_NO_MEMORY;
+ }
+ memcpy(*roles, targetEntry->cachedRoles, (targetEntry->cachedRolesLength * sizeof(OicSecRole_t)));
+ *roleCount = targetEntry->cachedRolesLength;
+
+ OICFree(publicKey);
+ OICFree(trustedCaCerts.data);
+ return OC_STACK_OK;
+}
+
+#endif /* defined(__WITH_DTLS__) || defined(__WITH_TLS__) */
\ No newline at end of file
OIC_RSRC_AMACL_URI,
OIC_RSRC_CRL_URI,
OIC_RSRC_CRED_URI,
+ OIC_RSRC_CSR_URI,
OIC_RSRC_ACL_URI,
OIC_RSRC_DOXM_URI,
OIC_RSRC_PSTAT_URI,
OIC_RSRC_PCONF_URI,
OIC_RSRC_DPAIRING_URI,
OIC_RSRC_VER_URI,
+ OIC_RSRC_ROLES_URI,
OC_RSRVD_PROV_CRL_URL
};
}
}
+ svrLen = strlen(OIC_RSRC_CSR_URI);
+ if (uriLen == svrLen)
+ {
+ if (0 == strncmp(uri, OIC_RSRC_CSR_URI, svrLen))
+ {
+ return OIC_R_CSR_TYPE;
+ }
+ }
+
svrLen = strlen(OIC_RSRC_DOXM_URI);
if (uriLen == svrLen)
{
}
}
+ svrLen = strlen(OIC_RSRC_ROLES_URI);
+ if (uriLen == svrLen)
+ {
+ if (0 == strncmp(uri, OIC_RSRC_ROLES_URI, svrLen))
+ {
+ return OIC_R_ROLES_TYPE;
+ }
+ }
+
svrLen = strlen(OIC_RSRC_SVC_URI);
if (uriLen == svrLen)
{
const char * OIC_JSON_ACLIST_NAME = "aclist";
const char * OIC_JSON_ACES_NAME = "aces";
+const char * OIC_RSRC_TYPE_SEC_ACL2 = "oic.r.acl2";
+const char * OIC_RSRC_ACL2_URI = "/oic/sec/acl2";
+const char * OIC_JSON_ACL2_NAME = "acl2";
+
//Pstat
const char * OIC_RSRC_TYPE_SEC_PSTAT = "oic.r.pstat";
const char * OIC_RSRC_PSTAT_URI = "/oic/sec/pstat";
const char * OIC_JSON_CRED_NAME = "cred";
const char * OIC_JSON_CREDS_NAME = "creds";
+//CSR
+const char * OIC_RSRC_TYPE_SEC_CSR = "oic.r.csr";
+const char * OIC_RSRC_CSR_URI = "/oic/sec/csr";
+const char * OIC_JSON_CSR_NAME = "csr";
+
+//roles
+const char * OIC_RSRC_TYPE_SEC_ROLES = "oic.r.roles";
+const char * OIC_RSRC_ROLES_URI = "/oic/sec/roles";
+const char * OIC_JSON_ROLES_NAME = "roles";
+
//CRL
const char * OIC_RSRC_TYPE_SEC_CRL = "oic.r.crl";
const char * OIC_RSRC_CRL_URI = "/oic/sec/crl";
const char * OIC_JSON_CREDID_NAME = "credid";
const char * OIC_JSON_SUBJECTID_NAME = "subjectuuid";
const char * OIC_JSON_ROLEIDS_NAME = "roleid";
+const char * OIC_JSON_AUTHORITY_NAME = "authority";
const char * OIC_JSON_CREDTYPE_NAME = "credtype";
const char * OIC_JSON_PUBLICDATA_NAME = "publicdata";
const char * OIC_JSON_PRIVATEDATA_NAME = "privatedata";
// Certificates provided by Cloud
const char * TRUST_CA = "oic.sec.cred.trustca";
const char * PRIMARY_CERT = "oic.sec.cred.cert";
+const char * PRIMARY_KEY = "primary_key";
// Certificates provided by manufacturer
const char * MF_TRUST_CA = "oic.sec.cred.mfgtrustca";
{
OicSecAcl_t *acl = JSONToAclBin(jsonStr);
VERIFY_NOT_NULL(TAG, acl, FATAL);
- ret = AclToCBORPayload(acl, &aclCbor, &aclCborSize);
+ ret = AclToCBORPayload(acl, OIC_SEC_ACL_V2, &aclCbor, &aclCborSize);
if(OC_STACK_OK != ret)
{
OIC_LOG (ERROR, TAG, "Failed converting Acl to Cbor Payload");
ret = ConvertStrToUuid(jsonObj->valuestring, &ace->subjectuuid);
VERIFY_SUCCESS(TAG, OC_STACK_OK == ret, ERROR);
}
+ ace->subjectType = OicSecAceUuidSubject;
// Resources -- Mandatory
jsonObj = cJSON_GetObjectItem(jsonAcl, OIC_JSON_RESOURCES_NAME);
VERIFY_NOT_NULL(TAG, jsonObj, ERROR);
size_t defaultAclSize = 0;
uint8_t *defaultPsStorage = NULL;
- OCStackResult convRet = AclToCBORPayload(defaultAcl, &defaultPsStorage, &defaultAclSize);
+ OCStackResult convRet = AclToCBORPayload(defaultAcl, OIC_SEC_ACL_LATEST, &defaultPsStorage, &defaultAclSize);
EXPECT_EQ(OC_STACK_OK, convRet);
ASSERT_TRUE(NULL != defaultPsStorage);
EXPECT_NE(static_cast<size_t>(0), defaultAclSize);
size_t size = 0;
uint8_t *psStorage = NULL;
- EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(secAcl, &psStorage, &size));
+ EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(secAcl, OIC_SEC_ACL_V1, &psStorage, &size));
ASSERT_TRUE(NULL != psStorage);
OicSecAcl_t *acl = CBORPayloadToAcl(psStorage, size);
ASSERT_TRUE(NULL != acl);
//GET CBOR POST payload
size_t size = 0;
uint8_t *payload = NULL;
- EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(&acl, &payload, &size));
+ EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(&acl, OIC_SEC_ACL_LATEST, &payload, &size));
ASSERT_TRUE(NULL != payload);
// Security Payload
//GET CBOR POST payload
size_t size = 0;
uint8_t *payload = NULL;
- EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(&acl, &payload, &size));
+ EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(&acl, OIC_SEC_ACL_LATEST, &payload, &size));
ASSERT_TRUE(NULL != payload);
// Security Payload
//GET CBOR POST payload
size_t size = 0;
uint8_t *payload = NULL;
- EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(&acl, &payload, &size));
+ EXPECT_EQ(OC_STACK_OK, AclToCBORPayload(&acl, OIC_SEC_ACL_LATEST, &payload, &size));
ASSERT_TRUE(NULL != payload);
// Security Payload
OICStrcpy((char *)subject.id, sizeof(subject.id), "subject11");
uint8_t privateKey[] = "My private Key11";
- OicSecKey_t key = {privateKey, sizeof(privateKey)};
+ OicSecKey_t key = {privateKey, sizeof(privateKey), OIC_ENCODING_RAW};
OicSecCred_t *cred1 = NULL;
OicSecCred_t *headCred = NULL;
LoadSecretJustWorksCallback
OCConfigSelfOwnership
+OCConvertDerCSRToPem
OCDeleteACLList
OCDeleteDiscoveredDevices
OCDeletePdAclList
+OCDeleteRoleCertificateByCredId
OCDeleteUuidList
OCDiscoverOwnedDevices
OCDiscoverSingleDevice
OCDiscoverUnownedDevices
OCDoOwnershipTransfer
+OCGenerateCACertificate
+OCGenerateIdentityCertificate
+OCGenerateKeyPair
+OCGenerateRandomSerialNumber
+OCGenerateRoleCertificate
OCGetACLResource
OCGetCredResource
+OCGetCSRResource
OCGetDevInfoFromNetwork
OCGetLinkedStatus
+OCGetPublicKeyFromCSR
+OCGetRolesResource
+OCGetUuidFromCSR
OCInitPM
OCPDMCleanupForTimeout
OCProvisionACL
OCSaveACL
+OCProvisionCertificate
OCProvisionCredentials
OCProvisionDirectPairing
OCProvisionPairwiseDevices
OCResetDevice
OCResetSVRDB
OCSaveTrustCertChain
+OCSaveOwnCertChain
OCSetOwnerTransferCallbackData
OCUnlinkDevices
OCSetOxmAllowStatus
+OCVerifyCSRSignature
SetClosePinDisplayCB
SetDisplayPinWithContextCB