Loading src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java +48 −3 Original line number Diff line number Diff line Loading @@ -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()); Loading @@ -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()); Loading tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java +69 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -859,6 +861,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() { Loading Loading @@ -1125,4 +1186,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; } } Loading
src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java +48 −3 Original line number Diff line number Diff line Loading @@ -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()); Loading @@ -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()); Loading
tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java +69 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -859,6 +861,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() { Loading Loading @@ -1125,4 +1186,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; } }