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

Commit e856b147 authored by Hunter Knepshield's avatar Hunter Knepshield Committed by android-build-merger
Browse files

Additional SGP.22 v2.1+ BPP validity checking.

am: 079aada1

Change-Id: I561cc3f53360e72c62bc84cb66d5df0ef3b54ac3
parents 49e51658 079aada1
Loading
Loading
Loading
Loading
+48 −3
Original line number Diff line number Diff line
@@ -752,17 +752,62 @@ public class EuiccCard extends UiccCard {
        sendApdu(
                newRequestProvider((RequestBuilder requestBuilder) -> {
                    Asn1Node bppNode = new Asn1Decoder(boundProfilePackage).nextNode();
                    int actualLength = bppNode.getDataLength();
                    int segmentedLength = 0;
                    // initialiseSecureChannelRequest (ES8+.InitialiseSecureChannel)
                    Asn1Node initialiseSecureChannelRequest = bppNode.getChild(
                            Tags.TAG_INITIALISE_SECURE_CHANNEL);
                    segmentedLength += initialiseSecureChannelRequest.getEncodedLength();
                    // firstSequenceOf87 (ES8+.ConfigureISDP)
                    Asn1Node firstSequenceOf87 = bppNode.getChild(Tags.TAG_CTX_COMP_0);
                    segmentedLength += firstSequenceOf87.getEncodedLength();
                    // sequenceOf88 (ES8+.StoreMetadata)
                    Asn1Node sequenceOf88 = bppNode.getChild(Tags.TAG_CTX_COMP_1);
                    List<Asn1Node> metaDataSeqs = sequenceOf88.getChildren(Tags.TAG_CTX_8);
                    // sequenceOf86 (ES8+.LoadProfileElements #1)
                    segmentedLength += sequenceOf88.getEncodedLength();
                    // secondSequenceOf87 (ES8+.ReplaceSessionKeys), optional
                    Asn1Node secondSequenceOf87 = null;
                    if (bppNode.hasChild(Tags.TAG_CTX_COMP_2)) {
                        secondSequenceOf87 = bppNode.getChild(Tags.TAG_CTX_COMP_2);
                        segmentedLength += secondSequenceOf87.getEncodedLength();
                    }
                    // sequenceOf86 (ES8+.LoadProfileElements)
                    Asn1Node sequenceOf86 = bppNode.getChild(Tags.TAG_CTX_COMP_3);
                    List<Asn1Node> elementSeqs = sequenceOf86.getChildren(Tags.TAG_CTX_6);
                    segmentedLength += sequenceOf86.getEncodedLength();

                    if (mSpecVersion.compareTo(SGP22_V_2_1) >= 0) {
                        // Per SGP.22 v2.1+ section 2.5.5, it's the LPA's job to "segment" the BPP
                        // before sending it to the eUICC. This check was only instituted in SGP.22
                        // v2.1 and higher. SGP.22 v2.0 doesn't mention this "segmentation" process
                        // at all, or what the LPA should do in the case of unrecognized or missing
                        // tags. Per section 3.1.3.3: "If the LPAd is unable to perform the
                        // segmentation (e.g., because of an error in the BPP structure), ... the
                        // LPAd SHALL perform the Sub-procedure "Profile Download and installation -
                        // Download rejection" with reason code 'Load BPP execution error'." This
                        // implies that if we detect an invalid BPP, we should short-circuit before
                        // sending anything to the eUICC. There are two cases to account for:
                        if (elementSeqs == null || elementSeqs.isEmpty()) {
                            // 1. The BPP is missing a required tag. Upon calling bppNode.getChild,
                            // an exception will occur if the expected tag is missing, though we
                            // should make sure that the sequences are non-empty when appropriate as
                            // well. A profile with no profile elements is invalid. This is
                            // explicitly tested by SGP.23 case 4.4.25.2.1_03.
                            throw new EuiccCardException("No profile elements in BPP");
                        } else if (actualLength != segmentedLength) {
                            // 2. The BPP came with extraneous tags other than what the spec
                            // mandates. We keep track of the total length of the BPP and compare it
                            // to the length of the segments we care about. If they're different,
                            // we'll throw an exception to indicate this. This is explicitly tested
                            // by SGP.23 case 4.4.25.2.1_05.
                            throw new EuiccCardException(
                                    "Actual BPP length ("
                                            + actualLength
                                            + ") does not match segmented length ("
                                            + segmentedLength
                                            + "), this must be due to a malformed BPP");
                        }
                    }

                    requestBuilder.addStoreData(bppNode.getHeadAsHex()
                            + initialiseSecureChannelRequest.toHex());
@@ -775,8 +820,8 @@ public class EuiccCard extends UiccCard {
                        requestBuilder.addStoreData(metaDataSeqs.get(i).toHex());
                    }

                    if (bppNode.hasChild(Tags.TAG_CTX_COMP_2)) {
                        requestBuilder.addStoreData(bppNode.getChild(Tags.TAG_CTX_COMP_2).toHex());
                    if (secondSequenceOf87 != null) {
                        requestBuilder.addStoreData(secondSequenceOf87.toHex());
                    }

                    requestBuilder.addStoreData(sequenceOf86.getHeadAsHex());
+69 −0
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -858,6 +860,65 @@ public class EuiccCardTest extends TelephonyTest {
        verifyStoreData(channel, "8803040506"); // ES8+.StoreMetadata
    }

    @Test
    public void testLoadBoundProfilePackage_NoProfileElements() {
        int channel = mockLogicalChannelResponses_sgp22v210();

        ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
        mEuiccCard.loadBoundProfilePackage(
                Asn1Node.newBuilder(0xBF36)
                        .addChild(Asn1Node.newBuilder(0xBF23))
                        .addChild(Asn1Node.newBuilder(0xA0)
                                .addChildAsBytes(0x87, new byte[] {1, 2, 3}))
                        .addChild(Asn1Node.newBuilder(0xA1)
                                .addChildAsBytes(0x88, new byte[] {4, 5, 6}))
                        .addChild(Asn1Node.newBuilder(0xA2))
                        // No children
                        .addChild(Asn1Node.newBuilder(0xA3))
                        .build().toBytes(),
                resultCaptor, mHandler);
        resultCaptor.await();

        EuiccCardException e = (EuiccCardException) resultCaptor.exception;
        assertEquals("No profile elements in BPP", e.getCause().getMessage());
        verify(mMockCi, never())
                .iccTransmitApduLogicalChannel(
                        eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
                        any());
    }

    @Test
    public void testLoadBoundProfilePackage_UnrecognizedTag() {
        int channel = mockLogicalChannelResponses_sgp22v210();

        ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
        mEuiccCard.loadBoundProfilePackage(
                Asn1Node.newBuilder(0xBF36)
                        .addChild(Asn1Node.newBuilder(0xBF23))
                        .addChild(Asn1Node.newBuilder(0xA0)
                                .addChildAsBytes(0x87, new byte[] {1, 2, 3}))
                        .addChild(Asn1Node.newBuilder(0xA1)
                                .addChildAsBytes(0x88, new byte[] {4, 5, 6}))
                        .addChild(Asn1Node.newBuilder(0xA2))
                        .addChild(Asn1Node.newBuilder(0xA3)
                                .addChildAsBytes(0x86, new byte[] {7, 8, 9})
                                .addChildAsBytes(0x86, new byte[] {0xA, 0xB, 0xC}))
                        // Unrecognized tag
                        .addChild(Asn1Node.newBuilder(0xA4))
                        .build().toBytes(),
                resultCaptor, mHandler);
        resultCaptor.await();

        EuiccCardException e = (EuiccCardException) resultCaptor.exception;
        assertEquals(
                "Actual BPP length (33) does not match segmented length (31), this must be due to a"
                        + " malformed BPP",
                e.getCause().getMessage());
        verify(mMockCi, never())
                .iccTransmitApduLogicalChannel(
                        eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
                        any());
    }

    @Test
    public void testCancelSession() {
@@ -1124,4 +1185,12 @@ public class EuiccCardTest extends TelephonyTest {
        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
        return channel;
    }

    private int mockLogicalChannelResponses_sgp22v210(Object... responses) {
        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
                "E00582030201009000");
        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, responses);
        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
        return channel;
    }
}