Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f23aa640 authored by Andrew Scull's avatar Andrew Scull Committed by Automerger Merge Worker
Browse files

Merge changes from topic "rkp-csrv3-updates" am: 5eeb73be

parents db93905f 5eeb73be
Loading
Loading
Loading
Loading
+7 −9
Original line number Diff line number Diff line
@@ -701,7 +701,8 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequest) {
}

/**
 * Generate a non-empty certificate request.  Make sure contents are reproducible.
 * Generate a non-empty certificate request.  Make sure contents are reproducible but allow for the
 * signature to be different since algorithms including ECDSA P-256 can include a random value.
 */
TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
    generateKeys(false /* testMode */, 1 /* numKeys */);
@@ -711,19 +712,16 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
    ASSERT_TRUE(status.isOk()) << status.getMessage();

    auto firstBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
    ASSERT_TRUE(firstBcc) << firstBcc.message();
    auto firstCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
    ASSERT_TRUE(firstCsr) << firstCsr.message();

    status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
    ASSERT_TRUE(status.isOk()) << status.getMessage();

    auto secondBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
    ASSERT_TRUE(secondBcc) << secondBcc.message();
    auto secondCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
    ASSERT_TRUE(secondCsr) << secondCsr.message();

    ASSERT_EQ(firstBcc->size(), secondBcc->size());
    for (auto i = 0; i < firstBcc->size(); i++) {
        ASSERT_EQ(firstBcc->at(i).pubKey, secondBcc->at(i).pubKey);
    }
    ASSERT_EQ(**firstCsr, **secondCsr);
}

/**
+4 −5
Original line number Diff line number Diff line
@@ -181,14 +181,13 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
 * Verify the CSR as if the device is still early in the factory process and may not
 * have all device identifiers provisioned yet.
 */
ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
                                                     const std::vector<uint8_t>& csr,
                                                     IRemotelyProvisionedComponent* provisionable,
                                                     const std::vector<uint8_t>& challenge);
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
/**
 * Verify the CSR as if the device is a final production sample.
 */
ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);

+110 −79
Original line number Diff line number Diff line
@@ -521,11 +521,10 @@ ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
        return errMsg;
    }

    std::unique_ptr<cppbor::Map> parsed(parsedVerifiedDeviceInfo->asMap());
    std::unique_ptr<cppbor::Map> parsed(parsedVerifiedDeviceInfo.release()->asMap());
    if (!parsed) {
        return "DeviceInfo must be a CBOR map.";
    }
    parsedVerifiedDeviceInfo.release();

    if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
        return "DeviceInfo ordering is non-canonical.";
@@ -846,54 +845,79 @@ std::string validateUdsCerts(const cppbor::Map& udsCerts, const bytevec& udsPub)
    return "";
}

