Bladeren bron

- Added support to send signed sales tariffs (SECC side)
- Corrected the verification process of received (signed) sales tariffs (EVCC side)
- changed default value for XMLRepresentationOfMessages to false (SECC side)

Marc Mültin 9 jaren geleden
bovenliggende
commit
458babda11

+ 5 - 0
RISE-V2G-EVCC/src/main/java/org/eclipse/risev2g/evcc/session/V2GCommunicationSessionHandlerEVCC.java

@@ -138,6 +138,11 @@ public class V2GCommunicationSessionHandlerEVCC implements Observer {
 			getTransportLayerClient().addObserver(getV2gCommunicationSessionEVCC());
 			
 			getV2gCommunicationSessionEVCC().addObserver(this);
+			
+			// Set TLS security flag for communication session
+			boolean secureConn = (((Byte) getSecurity()).compareTo((Byte) GlobalValues.V2G_SECURITY_WITH_TLS.getByteValue()) == 0) ? true : false;
+			getV2gCommunicationSessionEVCC().setTlsConnection(secureConn);
+			
 			sendSupportedAppProtocolReq();
 		} else {
 			getLogger().fatal("Maximum number of SECCDiscoveryReq messages reached");

+ 39 - 4
RISE-V2G-EVCC/src/main/java/org/eclipse/risev2g/evcc/states/WaitForChargeParameterDiscoveryRes.java

@@ -28,6 +28,7 @@ import org.eclipse.risev2g.shared.v2gMessages.msgDef.ChargeProgressType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.DCEVSEChargeParameterType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.EVSEProcessingType;
+import org.eclipse.risev2g.shared.v2gMessages.msgDef.PaymentOptionType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleListType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleTupleType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.SignatureType;
@@ -77,9 +78,8 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
 					
 					SAScheduleListType saSchedules = (SAScheduleListType) chargeParameterDiscoveryRes.getSASchedules().getValue();
 					
-					// Verify each sales tariff with the mobility operator sub 2 certificate
-					if (saSchedules != null && !verifySalesTariffs(saSchedules, v2gMessageRes.getHeader().getSignature()))
-						return new TerminateSession("Verification of sales tariffs failed");
+					if (saSchedules != null && getCommSessionContext().getSelectedPaymentOption().equals(PaymentOptionType.CONTRACT))
+						verifySalesTariffs(saSchedules, v2gMessageRes.getHeader().getSignature());
 					
 					// Save the list of SASchedules (saves the time of reception as well)
 					getCommSessionContext().setSaSchedules(saSchedules);
@@ -111,8 +111,31 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
 		}
 	}
 	
-	
+	/**
+	 * Verifies each sales tariff given with the ChargeParameterDiscoveryRes message with the 
+	 * mobility operator sub 2 certificate.
+	 * 
+	 * @param saSchedules The SASchedule list which holds all PMaxSchedules and SalesTariffs
+	 * @param signature The signature for the sales tariffs
+	 * @return True, if the verification of the sales tariffs was successful, false otherwise
+	 */
 	private boolean verifySalesTariffs(SAScheduleListType saSchedules, SignatureType signature) {
+		 /* 
+		 * Some important requirements: 
+		 * 
+		 * 1. In case of PnC, and if a Tariff Table is used by the secondary actor, the secondary actor SHALL
+		 * sign the field SalesTariff of type SalesTariffType. In case of EIM, the secondary actor MAY sign
+		 * this field.
+		 * 
+		 * 2. If the EVCC treats the SalesTariff as invalid, it shall ignore the SalesTariff table, i.e. the
+		 * behaviour of the EVCC shall be the same as if no tariff tables were received. Furthermore, the
+		 * EVCC MAY close the connection. It then may reopen the connection again.
+		 */
+		
+		boolean salesTariffSignatureAvailable = (signature == null) ? false : true;
+		boolean ignoreSalesTariffs = (getCommSessionContext().isTlsConnection() && !salesTariffSignatureAvailable) ? true : false;
+		short ignoredSalesTariffs = 0;
+		
 		HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
 		List<SAScheduleTupleType> saScheduleTuples = saSchedules.getSAScheduleTuple();
 		int salesTariffCounter = 0;
@@ -121,6 +144,13 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
 			// verification regards only sales tariffs, not PMaxSchedules
 			if (saScheduleTuple.getSalesTariff() == null) continue;
 			
+			// Check if signature is given during TLS communication. If no signature is given, delete SalesTariff
+			if (ignoreSalesTariffs) {
+				ignoredSalesTariffs++;
+				saScheduleTuple.setSalesTariff(null);
+				continue;
+			}
+			
 			salesTariffCounter++;
 			
 			verifyXMLSigRefElements.put(
@@ -142,6 +172,11 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
 			}
 		}
 		
