DID Functions
#include <keychain/keychain_headers.h>
Overview
DID functions implement basic parsing and manipulation of W3C Decentralized Identifiers (DIDs). DIDs are a standardized way to identify entities in a decentralized manner according to the W3C DID specification. A DID consists of three components: a scheme (always "did"), a method name, and a method-specific identifier.
The format is: did:<method>:<method-specific-id>
Object Lifecycle Functions
kc_did_construct_from_serialized_string()
int kc_did_construct_from_serialized_string(
kc_did_t** out_did_ptr,
const char* serialized_string,
const unsigned int serialized_string_size
)
Deserializes a DID from a string representation according to W3C DID specification.
Parameters:
-
out_did_ptr
- Pointer to DID pointer (output, caller must free) -
serialized_string
- DID string as defined in W3C DID specification -
serialized_string_size
- Number of bytes in the serialized string
Returns: Error code (0 = success)
Example:
kc_did_t* did = NULL;
const char* did_string = "did:example:123456789abcdefghi";
int result = kc_did_construct_from_serialized_string(&did,
did_string, strlen(did_string));
if (result == 0) {
// Use DID...
kc_did_destruct(&did);
}
kc_did_construct_from_copy()
int kc_did_construct_from_copy(
kc_did_t** out_did_ptr,
const kc_did_t* rhs_did_ptr
)
Creates a deep copy of a DID object.
Parameters:
-
out_did_ptr
- Pointer to new DID pointer (output, caller must free) -
rhs_did_ptr
- DID object to copy
Returns: Error code (0 = success)
Example:
kc_did_t* original_did = NULL;
kc_did_t* copied_did = NULL;
// ... create original_did ...
int result = kc_did_construct_from_copy(&copied_did, original_did);
if (result == 0) {
// Use copied DID...
kc_did_destruct(&copied_did);
}
Component Access Functions
kc_did_scheme()
int kc_did_scheme(
const kc_did_t* did_ptr,
char** out_scheme,
unsigned int* out_size
)
Gets the DID scheme (always "did" for valid DIDs).
Parameters:
-
did_ptr
- DID object to query -
out_scheme
- Pointer to scheme string pointer (output, caller must free) -
out_size
- Pointer to scheme string size (output)
Returns: Error code (0 = success)
Example:
char* scheme = NULL;
unsigned int scheme_size = 0;
int result = kc_did_scheme(did, &scheme, &scheme_size);
if (result == 0) {
printf("DID scheme: %.*s\n", scheme_size, scheme);
kc_common_delete_buffer(&scheme);
}
kc_did_method()
int kc_did_method(
const kc_did_t* did_ptr,
char** out_method,
unsigned int* out_size
)
Gets the DID method name.
Parameters:
-
did_ptr
- DID object to query -
out_method
- Pointer to method string pointer (output, caller must free) -
out_size
- Pointer to method string size (output)
Returns: Error code (0 = success)
Example:
char* method = NULL;
unsigned int method_size = 0;
int result = kc_did_method(did, &method, &method_size);
if (result == 0) {
printf("DID method: %.*s\n", method_size, method);
kc_common_delete_buffer(&method);
}
kc_did_id()
int kc_did_id(
const kc_did_t* did_ptr,
char** out_id,
unsigned int* out_size
)
Gets the method-specific identifier.
Parameters:
-
did_ptr
- DID object to query -
out_id
- Pointer to ID string pointer (output, caller must free) -
out_size
- Pointer to ID string size (output)
Returns: Error code (0 = success)
Example:
char* id = NULL;
unsigned int id_size = 0;
int result = kc_did_id(did, &id, &id_size);
if (result == 0) {
printf("Method-specific ID: %.*s\n", id_size, id);
kc_common_delete_buffer(&id);
}
Serialization Functions
kc_did_serialize()
int kc_did_serialize(
const kc_did_t* did_ptr,
char** out_serialized_str,
unsigned int* out_size
)
Serializes the DID to a string as defined in the W3C specification.
Parameters:
-
did_ptr
- DID object to serialize -
out_serialized_str
- Pointer to serialized string pointer (output, caller must free) -
out_size
- Pointer to string size (output)
Returns: Error code (0 = success)
Example:
char* serialized = NULL;
unsigned int serialized_size = 0;
int result = kc_did_serialize(did, &serialized, &serialized_size);
if (result == 0) {
printf("Serialized DID: %.*s\n", serialized_size, serialized);
kc_common_delete_buffer(&serialized);
}
Query Functions
kc_did_equals()
int kc_did_equals(
const kc_did_t* lhs_did_ptr,
bool* out_is_equal,
const kc_did_t* rhs_did_ptr
)
Tests equality of two DID objects.
Parameters:
-
lhs_did_ptr
- First DID to compare -
out_is_equal
- Pointer to equality result (output) -
rhs_did_ptr
- Second DID to compare
Returns: Error code (0 = success)
Example: DID Parsing and Analysis
void demonstrate_did_parsing(const char* did_string) {
kc_did_t* did = NULL;
printf("Parsing DID: %s\n", did_string);
// Parse the DID string
int result = kc_did_construct_from_serialized_string(&did,
did_string, strlen(did_string));
if (result != 0) {
printf("Failed to parse DID: %d\n", result);
return;
}
// Extract and display components
char* scheme = NULL;
char* method = NULL;
char* id = NULL;
unsigned int scheme_size = 0;
unsigned int method_size = 0;
unsigned int id_size = 0;
kc_did_scheme(did, &scheme, &scheme_size);
kc_did_method(did, &method, &method_size);
kc_did_id(did, &id, &id_size);
printf("DID Components:\n");
printf(" Scheme: %.*s\n", scheme_size, scheme ? scheme : "Unknown");
printf(" Method: %.*s\n", method_size, method ? method : "Unknown");
printf(" Method-specific ID: %.*s\n", id_size, id ? id : "Unknown");
// Verify round-trip serialization
char* serialized = NULL;
unsigned int serialized_size = 0;
result = kc_did_serialize(did, &serialized, &serialized_size);
if (result == 0) {
printf("Serialized: %.*s\n", serialized_size, serialized);
// Verify it matches original
if (strlen(did_string) == serialized_size &&
memcmp(did_string, serialized, serialized_size) == 0) {
printf("✓ Round-trip serialization successful\n");
} else {
printf("✗ Round-trip serialization failed\n");
}
kc_common_delete_buffer(&serialized);
}
// Cleanup
if (scheme) kc_common_delete_buffer(&scheme);
if (method) kc_common_delete_buffer(&method);
if (id) kc_common_delete_buffer(&id);
kc_did_destruct(&did);
}
void test_various_did_formats() {
// Test different DID formats
const char* test_dids[] = {
"did:example:123456789abcdefghi",
"did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
"did:web:example.com",
"did:ethr:0x3b0BC51Ab9De1e5B7B6E34E5b960285805C41736",
"did:ion:EiClkZMDxPKqC9c-umQfTkR8vvZ9JPhl_xLDI9Nfk38w5w",
"did:keychain:mainnet:alice.example.com"
};
for (size_t i = 0; i < sizeof(test_dids) / sizeof(test_dids[0]); i++) {
printf("\n=== Test %zu ===\n", i + 1);
demonstrate_did_parsing(test_dids[i]);
}
}
Example: DID Comparison and Validation
bool validate_did_format(const char* did_string) {
kc_did_t* did = NULL;
// Try to parse the DID
int result = kc_did_construct_from_serialized_string(&did,
did_string, strlen(did_string));
if (result != 0) {
printf("Invalid DID format: %s\n", did_string);
return false;
}
// Verify scheme is "did"
char* scheme = NULL;
unsigned int scheme_size = 0;
result = kc_did_scheme(did, &scheme, &scheme_size);
bool valid_scheme = false;
if (result == 0 && scheme) {
if (scheme_size == 3 && memcmp(scheme, "did", 3) == 0) {
valid_scheme = true;
}
kc_common_delete_buffer(&scheme);
}
if (!valid_scheme) {
printf("Invalid DID scheme (must be 'did'): %s\n", did_string);
kc_did_destruct(&did);
return false;
}
// Verify method is not empty
char* method = NULL;
unsigned int method_size = 0;
result = kc_did_method(did, &method, &method_size);
bool valid_method = false;
if (result == 0 && method && method_size > 0) {
valid_method = true;
}
if (method) kc_common_delete_buffer(&method);
if (!valid_method) {
printf("Invalid DID method (cannot be empty): %s\n", did_string);
kc_did_destruct(&did);
return false;
}
// Verify method-specific ID is not empty
char* id = NULL;
unsigned int id_size = 0;
result = kc_did_id(did, &id, &id_size);
bool valid_id = false;
if (result == 0 && id && id_size > 0) {
valid_id = true;
}
if (id) kc_common_delete_buffer(&id);
if (!valid_id) {
printf("Invalid method-specific ID (cannot be empty): %s\n", did_string);
kc_did_destruct(&did);
return false;
}
kc_did_destruct(&did);
printf("✓ Valid DID format: %s\n", did_string);
return true;
}
void compare_dids(const char* did1_str, const char* did2_str) {
kc_did_t* did1 = NULL;
kc_did_t* did2 = NULL;
// Parse both DIDs
int result1 = kc_did_construct_from_serialized_string(&did1,
did1_str, strlen(did1_str));
int result2 = kc_did_construct_from_serialized_string(&did2,
did2_str, strlen(did2_str));
if (result1 != 0 || result2 != 0) {
printf("Failed to parse one or both DIDs\n");
if (did1) kc_did_destruct(&did1);
if (did2) kc_did_destruct(&did2);
return;
}
// Compare for equality
bool are_equal = false;
int result = kc_did_equals(did1, &are_equal, did2);
if (result == 0) {
printf("Comparing DIDs:\n");
printf(" DID 1: %s\n", did1_str);
printf(" DID 2: %s\n", did2_str);
printf(" Equal: %s\n", are_equal ? "Yes" : "No");
} else {
printf("Failed to compare DIDs: %d\n", result);
}
kc_did_destruct(&did1);
kc_did_destruct(&did2);
}
void demonstrate_did_validation() {
// Test valid DIDs
const char* valid_dids[] = {
"did:example:123",
"did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
"did:web:example.com"
};
printf("Testing valid DIDs:\n");
for (size_t i = 0; i < sizeof(valid_dids) / sizeof(valid_dids[0]); i++) {
validate_did_format(valid_dids[i]);
}
// Test invalid DIDs
const char* invalid_dids[] = {
"notadid:example:123",
"did::123",
"did:example:",
"",
"did"
};
printf("\nTesting invalid DIDs:\n");
for (size_t i = 0; i < sizeof(invalid_dids) / sizeof(invalid_dids[0]); i++) {
validate_did_format(invalid_dids[i]);
}
// Test DID comparison
printf("\nTesting DID comparison:\n");
compare_dids("did:example:123", "did:example:123");
compare_dids("did:example:123", "did:example:456");
compare_dids("did:key:abc", "did:web:abc");
}
Example: DID Utility Functions
// Extract method from DID string without full parsing
char* extract_did_method_quick(const char* did_string) {
if (!did_string) return NULL;
// Check for "did:" prefix
if (strlen(did_string) < 4 || memcmp(did_string, "did:", 4) != 0) {
return NULL;
}
// Find the method part (between first and second colon)
const char* method_start = did_string + 4;
const char* method_end = strchr(method_start, ':');
if (!method_end) return NULL;
size_t method_length = method_end - method_start;
if (method_length == 0) return NULL;
char* method = malloc(method_length + 1);
if (method) {
memcpy(method, method_start, method_length);
method[method_length] = '\0';
}
return method;
}
bool is_keychain_did(const char* did_string) {
char* method = extract_did_method_quick(did_string);
if (!method) return false;
bool is_keychain = (strcmp(method, "keychain") == 0);
free(method);
return is_keychain;
}
void categorize_did_by_method(const char* did_string) {
kc_did_t* did = NULL;
int result = kc_did_construct_from_serialized_string(&did,
did_string, strlen(did_string));
if (result != 0) {
printf("Invalid DID: %s\n", did_string);
return;
}
char* method = NULL;
unsigned int method_size = 0;
result = kc_did_method(did, &method, &method_size);
if (result == 0 && method) {
printf("DID: %s\n", did_string);
printf("Method: %.*s\n", method_size, method);
// Categorize by method
if (method_size == 3 && memcmp(method, "key", 3) == 0) {
printf("Category: Cryptographic key-based DID\n");
} else if (method_size == 3 && memcmp(method, "web", 3) == 0) {
printf("Category: Web-based DID\n");
} else if (method_size == 4 && memcmp(method, "ethr", 4) == 0) {
printf("Category: Ethereum-based DID\n");
} else if (method_size == 3 && memcmp(method, "ion", 3) == 0) {
printf("Category: ION (Bitcoin-based) DID\n");
} else if (method_size == 8 && memcmp(method, "keychain", 8) == 0) {
printf("Category: Keychain-specific DID\n");
} else {
printf("Category: Other method\n");
}
kc_common_delete_buffer(&method);
}
kc_did_destruct(&did);
}
Memory Management
-
Use
kc_did_destruct()
to free DID objects -
Use
kc_common_delete_buffer()
for string outputs (scheme, method, ID, serialized data) -
Always check return codes before using output parameters
-
DIDs are immutable once created - create new DIDs for different identifiers
Best Practices
-
Always validate DID format before using in critical operations
-
Use appropriate DID methods for your use case
-
Store DIDs in their canonical string form for persistence
-
Compare DIDs using the equals function rather than string comparison
-
Cache parsed DID objects if you need to access components multiple times
See Also
-
Keychain DID Functions - Keychain-specific DID extensions
-
Persona DID Functions - Persona-specific DID handling
-
Persona Functions - Using DIDs with personas
-
Contact Functions - Using DIDs with contacts
-
C++ DID Class - Object-oriented interface