ErrMsgOr<cppbor::Array> parseAndValidateCsrPayload(const cppbor::Array& keysToSign,
                                                   const std::vector<uint8_t>& csrPayload,
                                                   IRemotelyProvisionedComponent* provisionable,
                                                   const std::vector<uint8_t>& challenge,
                                                   bool isFactory) {
ErrMsgOr<std::unique_ptr<cppbor::Array>> parseAndValidateCsrPayload(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csrPayload,
        IRemotelyProvisionedComponent* provisionable, bool isFactory) {
    auto [parsedCsrPayload, _, errMsg] = cppbor::parse(csrPayload);
    if (!parsedCsrPayload) {
        return errMsg;
    }
    if (!parsedCsrPayload->asArray()) {

    std::unique_ptr<cppbor::Array> parsed(parsedCsrPayload.release()->asArray());
    if (!parsed) {
        return "CSR payload is not a CBOR array.";
    }
    if (parsedCsrPayload->asArray()->size() != 5U) {
        return "CSR payload must contain version, certificate type, device info, challenge, keys. "

    if (parsed->size() != 4U) {
        return "CSR payload must contain version, certificate type, device info, keys. "
               "However, the parsed CSR payload has " +
               std::to_string(parsedCsrPayload->asArray()->size()) + " entries.";
               std::to_string(parsed->size()) + " entries.";
    }

    auto& signedVersion = parsedCsrPayload->asArray()->get(0);
    auto& signedCertificateType = parsedCsrPayload->asArray()->get(1);
    auto& signedDeviceInfo = parsedCsrPayload->asArray()->get(2);
    auto& signedChallenge = parsedCsrPayload->asArray()->get(3);
    auto& signedKeys = parsedCsrPayload->asArray()->get(4);
    auto signedVersion = parsed->get(0)->asUint();
    auto signedCertificateType = parsed->get(1)->asTstr();
    auto signedDeviceInfo = parsed->get(2)->asMap();
    auto signedKeys = parsed->get(3)->asArray();

    if (!signedVersion || !signedVersion->asUint() || signedVersion->asUint()->value() != 1U) {
        return "CSR payload version must be an unsigned integer and must be equal to 1.";
    if (!signedVersion || signedVersion->value() != 3U) {
        return "CSR payload version must be an unsigned integer and must be equal to 3.";
    }
    if (!signedCertificateType || !signedCertificateType->asTstr()) {
    if (!signedCertificateType) {
        // Certificate type is allowed to be extendend by vendor, i.e. we can't
        // enforce its value.
        return "Certificate type must be a Tstr.";
    }
    if (!signedDeviceInfo || !signedDeviceInfo->asMap()) {
    if (!signedDeviceInfo) {
        return "Device info must be an Map.";
    }
    if (!signedChallenge || !signedChallenge->asBstr()) {
        return "Challenge must be a Bstr.";
    }
    if (!signedKeys || !signedKeys->asArray()) {
    if (!signedKeys) {
        return "Keys must be an Array.";
    }

    auto result = parseAndValidateDeviceInfo(signedDeviceInfo->asMap()->encode(), provisionable,
                                             isFactory);
    auto result = parseAndValidateDeviceInfo(signedDeviceInfo->encode(), provisionable, isFactory);
    if (!result) {
        return result.message();
    }

    if (signedKeys->encode() != keysToSign.encode()) {
        return "Signed keys do not match.";
    }

    return std::move(parsed);
}

ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequestSignedPayload(
        const std::vector<uint8_t>& signedPayload, const std::vector<uint8_t>& challenge) {
    auto [parsedSignedPayload, _, errMsg] = cppbor::parse(signedPayload);
    if (!parsedSignedPayload) {
        return errMsg;
    }
    if (!parsedSignedPayload->asArray()) {
        return "SignedData payload is not a CBOR array.";
    }
    if (parsedSignedPayload->asArray()->size() != 2U) {
        return "SignedData payload must contain the challenge and request. However, the parsed "
               "SignedData payload has " +
               std::to_string(parsedSignedPayload->asArray()->size()) + " entries.";
    }

    auto signedChallenge = parsedSignedPayload->asArray()->get(0)->asBstr();
    auto signedRequest = parsedSignedPayload->asArray()->get(1)->asBstr();

    if (!signedChallenge) {
        return "Challenge must be a Bstr.";
    }

    if (challenge.size() < 32 || challenge.size() > 64) {
        return "Challenge size must be between 32 and 64 bytes inclusive. "
               "However, challenge is " +
@@ -901,68 +925,57 @@ ErrMsgOr<cppbor::Array> parseAndValidateCsrPayload(const cppbor::Array& keysToSi
    }

    auto challengeBstr = cppbor::Bstr(challenge);
    if (*signedChallenge->asBstr() != challengeBstr) {
    if (*signedChallenge != challengeBstr) {
        return "Signed challenge does not match."
               "\n  Actual: " +
               cppbor::prettyPrint(signedChallenge->asBstr(), 64 /* maxBStrSize */) +
               "\nExpected: " + cppbor::prettyPrint(&challengeBstr, 64 /* maxBStrSize */);
    }

    if (signedKeys->asArray()->encode() != keysToSign.encode()) {
        return "Signed keys do not match.";
    if (!signedRequest) {
        return "Request must be a Bstr.";
    }

    return std::move(*parsedCsrPayload->asArray());
    return signedRequest->value();
}

ErrMsgOr<std::vector<BccEntryData>> verifyCsr(const cppbor::Array& keysToSign,
                                              const std::vector<uint8_t>& csr,
                                              IRemotelyProvisionedComponent* provisionable,
                                              const std::vector<uint8_t>& challenge,
                                              bool isFactory) {
    auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
    if (!parsedCsr) {
ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t>& request,
                                                       const std::vector<uint8_t>& challenge) {
    auto [parsedRequest, _, csrErrMsg] = cppbor::parse(request);
    if (!parsedRequest) {
        return csrErrMsg;
    }
    if (!parsedCsr->asArray()) {
        return "CSR is not a CBOR array.";
    if (!parsedRequest->asArray()) {
        return "AuthenticatedRequest is not a CBOR array.";
    }
    if (parsedCsr->asArray()->size() != 4U) {
        return "CSR must contain version, UDS certificates, DICE chain, and signed data. "
               "However, the parsed CSR has " +
               std::to_string(parsedCsr->asArray()->size()) + " entries.";
    if (parsedRequest->asArray()->size() != 4U) {
        return "AuthenticatedRequest must contain version, UDS certificates, DICE chain, and "
               "signed data. However, the parsed AuthenticatedRequest has " +
               std::to_string(parsedRequest->asArray()->size()) + " entries.";
    }

    auto& version = parsedCsr->asArray()->get(0);
    auto& udsCerts = parsedCsr->asArray()->get(1);
    auto& diceCertChain = parsedCsr->asArray()->get(2);
    auto& signedData = parsedCsr->asArray()->get(3);
    auto version = parsedRequest->asArray()->get(0)->asUint();
    auto udsCerts = parsedRequest->asArray()->get(1)->asMap();
    auto diceCertChain = parsedRequest->asArray()->get(2)->asArray();
    auto signedData = parsedRequest->asArray()->get(3)->asArray();

    if (!version || !version->asUint() || version->asUint()->value() != 3U) {
        return "Version must be an unsigned integer and must be equal to 3.";
    }
    if (!udsCerts || !udsCerts->asMap()) {
        return "UdsCerts must be an Map.";
    if (!version || version->value() != 1U) {
        return "AuthenticatedRequest version must be an unsigned integer and must be equal to 1.";
    }
    if (!diceCertChain || !diceCertChain->asArray()) {
        return "DiceCertChain must be an Array.";
    if (!udsCerts) {
        return "AuthenticatedRequest UdsCerts must be an Map.";
    }
    if (!signedData || !signedData->asArray()) {
        return "SignedData must be an Array.";
    if (!diceCertChain) {
        return "AuthenticatedRequest DiceCertChain must be an Array.";
    }

    RpcHardwareInfo info;
    provisionable->getHardwareInfo(&info);
    if (version->asUint()->value() != info.versionNumber) {
        return "CSR version (" + std::to_string(version->asUint()->value()) +
               ") does not match the remotely provisioned component version (" +
               std::to_string(info.versionNumber) + ").";
    if (!signedData) {
        return "AuthenticatedRequest SignedData must be an Array.";
    }

    // DICE chain is [ pubkey, + DiceChainEntry ]. Its format is the same as BCC from RKP v1-2.
    auto diceContents = validateBcc(diceCertChain->asArray());
    auto diceContents = validateBcc(diceCertChain);
    if (!diceContents) {
        return diceContents.message() + "\n" + prettyPrint(diceCertChain.get());
        return diceContents.message() + "\n" + prettyPrint(diceCertChain);
    }
    if (diceContents->size() == 0U) {
        return "The DICE chain is empty. It must contain at least one entry.";
@@ -970,33 +983,51 @@ ErrMsgOr<std::vector<BccEntryData>> verifyCsr(const cppbor::Array& keysToSign,

    auto& udsPub = diceContents->back().pubKey;

    auto error = validateUdsCerts(*udsCerts->asMap(), udsPub);
    auto error = validateUdsCerts(*udsCerts, udsPub);
    if (!error.empty()) {
        return error;
    }

    auto csrPayload = verifyAndParseCoseSign1(signedData->asArray(), udsPub, {} /* aad */);
    if (!csrPayload) {
        return csrPayload.message();
    auto signedPayload = verifyAndParseCoseSign1(signedData, udsPub, {} /* aad */);
    if (!signedPayload) {
        return signedPayload.message();
    }

    auto parsedCsrPayload = parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable,
                                                       challenge, isFactory);
    if (!parsedCsrPayload) {
        return parsedCsrPayload.message();
    auto payload = parseAndValidateAuthenticatedRequestSignedPayload(*signedPayload, challenge);
    if (!payload) {
        return payload.message();
    }

    return *diceContents;
    return payload;
}

ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSign,
                                                   const std::vector<uint8_t>& csr,
                                                   IRemotelyProvisionedComponent* provisionable,
                                                     const std::vector<uint8_t>& challenge) {
                                                   const std::vector<uint8_t>& challenge,
                                                   bool isFactory) {
    RpcHardwareInfo info;
    provisionable->getHardwareInfo(&info);
    if (info.versionNumber != 3) {
        return "Remotely provisioned component version (" + std::to_string(info.versionNumber) +
               ") does not match expected version (3).";
    }

    auto csrPayload = parseAndValidateAuthenticatedRequest(csr, challenge);
    if (!csrPayload) {
        return csrPayload.message();
    }

    return parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable, isFactory);
}

ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/true);
}

ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/false);
+10 −8
Original line number Diff line number Diff line
@@ -315,13 +315,12 @@ interface IRemotelyProvisionedComponent {
     *
     * @return the following CBOR Certificate Signing Request (Csr) serialized into a byte array:
     *
     * Csr = AuthenticatedMessage<CsrPayload>
     * Csr = AuthenticatedRequest<CsrPayload>
     *
     * CsrPayload = [                      ; CBOR Array defining the payload for Csr
     *     version: 1,                     ; The CsrPayload CDDL Schema version.
     *     version: 3,                     ; The CsrPayload CDDL Schema version.
     *     CertificateType,                ; The type of certificate being requested.
     *     DeviceInfo,                     ; Defined in DeviceInfo.aidl
     *     challenge: bstr .size (32..64), ; Provided by the method parameters
     *     KeysToSign,                     ; Provided by the method parameters
     * ]
     *
@@ -335,11 +334,14 @@ interface IRemotelyProvisionedComponent {
     *
     * KeysToSign = [ * PublicKey ]   ; Please see MacedPublicKey.aidl for the PublicKey definition.
     *
     * AuthenticatedMessage<T> = [
     *    version: 3,              ; The AuthenticatedMessage CDDL Schema version.
     * AuthenticatedRequest<T> = [
     *     version: 1,              ; The AuthenticatedRequest CDDL Schema version.
     *     UdsCerts,
     *     DiceCertChain,
     *    SignedData<T>,
     *     SignedData<[
     *         challenge: bstr .size (32..64), ; Provided by the method parameters
     *         bstr .cbor T,
     *     ]>,
     * ]
     *
     * ; COSE_Sign1 (untagged)