+		if (ignoredSalesTariffs > 0) {
+			getLogger().info("Sales tariffs could not be verified because of missing signature and will therefore be ignored");
+			return false;
+		}
+		
 		return true;
 	}
 }

+ 1 - 1
RISE-V2G-SECC/SECCConfig.properties

@@ -72,5 +72,5 @@ PrivateEnvironment = false
 # - false
 # If this value is set to 'true', the EXICodec will print each message's XML representation (for debugging purposes) 
 # If no correct value is provided here, 'false' will be chosen
-XMLRepresentationOfMessages = true
+XMLRepresentationOfMessages = false
 

+ 102 - 25
RISE-V2G-SECC/src/main/java/org/eclipse/risev2g/secc/backend/DummyBackendInterface.java

@@ -12,6 +12,7 @@ package org.eclipse.risev2g.secc.backend;
 
 import java.security.KeyStore;
 import java.security.interfaces.ECPrivateKey;
+import java.util.HashMap;
 
 import javax.xml.bind.JAXBElement;
 import javax.xml.namespace.QName;
@@ -19,6 +20,7 @@ import javax.xml.namespace.QName;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.eclipse.risev2g.secc.session.V2GCommunicationSessionSECC;
+import org.eclipse.risev2g.secc.states.ServerState;
 import org.eclipse.risev2g.shared.enumerations.GlobalValues;
 import org.eclipse.risev2g.shared.utils.SecurityUtils;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
@@ -28,6 +30,8 @@ import org.eclipse.risev2g.shared.v2gMessages.msgDef.PhysicalValueType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.RelativeTimeIntervalType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleListType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleTupleType;
+import org.eclipse.risev2g.shared.v2gMessages.msgDef.SalesTariffEntryType;
+import org.eclipse.risev2g.shared.v2gMessages.msgDef.SalesTariffType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.UnitSymbolType;
 
 public class DummyBackendInterface implements IBackendInterface {
@@ -40,50 +44,123 @@ public class DummyBackendInterface implements IBackendInterface {
 	}
 	
 	@Override
-	public SAScheduleListType getSAScheduleList() {
+	public SAScheduleListType getSAScheduleList(int maxEntriesSAScheduleTuple, HashMap<String, byte[]> xmlSignatureRefElements) {
 		/*
-		 * PMaxSchedule
+		 * Some important requirements:
+		 * 
+		 * 1. The sum of the individual time intervals described in the PMaxSchedule and
+		 * SalesTariff provided in the ChargeParameterDiscoveryRes message shall match
+		 * the period of time indicated by the EVCC in the message element DepartureTime of the
+		 * ChargeParameterDiscoveryReq message.
+		 * 
+		 * 2. If the EVCC did not provide a DepartureTime Target Setting in the ChargeParameterDiscoveryReq 
+		 * message, the sum of the individual time intervals described in the PMaxSchedule and SalesTariff 
+		 * provided in the ChargeParameterDiscoveryRes message, shall be greater or equal to 24 hours.
+		 * 
+		 * 3. If the number of SalesTariffEntry elements in the SalesTariff or the number of
+		 * PMaxScheduleEntry elements in the PMaxSchedule provided by the secondary actor(s) are not
+		 * covering the entire period of time until DepartureTime, the Target Setting EAmount has not 
+		 * been met and the communication session has not been finished, it is the responsibility of 
+		 * the EVCC to request a new element of type SAScheduleListType as soon as the last 
+		 * SalesTariffEntry element or the last PMaxScheduleEntry element becomes active by sending 
+		 * a new ChargeParameterDiscoveryReq message.
+		 * 
+		 * 4. In case of PnC, and if a Tariff Table is used by the secondary actor, the secondary actor SHALL
+		 * sign the field SalesTariff of type SalesTariffType. In case of EIM, the secondary actor MAY sign
+		 * this field.
+		 * 
+		 * 5. The SECC shall 'copy' (not change!) the signature value received from the SA and transmit this value in the
+		 * header of the ChargeParameterDiscoveryRes message.
+		 * 
+		 * 6. 
+		 * If the element SalesTariff is signed, it shall be signed by the same private key that was used to
+		 * issue the leaf contract certificate that the EVCC used during this connection for contract
+		 * authentication (PnC).
+		 * 
+		 * 7. An EVCC shall support 12 entries for PMaxScheduleEntry and SalesTariffEntry elements inside
+		 * one SAScheduleTuple if MaxEntriesSAScheduleTuple is not transmitted in ChargeParameterDiscoveryReq.
+		 * 
+		 * 8. The valid range for the value of EPriceLevel element shall be defined as being between 0 and
+		 * the value of NumEPriceLevels element including the boundary values.
 		 */
-		PhysicalValueType pMaxValue = new PhysicalValueType();
-		pMaxValue.setMultiplier(new Byte("3"));
-		pMaxValue.setUnit(UnitSymbolType.W);
-		pMaxValue.setValue((short) 11);
-		
-		RelativeTimeIntervalType timeInterval = new RelativeTimeIntervalType();
-		timeInterval.setStart(0);
-		timeInterval.setDuration(3600L);
-		
-		PMaxScheduleEntryType pMaxScheduleEntry = new PMaxScheduleEntryType();
-		pMaxScheduleEntry.setTimeInterval(new JAXBElement<RelativeTimeIntervalType>(
-				new QName("urn:iso:15118:2:2013:MsgDataTypes", "RelativeTimeInterval"),
-				RelativeTimeIntervalType.class, 
-				timeInterval));
-		pMaxScheduleEntry.setPMax(pMaxValue);
 		
+		// PMaxSchedule
+		// IMPORTANT: check that you do not add more pMax entries than parameter maxEntriesSAScheduleTuple
 		PMaxScheduleType pMaxSchedule = new PMaxScheduleType();
-		pMaxSchedule.getPMaxScheduleEntry().add(pMaxScheduleEntry);
-		
+		pMaxSchedule.getPMaxScheduleEntry().add(createPMaxScheduleEntry("3", (short) 11, 0, 7200L));
 		
 		/*
 		 * SalesTariff (add some meaningful things)
 		 * But: If it is instantiated, it must be filled with meaningful data, otherwise there will
 		 * occur an error with the EXIDecoder (at least at Vector)
+		 * 
+		 * IMPORTANT: check that you do not add more sales tariff entries than parameter maxEntriesSAScheduleTuple
 		 */
+		SalesTariffType salesTariff = new SalesTariffType();
+		salesTariff.setId("salesTariff");
+		salesTariff.setSalesTariffID((short) 1);
+		salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(0L, (short) 1));
+		salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(1800L, (short) 4));
+		salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(3600L, (short) 2));
+		salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(5400L, (short) 3));
 		
