Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

credentials_issuer (CON-1525) #1256

Open
stefano055415 opened this issue Jan 22, 2025 · 3 comments
Open

credentials_issuer (CON-1525) #1256

stefano055415 opened this issue Jan 22, 2025 · 3 comments

Comments

@stefano055415
Copy link

Hi, I'm trying to create a custom credentials_issuer for a controller.

I'm using the controller example that works perfectly with the default option CONFIG_TEST_OPERATIONAL_CREDS_ISSUER

Now I wanted to use the CONFIG_CUSTOM_OPERATIONAL_CREDS_ISSUER passing to the controller the certificates that come from an external server, to be shared on multiple controllers.

I saw that there is the function set_custom_credentials_issuer, but I have some difficulty in creating the credentials_issuer to pass to it

Could someone help me?

Thanks

@github-actions github-actions bot changed the title credentials_issuer credentials_issuer (CON-1525) Jan 22, 2025
@wqx6
Copy link
Contributor

wqx6 commented Jan 23, 2025

To implement a credential issuer,your should implement an OperationalCredentialsDelegate class firstly. The OperationalCredentialsDelegate class is used to generate NOC chains for the end devices if your controller acts as a commissioner in your matter fabric. You can find there is a GenerateNOCChain function in the OperationCredentialsDelegate class and you need to override this function to generate an operational certificate chain for a remote device that is being commissioned. If your controller is not a commissioner, you can return NOT_SUPPORTED error in this function as this function will never be called.

After implementing the OperationalCredentialsDelegate, you can implement the credential issuer class. There are three functions to be override in the class.

  • initialize_credentials_issuer() is the function to initialize the Credentials Issuer, the storage input might be used to store the controller's credentials generated previously as you will not generate new credentials after rebooting the controller.
  • get_delegate() is the function to get the OperationalCredentialsDelegate that you implement in the above steps.
  • generate_controller_noc_chain() is the function to get the NOC chain for your controller. In this function, you can read the NOC chain from NVS if the controller has previously generated the NOC chain, or fetch the chain from a remote server for the first NOC chain initialization.

Note that please use different NOCs for multiple controllers, in Matter SPEC, an unique node(no matter it is a controller or an end-device) should have an unique NOC with an unique node ID. To make the multiple controllers have access for the end devices, we would include CAT in the NOC of controllers.

@stefano055415
Copy link
Author

hi @wqx6,

Thanks for the advice I have implemented both OperationalCredentialsDelegate and credential_issuer

I run:

esp_matter::controller::app_controller_credentials_issuer s_creds_issuer;
esp_matter::lock::chip_stack_lock(portMAX_DELAY);
esp_matter::controller::matter_controller_client::get_instance().init(node_id, fabric_id, 5580);
esp_matter::controller::set_custom_credentials_issuer(&s_creds_issuer);
esp_matter::controller::matter_controller_client::get_instance().setup_commissioner();
esp_matter::lock::chip_stack_unlock();

and the SDK calls my initialize_credentials_issuer, get_delegate and finally the generate_controller_noc_chain

in my OperationalCredentialsDelegate I implemented the certificate request to the server (http request), after having generated the CSR that I need to always generate the NOC on the server

so far everything is ok

certificates arrive in PEM format and with this function I convert them to MutableByteSpan

CHIP_ERROR ConvertPemToDer(const char *pem_data, MutableByteSpan &outX509Cert)
            {
                constexpr size_t kMaxDERCertLength = 2048; // Dimensione massima del certificato DER

                // Buffer per i dati DER
                uint8_t der_cert[kMaxDERCertLength];
                size_t der_len = 0;

                mbedtls_x509_crt cert;
                mbedtls_x509_crt_init(&cert);

                // Parsing del certificato PEM
                int ret = mbedtls_x509_crt_parse(&cert, reinterpret_cast<const unsigned char *>(pem_data), strlen(pem_data) + 1);
                if (ret != 0)
                {
                    mbedtls_x509_crt_free(&cert);
                    return CHIP_ERROR_INTERNAL; // Errore nel parsing
                }

                // Verifica che la lunghezza del certificato DER non ecceda il limite
                if (cert.raw.len > kMaxDERCertLength)
                {
                    mbedtls_x509_crt_free(&cert);
                    return CHIP_ERROR_INTERNAL; // Certificato troppo grande
                }

                // Copia dei dati DER nel buffer
                memcpy(der_cert, cert.raw.p, cert.raw.len);
                der_len = cert.raw.len;

                mbedtls_x509_crt_free(&cert);

                // Copia dei dati DER nel MutableByteSpan
                outX509Cert = MutableByteSpan(der_cert, der_len);

                return CHIP_NO_ERROR;
            }

the conversion is successful and finally I save everything on the storage (This is done for all three certificates)

ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(mIssuerId));

            ChipLogProgress(Controller, "Generating RCAC");

            ReturnErrorOnFailure(ConvertPemToDer(root_ca, rcac));
            // ReturnErrorOnFailure(IssueX509Cert(mNow, mValidity, rcac_dn, rcac_dn, CertType::kRcac, mUseMaximallySizedCerts,
            //                                    mIssuer.Pubkey(), mIssuer, rcac));
            VerifyOrReturnError(CanCastTo<uint16_t>(rcac.size()), CHIP_ERROR_INTERNAL);

            // Re-extract DN based on final generated cert

            rcac_dn = ChipDN{};

            ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(rcac, rcac_dn));

            PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsRootCertificateStorage, key,
                              ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key, rcac.data(), static_cast<uint16_t>(rcac.size()))))

but the function of

esp_matter::controller::matter_controller_client::get_instance().setup_commissioner();

ends with an error
refers to the function

static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer, Tag tag);

when it gets to the macro ASN1_PARSE_ENTER_SEQUENCE it goes into error

if (reader.GetClass() != kASN1TagClass_Universal || reader.GetTag() != kASN1UniversalTag_Sequence)
    {
        printf("Error: Class: %d, Tag: %d\n", reader.GetClass(), reader.GetTag());
        printf("Class: %d, Tag: %d, Value Length: %zu\n", reader.GetClass(), reader.GetTag(), reader.GetValueLen());

        return ASN1_ERROR_UNSUPPORTED_ENCODING;
    }

I tried to print this data which gives me all 0 as a result

Class: 0, Tag: 0, Value Length: 0

The certificates that come from the server are definitely correct, because I have already used them with another controller (Android) and they work correctly

I hope you can help me

@stefano055415
Copy link
Author

@wqx6

Update:

they go ahead with the analysis of the error I came to understand that the error is generated in the function

CHIP_ERROR ASN1Reader::DecodeHead();

More precisely in verifying the length of the buffer

VerifyOrReturnError(static_cast<uint32_t>(mBufEnd - p) >= ValueLen, ASN1_ERROR_VALUE_OVERFLOW);

where it turns out that ValueLen is greater than (mBufEnd - p)

I hope it can help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants