From d1f136f8e9411011afd5da84c967c720e11a0dc3 Mon Sep 17 00:00:00 2001 From: waner Date: Mon, 11 May 2026 16:52:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=86=E9=92=A5=E6=81=A2=E5=A4=8D=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pcie/model/MasterKeyRecoveryRequest.java | 61 ++++++++++ .../pcie/model/MasterKeyRecoveryResult.java | 23 ++++ .../pcie/service/JnaPcieCryptoService.java | 111 ++++++++++++++++-- .../pcie/service/MockPcieCryptoService.java | 24 ++++ .../pcie/service/PcieCryptoService.java | 7 ++ .../tms/modules/mk/service/LmkService.java | 2 +- .../mk/service/impl/LmkServiceImpl.java | 36 +++--- .../service/JnaPcieCryptoServiceTest.java | 78 ++++++++++++ .../mk/controller/LmkControllerTest.java | 45 +++++++ .../modules/mk/service/LmkServiceTest.java | 54 ++++----- 10 files changed, 387 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryRequest.java create mode 100644 src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryResult.java diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryRequest.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryRequest.java new file mode 100644 index 0000000..7301ec8 --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryRequest.java @@ -0,0 +1,61 @@ +package com.cisd.tms.integration.crypto.pcie.model; + +import java.util.List; + +public class MasterKeyRecoveryRequest { + + private byte[] oldPin; + private byte[] newPin; + private byte[] lmkComponent; + private byte[] authIkComponent; + private byte[] deviceIkComponent; + private List userKeyRequests; + + public byte[] getOldPin() { + return oldPin; + } + + public void setOldPin(byte[] oldPin) { + this.oldPin = oldPin; + } + + public byte[] getNewPin() { + return newPin; + } + + public void setNewPin(byte[] newPin) { + this.newPin = newPin; + } + + public byte[] getLmkComponent() { + return lmkComponent; + } + + public void setLmkComponent(byte[] lmkComponent) { + this.lmkComponent = lmkComponent; + } + + public byte[] getAuthIkComponent() { + return authIkComponent; + } + + public void setAuthIkComponent(byte[] authIkComponent) { + this.authIkComponent = authIkComponent; + } + + public byte[] getDeviceIkComponent() { + return deviceIkComponent; + } + + public void setDeviceIkComponent(byte[] deviceIkComponent) { + this.deviceIkComponent = deviceIkComponent; + } + + public List getUserKeyRequests() { + return userKeyRequests; + } + + public void setUserKeyRequests(List userKeyRequests) { + this.userKeyRequests = userKeyRequests; + } +} diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryResult.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryResult.java new file mode 100644 index 0000000..6a4d804 --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/MasterKeyRecoveryResult.java @@ -0,0 +1,23 @@ +package com.cisd.tms.integration.crypto.pcie.model; + +public class MasterKeyRecoveryResult { + + private byte[] lmkSeedMac; + private DeviceStatusResult deviceStatus; + + public byte[] getLmkSeedMac() { + return lmkSeedMac; + } + + public void setLmkSeedMac(byte[] lmkSeedMac) { + this.lmkSeedMac = lmkSeedMac; + } + + public DeviceStatusResult getDeviceStatus() { + return deviceStatus; + } + + public void setDeviceStatus(DeviceStatusResult deviceStatus) { + this.deviceStatus = deviceStatus; + } +} diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java index 9a9d971..e5e1d5e 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java @@ -12,6 +12,7 @@ import com.sun.jna.ptr.PointerByReference; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; import java.util.function.Supplier; public class JnaPcieCryptoService implements PcieCryptoService { @@ -177,16 +178,7 @@ public class JnaPcieCryptoService implements PcieCryptoService { @Override public DeviceStatusResult getDeviceStatus() { return sessionTemplate.withSession("SDFE_DeviceStatusGet", (lib, deviceHandle, sessionHandle) -> { - SdfeDeviceStatus status = new SdfeDeviceStatus(); - sessionTemplate.ensureSuccess("SDFE_DeviceStatusGet", lib.SDFE_DeviceStatusGet(sessionHandle, status)); - status.read(); - int size = status.size(); - - DeviceStatusResult result = new DeviceStatusResult(); - result.setLength(size); - result.setStatusData(status.getPointer().getByteArray(0, size)); - PcieDeviceStatusFormatter.enrich(result, status); - return result; + return readDeviceStatus(lib, sessionHandle); }); } @@ -1928,6 +1920,57 @@ public class JnaPcieCryptoService implements PcieCryptoService { }); } + @Override + public MasterKeyRecoveryResult recoverMasterKeyMaterial(MasterKeyRecoveryRequest request) { + MasterKeyRecoveryRequest req = requireRequest("request", request); + byte[] oldPin = requireNonEmptyBytes("oldPin", req.getOldPin()); + byte[] newPin = requireNonEmptyBytes("newPin", req.getNewPin()); + SdfeLmkComponent lmkComponent = toSdfeLmkComponent("lmkComponent", req.getLmkComponent()); + SdfeIkComponent authIkComponent = toSdfeIkComponent("authIkComponent", req.getAuthIkComponent()); + SdfeIkComponent deviceIkComponent = toSdfeIkComponent("deviceIkComponent", req.getDeviceIkComponent()); + List userKeyRequests = requireUserKeyRequests(req.getUserKeyRequests()); + + try { + return sessionTemplate.withSession("SDFE_RecoverMasterKeyMaterial", (lib, deviceHandle, sessionHandle) -> { + sessionTemplate.ensureSuccess( + "SDFE_InitIdentify", + lib.SDFE_InitIdentify(sessionHandle, oldPin, oldPin.length, newPin, newPin.length) + ); + sessionTemplate.ensureSuccess("SDFE_RecoverLMK_EX", lib.SDFE_RecoverLMK_EX(sessionHandle, lmkComponent)); + sessionTemplate.ensureSuccess("SDFE_LoadLMK", lib.SDFE_LoadLMK(sessionHandle)); + sessionTemplate.ensureSuccess( + "SDFE_RecoverIK_EX", + lib.SDFE_RecoverIK_EX(sessionHandle, IK_KEY_TYPE_AUTH, authIkComponent) + ); + sessionTemplate.ensureSuccess( + "SDFE_RecoverIK_EX", + lib.SDFE_RecoverIK_EX(sessionHandle, IK_KEY_TYPE_DEVICE, deviceIkComponent) + ); + DeviceStatusResult deviceStatus = readDeviceStatus(lib, sessionHandle); + for (RecoverUserKeyRequest userKeyRequest : userKeyRequests) { + recoverUserKeyInCurrentSession(lib, sessionHandle, userKeyRequest); + } + sessionTemplate.ensureSuccess("SDFE_CheckLMK", lib.SDFE_CheckLMK(sessionHandle)); + byte[] mac = new byte[LMK_SEED_MAC_LENGTH]; + sessionTemplate.ensureSuccess("SDFE_ExportLMKSeedMAC", lib.SDFE_ExportLMKSeedMAC(sessionHandle, mac)); + + MasterKeyRecoveryResult result = new MasterKeyRecoveryResult(); + result.setDeviceStatus(deviceStatus); + result.setLmkSeedMac(Arrays.copyOf(mac, mac.length)); + return result; + }); + } finally { + wipe(oldPin); + wipe(newPin); + wipe(req.getLmkComponent()); + wipe(req.getAuthIkComponent()); + wipe(req.getDeviceIkComponent()); + for (RecoverUserKeyRequest userKeyRequest : userKeyRequests) { + wipe(userKeyRequest.getEncryptedKey()); + } + } + } + @Override public void generateIk(int keyType) { int safeKeyType = requireNonNegative("keyType", keyType); @@ -2057,6 +2100,54 @@ public class JnaPcieCryptoService implements PcieCryptoService { }); } + private DeviceStatusResult readDeviceStatus(PcieNativeLibrary lib, Pointer sessionHandle) { + SdfeDeviceStatus status = new SdfeDeviceStatus(); + sessionTemplate.ensureSuccess("SDFE_DeviceStatusGet", lib.SDFE_DeviceStatusGet(sessionHandle, status)); + status.read(); + int size = status.size(); + + DeviceStatusResult result = new DeviceStatusResult(); + result.setLength(size); + result.setStatusData(status.getPointer().getByteArray(0, size)); + PcieDeviceStatusFormatter.enrich(result, status); + return result; + } + + private static List requireUserKeyRequests(List requests) { + if (requests == null || requests.isEmpty()) { + throw new IllegalArgumentException("userKeyRequests must not be empty"); + } + for (RecoverUserKeyRequest request : requests) { + RecoverUserKeyRequest req = requireRequest("userKeyRequest", request); + requireNonNegative("keyIndex", req.getKeyIndex()); + requireUserKeyType(req.getKeyType()); + requireNonEmptyBytes("encryptedKey", req.getEncryptedKey()); + int storeFlag = req.getStoreFlag(); + if (storeFlag != 0 && storeFlag != 1) { + throw new IllegalArgumentException("storeFlag必须为0或1"); + } + } + return List.copyOf(requests); + } + + private void recoverUserKeyInCurrentSession( + PcieNativeLibrary lib, + Pointer sessionHandle, + RecoverUserKeyRequest request + ) { + sessionTemplate.ensureSuccess( + "SDFE_RecoverUserKey", + lib.SDFE_RecoverUserKey( + sessionHandle, + request.getKeyIndex(), + request.getKeyType(), + request.getEncryptedKey(), + request.getEncryptedKey().length, + (byte) request.getStoreFlag() + ) + ); + } + @FunctionalInterface private interface KeyHandleCallback { T apply(Pointer keyHandle); diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java index d8592f4..9b65490 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java @@ -63,6 +63,8 @@ import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest; import com.cisd.tms.integration.crypto.pcie.model.KekComponentRequest; import com.cisd.tms.integration.crypto.pcie.model.KeyStatusResult; import com.cisd.tms.integration.crypto.pcie.model.MacCalcRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryResult; import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest; import com.cisd.tms.integration.crypto.pcie.model.RecoverKekRequest; import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest; @@ -904,6 +906,28 @@ public class MockPcieCryptoService implements PcieCryptoService { requireNonEmptyBytes("lmkComponent", lmkComponent); } + @Override + public MasterKeyRecoveryResult recoverMasterKeyMaterial(MasterKeyRecoveryRequest request) { + MasterKeyRecoveryRequest req = requireRequest("request", request); + initIdentify(req.getOldPin(), req.getNewPin()); + recoverLmkEx(req.getLmkComponent()); + loadLmk(); + recoverIkComponent(2, req.getAuthIkComponent()); + recoverIkComponent(1, req.getDeviceIkComponent()); + if (req.getUserKeyRequests() == null || req.getUserKeyRequests().isEmpty()) { + throw new IllegalArgumentException("userKeyRequests must not be empty"); + } + req.getUserKeyRequests().forEach(this::recoverUserKey); + if (!checkLmk()) { + throw new IllegalStateException("主密钥恢复后校验失败"); + } + + MasterKeyRecoveryResult result = new MasterKeyRecoveryResult(); + result.setDeviceStatus(getDeviceStatus()); + result.setLmkSeedMac(exportLmkSeedMac()); + return result; + } + @Override public void generateIk(int keyType) { requireNonNegative("keyType", keyType); diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java index aaff2b6..24288a3 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java @@ -53,6 +53,8 @@ import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest; import com.cisd.tms.integration.crypto.pcie.model.KekComponentRequest; import com.cisd.tms.integration.crypto.pcie.model.KeyStatusResult; import com.cisd.tms.integration.crypto.pcie.model.MacCalcRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryResult; import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest; import com.cisd.tms.integration.crypto.pcie.model.RecoverKekRequest; import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest; @@ -564,6 +566,11 @@ public interface PcieCryptoService { */ void recoverLmkEx(byte[] lmkComponent); + /** + * 在单个设备 session 中恢复 LMK、IK 和用户密钥,并返回恢复后的 LMK seed MAC。 + */ + MasterKeyRecoveryResult recoverMasterKeyMaterial(MasterKeyRecoveryRequest request); + //----------------------------------------IK 生命周期类接口。------------------------------------------ /** diff --git a/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java b/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java index ba9b3cf..c3bbb6f 100644 --- a/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java +++ b/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java @@ -27,7 +27,7 @@ public interface LmkService { String getComponent(int uid); - void recoverKeyPackets(List packets); + MasterKeyStateResult recoverKeyPackets(List packets); void recoverLmkEx(String lmkMacHex, List components); diff --git a/src/main/java/com/cisd/tms/modules/mk/service/impl/LmkServiceImpl.java b/src/main/java/com/cisd/tms/modules/mk/service/impl/LmkServiceImpl.java index ca5a1e8..5dc56d7 100644 --- a/src/main/java/com/cisd/tms/modules/mk/service/impl/LmkServiceImpl.java +++ b/src/main/java/com/cisd/tms/modules/mk/service/impl/LmkServiceImpl.java @@ -170,7 +170,7 @@ public class LmkServiceImpl implements LmkService { } @Override - public void recoverKeyPackets(List packets) { + public MasterKeyStateResult recoverKeyPackets(List packets) { // 进入恢复流程后,旧的备份轮次缓存不再可信,避免和恢复后的新密钥体系混用。 clearCachedBackupRound(); // 调用方可以任意顺序传入两份包,这里统一按 packetIndex 排序并校验必须是 1、2 两包。 @@ -178,21 +178,25 @@ public class LmkServiceImpl implements LmkService { byte[] oldPin = decodeConfiguredPin("tms.mk.init-identify.old-pin-base64", masterKeyInitProperties.getOldPinBase64()); byte[] newPin = decodeConfiguredPin("tms.mk.init-identify.new-pin-base64", masterKeyInitProperties.getNewPinBase64()); - // 恢复顺序不能打乱: - // 1. InitIdentify 建立恢复所需的设备认证上下文 - // 2. 先恢复 LMK 并 LoadLMK,后续 IK 和用户密钥都依赖 LMK 环境 - // 3. 再恢复两把 IK - // 4. 最后恢复用户密钥 - pcieCryptoService.initIdentify(oldPin, newPin); - pcieCryptoService.recoverLmkEx(Hex.decode(composeLmkHex(orderedPackets))); - pcieCryptoService.loadLmk(); - pcieCryptoService.recoverIkComponent(IKEnums.KEY_TYPE_AUTH.getCode(), Hex.decode(composeIkHex(orderedPackets, true))); - pcieCryptoService.recoverIkComponent(IKEnums.KEY_TYPE_DEVICE.getCode(), Hex.decode(composeIkHex(orderedPackets, false))); - //务必先查询是否是就绪状态 - DeviceStatusResult result = pcieCryptoService.getDeviceStatus(); - log.info("device status:{}", result.getFsmState()); - pcieCryptoService.recoverUserKey(toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_SIGN)); - pcieCryptoService.recoverUserKey(toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_ENC)); + MasterKeyRecoveryRequest recoveryRequest = new MasterKeyRecoveryRequest(); + recoveryRequest.setOldPin(oldPin); + recoveryRequest.setNewPin(newPin); + recoveryRequest.setLmkComponent(Hex.decode(composeLmkHex(orderedPackets))); + recoveryRequest.setAuthIkComponent(Hex.decode(composeIkHex(orderedPackets, true))); + recoveryRequest.setDeviceIkComponent(Hex.decode(composeIkHex(orderedPackets, false))); + recoveryRequest.setUserKeyRequests(List.of( + toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_SIGN), + toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_ENC) + )); + MasterKeyRecoveryResult recoveryResult = pcieCryptoService.recoverMasterKeyMaterial(recoveryRequest); + DeviceStatusResult deviceStatus = recoveryResult.getDeviceStatus(); + if (deviceStatus != null) { + log.info("device status:{}", deviceStatus.getFsmState()); + } + MasterKeyStateResult result = new MasterKeyStateResult(); + result.setStatus(true); + result.setSeedMac(Hex.toHexString(recoveryResult.getLmkSeedMac())); + return result; } @Override diff --git a/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java b/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java index 0997ea6..2afd9f9 100644 --- a/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java +++ b/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java @@ -7,7 +7,9 @@ import com.cisd.tms.integration.crypto.pcie.jna.EccRefPublicKey; import com.cisd.tms.integration.crypto.pcie.jna.EccSignature; import com.cisd.tms.integration.crypto.pcie.jna.PcieNativeLibrary; import com.cisd.tms.integration.crypto.pcie.jna.SdfFirmwareInfo; +import com.cisd.tms.integration.crypto.pcie.jna.SdfeDeviceStatus; import com.cisd.tms.integration.crypto.pcie.jna.SdfeIkComponent; +import com.cisd.tms.integration.crypto.pcie.jna.SdfeLmkComponent; import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult; import com.cisd.tms.integration.crypto.pcie.model.DigestRequest; import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest; @@ -23,6 +25,9 @@ import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptResult; import com.cisd.tms.integration.crypto.pcie.model.FirmwareInfoResult; import com.cisd.tms.integration.crypto.pcie.model.IkSignRequest; import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryResult; +import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest; import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest; import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest; @@ -33,6 +38,7 @@ import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -134,6 +140,72 @@ class JnaPcieCryptoServiceTest { Mockito.verify(sessionTemplate).withSession(Mockito.eq("SDFE_RecoverIK_EX"), Mockito.any()); } + @Test + void shouldRecoverMasterKeyMaterialWithinSingleSessionAndReturnSeedMac() { + PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class); + CryptoCardProperties properties = new CryptoCardProperties(); + JnaPcieCryptoService service = new JnaPcieCryptoService(sessionTemplate, properties); + Pointer session = Pointer.createConstant(2); + byte[] seedMac = new byte[] {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; + + MasterKeyRecoveryRequest request = new MasterKeyRecoveryRequest(); + request.setOldPin("12345678".getBytes(StandardCharsets.UTF_8)); + request.setNewPin("87654321".getBytes(StandardCharsets.UTF_8)); + request.setLmkComponent(filledBytes(new SdfeLmkComponent().size(), 0x21)); + request.setAuthIkComponent(filledBytes(new SdfeIkComponent().size(), 0x31)); + request.setDeviceIkComponent(filledBytes(new SdfeIkComponent().size(), 0x41)); + RecoverUserKeyRequest signKey = new RecoverUserKeyRequest(); + signKey.setKeyIndex(1); + signKey.setKeyType(0); + signKey.setEncryptedKey(new byte[] {0x01, 0x02, 0x03}); + RecoverUserKeyRequest encKey = new RecoverUserKeyRequest(); + encKey.setKeyIndex(1); + encKey.setKeyType(1); + encKey.setEncryptedKey(new byte[] {0x11, 0x12, 0x13}); + request.setUserKeyRequests(List.of(signKey, encKey)); + + AtomicReference libRef = new AtomicReference<>(); + Mockito.when(sessionTemplate.withSession(Mockito.eq("SDFE_RecoverMasterKeyMaterial"), Mockito.any())) + .thenAnswer(invocation -> { + @SuppressWarnings("unchecked") + PcieSessionTemplate.SessionCallback callback = + (PcieSessionTemplate.SessionCallback) invocation.getArgument(1); + PcieNativeLibrary lib = Mockito.mock(PcieNativeLibrary.class); + libRef.set(lib); + Mockito.when(lib.SDFE_InitIdentify(Mockito.eq(session), Mockito.any(), Mockito.anyInt(), Mockito.any(), Mockito.anyInt())).thenReturn(0); + Mockito.when(lib.SDFE_RecoverLMK_EX(Mockito.eq(session), Mockito.any(SdfeLmkComponent.class))).thenReturn(0); + Mockito.when(lib.SDFE_LoadLMK(Mockito.eq(session))).thenReturn(0); + Mockito.when(lib.SDFE_RecoverIK_EX(Mockito.eq(session), Mockito.eq(2), Mockito.any(SdfeIkComponent.class))).thenReturn(0); + Mockito.when(lib.SDFE_RecoverIK_EX(Mockito.eq(session), Mockito.eq(1), Mockito.any(SdfeIkComponent.class))).thenReturn(0); + Mockito.when(lib.SDFE_DeviceStatusGet(Mockito.eq(session), Mockito.any(SdfeDeviceStatus.class))).thenReturn(0); + Mockito.when(lib.SDFE_RecoverUserKey(Mockito.eq(session), Mockito.eq(1), Mockito.anyInt(), Mockito.any(), Mockito.anyInt(), Mockito.anyByte())).thenReturn(0); + Mockito.when(lib.SDFE_CheckLMK(Mockito.eq(session))).thenReturn(0); + Mockito.when(lib.SDFE_ExportLMKSeedMAC(Mockito.eq(session), Mockito.any())).thenAnswer(exportInvocation -> { + byte[] out = exportInvocation.getArgument(1); + System.arraycopy(seedMac, 0, out, 0, seedMac.length); + return 0; + }); + return callback.apply(lib, Pointer.createConstant(1), session); + }); + + MasterKeyRecoveryResult result = service.recoverMasterKeyMaterial(request); + + Assertions.assertArrayEquals(seedMac, result.getLmkSeedMac()); + Assertions.assertNotNull(result.getDeviceStatus()); + Mockito.verify(sessionTemplate).withSession(Mockito.eq("SDFE_RecoverMasterKeyMaterial"), Mockito.any()); + PcieNativeLibrary lib = libRef.get(); + InOrder inOrder = Mockito.inOrder(lib); + inOrder.verify(lib).SDFE_InitIdentify(Mockito.eq(session), Mockito.any(), Mockito.anyInt(), Mockito.any(), Mockito.anyInt()); + inOrder.verify(lib).SDFE_RecoverLMK_EX(Mockito.eq(session), Mockito.any(SdfeLmkComponent.class)); + inOrder.verify(lib).SDFE_LoadLMK(Mockito.eq(session)); + inOrder.verify(lib).SDFE_RecoverIK_EX(Mockito.eq(session), Mockito.eq(2), Mockito.any(SdfeIkComponent.class)); + inOrder.verify(lib).SDFE_RecoverIK_EX(Mockito.eq(session), Mockito.eq(1), Mockito.any(SdfeIkComponent.class)); + inOrder.verify(lib).SDFE_DeviceStatusGet(Mockito.eq(session), Mockito.any(SdfeDeviceStatus.class)); + inOrder.verify(lib, Mockito.times(2)).SDFE_RecoverUserKey(Mockito.eq(session), Mockito.eq(1), Mockito.anyInt(), Mockito.any(), Mockito.anyInt(), Mockito.anyByte()); + inOrder.verify(lib).SDFE_CheckLMK(Mockito.eq(session)); + inOrder.verify(lib).SDFE_ExportLMKSeedMAC(Mockito.eq(session), Mockito.any()); + } + @Test void shouldExportIkPublicKeyViaSdfeExportIkPublicKey() { PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class); @@ -875,4 +947,10 @@ class JnaPcieCryptoServiceTest { Assertions.assertArrayEquals(new byte[] {0x21, 0x22, 0x23}, verifyCaptor.getValue().getData()); Assertions.assertArrayEquals(new byte[] {0x31, 0x32}, verifyCaptor.getValue().getSignature()); } + + private static byte[] filledBytes(int length, int value) { + byte[] bytes = new byte[length]; + Arrays.fill(bytes, (byte) value); + return bytes; + } } diff --git a/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java b/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java index c4d14fd..c0b7fc5 100644 --- a/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java +++ b/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java @@ -1,6 +1,7 @@ package com.cisd.tms.modules.mk.controller; import com.cisd.tms.common.exception.GlobalExceptionHandler; +import com.cisd.tms.modules.mk.dto.MasterKeyStateResult; import com.cisd.tms.modules.mk.service.LmkService; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -14,6 +15,50 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. class LmkControllerTest { + @Test + void shouldReturnRecoveredMasterKeySeedMac() throws Exception { + LmkService lmkService = Mockito.mock(LmkService.class); + MasterKeyStateResult result = new MasterKeyStateResult(); + result.setStatus(true); + result.setSeedMac("1112131415161718"); + Mockito.when(lmkService.recoverKeyPackets(Mockito.any())).thenReturn(result); + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new LmkController(lmkService)) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + + mockMvc.perform(post("/api/v1/masterKey/recover") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "packets": [ + { + "packetIndex": 1, + "lmk": { + "lmkMac": "1112131415161718", + "components": { + "1": "010203040506", + "2": "0708090a0b0c" + } + } + }, + { + "packetIndex": 2, + "lmk": { + "lmkMac": "1112131415161718", + "components": { + "2": "0708090a0b0c", + "3": "131415161718" + } + } + } + ] + } + """)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.status").value(true)) + .andExpect(jsonPath("$.data.seedMac").value("1112131415161718")); + } + @Test void shouldRejectRecoverPacketsWithPacketIndexesOtherThanOneAndTwo() throws Exception { LmkService lmkService = Mockito.mock(LmkService.class); diff --git a/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java b/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java index 5569424..f81f471 100644 --- a/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java +++ b/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java @@ -10,6 +10,8 @@ import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest; import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest; import com.cisd.tms.integration.crypto.pcie.model.IkSignRequest; import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryRequest; +import com.cisd.tms.integration.crypto.pcie.model.MasterKeyRecoveryResult; import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest; import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService; import com.cisd.tms.modules.mk.config.MasterKeyInitProperties; @@ -68,7 +70,7 @@ class LmkServiceTest { } @Test - void shouldDestroyMasterKeyInOriginalOrder() { + void shouldDestroyMasterKeyAfterDeletingUserKey() { PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE="); LmkService service = new LmkServiceImpl(pcieCryptoService, properties); @@ -76,10 +78,10 @@ class LmkServiceTest { service.destroyMasterKey(); InOrder inOrder = Mockito.inOrder(pcieCryptoService); + inOrder.verify(pcieCryptoService).deleteUserKey(1); inOrder.verify(pcieCryptoService).destroyLmk(); inOrder.verify(pcieCryptoService).destroyIk(2); inOrder.verify(pcieCryptoService).destroyIk(1); - inOrder.verify(pcieCryptoService).deleteUserKey(1); } @Test @@ -299,40 +301,38 @@ class LmkServiceTest { void shouldRecoverAllKeyPacketsInOrder() { PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE="); - DeviceStatusResult deviceStatus = new DeviceStatusResult(); - deviceStatus.setFsmState(0); - Mockito.when(pcieCryptoService.getDeviceStatus()).thenReturn(deviceStatus); + MasterKeyRecoveryResult recoveryResult = new MasterKeyRecoveryResult(); + recoveryResult.setLmkSeedMac(hex("1112131415161718")); + recoveryResult.setDeviceStatus(new DeviceStatusResult()); + Mockito.when(pcieCryptoService.recoverMasterKeyMaterial(Mockito.any())).thenReturn(recoveryResult); LmkService service = new LmkServiceImpl(pcieCryptoService, properties); MasterKeyBackupPacket packet1 = recoverPacketFixture(1); MasterKeyBackupPacket packet2 = recoverPacketFixture(2); - service.recoverKeyPackets(List.of(packet1, packet2)); + MasterKeyStateResult result = service.recoverKeyPackets(List.of(packet1, packet2)); - ArgumentCaptor oldPinCaptor = ArgumentCaptor.forClass(byte[].class); - ArgumentCaptor newPinCaptor = ArgumentCaptor.forClass(byte[].class); - ArgumentCaptor lmkCaptor = ArgumentCaptor.forClass(byte[].class); - ArgumentCaptor ikAuthCaptor = ArgumentCaptor.forClass(byte[].class); - ArgumentCaptor ikDeviceCaptor = ArgumentCaptor.forClass(byte[].class); - ArgumentCaptor recoverCaptor = ArgumentCaptor.forClass(RecoverUserKeyRequest.class); - InOrder inOrder = Mockito.inOrder(pcieCryptoService); - inOrder.verify(pcieCryptoService).initIdentify(oldPinCaptor.capture(), newPinCaptor.capture()); - inOrder.verify(pcieCryptoService).recoverLmkEx(lmkCaptor.capture()); - inOrder.verify(pcieCryptoService).loadLmk(); - inOrder.verify(pcieCryptoService).recoverIkComponent(Mockito.eq(2), ikAuthCaptor.capture()); - inOrder.verify(pcieCryptoService).recoverIkComponent(Mockito.eq(1), ikDeviceCaptor.capture()); - inOrder.verify(pcieCryptoService).getDeviceStatus(); - inOrder.verify(pcieCryptoService, Mockito.times(2)).recoverUserKey(recoverCaptor.capture()); - Assertions.assertArrayEquals("12345678".getBytes(StandardCharsets.UTF_8), oldPinCaptor.getValue()); - Assertions.assertArrayEquals("87654321".getBytes(StandardCharsets.UTF_8), newPinCaptor.getValue()); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(MasterKeyRecoveryRequest.class); + Mockito.verify(pcieCryptoService).recoverMasterKeyMaterial(requestCaptor.capture()); + MasterKeyRecoveryRequest recoveryRequest = requestCaptor.getValue(); + Assertions.assertArrayEquals("12345678".getBytes(StandardCharsets.UTF_8), recoveryRequest.getOldPin()); + Assertions.assertArrayEquals("87654321".getBytes(StandardCharsets.UTF_8), recoveryRequest.getNewPin()); Assertions.assertArrayEquals(new byte[] { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 - }, lmkCaptor.getValue()); - Assertions.assertArrayEquals(hex("1112131415162122232425262728313233344142434445464748"), ikAuthCaptor.getValue()); - Assertions.assertArrayEquals(hex("2122232425263132333435363738515253546162636465666768"), ikDeviceCaptor.getValue()); - Assertions.assertArrayEquals(hex("010203040506"), recoverCaptor.getAllValues().get(0).getEncryptedKey()); - Assertions.assertArrayEquals(hex("111213141516"), recoverCaptor.getAllValues().get(1).getEncryptedKey()); + }, recoveryRequest.getLmkComponent()); + Assertions.assertArrayEquals(hex("1112131415162122232425262728313233344142434445464748"), recoveryRequest.getAuthIkComponent()); + Assertions.assertArrayEquals(hex("2122232425263132333435363738515253546162636465666768"), recoveryRequest.getDeviceIkComponent()); + Assertions.assertEquals(2, recoveryRequest.getUserKeyRequests().size()); + Assertions.assertArrayEquals(hex("010203040506"), recoveryRequest.getUserKeyRequests().get(0).getEncryptedKey()); + Assertions.assertArrayEquals(hex("111213141516"), recoveryRequest.getUserKeyRequests().get(1).getEncryptedKey()); + Assertions.assertTrue(result.isStatus()); + Assertions.assertEquals("1112131415161718", result.getSeedMac()); + Mockito.verify(pcieCryptoService, Mockito.never()).initIdentify(Mockito.any(), Mockito.any()); + Mockito.verify(pcieCryptoService, Mockito.never()).recoverLmkEx(Mockito.any()); + Mockito.verify(pcieCryptoService, Mockito.never()).loadLmk(); + Mockito.verify(pcieCryptoService, Mockito.never()).recoverIkComponent(Mockito.anyInt(), Mockito.any()); + Mockito.verify(pcieCryptoService, Mockito.never()).recoverUserKey(Mockito.any()); } @Test