-		
-		/*
-		 * Put 'em all together
-		 */
+		// Put 'em all together
 		SAScheduleTupleType saScheduleTuple = new SAScheduleTupleType();
 		saScheduleTuple.setSAScheduleTupleID((short) 1); 
 		saScheduleTuple.setPMaxSchedule(pMaxSchedule);
-//		saScheduleTuple.setSalesTariff(salesTariff);
+		saScheduleTuple.setSalesTariff(salesTariff);
 		
 		SAScheduleListType saScheduleList = new SAScheduleListType();
 		saScheduleList.getSAScheduleTuple().add(saScheduleTuple);
-
+		
+		// Set xml reference elements (repeat this for every sales tariff)
+		xmlSignatureRefElements.put(
+				salesTariff.getId(), 
+				SecurityUtils.generateDigest(salesTariff, false));
+	
 		return saScheduleList;
 	}
+	
+	private SalesTariffEntryType createSalesTariffEntry(long start, short ePriceLevel) {
+		RelativeTimeIntervalType salesTariffTimeInterval = new RelativeTimeIntervalType();
+		salesTariffTimeInterval.setStart(start);
+		
+		SalesTariffEntryType salesTariffEntry = new SalesTariffEntryType();
+		salesTariffEntry.setTimeInterval(new JAXBElement<RelativeTimeIntervalType>(
+				new QName("urn:iso:15118:2:2013:MsgDataTypes", "RelativeTimeInterval"),
+				RelativeTimeIntervalType.class, 
+				salesTariffTimeInterval));
+		salesTariffEntry.setEPriceLevel(ePriceLevel);
+		
+		return salesTariffEntry;
+	}
+	
+	private PMaxScheduleEntryType createPMaxScheduleEntry(String multiplier, short pMax, long start) {
+		PhysicalValueType pMaxValue = new PhysicalValueType();
+		pMaxValue.setMultiplier(new Byte(multiplier));
+		pMaxValue.setUnit(UnitSymbolType.W);
+		pMaxValue.setValue(pMax);
+		
+		RelativeTimeIntervalType pMaxTimeInterval = new RelativeTimeIntervalType();
+		pMaxTimeInterval.setStart(0);
+		pMaxTimeInterval.setDuration(7200L); // 2 hours
+		
+		PMaxScheduleEntryType pMaxScheduleEntry = new PMaxScheduleEntryType();
+		pMaxScheduleEntry.setTimeInterval(new JAXBElement<RelativeTimeIntervalType>(
+				new QName("urn:iso:15118:2:2013:MsgDataTypes", "RelativeTimeInterval"),
+				RelativeTimeIntervalType.class, 
+				pMaxTimeInterval));
+		pMaxScheduleEntry.setPMax(pMaxValue);
+		
+		return pMaxScheduleEntry;
+	}
+	
+	private PMaxScheduleEntryType createPMaxScheduleEntry(String multiplier, short pMax, long start, long duration) {
+		PMaxScheduleEntryType pMaxScheduleEntry = createPMaxScheduleEntry(multiplier, pMax, start);
+		((RelativeTimeIntervalType) pMaxScheduleEntry.getTimeInterval().getValue()).setDuration(duration);
+		
+		return pMaxScheduleEntry;
+	}
 
 
 	@Override

