Skip to content

Validating Credential Signatures and Issuers in Self

This documentation provides a comprehensive guide on how to validate that a credential has been signed by a valid party (Self) using the Self SDK. It covers the necessary steps to ensure the authenticity and integrity of credentials by verifying their signatures and confirming the issuer's identity.

Introduction

In decentralized identity systems, ensuring that credentials are issued and signed by trusted parties is crucial for maintaining trust and security. This guide walks you through the steps to validate credentials within the Self ecosystem, focusing on verifying that a credential has been signed by a valid issuer (Self).

Validation Process

The validation process involves multiple steps to ensure both the presentation and the credentials are authentic and issued by a trusted entity.

1. Validate the Presentation

Before examining individual credentials, validate the overall presentation:

for _, p := range response.Presentations() {
    err = p.Validate()
    if err != nil {
        log.Warn("failed to validate presentation", "error", err)
        continue
    }

    // Verify the presentation holder matches the expected address
    if !p.Holder().Address().Matches(responderAddress) {
        log.Warn("received a presentation response for a different holder address")
        continue
    }
    // Additional checks...
}
credentialResponse?.presentations()?.forEach { presentation ->
    try {
        presentation.validate()
    } catch (ex: Exception) {
        println("failed to validate presentation: ${ex.message}")
        return@forEach
    }

    // Verify the presentation holder matches the expected address
    if (presentation.holder().address().encodeHex() != responderAddress?.encodeHex()) {
        println("received a presentation response for a different holder address")
        return@forEach
    }
    // Process credentials...
}

2. Validate Each Credential

For each credential in the presentation:

for _, c := range p.Credentials() {
    // Validate credential structure and signature
    err = c.Validate()
    if err != nil {
        log.Warn("failed to validate credential", "error", err)
        continue
    }

    // Check validity period
    if c.ValidFrom().After(time.Now()) {
        log.Warn("credential is intended to be used in the future")
        continue
    }

    // Parse and process credential claims
    claims, err := c.CredentialSubjectClaims()
    if err != nil {
        log.Warn("failed to parse credential claims", "error", err)
        continue
    }

    // Process individual claims
    for k, v := range claims {
        log.Info(
            "credential value",
            "credentialType", c.CredentialType(),
            "field", k,
            "value", v,
        )
    }
}
presentation.credentials().forEach { credential ->
    try {
        // Validate credential structure and signature
        credential.validate()

        // Check validity period
        if (credential.validFrom() > Timestamp.now()) {
            println("credential is intended to be used in the future")
            return@forEach
        }

        // Parse and process credential claims
        val claims = credential.credentialSubjectClaims()
        claims.forEach {
            println(
                "credential value" +
                "\ncredentialType:${credential.credentialType()}" +
                "\nfield:${it.key}" +
                "\nvalue:${it.value}"
            )
        }
    } catch (ex: Exception) {
        println("failed to validate credential: ${ex.message}")
        return@forEach
    }
}

3. Validate the Issuer (TODO)

Note: This feature is currently under development. The following code shows the intended implementation:

if c.Issuer().Method() == credential.MethodAure {
    // Resolve the issuer's identity document
    document, err := selfAccount.IdentityResolve(c.Issuer().Address())
    if err != nil {
        log.Warn("failed to resolve credential issuer", "error", err)
        continue
    }

    // Verify the issuer has the appropriate roles at the time of issuance
    if !document.HasRolesAt(c.Issuer().SigningKey(), identity.RoleAssertion, c.Created()) {
        log.Warn("credential signing key was not valid at the time of issuance")
        continue
    }
}
// TODO: Implementation coming soon
if (credential.issuer().method() == Method.AURE) {
    // Resolve the issuer's identity document
    val document = account.identityResolve(credential.issuer().address())

    // Verify the issuer has the appropriate roles at the time of issuance
    if (!document.hasRolesAt(credential.issuer().signingKey(), Role.ASSERTION, credential.created())) {
        println("credential signing key was not valid at the time of issuance")
        return@forEach
    }
}

Conclusion

Validating credentials is a critical step in maintaining the security and trustworthiness of decentralized identity systems. By following the steps outlined in this guide, you can ensure that credentials are issued and signed by valid parties, thereby safeguarding your application's integrity.