From 0562debfc5cdd31e61c016aea40272c6c02ad3cb Mon Sep 17 00:00:00 2001 From: Bogumil Zebek Date: Wed, 14 Aug 2019 10:52:37 +0200 Subject: CMS signature validation Change-Id: Ie5d1c835d0e6a760f1b7de651a3833cb87b727e0 Issue-ID: VNFSDK-396 Signed-off-by: Zebek Bogumil --- .../main/java/org/onap/cvc/csar/CSARArchive.java | 6 +- .../java/org/onap/cvc/csar/PnfCSARArchive.java | 5 + .../cvc/csar/cc/sol004/VTPValidateCSARR130206.java | 234 +++++++++++++++++++++ .../cvc/csar/cc/sol004/VTPValidateCSARR787965.java | 26 ++- .../cvc/csar/cc/sol004/VTPValidateCSARR787966.java | 177 ---------------- .../onap/cvc/csar/parser/ManifestFileModel.java | 39 ++++ .../onap/cvc/csar/parser/ManifestFileSplitter.java | 62 ++++++ .../cvc/csar/security/CmsSignatureValidator.java | 45 ++-- .../services/org.onap.cli.fw.cmd.OnapCommand | 2 +- .../sol004/vtp-validate-csar-r130206.yaml | 60 ++++++ .../sol004/vtp-validate-csar-r787966.yaml | 60 ------ .../src/main/resources/vnfreqs.properties | 6 +- .../org/onap/cvc/csar/PnfManifestParserTest.java | 36 +++- .../VTPValidateCSARR130206IntegrationTest.java | 124 +++++++++++ .../VTPValidateCSARR787965IntegrationTest.java | 22 +- .../VTPValidateCSARR787966IntegrationTest.java | 100 --------- .../cvc/csar/parser/ManifestFileSplitterTest.java | 50 +++++ csarvalidation/src/test/resources/README.txt | 20 ++ .../cvc/csar/parser/MainServiceTemplate.mf | 10 + .../src/test/resources/pnf/MainServiceTemplate.mf | 38 +++- csarvalidation/src/test/resources/pnf/README.txt | 6 - .../pnf/r130206/csar-option1-invalid.csar | Bin 0 -> 5743 bytes .../resources/pnf/r130206/csar-option1-valid.csar | Bin 0 -> 7324 bytes .../pnf/r130206/csar-option1-validSection.csar | Bin 0 -> 6170 bytes .../pnf/r787966/csar-option1-invalid.csar | Bin 5745 -> 0 bytes .../resources/pnf/r787966/csar-option1-valid.csar | Bin 6549 -> 0 bytes 26 files changed, 737 insertions(+), 391 deletions(-) create mode 100644 csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java delete mode 100644 csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java create mode 100644 csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java create mode 100644 csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java create mode 100644 csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r130206.yaml delete mode 100644 csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml create mode 100644 csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java delete mode 100644 csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966IntegrationTest.java create mode 100644 csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java create mode 100644 csarvalidation/src/test/resources/README.txt create mode 100644 csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf delete mode 100644 csarvalidation/src/test/resources/pnf/README.txt create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-option1-invalid.csar create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csar create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-option1-validSection.csar delete mode 100644 csarvalidation/src/test/resources/pnf/r787966/csar-option1-invalid.csar delete mode 100644 csarvalidation/src/test/resources/pnf/r787966/csar-option1-valid.csar (limited to 'csarvalidation') diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java b/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java index 180dadd..beb556d 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java @@ -1093,7 +1093,7 @@ public class CSARArchive implements AutoCloseable { this.toscaMeta.getEntryLicense(), lineNo)); } - } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_CERTIFICATE)) { + } else if(key.equalsIgnoreCase(getEntryCertificateParamName())) { this.toscaMeta.setEntryCertificate(value); this.certificatesFile= this.tempDir.resolve(this.toscaMeta.getEntryCertificate()).toFile(); if (!this.certificatesFile.exists()) { @@ -1247,6 +1247,10 @@ public class CSARArchive implements AutoCloseable { } } + public String getEntryCertificateParamName() { + return TOSCA_METADATA_TOSCA_META_ENTRY_CERTIFICATE; + } + String getEntryManifestParamName(){ return TOSCA_METADATA_TOSCA_META_ENTRY_MANIFEST; } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java index a6e2745..f3ce960 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java @@ -66,4 +66,9 @@ public class PnfCSARArchive extends CSARArchive { String getEntryChangeLogParamName() { return "ETSI-Entry-Change-Log"; } + + @Override + public String getEntryCertificateParamName() { + return "ETSI-Entry-Certificate"; + } } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java new file mode 100644 index 0000000..2d85e35 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java @@ -0,0 +1,234 @@ +/* + * Copyright 2019 Nokia + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.cc.sol004; + + +import org.onap.cli.fw.error.OnapCommandException; +import org.onap.cli.fw.schema.OnapCommandSchema; +import org.onap.cvc.csar.CSARArchive; +import org.onap.cvc.csar.FileArchive; +import org.onap.cvc.csar.cc.VTPValidateCSARBase; +import org.onap.cvc.csar.parser.ManifestFileModel; +import org.onap.cvc.csar.parser.ManifestFileSplitter; +import org.onap.cvc.csar.parser.SourcesParser; +import org.onap.cvc.csar.security.CmsSignatureValidator; +import org.onap.cvc.csar.security.CmsSignatureValidatorException; +import org.onap.cvc.csar.security.ShaHashCodeGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@OnapCommandSchema(schema = "vtp-validate-csar-r130206.yaml") +public class VTPValidateCSARR130206 extends VTPValidateCSARBase { + + private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR130206.class); + private static final String SHA_256 = "SHA-256"; + private static final String SHA_512 = "SHA-512"; + + private final ShaHashCodeGenerator shaHashCodeGenerator = new ShaHashCodeGenerator(); + private final ManifestFileSignatureValidator manifestFileSignatureValidator = new ManifestFileSignatureValidator(); + + + public static class CSARErrorUnableToFindCertificate extends CSARArchive.CSARError { + CSARErrorUnableToFindCertificate(String paramName) { + super("0x4001"); + this.message = String.format("Unable to find cert file defined by %s!", paramName); + } + } + + public static class CSARErrorUnableToFindCmsSection extends CSARArchive.CSARError { + CSARErrorUnableToFindCmsSection() { + super("0x4002"); + this.message = "Unable to find CMS section in manifest!"; + } + } + + public static class CSARErrorUnableToFindCsarContent extends CSARArchive.CSARError { + CSARErrorUnableToFindCsarContent() { + super("0x4003"); + this.message = "Unable to find csar content!"; + } + } + + public static class CSARErrorWrongHashCode extends CSARArchive.CSARError { + CSARErrorWrongHashCode(String path) { + super("0x4004"); + this.message = String.format("Source '%s' has wrong hash!", path); + } + } + + public static class CSARErrorUnableToFindAlgorithm extends CSARArchive.CSARError { + CSARErrorUnableToFindAlgorithm(String path) { + super("0x4005"); + this.message = String.format("Source '%s' has hash, but unable to find algorithm tag!", path); + } + } + + public static class CSARErrorUnableToFindSource extends CSARArchive.CSARError { + CSARErrorUnableToFindSource(String path) { + super("0x4006"); + this.message = String.format("Unable to calculate digest - file missing: %s", path); + } + } + + public static class CSARErrorInvalidSignature extends CSARArchive.CSARError { + CSARErrorInvalidSignature() { + super("0x4007"); + this.message = "File has invalid CMS signature!"; + } + } + + @Override + protected void validateCSAR(CSARArchive csar) throws OnapCommandException { + + try { + FileArchive.Workspace workspace = csar.getWorkspace(); + final Optional pathToCsarFolder = workspace.getPathToCsarFolder(); + if (pathToCsarFolder.isPresent()) { + validate(csar, pathToCsarFolder.get()); + } else { + this.errors.add(new CSARErrorUnableToFindCsarContent()); + } + } catch (Exception e) { + LOG.error("Internal VTPValidateCSARR130206 command error", e); + throw new OnapCommandException("0x3000", "Internal VTPValidateCSARR787966 command error. See logs."); + } + + } + + private void validate(CSARArchive csar, Path csarRootDirectory) throws IOException, NoSuchAlgorithmException { + final CSARArchive.Manifest manifest = csar.getManifest(); + + validateSecurityStructure(csar, csarRootDirectory); + validateSources(csarRootDirectory, manifest); + + final File manifestMfFile = csar.getManifestMfFile(); + if (manifestMfFile != null) { + validateFileSignature(manifestMfFile); + } + } + + private void validateFileSignature(File manifestMfFile) { + final boolean isValid = this.manifestFileSignatureValidator.isValid(manifestMfFile); + if (!isValid) { + this.errors.add(new CSARErrorInvalidSignature()); + } + } + + private void validateSecurityStructure(CSARArchive csar, Path csarRootDirectory) { + final CSARArchive.Manifest manifest = csar.getManifest(); + final CSARArchive.TOSCAMeta toscaMeta = csar.getToscaMeta(); + final String entryCertificateParamName = csar.getEntryCertificateParamName(); + final Optional entryCertificate = resolveCertificateFilePath(toscaMeta, csarRootDirectory); + if (!entryCertificate.isPresent() || !entryCertificate.get().exists()) { + this.errors.add(new CSARErrorUnableToFindCertificate(entryCertificateParamName)); + } + + if (manifest.getCms() == null || manifest.getCms().isEmpty()) { + this.errors.add(new CSARErrorUnableToFindCmsSection()); + } + } + + private Optional resolveCertificateFilePath(CSARArchive.TOSCAMeta toscaMeta, Path csarRootDirectory) { + final String certificatePath = toscaMeta.getEntryCertificate(); + if (certificatePath == null) { + return Optional.empty(); + } else { + return Optional.of(csarRootDirectory.resolve(certificatePath).toFile()); + } + } + + private void validateSources(Path csarRootDirectory, CSARArchive.Manifest manifest) throws NoSuchAlgorithmException, IOException { + final List sources = manifest.getSources(); + for (SourcesParser.Source source : sources) { + if (!source.getAlgorithm().isEmpty() || !source.getHash().isEmpty()) { + validateSource(csarRootDirectory, source); + } + } + } + + private void validateSource(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { + final Path sourcePath = csarRootDirectory.resolve(source.getValue()); + if (!sourcePath.toFile().exists()) { + this.errors.add(new CSARErrorUnableToFindSource(source.getValue())); + } else { + if (!source.getAlgorithm().isEmpty()) { + validateSourceHashCode(csarRootDirectory, source); + } else if (source.getAlgorithm().isEmpty() && !source.getHash().isEmpty()) { + this.errors.add(new CSARErrorUnableToFindAlgorithm(source.getValue())); + } + } + } + + private void validateSourceHashCode(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { + String hashCode = generateHashCode(csarRootDirectory, source); + if (!hashCode.equals(source.getHash())) { + this.errors.add(new CSARErrorWrongHashCode(source.getValue())); + } + } + + private String generateHashCode(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { + final byte[] sourceData = Files.readAllBytes(csarRootDirectory.resolve(source.getValue())); + final String algorithm = source.getAlgorithm(); + + if (algorithm.equalsIgnoreCase(SHA_256)) { + return this.shaHashCodeGenerator.generateSha256(sourceData); + } else if (algorithm.equalsIgnoreCase(SHA_512)) { + return this.shaHashCodeGenerator.generateSha512(sourceData); + } + + throw new UnsupportedOperationException(String.format("Algorithm '%s' is not supported!", algorithm)); + } + + @Override + protected String getVnfReqsNo() { + return "R130206"; + } + + +} + +class ManifestFileSignatureValidator { + private static final Logger LOG = LoggerFactory.getLogger(ManifestFileSignatureValidator.class); + private final ManifestFileSplitter manifestFileSplitter = new ManifestFileSplitter(); + private final CmsSignatureValidator cmsSignatureValidator = new CmsSignatureValidator(); + + boolean isValid(File manifestFile) { + try { + ManifestFileModel mf = manifestFileSplitter.split(manifestFile); + return cmsSignatureValidator.verifySignedData(toBytes(mf.getCMS()), Optional.empty(), toBytes(mf.getData())); + } catch (CmsSignatureValidatorException e) { + LOG.error("Unable to verify signed data!", e); + return false; + } + } + + private byte[] toBytes(List data) { + final String updatedData = data.stream().map(it -> it + "\r\n").collect(Collectors.joining()); + return updatedData.getBytes(Charset.defaultCharset()); + } +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java index a3ab865..034d35e 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java @@ -37,6 +37,7 @@ import java.util.Optional; public class VTPValidateCSARR787965 extends VTPValidateCSARBase { private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR787965.class); + private final CmsSignatureValidator securityManager = new CmsSignatureValidator(); static class CSARErrorInvalidSignature extends CSARArchive.CSARError { CSARErrorInvalidSignature() { @@ -52,16 +53,6 @@ public class VTPValidateCSARR787965 extends VTPValidateCSARBase { } } - static class SignatureWithCertificationOnlyWarning extends CSARArchive.CSARError { - SignatureWithCertificationOnlyWarning() { - super("0x3003"); - this.message = "Warning. Zip package probably is valid. " + - "It contains only signature with certification cms and csar package. " + - "Unable to verify csar signature."; - } - } - - static class BrokenZipPackageError extends CSARArchive.CSARError { BrokenZipPackageError() { super("0x3004"); @@ -95,7 +86,7 @@ public class VTPValidateCSARR787965 extends VTPValidateCSARBase { if (pathToCertFile.isPresent() && pathToCmsFile.isPresent()) { verifyTwoFileCertification(pathToCsarFile.get(), pathToCertFile.get(), pathToCmsFile.get()); } else if (pathToCmsFile.isPresent()) { - this.errors.add(new SignatureWithCertificationOnlyWarning()); + verifyOneFileCertification(pathToCsarFile.get(), pathToCmsFile.get()); } else { this.errors.add(new BrokenZipPackageError()); } @@ -103,13 +94,20 @@ public class VTPValidateCSARR787965 extends VTPValidateCSARBase { } private void verifyTwoFileCertification(Path pathToCsarFile, Path pathToCertFile, Path pathToCmsFile) throws IOException, CmsSignatureValidatorException { - final CmsSignatureValidator securityManager = new CmsSignatureValidator(); - byte[] csarContent = Files.readAllBytes(pathToCsarFile); byte[] signature = Files.readAllBytes(pathToCmsFile); byte[] publicCertification = Files.readAllBytes(pathToCertFile); - if (!securityManager.verifySignedData(signature, publicCertification,csarContent)) { + if (!securityManager.verifySignedData(signature, Optional.of(publicCertification) ,csarContent)) { + this.errors.add(new CSARErrorInvalidSignature()); + } + } + + private void verifyOneFileCertification(Path pathToCsarFile, Path pathToSignatureAndCmsFile) throws IOException, CmsSignatureValidatorException { + byte[] csarContent = Files.readAllBytes(pathToCsarFile); + byte[] signature = Files.readAllBytes(pathToSignatureAndCmsFile); + + if(!securityManager.verifySignedData(signature, Optional.empty(), csarContent)){ this.errors.add(new CSARErrorInvalidSignature()); } } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java deleted file mode 100644 index 7a14709..0000000 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2019 Nokia - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.onap.cvc.csar.cc.sol004; - - -import org.onap.cli.fw.error.OnapCommandException; -import org.onap.cli.fw.schema.OnapCommandSchema; -import org.onap.cvc.csar.CSARArchive; -import org.onap.cvc.csar.FileArchive; -import org.onap.cvc.csar.cc.VTPValidateCSARBase; -import org.onap.cvc.csar.parser.SourcesParser; -import org.onap.cvc.csar.security.ShaHashCodeGenerator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.Optional; - -@OnapCommandSchema(schema = "vtp-validate-csar-r787966.yaml") -public class VTPValidateCSARR787966 extends VTPValidateCSARBase { - - private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR787966.class); - private static final String SHA_256 = "SHA-256"; - private static final String SHA_512 = "SHA-512"; - - private final ShaHashCodeGenerator shaHashCodeGenerator = new ShaHashCodeGenerator(); - - - public static class CSARErrorUnableToFindCertificate extends CSARArchive.CSARError { - CSARErrorUnableToFindCertificate() { - super("0x4001"); - this.message = "Unable to find cert file defined by Entry-Certificate!"; - } - } - - public static class CSARErrorUnableToFindCmsSection extends CSARArchive.CSARError { - CSARErrorUnableToFindCmsSection() { - super("0x4002"); - this.message = "Unable to find CMS section in manifest!"; - } - } - - public static class CSARErrorUnableToFindCsarContent extends CSARArchive.CSARError { - CSARErrorUnableToFindCsarContent() { - super("0x4003"); - this.message = "Unable to find csar content!"; - } - } - - public static class CSARErrorWrongHashCode extends CSARArchive.CSARError { - CSARErrorWrongHashCode(String path) { - super("0x4004"); - this.message = String.format("Source '%s' has wrong hash!", path); - } - } - - public static class CSARErrorUnableToFindAlgorithm extends CSARArchive.CSARError { - CSARErrorUnableToFindAlgorithm(String path) { - super("0x4005"); - this.message = String.format("Source '%s' has hash, but unable to find algorithm tag!", path); - } - } - - public static class CSARErrorUnableToFindSource extends CSARArchive.CSARError { - CSARErrorUnableToFindSource(String path) { - super("0x4006"); - this.message = String.format("Source '%s' does not exist!", path); - } - } - - @Override - protected void validateCSAR(CSARArchive csar) throws OnapCommandException { - - try { - FileArchive.Workspace workspace = csar.getWorkspace(); - final Optional pathToCsarFolder = workspace.getPathToCsarFolder(); - if(pathToCsarFolder.isPresent()) { - validate(csar, pathToCsarFolder.get()); - } else { - this.errors.add(new CSARErrorUnableToFindCsarContent()); - } - } catch (Exception e) { - LOG.error("Internal VTPValidateCSARR787966 command error", e); - throw new OnapCommandException("0x3000", "Internal VTPValidateCSARR787966 command error. See logs."); - } - - } - - private void validate(CSARArchive csar, Path csarRootDirectory ) throws IOException, NoSuchAlgorithmException { - - final CSARArchive.Manifest manifest = csar.getManifest(); - final CSARArchive.TOSCAMeta toscaMeta = csar.getToscaMeta(); - validateSecurityStructure(toscaMeta, csarRootDirectory, manifest); - validateSources(csarRootDirectory, manifest); - } - - private void validateSecurityStructure(CSARArchive.TOSCAMeta toscaMeta , Path csarRootDirectory, CSARArchive.Manifest manifest) { - final Optional entryCertificate = resolveCertificateFilePath(toscaMeta, csarRootDirectory); - if (!entryCertificate.isPresent() || !entryCertificate.get().exists() && !manifest.getCms().isEmpty()) { - this.errors.add(new CSARErrorUnableToFindCertificate()); - } else if (entryCertificate.get().exists() && manifest.getCms().isEmpty()) { - this.errors.add(new CSARErrorUnableToFindCmsSection()); - } - } - - private Optional resolveCertificateFilePath(CSARArchive.TOSCAMeta toscaMeta, Path csarRootDirectory) { - final String certificatePath = toscaMeta.getEntryCertificate(); - if(certificatePath == null){ - return Optional.empty(); - } else { - return Optional.of(csarRootDirectory.resolve(certificatePath).toFile()); - } - } - - private void validateSources(Path csarRootDirectory, CSARArchive.Manifest manifest) throws NoSuchAlgorithmException, IOException { - final List sources = manifest.getSources(); - for (SourcesParser.Source source: sources){ - final Path sourcePath = csarRootDirectory.resolve(source.getValue()); - if(!Files.exists(sourcePath)){ - this.errors.add(new CSARErrorUnableToFindSource(source.getValue())); - } else { - if (!source.getAlgorithm().isEmpty()) { - validateSourceHashCode(csarRootDirectory, source); - } else if (source.getAlgorithm().isEmpty() && !source.getHash().isEmpty()) { - this.errors.add(new CSARErrorUnableToFindAlgorithm(source.getValue())); - } - } - } - } - - private void validateSourceHashCode(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { - String hashCode = generateHashCode(csarRootDirectory, source); - if (!hashCode.equals(source.getHash())) { - this.errors.add(new CSARErrorWrongHashCode(source.getValue())); - } - } - - private String generateHashCode(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { - final byte[] sourceData = Files.readAllBytes(csarRootDirectory.resolve(source.getValue())); - final String algorithm = source.getAlgorithm(); - - if(algorithm.equalsIgnoreCase(SHA_256)) { - return this.shaHashCodeGenerator.generateSha256(sourceData); - } else if(algorithm.equalsIgnoreCase(SHA_512)){ - return this.shaHashCodeGenerator.generateSha512(sourceData); - } - - throw new UnsupportedOperationException(String.format("Algorithm '%s' is not supported!", algorithm)); - } - - @Override - protected String getVnfReqsNo() { - return "R787966"; - } - - -} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java new file mode 100644 index 0000000..f6b42fd --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019 Nokia + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + +import java.util.Collections; +import java.util.List; + +public class ManifestFileModel { + private final List data; + private final List cms; + + public ManifestFileModel(List data, List cms) { + this.data = data; + this.cms = cms; + } + + public List getData() { + return Collections.unmodifiableList(data); + } + + public List getCMS() { + return Collections.unmodifiableList(cms); + } +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java new file mode 100644 index 0000000..03a628c --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Nokia + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ManifestFileSplitter { + + public ManifestFileModel split(File manifestFile) { + String fileName = manifestFile.getAbsolutePath(); + List data = new ArrayList<>(); + List cms = new ArrayList<>(); + + try (Stream stream = Files.lines(Paths.get(fileName))) { + List lines = stream.collect(Collectors.toList()); + return createManifestFileModel(data, cms, lines); + + } catch (IOException e) { + throw new IllegalArgumentException(String.format("Unable to process manifest file! Wrong file path: '%s'", fileName)); + } + } + + private ManifestFileModel createManifestFileModel(List data, List cms, List lines) { + boolean isCmsSection = false; + + for (String line : lines) { + if (line.contains("BEGIN CMS")) { + isCmsSection = true; + } + + if (isCmsSection) { + cms.add(line); + } else { + data.add(line); + } + } + return new ManifestFileModel(data, cms); + } +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java index a168541..b8b3714 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java @@ -18,6 +18,7 @@ package org.onap.cvc.csar.security; import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; @@ -27,6 +28,7 @@ import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.util.Store; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,23 +36,35 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Collection; +import java.util.Optional; public class CmsSignatureValidator { private static final Logger LOG = LoggerFactory.getLogger(CmsSignatureValidator.class); public boolean verifySignedData( - final byte[] signature, - final byte[] certificate, - final byte[] csarFileContent) throws CmsSignatureValidatorException { - - try (ByteArrayInputStream signatureStream = new ByteArrayInputStream(signature)) { - SignerInformation firstSigner = getSignerInformation(csarFileContent, signatureStream); - X509Certificate cert = loadCertificate(certificate); + final byte[] cmsSignature, + final Optional certificate, + final byte[] fileContent) throws CmsSignatureValidatorException { + + try (ByteArrayInputStream cmsSignatureStream = new ByteArrayInputStream(cmsSignature)) { + CMSSignedData signedData = getCMSSignedData(fileContent, cmsSignatureStream); + Collection signers = signedData.getSignerInfos().getSigners(); + SignerInformation firstSigner = signers.iterator().next(); + + Store certificates = signedData.getCertificates(); + X509Certificate cert; + if (!certificate.isPresent()) { + X509CertificateHolder firstSignerFirstCertificate = getX509CertificateHolder(firstSigner, certificates); + cert = loadCertificate(firstSignerFirstCertificate.getEncoded()); + } else { + cert = loadCertificate(certificate.get()); + } return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert)); } catch (CMSSignerDigestMismatchException e){ @@ -63,17 +77,22 @@ public class CmsSignatureValidator { } } - private SignerInformation getSignerInformation(byte[] innerPackageFileCSAR, ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException, CMSException { + private X509CertificateHolder getX509CertificateHolder(SignerInformation firstSigner, Store certificates) throws CmsSignatureValidatorException { + Collection firstSignerCertificates = certificates.getMatches(firstSigner.getSID()); + if(!firstSignerCertificates.iterator().hasNext()){ + throw new CmsSignatureValidatorException("No certificate found in cms signature that should contain one!"); + } + return firstSignerCertificates.iterator().next(); + } + + private CMSSignedData getCMSSignedData(byte[] innerPackageFileCSAR, ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException, CMSException { ContentInfo signature = produceSignature(signatureStream); CMSTypedData signedContent = new CMSProcessableByteArray(innerPackageFileCSAR); - CMSSignedData signedData = new CMSSignedData(signedContent, signature); - - Collection signers = signedData.getSignerInfos().getSigners(); - return signers.iterator().next(); + return new CMSSignedData(signedContent, signature); } private ContentInfo produceSignature(ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException { - Object parsedObject = new PEMParser(new InputStreamReader(signatureStream)).readObject(); + Object parsedObject = new PEMParser(new InputStreamReader(signatureStream, Charset.defaultCharset())).readObject(); if (!(parsedObject instanceof ContentInfo)) { throw new CmsSignatureValidatorException("Signature is not recognized!"); } diff --git a/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand b/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand index c6f2934..aadb653 100644 --- a/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand +++ b/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand @@ -47,5 +47,5 @@ org.onap.cvc.csar.cc.sol004.VTPValidateCSARR293901 org.onap.cvc.csar.cc.sol004.VTPValidateCSARR146092 org.onap.cvc.csar.cc.sol004.VTPValidateCSARR57019 org.onap.cvc.csar.cc.sol004.VTPValidateCSARR787965 -org.onap.cvc.csar.cc.sol004.VTPValidateCSARR787966 +org.onap.cvc.csar.cc.sol004.VTPValidateCSARR130206 diff --git a/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r130206.yaml b/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r130206.yaml new file mode 100644 index 0000000..bf8be04 --- /dev/null +++ b/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r130206.yaml @@ -0,0 +1,60 @@ +# Copyright 2019 Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +open_cli_schema_version: 1.0 + +name: csar-validate-r130206 + +description: | + The VNF/PNF package shall contain a Digest (a.k.a. hash) for each of the components of the VNF package. The table of hashes is included in the manifest file, which is signed with the VNF provider private key. In addition, the VNF provider shall include a signing certificate that includes the VNF provider public key, following a pre-defined naming convention and located either at the root of the archive or in a predefined location (e.g. directory). + +info: + product: onap-vtp + version: 1.0 + service: validation + author: ONAP VTP Team onap-discuss@lists.onap.org + +parameters: + - name: csar + description: CSAR file path + long_option: csar + short_option: b + type: binary + is_optional: false + - name: pnf + description: CSAR file contains PNF + long_option: pnf + short_option: p + type: bool + is_optional: true + default_value: false +results: + direction: landscape + attributes: + - name: code + description: Error code + scope: short + type: string + - name: message + description: Error message + scope: short + type: string + - name: file + description: File in which error occured + scope: short + type: string + - name: line-no + description: Line no at which error occured + scope: short + type: string diff --git a/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml b/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml deleted file mode 100644 index 0482836..0000000 --- a/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2019 Nokia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -open_cli_schema_version: 1.0 - -name: csar-validate-r787966 - -description: | - The VNF/PNF package shall contain a Digest (a.k.a. hash) for each of the components of the VNF package. The table of hashes is included in the manifest file, which is signed with the VNF provider private key. In addition, the VNF provider shall include a signing certificate that includes the VNF provider public key, following a pre-defined naming convention and located either at the root of the archive or in a predefined location (e.g. directory). - -info: - product: onap-vtp - version: 1.0 - service: validation - author: ONAP VTP Team onap-discuss@lists.onap.org - -parameters: - - name: csar - description: CSAR file path - long_option: csar - short_option: b - type: binary - is_optional: false - - name: pnf - description: CSAR file contains PNF - long_option: pnf - short_option: p - type: bool - is_optional: true - default_value: false -results: - direction: landscape - attributes: - - name: code - description: Error code - scope: short - type: string - - name: message - description: Error message - scope: short - type: string - - name: file - description: File in which error occured - scope: short - type: string - - name: line-no - description: Line no at which error occured - scope: short - type: string diff --git a/csarvalidation/src/main/resources/vnfreqs.properties b/csarvalidation/src/main/resources/vnfreqs.properties index 8b5d488..abbdb73 100644 --- a/csarvalidation/src/main/resources/vnfreqs.properties +++ b/csarvalidation/src/main/resources/vnfreqs.properties @@ -1,5 +1,5 @@ -vnfreqs.enabled=r02454,r04298,r07879,r09467,r13390,r23823,r26881,r27310,r35851,r40293,r43958,r66070,r77707,r77786,r87234,r10087,r21322,r26885,r40820,r35854,r65486,r17852,r46527,r15837,r54356,r67895,r95321,r32155,r01123,r51347,r787965,r787966 -pnfreqs.enabled=r10087,r87234,r35854,r15837,r17852,r293901,r146092,r57019,r787965,r787966 +vnfreqs.enabled=r02454,r04298,r07879,r09467,r13390,r23823,r26881,r27310,r35851,r40293,r43958,r66070,r77707,r77786,r87234,r10087,r21322,r26885,r40820,r35854,r65486,r17852,r46527,r15837,r54356,r67895,r95321,r32155,r01123,r51347,r787965,r130206 +pnfreqs.enabled=r10087,r87234,r35854,r15837,r17852,r293901,r146092,r57019,r787965,r130206 # ignored all chef and ansible related tests vnferrors.ignored=0x1005,0x1006,r07879-0x1000,r13390-0x1000,r27310-0x1000,r40293-0x1000,r77786-0x1000,r04298-0x1000,r07879-0x1000,r10087-0x1000,r13390-0x1000,r23823-0x1000,r26881-0x1000,r40820-0x1000,r35851-0x1000,r32155-0x1000,r54356-0x1000,r67895-0x1000,r95321-0x1000,r46527-0x1000,r02454-0x1000 -pnferrors.ignored= \ No newline at end of file +pnferrors.ignored= diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java index 6e56959..a708f47 100644 --- a/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java +++ b/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java @@ -120,9 +120,39 @@ public class PnfManifestParserTest { List errors = sourcesPair.getValue(); assertThat(cms).isEqualTo( - "MIGDBgsqhkiG9w0BCRABCaB0MHICAQAwDQYLKoZIhvcNAQkQAwgwXgYJKoZIhvcN" + - "AQcBoFEET3icc87PK0nNK9ENqSxItVIoSa0o0S/ISczMs1ZIzkgsKk4tsQ0N1nUM" + - "dvb05OXi5XLPLEtViMwvLVLwSE0sKlFIVHAqSk3MBkkBAJv0Fx0=" + "MIIGDAYJKoZIhvcNAQcCoIIF/TCCBfkCAQExDTALBglghkgBZQMEAgEwCwYJKoZI"+ + "hvcNAQcBoIIDRTCCA0EwggIpAhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDANBgkqhkiG"+ + "9w0BAQsFADBlMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G"+ + "A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMR4wHAYDVQQLDBVDZXJ0aWZp"+ + "Y2F0ZSBBdXRob3JpdHkwHhcNMTkwODEzMDg0NDU4WhcNMTkwOTEyMDg0NDU4WjBV"+ + "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50"+ + "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ4wDAYDVQQLDAVOb2tpYTCCASIwDQYJKoZI"+ + "hvcNAQEBBQADggEPADCCAQoCggEBAPRCM8g0cm1nuojFuk01Lo1iAbj7STEbiqJn"+ + "Xk4BEEspM4snShj35bO9DHSunXivdCzen4BE7hLpetpbr+7ptqpV7NuR9DgYD399"+ + "eAltb4oLnkLWgODCxhFOnwrKjnXSbP8KX3kmYRJmDzsSjJrpattfxNCa2aHzubyA"+ + "W0Mv9Ni2R0scnBY+ubydwn223d/743T2pfXsiOV6Ucjhz+9XWU96b7e9GxN12EJQ"+ + "R6R4O9dz3CSZmQsiMMYROD5elV59Y9ucSkhdrUjPzjveqjEA9FWc0piBpe42c9Mo"+ + "Lr8S5hKaaC8ONfSUBuEysKC5g6D6OS1Kxii3zbUbNzpxXti8tmUCAwEAATANBgkq"+ + "hkiG9w0BAQsFAAOCAQEAVJGCH8VL/ha1RYmoZBefCT/AQc50GlcIJtPCB8Y7ygkX"+ + "Y2Ybj6SrF66+wq6hQsU9xtxHyn08nfOdGWfNJ9yq4SO8RF7Oz4NxkQ+KFhi2QUGZ"+ + "5TwdWLr0Q+zKTZgpLZm1rtlyyz+2AUwcPPVHhDfJX0kqz/0UPHWFDxXfJyOwmQdN"+ + "E4qhO9uB3zEujJwM8B7wXfDwsNg6xbKBytm67IHQN3OF/Bfcugx7eCVJ08XA8Irj"+ + "CovwPvjxaL32iYTXmiBl+vSb3lEarbinMkMCq80yx3LtIg1goGVO+Tp+yOoVxNUL"+ + "psSXr9kdWncI1venEjk/SvggxtT4RJ6dLH358qFu+TGCAo0wggKJAgEBMH0wZTEL"+ + "MAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy"+ + "bmV0IFdpZGdpdHMgUHR5IEx0ZDEeMBwGA1UECwwVQ2VydGlmaWNhdGUgQXV0aG9y"+ + "aXR5AhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDALBglghkgBZQMEAgGggeQwGAYJKoZI"+ + "hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwODEzMDg0NTI2"+ + "WjAvBgkqhkiG9w0BCQQxIgQgOtAYNJkSFj5rU8K7Ujz6BdefH7sITKBcMmBcm/hI"+ + "TUAweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZIAWUDBAEWMAsG"+ + "CWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAwDQYIKoZIhvcN"+ + "AwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwDQYJKoZIhvcNAQEBBQAEggEA"+ + "Sj+3i3Mcxz6Uqf8WcLNiR3K3QeQUEQJurPHW/BzidjPx+PoZ+6jP8sAkulUu/yeo"+ + "rv3dDQGq0cF6KE3gKi3IXgCOB5nZ/O4BtvPcKOlQk14fMdBnHQMgGb27dNLMheuo"+ + "t4YJVEZNm+1NoYZBMyESm1Ns3DHmq7dqpFMWSad85gMTsbD/q896ZMiua+bLvnlg"+ + "qJXtYrnJPx9KqSzNFhzTqwFMJ9OASaHm+eV9/EWWLJ0rgUmheI0sb2Pa5i93w6dr"+ + "HhE7UbSCHDlDDgrOosJkbuI4UCX/njXrU2ukXbrWz/FjH84Mek039z+w4M6fBnl5"+ + "4xuyO1o65LlKHoxwnRH9lQ==" ); assertThat(errors.size()).isEqualTo(0); } diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java new file mode 100644 index 0000000..90da946 --- /dev/null +++ b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java @@ -0,0 +1,124 @@ +/* + * Copyright 2019 Nokia + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.cc.sol004; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.onap.cvc.csar.CSARArchive; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.configureTestCase; +import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.convertToMessagesList; + + +public class VTPValidateCSARR130206IntegrationTest { + + private static final boolean IS_PNF = true; + private VTPValidateCSARR130206 testCase; + + @Before + public void setUp() { + testCase = new VTPValidateCSARR130206(); + } + + @Test + public void shouldReturnProperRequestNumber() { + assertThat(testCase.getVnfReqsNo()).isEqualTo("R130206"); + } + + @Test + @Ignore("It is impossible to write test which will always pass, because certificate used to sign the file has time validity." + + "To verify signed package please please follow instructions from test/resources/README.txt file and comment @Ignore tag. " + + "Use instructions for option 1. Test was created for manual verification." + ) + public void manual_shouldValidateProperCsar() throws Exception { + + // given + configureTestCase(testCase, "pnf/r130206/csar-option1-valid.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + assertThat(errors.size()).isEqualTo(0); + } + + @Test + public void shouldReportThatOnlySignatureIsInvalid() throws Exception { + + // given + configureTestCase(testCase, "pnf/r130206/csar-option1-validSection.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + assertThat(errors.size()).isEqualTo(1); + assertThat(convertToMessagesList(errors)).contains( + "File has invalid CMS signature!" + ); + } + + @Test + public void shouldReportErrorsForInvalidCsar() throws Exception { + + // given + configureTestCase(testCase, "pnf/r130206/csar-option1-invalid.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + assertThat(errors.size()).isEqualTo(5); + assertThat(convertToMessagesList(errors)).contains( + "Unable to find CMS section in manifest!", + "Source 'Definitions/MainServiceTemplate.yaml' has wrong hash!", + "Source 'Artifacts/Other/my_script.csh' has hash, but unable to find algorithm tag!", + "Unable to calculate digest - file missing: Artifacts/NonExisting2.txt", + "File has invalid CMS signature!" + ); + } + + + @Test + public void shouldReportThanInVnfPackageCertFileWasNotDefined() throws Exception { + + // given + configureTestCase(testCase, "sample2.csar", "vtp-validate-csar-r130206.yaml", false); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + assertThat(convertToMessagesList(errors)).contains( + "Unable to find cert file defined by Entry-Certificate!", + "Unable to find CMS section in manifest!", + "Missing. Entry [tosca_definitions_version]" + ); + } + + + +} diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java index eb41d6a..49696e6 100644 --- a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java +++ b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java @@ -18,6 +18,7 @@ package org.onap.cvc.csar.cc.sol004; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.onap.cvc.csar.CSARArchive; @@ -61,7 +62,11 @@ public class VTPValidateCSARR787965IntegrationTest { } @Test - public void shouldReportThatZipContainsSignatureWithCertificationFileAndPackageIsProbableValid() throws Exception { + @Ignore("It is impossible to write test which will always pass, because certificate used to sign the file has time validity." + + "To verify signed package please please follow instructions from test/resources/README.txt file and comment @Ignore tag. " + + "Use instructions for option 2. Test was created for manual verification." + ) + public void manual_shouldReportThatZipContainsSignatureWithCertificationFileAndPackageIsValid() throws Exception { // given configureTestCase(testCase, "pnf/r787965/signature-and-certificate.zip", "vtp-validate-csar-r787965.yaml", IS_PNF); @@ -71,12 +76,7 @@ public class VTPValidateCSARR787965IntegrationTest { // then List errors = testCase.getErrors(); - assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( - "Warning. Zip package probably is valid. " + - "It contains only signature with certification cms and csar package. " + - "Unable to verify csar signature." - ); + assertThat(errors.size()).isEqualTo(0); } @Test @@ -97,7 +97,11 @@ public class VTPValidateCSARR787965IntegrationTest { } @Test - public void shouldDoNotReportAnyErrorWhenPackageHasValidSignature() throws Exception { + @Ignore("It is impossible to write test which will always pass, because certificate used to sign the file has time validity." + + "To verify signed package please please follow instructions from test/resources/README.txt file and comment @Ignore tag. " + + "Use instructions for option 2. Test was created for manual verification." + ) + public void manual_shouldDoNotReportAnyErrorWhenPackageHasValidSignature() throws Exception { // given configureTestCase(testCase, "pnf/signed-package-valid-signature.zip", "vtp-validate-csar-r787965.yaml", IS_PNF); @@ -110,4 +114,4 @@ public class VTPValidateCSARR787965IntegrationTest { assertThat(errors.size()).isEqualTo(0); } -} \ No newline at end of file +} diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966IntegrationTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966IntegrationTest.java deleted file mode 100644 index d48869a..0000000 --- a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966IntegrationTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2019 Nokia - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.onap.cvc.csar.cc.sol004; - -import org.junit.Before; -import org.junit.Test; -import org.onap.cvc.csar.CSARArchive; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.configureTestCase; -import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.convertToMessagesList; - - -public class VTPValidateCSARR787966IntegrationTest { - - private static final boolean IS_PNF = true; - private VTPValidateCSARR787966 testCase; - - @Before - public void setUp() { - testCase = new VTPValidateCSARR787966(); - } - - @Test - public void shouldReturnProperRequestNumber() { - assertThat(testCase.getVnfReqsNo()).isEqualTo("R787966"); - } - - @Test - public void shouldValidateProperCsar() throws Exception { - - // given - configureTestCase(testCase, "pnf/r787966/csar-option1-valid.csar", "vtp-validate-csar-r787966.yaml", IS_PNF); - - // when - testCase.execute(); - - // then - List errors = testCase.getErrors(); - assertThat(errors.size()).isEqualTo(0); - } - - @Test - public void shouldReportErrorsForInvalidCsar() throws Exception { - - // given - configureTestCase(testCase, "pnf/r787966/csar-option1-invalid.csar", "vtp-validate-csar-r787966.yaml", IS_PNF); - - // when - testCase.execute(); - - // then - List errors = testCase.getErrors(); - assertThat(errors.size()).isEqualTo(4); - assertThat(convertToMessagesList(errors)).contains( - "Unable to find CMS section in manifest!", - "Source 'Definitions/MainServiceTemplate.yaml' has wrong hash!", - "Source 'Artifacts/Other/my_script.csh' has hash, but unable to find algorithm tag!", - "Source 'Artifacts/NonExisting.txt' does not exist!" - ); - } - - - @Test - public void shouldReportThanInVnfPackageCertFileWasNotDefined() throws Exception { - - // given - configureTestCase(testCase, "sample2.csar", "vtp-validate-csar-r787966.yaml", false); - - // when - testCase.execute(); - - // then - List errors = testCase.getErrors(); - assertThat(convertToMessagesList(errors)).contains( - "Unable to find cert file defined by Entry-Certificate!", - "Missing. Entry [tosca_definitions_version]" - ); - } - - - -} \ No newline at end of file diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java new file mode 100644 index 0000000..b530691 --- /dev/null +++ b/csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2019 Nokia + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.io.File; + +/* + How to sing files see to README.txt file into test/resources folder + */ +public class ManifestFileSplitterTest { + + @Test + public void shouldSplitManifestFileOnDataPartAndCMS() { + File file = new File("./src/test/resources/cvc/csar/parser/MainServiceTemplate.mf"); + ManifestFileSplitter manifestFileSplitter = new ManifestFileSplitter(); + + ManifestFileModel manifestFileModel = manifestFileSplitter.split(file); + + Assertions.assertThat(manifestFileModel.getData()).contains("metadata:", + " pnfd_name: RadioNode", + " pnfd_provider: Ericsson", + " pnfd_archive_version: 1.0", + " pnfd_release_date_time: 2019-01-14T11:25:00+00:00"); + + Assertions.assertThat(manifestFileModel.getCMS()).contains( + "-----BEGIN CMS-----", + "MIIGDAYJKoZIhvcNAQcCoIIF/TCCBfkCAQExDTALBglghkgBZQMEAgEwCwYJKoZI", + "hvcNAQcBoIIDRTCCA0EwggIpAhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDANBgkqhkiG", + "-----END CMS-----" + ); + } +} diff --git a/csarvalidation/src/test/resources/README.txt b/csarvalidation/src/test/resources/README.txt new file mode 100644 index 0000000..1360ce9 --- /dev/null +++ b/csarvalidation/src/test/resources/README.txt @@ -0,0 +1,20 @@ +How to sign CSAR file - option 2 +--------------------- +openssl req -new -nodes -x509 -keyout root-private-key.pem > root.cert +openssl req -new -nodes -keyout sample-pnf-private-key.pem > sample-pnf-request.pem +openssl x509 -req -CA root.cert -CAkey root-private-key.pem -CAcreateserial < sample-pnf-request.pem > sample-pnf.cert +openssl cms -sign -binary -nocerts -outform pem -signer sample-pnf.cert -inkey sample-pnf-private-key.pem < sample-pnf.csar > sample-pnf.cms + +How to sign CSAR file - option 1 +-------------------------------- +openssl req -new -nodes -x509 -keyout root-private-key.pem > root-certificate.cert +TIP: As a 'Organizational Unit Name' set, for example: Certificate Authority + +openssl req -new -nodes -keyout signing-private-key.pem > signing-request.pem +TIP: As a 'Organizational Unit Name' set, for example: Nokia. Name values must be different! + +openssl x509 -req -CA root-certificate.cert -CAkey root-private-key.pem -CAcreateserial < signing-request.pem > signing-certificate.cert + +openssl cms -sign -signer signing-certificate.cert -inkey signing-private-key.pem -outform pem -binary < MainServiceTemplate.mf > signature-and-certificate.cms + +openssl cms -verify -content MainServiceTemplate.mf -CAfile root-certificate.cert -inform pem -binary < signature-and-certificate.cms > /dev/null diff --git a/csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf b/csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf new file mode 100644 index 0000000..556c006 --- /dev/null +++ b/csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf @@ -0,0 +1,10 @@ +metadata: + pnfd_name: RadioNode + pnfd_provider: Ericsson + pnfd_archive_version: 1.0 + pnfd_release_date_time: 2019-01-14T11:25:00+00:00 + +-----BEGIN CMS----- +MIIGDAYJKoZIhvcNAQcCoIIF/TCCBfkCAQExDTALBglghkgBZQMEAgEwCwYJKoZI +hvcNAQcBoIIDRTCCA0EwggIpAhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDANBgkqhkiG +-----END CMS----- diff --git a/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf b/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf index 6987eb1..6bc88b2 100644 --- a/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf +++ b/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf @@ -35,7 +35,37 @@ non_mano_artifact_sets: source: Artifacts/Other/review_log.txt -----BEGIN CMS----- -MIGDBgsqhkiG9w0BCRABCaB0MHICAQAwDQYLKoZIhvcNAQkQAwgwXgYJKoZIhvcN -AQcBoFEET3icc87PK0nNK9ENqSxItVIoSa0o0S/ISczMs1ZIzkgsKk4tsQ0N1nUM -dvb05OXi5XLPLEtViMwvLVLwSE0sKlFIVHAqSk3MBkkBAJv0Fx0= ------END CMS----- \ No newline at end of file +MIIGDAYJKoZIhvcNAQcCoIIF/TCCBfkCAQExDTALBglghkgBZQMEAgEwCwYJKoZI +hvcNAQcBoIIDRTCCA0EwggIpAhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDANBgkqhkiG +9w0BAQsFADBlMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G +A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMR4wHAYDVQQLDBVDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkwHhcNMTkwODEzMDg0NDU4WhcNMTkwOTEyMDg0NDU4WjBV +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ4wDAYDVQQLDAVOb2tpYTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAPRCM8g0cm1nuojFuk01Lo1iAbj7STEbiqJn +Xk4BEEspM4snShj35bO9DHSunXivdCzen4BE7hLpetpbr+7ptqpV7NuR9DgYD399 +eAltb4oLnkLWgODCxhFOnwrKjnXSbP8KX3kmYRJmDzsSjJrpattfxNCa2aHzubyA +W0Mv9Ni2R0scnBY+ubydwn223d/743T2pfXsiOV6Ucjhz+9XWU96b7e9GxN12EJQ +R6R4O9dz3CSZmQsiMMYROD5elV59Y9ucSkhdrUjPzjveqjEA9FWc0piBpe42c9Mo +Lr8S5hKaaC8ONfSUBuEysKC5g6D6OS1Kxii3zbUbNzpxXti8tmUCAwEAATANBgkq +hkiG9w0BAQsFAAOCAQEAVJGCH8VL/ha1RYmoZBefCT/AQc50GlcIJtPCB8Y7ygkX +Y2Ybj6SrF66+wq6hQsU9xtxHyn08nfOdGWfNJ9yq4SO8RF7Oz4NxkQ+KFhi2QUGZ +5TwdWLr0Q+zKTZgpLZm1rtlyyz+2AUwcPPVHhDfJX0kqz/0UPHWFDxXfJyOwmQdN +E4qhO9uB3zEujJwM8B7wXfDwsNg6xbKBytm67IHQN3OF/Bfcugx7eCVJ08XA8Irj +CovwPvjxaL32iYTXmiBl+vSb3lEarbinMkMCq80yx3LtIg1goGVO+Tp+yOoVxNUL +psSXr9kdWncI1venEjk/SvggxtT4RJ6dLH358qFu+TGCAo0wggKJAgEBMH0wZTEL +MAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy +bmV0IFdpZGdpdHMgUHR5IEx0ZDEeMBwGA1UECwwVQ2VydGlmaWNhdGUgQXV0aG9y +aXR5AhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDALBglghkgBZQMEAgGggeQwGAYJKoZI +hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwODEzMDg0NTI2 +WjAvBgkqhkiG9w0BCQQxIgQgOtAYNJkSFj5rU8K7Ujz6BdefH7sITKBcMmBcm/hI +TUAweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZIAWUDBAEWMAsG +CWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAwDQYIKoZIhvcN +AwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwDQYJKoZIhvcNAQEBBQAEggEA +Sj+3i3Mcxz6Uqf8WcLNiR3K3QeQUEQJurPHW/BzidjPx+PoZ+6jP8sAkulUu/yeo +rv3dDQGq0cF6KE3gKi3IXgCOB5nZ/O4BtvPcKOlQk14fMdBnHQMgGb27dNLMheuo +t4YJVEZNm+1NoYZBMyESm1Ns3DHmq7dqpFMWSad85gMTsbD/q896ZMiua+bLvnlg +qJXtYrnJPx9KqSzNFhzTqwFMJ9OASaHm+eV9/EWWLJ0rgUmheI0sb2Pa5i93w6dr +HhE7UbSCHDlDDgrOosJkbuI4UCX/njXrU2ukXbrWz/FjH84Mek039z+w4M6fBnl5 +4xuyO1o65LlKHoxwnRH9lQ== +-----END CMS----- diff --git a/csarvalidation/src/test/resources/pnf/README.txt b/csarvalidation/src/test/resources/pnf/README.txt deleted file mode 100644 index 8984d6f..0000000 --- a/csarvalidation/src/test/resources/pnf/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -How to sign CSAR file ---------------------- -openssl req -new -nodes -x509 -keyout root-private-key.pem > root.cert -openssl req -new -nodes -keyout sample-pnf-private-key.pem > sample-pnf-request.pem -openssl x509 -req -CA root.cert -CAkey root-private-key.pem -CAcreateserial < sample-pnf-request.pem > sample-pnf.cert -openssl cms -sign -binary -nocerts -outform pem -signer sample-pnf.cert -inkey sample-pnf-private-key.pem < sample-pnf.csar > sample-pnf.cms \ No newline at end of file diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-option1-invalid.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-invalid.csar new file mode 100644 index 0000000..187c008 Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-invalid.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csar new file mode 100644 index 0000000..7cca18d Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-option1-validSection.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-validSection.csar new file mode 100644 index 0000000..bc90a75 Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-validSection.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-invalid.csar b/csarvalidation/src/test/resources/pnf/r787966/csar-option1-invalid.csar deleted file mode 100644 index 0213b60..0000000 Binary files a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-invalid.csar and /dev/null differ diff --git a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-valid.csar b/csarvalidation/src/test/resources/pnf/r787966/csar-option1-valid.csar deleted file mode 100644 index 17aa662..0000000 Binary files a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-valid.csar and /dev/null differ -- cgit 1.2.3-korg