+ 5 - 1
RISE-V2G-SECC/src/main/java/org/eclipse/risev2g/secc/backend/IBackendInterface.java

@@ -11,6 +11,7 @@
 package org.eclipse.risev2g.secc.backend;
 
 import java.security.interfaces.ECPrivateKey;
+import java.util.HashMap;
 
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
 import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleListType;
@@ -20,9 +21,12 @@ public interface IBackendInterface {
 	/**
 	 * Provides a list of schedules coming from a secondary actor (SAScheduleList) with pMax values
 	 * and optional tariff incentives which shall influence the charging behaviour of the EV.
+	 * 
+	 * @param maxEntriesSAScheduleTuple The maximum number of PMaxEntries and SalesTariff entries allowed by EVCC
+	 * @param xmlSignatureRefElements Signature reference parameter provided to put sales tariff IDs and sales tariffs in
 	 * @return An SASchedulesType element with a list of secondary actor schedules 
 	 */
-	public SAScheduleListType getSAScheduleList();
+	public SAScheduleListType getSAScheduleList(int maxEntriesSAScheduleTuple, HashMap<String, byte[]> xmlSignatureRefElements);
 	
 	
 	/**

+ 14 - 3
RISE-V2G-SECC/src/main/java/org/eclipse/risev2g/secc/states/WaitForChargeParameterDiscoveryReq.java

@@ -55,14 +55,22 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
 				
 				/*
 				 * Request a new schedule in case of first ChargeParameterDiscoveryReq.
-				 * If EVSEProcessingType.ONGOING was sent in previous ChargeParameterDiscoveryReq
-				 * response message, do not request again.
+				 * If EVSEProcessingType.ONGOING was sent in previous ChargeParameterDiscoveryRes
+				 * message, do not request again.
 				 */
 				if (!isWaitingForSchedule()) {
 					// TODO we need a timeout mechanism here so that a response can be sent within 2s
 					setWaitingForSchedule(true);
+					
+					// The max. number of PMaxScheduleEntries and SalesTariffEntries is 1024 if not provided otherwise by EVCC
+					int maxEntriesSAScheduleTuple = (chargeParameterDiscoveryReq.getMaxEntriesSAScheduleTuple() != null) ? 
+													chargeParameterDiscoveryReq.getMaxEntriesSAScheduleTuple() : 1024;
+					
 					getCommSessionContext().setSaSchedules(
-							getCommSessionContext().getBackendInterface().getSAScheduleList());
+							getCommSessionContext().getBackendInterface().getSAScheduleList(
+									maxEntriesSAScheduleTuple, 
+									getXMLSignatureRefElements())
+							);
 				}
 				
 				// Wait a bit and check if the schedule has already been provided
@@ -98,6 +106,9 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
 					chargeParameterDiscoveryRes.setSASchedules(
 							getSASchedulesAsJAXBElement(getCommSessionContext().getSaSchedules()));
 					
+					// Set signing private key
+					setSignaturePrivateKey(getCommSessionContext().getBackendInterface().getSAProvisioningCertificatePrivateKey());
+					
 					if (chargeParameterDiscoveryReq.getRequestedEnergyTransferMode().toString().startsWith("AC")) 
 						return getSendMessage(chargeParameterDiscoveryRes, V2GMessages.POWER_DELIVERY_REQ);
 					else