Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create signature ZIP archive #928

Merged
merged 1 commit into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import org.eclipse.openvsx.cache.CacheService;
import org.eclipse.openvsx.entities.FileResource;
import org.eclipse.openvsx.entities.SignatureKeyPair;
import org.eclipse.openvsx.publish.ExtensionVersionIntegrityService;
import org.eclipse.openvsx.repositories.RepositoryService;
import org.eclipse.openvsx.util.NamingUtil;
Expand All @@ -22,7 +23,9 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Files;
import java.util.Map;

@Component
@ConditionalOnProperty(value = "ovsx.data.mirror.enabled", havingValue = "false", matchIfMissing = true)
Expand Down Expand Up @@ -55,37 +58,45 @@ public void run(MigrationJobRequest jobRequest) throws Exception {
return;
}

var entry = migrations.getDownload(extVersion);
if(entry == null) {
return;
}

logger.info("Generating signature for: {}", NamingUtil.toLogFormat(extVersion));

var keyPair = repositories.findActiveKeyPair();
var signature = createSignature(entry, keyPair);
if(signature == null) {
return;
}

integrityService.setSignatureKeyPair(extVersion, keyPair);
var extension = extVersion.getExtension();
cache.evictExtensionJsons(extVersion);
cache.evictLatestExtensionVersion(extension);
cache.evictNamespaceDetails(extension);

var existingSignature = migrations.getFileResource(extVersion, FileResource.DOWNLOAD_SIG);
if(existingSignature != null) {
migrations.removeFile(existingSignature);
migrations.deleteFileResource(existingSignature);
}

var entry = migrations.getDownload(extVersion);
if(entry == null) {
return;
}
migrations.uploadFileResource(signature);
migrations.persistFileResource(signature);
}

private FileResource createSignature(Map.Entry<FileResource, byte[]> entry, SignatureKeyPair keyPair) throws IOException {
try(var extensionFile = migrations.getExtensionFile(entry)) {
if(Files.size(extensionFile.getPath()) == 0) {
return;
return null;
}

var download = entry.getKey();
var keyPair = repositories.findActiveKeyPair();
var signature = integrityService.generateSignature(download, extensionFile, keyPair);
signature.setStorageType(download.getStorageType());
integrityService.setSignatureKeyPair(extVersion, keyPair);

var extension = extVersion.getExtension();
cache.evictExtensionJsons(extVersion);
cache.evictLatestExtensionVersion(extension);
cache.evictNamespaceDetails(extension);

migrations.uploadFileResource(signature);
migrations.persistFileResource(signature);
return signature;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import static org.eclipse.openvsx.entities.SignatureKeyPair.KEYPAIR_MODE_CREATE;
import static org.eclipse.openvsx.entities.SignatureKeyPair.KEYPAIR_MODE_RENEW;
Expand Down Expand Up @@ -106,12 +110,25 @@ public FileResource generateSignature(FileResource download, TempFile extensionF
resource.setType(FileResource.DOWNLOAD_SIG);

var privateKeyParameters = new Ed25519PrivateKeyParameters(keyPair.getPrivateKey(), 0);
try {
var signer = new Ed25519Signer();
signer.init(true, privateKeyParameters);
var fileBytes = Files.readAllBytes(extensionFile.getPath());
signer.update(fileBytes, 0, fileBytes.length);
resource.setContent(signer.generateSignature());
try (var out = new ByteArrayOutputStream()) {
try (var zip = new ZipOutputStream(out)) {
var signer = new Ed25519Signer();
signer.init(true, privateKeyParameters);
var fileBytes = Files.readAllBytes(extensionFile.getPath());
signer.update(fileBytes, 0, fileBytes.length);
var sigEntry = new ZipEntry(".signature.sig");
zip.putNextEntry(sigEntry);
zip.write(signer.generateSignature());
zip.closeEntry();

// Add dummy file to the archive because VS Code checks if it exists
var dummyEntry = new ZipEntry(".signature.p7s");
zip.putNextEntry(dummyEntry);
zip.write(new byte[0]);
zip.closeEntry();
}

resource.setContent(out.toByteArray());
} catch (IOException e) {
throw new ErrorResultException("Failed to sign extension file", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected void uploadFile(byte[] content, String fileName, String blobName) {
var blobClient = getContainerClient().getBlobClient(blobName);
var headers = new BlobHttpHeaders();
headers.setContentType(StorageUtil.getFileType(fileName).toString());
if (fileName.endsWith(".vsix")) {
if (fileName.endsWith(".vsix") || fileName.endsWith(".sigzip")) {
headers.setContentDisposition("attachment; filename=\"" + fileName + "\"");
} else {
var cacheControl = StorageUtil.getCacheControl(fileName);
Expand Down Expand Up @@ -115,7 +115,7 @@ protected void uploadFile(TempFile file, String fileName, String blobName) {
var blobClient = getContainerClient().getBlobClient(blobName);
var headers = new BlobHttpHeaders();
headers.setContentType(StorageUtil.getFileType(fileName).toString());
if (fileName.endsWith(".vsix")) {
if (fileName.endsWith(".vsix") || fileName.endsWith(".sigzip")) {
headers.setContentDisposition("attachment; filename=\"" + fileName + "\"");
} else {
var cacheControl = StorageUtil.getCacheControl(fileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void uploadNamespaceLogo(Namespace namespace) {
protected void uploadFile(byte[] content, String fileName, String objectId) {
var blobInfoBuilder = BlobInfo.newBuilder(BlobId.of(bucketId, objectId))
.setContentType(StorageUtil.getFileType(fileName).toString());
if (fileName.endsWith(".vsix")) {
if (fileName.endsWith(".vsix") || fileName.endsWith(".sigzip")) {
blobInfoBuilder.setContentDisposition("attachment; filename=\"" + fileName + "\"");
} else {
var cacheControl = StorageUtil.getCacheControl(fileName);
Expand All @@ -111,7 +111,7 @@ public void uploadFile(FileResource resource, TempFile file) {
protected void uploadFile(TempFile file, String fileName, String objectId) {
var blobInfoBuilder = BlobInfo.newBuilder(BlobId.of(bucketId, objectId))
.setContentType(StorageUtil.getFileType(fileName).toString());
if (fileName.endsWith(".vsix")) {
if (fileName.endsWith(".vsix") || fileName.endsWith(".sigzip")) {
blobInfoBuilder.setContentDisposition("attachment; filename=\"" + fileName + "\"");
} else {
var cacheControl = StorageUtil.getCacheControl(fileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ static MediaType getFileType(String fileName) {
return MediaType.APPLICATION_OCTET_STREAM;
if (fileName.endsWith(".json"))
return MediaType.APPLICATION_JSON;
if (fileName.endsWith(".sigzip"))
return MediaType.valueOf("application/zip");
var contentType = URLConnection.guessContentTypeFromName(fileName);
if (contentType != null)
return MediaType.parseMediaType(contentType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ public void increaseDownloadCount(FileResource resource) {
public HttpHeaders getFileResponseHeaders(String fileName) {
var headers = new HttpHeaders();
headers.setContentType(StorageUtil.getFileType(fileName));
if (fileName.endsWith(".vsix")) {
if (fileName.endsWith(".vsix") || fileName.endsWith(".sigzip")) {
headers.add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
} else {
headers.setCacheControl(StorageUtil.getCacheControl(fileName));
Expand Down
Loading