aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/at/gv/egiz/moazs/config/MoaSigConfig.java7
-rw-r--r--src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java12
-rw-r--r--src/main/java/at/gv/egiz/moazs/verify/MoaSPSSSignatureVerifier.java87
-rw-r--r--src/main/java/at/gv/egiz/moazs/verify/SignatureVerifier.java8
-rw-r--r--src/main/resources/application.yaml2
-rw-r--r--src/main/resources/moa-spss/certstore/toBeAdded/IAIK_test_intermediate_CA.derbin0 -> 1199 bytes
-rw-r--r--src/main/resources/moa-spss/certstore/toBeAdded/msz-test-root-cert.derbin1565 -> 0 bytes
-rw-r--r--src/main/resources/moa-spss/trustProfiles/test-trustprofile/IAIK_test_intermediate_CA.derbin0 -> 1199 bytes
-rw-r--r--src/main/resources/moa-spss/trustProfiles/test-trustprofile/msz-test-root-cert.derbin1565 -> 0 bytes
-rw-r--r--src/test/java/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest.java49
-rw-r--r--src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java11
-rw-r--r--src/test/resources/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest/valid-signed-delivery-response.xml30
12 files changed, 183 insertions, 23 deletions
diff --git a/src/main/java/at/gv/egiz/moazs/config/MoaSigConfig.java b/src/main/java/at/gv/egiz/moazs/config/MoaSigConfig.java
index 84e5299..05ecac1 100644
--- a/src/main/java/at/gv/egiz/moazs/config/MoaSigConfig.java
+++ b/src/main/java/at/gv/egiz/moazs/config/MoaSigConfig.java
@@ -90,13 +90,14 @@ public class MoaSigConfig {
}
@Bean
- public SignatureVerifier signatureVerifier(@Value("${moa.spss.is-active}") boolean isMoaSPSSActive) {
+ public SignatureVerifier signatureVerifier(@Value("${moa.spss.is-active}") boolean isMoaSPSSActive,
+ @Value("${moa.spss.is-manifest-check-active}") boolean isManifestCheckActive) {
if (isMoaSPSSActive) {
log.info("Moa SPSS is active. Signatures in SOAP Messages will be verified.");
- return new MoaSPSSSignatureVerifier(moaSigVerifyService(), defaultTrustProfile);
+ return new MoaSPSSSignatureVerifier(moaSigVerifyService(), defaultTrustProfile, isManifestCheckActive);
} else {
log.warn("Moa SPSS is not active. Signatures in SOAP Messages will not be verified.");
- return signedXMLdocument -> true;
+ return signedXMLdocument -> {};
}
}
}
diff --git a/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java b/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java
index 9f2b6d4..920e90d 100644
--- a/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java
+++ b/src/main/java/at/gv/egiz/moazs/pipeline/SameThreadDeliveryPipeline.java
@@ -75,11 +75,12 @@ public class SameThreadDeliveryPipeline implements DeliveryPipeline {
status = msgClientFactory.create(msgRequest, mzsRequest.getConfig(), interceptor).send();
var signedStatus = repository.getSignedDeliveryRequestStatus(appDeliveryId).get();
- if (verifier.verify(signedStatus)) {
- repository.add(status);
- } else {
+
+ try {
+ verifier.verify(signedStatus);
+ } catch (Exception ex) {
throw moaZSExceptionBuilder("Signature of DeliveryRequestStatus with AppDeliveryId={} " +
- "is invalid.", appDeliveryId)
+ " is not valid.", appDeliveryId)
.withErrorCode(MoaZSException.ERROR_MOASP_SIGNATURE_INVALID)
.withMzsRequest(mzsRequest)
.withTnvzResult(tnvzResult)
@@ -87,6 +88,9 @@ public class SameThreadDeliveryPipeline implements DeliveryPipeline {
.withMsgResult(status)
.build();
}
+
+ repository.add(status);
+
} catch (MoaZSException exception) {
var errorStatus = generateErrorStatus(exception, appDeliveryId);
repository.add(errorStatus);
diff --git a/src/main/java/at/gv/egiz/moazs/verify/MoaSPSSSignatureVerifier.java b/src/main/java/at/gv/egiz/moazs/verify/MoaSPSSSignatureVerifier.java
index 518cdb3..0757c5d 100644
--- a/src/main/java/at/gv/egiz/moazs/verify/MoaSPSSSignatureVerifier.java
+++ b/src/main/java/at/gv/egiz/moazs/verify/MoaSPSSSignatureVerifier.java
@@ -1,10 +1,15 @@
package at.gv.egiz.moazs.verify;
import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.ISignatureVerificationService;
+import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.data.IXMLSignatureVerificationResponse;
import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.exceptions.MOASigServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
+import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
+import static java.lang.String.*;
+
public class MoaSPSSSignatureVerifier implements SignatureVerifier {
private static final Logger log = LoggerFactory.getLogger(MoaSPSSSignatureVerifier.class);
@@ -13,22 +18,94 @@ public class MoaSPSSSignatureVerifier implements SignatureVerifier {
private final String trustProfile;
+ private final boolean isManifestCheckActive;
+
+ private static final int OK_CODE = 0;
+
public MoaSPSSSignatureVerifier(ISignatureVerificationService service,
- String trustProfile) {
+ String trustProfile, boolean isManifestCheckActive) {
this.service = service;
this.trustProfile = trustProfile;
+ this.isManifestCheckActive = isManifestCheckActive;
}
@Override
- public boolean verify(byte[] signedXMLdocument) {
+ public void verify(byte[] signedXMLdocument) {
try {
var response = service.verifyXMLSignature(signedXMLdocument, trustProfile);
- return response != null;
+
+ if (log.isDebugEnabled()) {
+ print(response);
+ }
+
+ if (response == null) {
+ throw moaZSException("MOA SPSS could not find the signature. ");
+ }
+
+ var builder = new StringBuilder();
+
+ if (response.getSignatureCheckCode() != OK_CODE) {
+ builder.append(format("Signature is not valid; SignatureCheckCode was %d. ",
+ response.getSignatureCheckCode()));
+ }
+
+ if (response.getCertificateCheckCode() != OK_CODE) {
+ builder.append(format("Certificate chain is not valid; CertificateCheckCode was %d. ",
+ response.getCertificateCheckCode()));
+ }
+
+ if (response.getSignatureManifestCheckCode() != OK_CODE) {
+ var signatureManifestErrorMsg = format("Signature Manifest is not valid; " +
+ "SignatureManifestCheckCode was %d. ", response.getSignatureManifestCheckCode());
+ if (isManifestCheckActive) {
+ builder.append(signatureManifestErrorMsg);
+ } else {
+ log.warn(signatureManifestErrorMsg);
+ }
+ }
+
+ if (response.isXmlDSIGManigest() && response.getXmlDSIGManifestCheckCode() != OK_CODE) {
+ var xmlDSIGManifestErrorMsg = format("XmlDSIGManifest Manifest is not valid; " +
+ "XmlDSIGManifest was %d. ", response.getXmlDSIGManifestCheckCode());
+ if (isManifestCheckActive) {
+ builder.append(xmlDSIGManifestErrorMsg);
+ } else {
+ log.warn(xmlDSIGManifestErrorMsg);
+ }
+ }
+
+ var msg = builder.toString();
+
+ if(msg.length() > 0) {
+ throw moaZSException(msg);
+ }
+
} catch (MOASigServiceException e) {
- MoaSPSSSignatureVerifier.log.error("Could not verify the XML signature.", e);
- return false;
+ throw moaZSExceptionBuilder("Could not verify the XML signature.")
+ .withCause(e)
+ .build();
+ }
+
+ }
+
+ private void print(IXMLSignatureVerificationResponse response) {
+ log.debug("Response:");
+
+ if (response == null) {
+ log.debug("null");
+ return;
}
+ log.debug(" XmlDsigSubjectName: " + response.getXmlDsigSubjectName());
+ log.debug(" SignatureManifestCheckCode: " + response.getSignatureManifestCheckCode());
+ log.debug(" XmlDSIGManifestCheckCode: " + response.getXmlDSIGManifestCheckCode());
+ log.debug(" CertificateCheckCode: " + response.getCertificateCheckCode());
+ log.debug(" SignatureCheckCode: " + response.getSignatureCheckCode());
+ log.debug(" SigningDateTime: " + response.getSigningDateTime());
+ log.debug(" isXmlDSIGManigest: " + response.isXmlDSIGManigest());
+ log.debug(" isPublicAuthority: " + response.isPublicAuthority());
+ log.debug(" isQualifiedCertificate: " + response.isQualifiedCertificate());
+ log.debug(" getPublicAuthorityCode: " + response.getPublicAuthorityCode());
}
}
diff --git a/src/main/java/at/gv/egiz/moazs/verify/SignatureVerifier.java b/src/main/java/at/gv/egiz/moazs/verify/SignatureVerifier.java
index 01e90c8..a31c4cf 100644
--- a/src/main/java/at/gv/egiz/moazs/verify/SignatureVerifier.java
+++ b/src/main/java/at/gv/egiz/moazs/verify/SignatureVerifier.java
@@ -4,10 +4,10 @@ package at.gv.egiz.moazs.verify;
public interface SignatureVerifier {
/**
- * Verifies the signature of a signed XML document.
+ * Verifies the signature of a signed XML document. Throws a at.gv.egiz.moazs.MoaZSException exception
+ * if the validation fails.
* @param signedXMLdocument
- * @return true if the signature is valid; false if there is no signature, if the signature is invalid,
- * or if an exception occured.
+ * @throws at.gv.egiz.moazs.MoaZSException
*/
- boolean verify(byte[] signedXMLdocument);
+ void verify(byte[] signedXMLdocument);
}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 961f437..9ce1158 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -80,6 +80,8 @@ javax.net.ssl:
### moa spss config
moa.spss:
is-active: true
+ # if active, moa spss will validate manifests in xml signatures
+ is-manifest-check-active: false
server:
# path that points to MoaSPSSConfiguration file; can be:
# - absolute path (unix: starts with /), or
diff --git a/src/main/resources/moa-spss/certstore/toBeAdded/IAIK_test_intermediate_CA.der b/src/main/resources/moa-spss/certstore/toBeAdded/IAIK_test_intermediate_CA.der
new file mode 100644
index 0000000..558ce15
--- /dev/null
+++ b/src/main/resources/moa-spss/certstore/toBeAdded/IAIK_test_intermediate_CA.der
Binary files differ
diff --git a/src/main/resources/moa-spss/certstore/toBeAdded/msz-test-root-cert.der b/src/main/resources/moa-spss/certstore/toBeAdded/msz-test-root-cert.der
deleted file mode 100644
index 3e136d4..0000000
--- a/src/main/resources/moa-spss/certstore/toBeAdded/msz-test-root-cert.der
+++ /dev/null
Binary files differ
diff --git a/src/main/resources/moa-spss/trustProfiles/test-trustprofile/IAIK_test_intermediate_CA.der b/src/main/resources/moa-spss/trustProfiles/test-trustprofile/IAIK_test_intermediate_CA.der
new file mode 100644
index 0000000..558ce15
--- /dev/null
+++ b/src/main/resources/moa-spss/trustProfiles/test-trustprofile/IAIK_test_intermediate_CA.der
Binary files differ
diff --git a/src/main/resources/moa-spss/trustProfiles/test-trustprofile/msz-test-root-cert.der b/src/main/resources/moa-spss/trustProfiles/test-trustprofile/msz-test-root-cert.der
deleted file mode 100644
index 3e136d4..0000000
--- a/src/main/resources/moa-spss/trustProfiles/test-trustprofile/msz-test-root-cert.der
+++ /dev/null
Binary files differ
diff --git a/src/test/java/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest.java b/src/test/java/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest.java
new file mode 100644
index 0000000..afa817f
--- /dev/null
+++ b/src/test/java/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest.java
@@ -0,0 +1,49 @@
+package at.gv.egiz.moazs;
+
+import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.ISignatureVerificationService;
+import at.gv.egiz.moazs.verify.MoaSPSSSignatureVerifier;
+import at.gv.egiz.moazs.verify.SignatureVerifier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+ public class MoaSPSSSignatureVerifierTest {
+
+ private final String resourcesPath = "src/test/resources/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest/";
+
+ @TestConfiguration
+ public class Config{
+
+ @Bean
+ public SignatureVerifier verifier(@Autowired ISignatureVerificationService service){
+ return new MoaSPSSSignatureVerifier(service, "test-trust-profile", true);
+ }
+
+ }
+
+ @Autowired
+ private SignatureVerifier verifier;
+
+ //TODO make sure that testcase does not depend on runtime because it's certificate expires in 2023-09-27.
+ @Test
+ public void acceptValidSignedDeliveryResponse() throws IOException {
+
+ var path = resourcesPath + "valid-signed-delivery-response.xml";
+ var signature = Files.readAllBytes(new File(path).toPath());
+
+ verifier.verify(signature);
+ }
+
+}
diff --git a/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java b/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java
index 768f376..cd454f2 100644
--- a/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java
+++ b/src/test/java/at/gv/egiz/moazs/SameThreadDeliveryPipelineTest.java
@@ -20,10 +20,12 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder;
import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.Payload;
import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.Payload.payloadBuilder;
@@ -148,7 +150,8 @@ public class SameThreadDeliveryPipelineTest {
@Test
public void rejectInvalidSignature() {
var appDeliveryId = "invalid-signature";
- setupMocks(appDeliveryId, true, List.of(), List.of("*/*"), false);
+ setupMocks(appDeliveryId, true, List.of(), List.of("*/*"));
+ doThrow(moaZSException("Signature Invalid!")).when(verifier).verify(any());
pipeline.processRequest(appDeliveryId);
var actualCode = repository.getDeliveryRequestStatus(appDeliveryId).get()
@@ -160,11 +163,6 @@ public class SameThreadDeliveryPipelineTest {
private DeliveryRequestStatusType setupMocks(String appDeliveryId, boolean tnvzRequest,
List<String> attachedTypes, List<String> acceptedTypes) {
- return setupMocks(appDeliveryId, tnvzRequest, attachedTypes, acceptedTypes, true);
- }
-
- private DeliveryRequestStatusType setupMocks(String appDeliveryId, boolean tnvzRequest,
- List<String> attachedTypes, List<String> acceptedTypes, boolean isSignedStatusValid) {
var mzsRequest = setupMzsRequest(appDeliveryId, tnvzRequest, attachedTypes);
var msgRequest = setupMsgRequest(appDeliveryId);
@@ -178,7 +176,6 @@ public class SameThreadDeliveryPipelineTest {
when(converter.convert(eq(mzsRequest), any())).thenReturn(msgRequest);
when(msgClientFactory.create(msgRequest, mzsRequest.getConfig(), interceptor)).thenReturn(msgClient);
when(msgClient.send()).thenReturn(status);
- when(verifier.verify(signedStatus)).thenReturn(isSignedStatusValid);
return status;
diff --git a/src/test/resources/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest/valid-signed-delivery-response.xml b/src/test/resources/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest/valid-signed-delivery-response.xml
new file mode 100644
index 0000000..59a90cf
--- /dev/null
+++ b/src/test/resources/at/gv/egiz/moazs/MoaSPSSSignatureVerifierTest/valid-signed-delivery-response.xml
@@ -0,0 +1,30 @@
+<DeliveryResponse xmlns="http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#" xmlns:ns2="http://reference.e-government.gv.at/namespace/persondata/phase2/20181206#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"><PartialSuccess><DeliverySystem>https://testzustellsystem.egiz.gv.at</DeliverySystem><ZSDeliveryID>zs-valid-delivery-request-id</ZSDeliveryID><AppDeliveryID>valid-delivery-request-id</AppDeliveryID><GZ>12345</GZ></PartialSuccess><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Id="signature-1-1"><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><dsig:Reference Id="reference-1-1" URI=""><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><dsig:DigestValue>ejvUI0yh/IIyauFe8x5ZonD/i5oznl8vFyS3oLNivzA=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>hmVZrLkMDbXaRLYQKOaV3OtK13TQgMu3csKyw9M4zWqNyva1yxnYkzoX3dKDOdc9
+O56yQJsjoA3Cuw7pXlGO7jSfVM77dTXbWSDaF95O9Vdsrmr7R6Uki0jA9SmgQLXg
+hZAUG8JpsHcBn8M0L2BXADKjSn0LuMDL2L7dmU3EM7eRy+OvFwDrXDw1fhjQO6L2
+KoflAWLgUerDhJSpzr0+YfmkrjzitLUA7oIg8ieOnfGyql31ECmDJEqgnL78hyPZ
+KaNZImDf3EWFs8je6mt+os1TwsyXYwz+GGbjoDR8lGTS9xVqnXdrgP8Jyv6p9FEu
+0IYgSY2FlbI3skPZC8ZVXg==</dsig:SignatureValue><dsig:KeyInfo><dsig:X509Data><dsig:X509Certificate>MIIEqzCCBBSgAwIBAgIHANux81oNezANBgkqhkiG9w0BAQUFADBAMSIwIAYDVQQD
+ExlJQUlLIFRlc3QgSW50ZXJtZWRpYXRlIENBMQ0wCwYDVQQKEwRJQUlLMQswCQYD
+VQQGEwJBVDAeFw0xMzA5MjcwNTMzMzdaFw0yMzA5MjcwNTMzMzdaMIHkMQswCQYD
+VQQGEwJBVDENMAsGA1UEBxMER3JhejEmMCQGA1UEChMdR3JheiBVbml2ZXJzaXR5
+IG9mIFRlY2hub2xvZ3kxSDBGBgNVBAsTP0luc3RpdHV0ZSBmb3IgQXBwbGllZCBJ
+bmZvcm1hdGlvbiBQcm9jZXNzaW5nIGFuZCBDb21tdW5pY2F0aW9uczEUMBIGA1UE
+BBMLTU9BLVNTIFRlc3QxGDAWBgNVBCoTD0VHSVogVGVzdHBvcnRhbDEkMCIGA1UE
+AxMbRUdJWiBUZXN0cG9ydGFsIE1PQS1TUyBUZXN0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuDjOyf+mY+oQL2FQzzuaiC8C23vVKbq/n2Zi7BqSibZH
+mtqMJfmj4pT+hWSNHvVvWsaxFcx4KeNqdCMzwnw1r4P3Sf+2o5uFku5KHEMLMokR
+yYQG9VqY/KkB94ye7Pv6zT8gvKqxGFg96UamECep4swPaSZrA8AOER5WAtyGDzKI
+Tz+a5zfFaTXDoba7f98PCWR96yKiFjVOhzp38WVz4VJgz+b8ZSY7Xsv5Kn7DXjOL
+STX4MevFLki3rFPup3+4vGToaMBW3PEj67HXBdqR855Le6+E6rVxORqsXqlVwhsI
+6nuS0CO2LWYmBNR1IB0mXteeYH/HfxvuZc+7yDjdPQIDAQABo4IBhDCCAYAwDgYD
+VR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFEmcH6VY4BG1EAGB
+TLoNR9vH/g6yMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jYS5pYWlrLnR1Z3Jh
+ei5hdC9jYXBzby9jcmxzL0lBSUtUZXN0X0ludGVybWVkaWF0ZUNBLmNybDCBqgYI
+KwYBBQUHAQEEgZ0wgZowSgYIKwYBBQUHMAGGPmh0dHA6Ly9jYS5pYWlrLnR1Z3Jh
+ei5hdC9jYXBzby9PQ1NQP2NhPUlBSUtUZXN0X0ludGVybWVkaWF0ZUNBMEwGCCsG
+AQUFBzAChkBodHRwOi8vY2EuaWFpay50dWdyYXouYXQvY2Fwc28vY2VydHMvSUFJ
+S1Rlc3RfSW50ZXJtZWRpYXRlQ0EuY2VyMCEGA1UdEQQaMBiBFnRob21hcy5sZW56
+QGVnaXouZ3YuYXQwHwYDVR0jBBgwFoAUaKJeEdreL4BrRES/jfplNoEkp28wDQYJ
+KoZIhvcNAQEFBQADgYEAlFGjUxXLs7SAT8NtXSrv2WrjlklaRnHTFHLQwyVo8JWb
+gvRkHHDUv2o8ofXUY2R2WJ38dxeDoccgbXrJb/Qhi8IY7YhCwv/TuIZDisyAqo8W
+ORKSip/6HWlGCSR/Vgoet1GtCmF0FoUxFUIGSAuQ2yyt4fIzt5GJrU1X5ujjI1w=</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo></dsig:Signature></DeliveryResponse>