org.gradle.api.internalverification.signatures.CrossBuildSignatureVerificationService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2019 the original author or authors.
*
* 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.gradle.api.internal.artifacts.verification.signatures;
import com.google.common.collect.Lists;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.gradle.api.internal.cache.StringInterner;
import org.gradle.cache.CacheRepository;
import org.gradle.cache.FileLockManager;
import org.gradle.cache.PersistentCache;
import org.gradle.cache.PersistentIndexedCache;
import org.gradle.cache.PersistentIndexedCacheParameters;
import org.gradle.cache.internal.CacheScopeMapping;
import org.gradle.cache.internal.InMemoryCacheDecoratorFactory;
import org.gradle.cache.internal.VersionStrategy;
import org.gradle.initialization.layout.ProjectCacheDir;
import org.gradle.internal.hash.FileHasher;
import org.gradle.internal.hash.HashCode;
import org.gradle.internal.serialize.AbstractSerializer;
import org.gradle.internal.serialize.Decoder;
import org.gradle.internal.serialize.Encoder;
import org.gradle.internal.serialize.InterningStringSerializer;
import org.gradle.internal.serialize.SetSerializer;
import org.gradle.security.internal.PublicKeyService;
import org.gradle.util.BuildCommencedTimeProvider;
import java.io.File;
import java.util.List;
import java.util.Set;
import static org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService.MISSING_KEY_TIMEOUT;
import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
public class CrossBuildSignatureVerificationService implements SignatureVerificationService {
private final SignatureVerificationService delegate;
private final FileHasher fileHasher;
private final BuildCommencedTimeProvider timeProvider;
private final boolean refreshKeys;
private final PersistentCache store;
private final PersistentIndexedCache cache;
public CrossBuildSignatureVerificationService(SignatureVerificationService delegate,
FileHasher fileHasher,
CacheScopeMapping cacheScopeMapping,
ProjectCacheDir projectCacheDir,
CacheRepository repository,
InMemoryCacheDecoratorFactory inMemoryCacheDecoratorFactory,
BuildCommencedTimeProvider timeProvider,
boolean refreshKeys) {
this.delegate = delegate;
this.fileHasher = fileHasher;
this.timeProvider = timeProvider;
this.refreshKeys = refreshKeys;
File cacheDir = cacheScopeMapping.getBaseDirectory(projectCacheDir.getDir(), "signature-verification", VersionStrategy.CachePerVersion);
store = repository.cache(cacheDir)
.withDisplayName("Signature verification cache")
.withLockOptions(mode(FileLockManager.LockMode.OnDemand)) // Lock on demand
.open();
InterningStringSerializer stringSerializer = new InterningStringSerializer(new StringInterner());
cache = store.createCache(
PersistentIndexedCacheParameters.of(
"signature-verification",
new CacheKeySerializer(stringSerializer, new SetSerializer<>(stringSerializer)),
new CacheEntrySerializer(stringSerializer)
).withCacheDecorator(inMemoryCacheDecoratorFactory.decorator(500, true)));
}
@Override
public void verify(File origin, File signature, Set trustedKeys, Set ignoredKeys, SignatureVerificationResultBuilder builder) {
CacheKey cacheKey = new CacheKey(origin.getAbsolutePath(), signature.getAbsolutePath(), trustedKeys, ignoredKeys);
HashCode originHash = fileHasher.hash(origin);
HashCode signatureHash = fileHasher.hash(signature);
CacheEntry entry = cache.getIfPresent(cacheKey);
if (entry == null || entry.updated(originHash, signatureHash) || hasExpired(entry)) {
entry = performActualVerification(origin, signature, trustedKeys, ignoredKeys, originHash, signatureHash);
cache.put(cacheKey, entry);
}
entry.applyTo(builder);
}
private boolean hasExpired(CacheEntry entry) {
List missingKeys = entry.missingKeys;
if (missingKeys == null || missingKeys.isEmpty()) {
return false;
}
long elapsed = entry.timestamp - timeProvider.getCurrentTime();
return refreshKeys || elapsed > MISSING_KEY_TIMEOUT;
}
@Override
public PublicKeyService getPublicKeyService() {
return delegate.getPublicKeyService();
}
private CacheEntry performActualVerification(File origin, File signature, Set trustedKeys, Set ignoredKeys, HashCode originHash, HashCode signatureHash) {
CacheEntryBuilder result = new CacheEntryBuilder(timeProvider.getCurrentTime(), originHash, signatureHash);
delegate.verify(origin, signature, trustedKeys, ignoredKeys, result);
return result.build();
}
@Override
public void stop() {
delegate.stop();
store.close();
}
private static class CacheKey {
private final String filePath;
private final String signaturePath;
private final Set trustedKeys;
private final Set ignoredKeys;
private CacheKey(String filePath, String signaturePath, Set trustedKeys, Set ignoredKeys) {
this.filePath = filePath;
this.signaturePath = signaturePath;
this.trustedKeys = trustedKeys;
this.ignoredKeys = ignoredKeys;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CacheKey cacheKey = (CacheKey) o;
if (!filePath.equals(cacheKey.filePath)) {
return false;
}
if (!signaturePath.equals(cacheKey.signaturePath)) {
return false;
}
if (!trustedKeys.equals(cacheKey.trustedKeys)) {
return false;
}
return ignoredKeys.equals(cacheKey.ignoredKeys);
}
@Override
public int hashCode() {
int result = filePath.hashCode();
result = 31 * result + signaturePath.hashCode();
result = 31 * result + trustedKeys.hashCode();
result = 31 * result + ignoredKeys.hashCode();
return result;
}
}
private static class CacheKeySerializer extends AbstractSerializer {
private final InterningStringSerializer delegate;
private final SetSerializer setSerializer;
private CacheKeySerializer(InterningStringSerializer stringSerializer, SetSerializer setSerializer) {
this.delegate = stringSerializer;
this.setSerializer = setSerializer;
}
@Override
public CacheKey read(Decoder decoder) throws Exception {
return new CacheKey(delegate.read(decoder), delegate.read(decoder), setSerializer.read(decoder), setSerializer.read(decoder));
}
@Override
public void write(Encoder encoder, CacheKey value) throws Exception {
delegate.write(encoder, value.filePath);
delegate.write(encoder, value.signaturePath);
setSerializer.write(encoder, value.trustedKeys);
setSerializer.write(encoder, value.ignoredKeys);
}
}
private static class CacheEntryBuilder implements SignatureVerificationResultBuilder {
private final long timestamp;
private final HashCode originHash;
private final HashCode signatureHash;
private List missingKeys = null;
private List trustedKeys = null;
private List validKeys = null;
private List failedKeys = null;
private List ignoredKeys = null;
private CacheEntryBuilder(long timestamp, HashCode originHash, HashCode signatureHash) {
this.timestamp = timestamp;
this.originHash = originHash;
this.signatureHash = signatureHash;
}
@Override
public void missingKey(String keyId) {
if (missingKeys == null) {
missingKeys = Lists.newArrayList();
}
missingKeys.add(keyId);
}
@Override
public void verified(PGPPublicKey key, boolean trusted) {
if (trusted) {
if (trustedKeys == null) {
trustedKeys = Lists.newArrayList();
}
trustedKeys.add(key);
} else {
if (validKeys == null) {
validKeys = Lists.newArrayList();
}
validKeys.add(key);
}
}
@Override
public void failed(PGPPublicKey pgpPublicKey) {
if (failedKeys == null) {
failedKeys = Lists.newArrayList();
}
failedKeys.add(pgpPublicKey);
}
@Override
public void ignored(String keyId) {
if (ignoredKeys == null) {
ignoredKeys = Lists.newArrayList();
}
ignoredKeys.add(keyId);
}
CacheEntry build() {
return new CacheEntry(timestamp, originHash, signatureHash, missingKeys, trustedKeys, validKeys, failedKeys, ignoredKeys);
}
}
private static class CacheEntry {
private final long timestamp;
private final HashCode originHash;
private final HashCode signatureHash;
private final List missingKeys;
private final List trustedKeys;
private final List validKeys;
private final List failedKeys;
private final List ignoredKeys;
public CacheEntry(long timestamp, HashCode originHash, HashCode signatureHash, List missingKeys, List trustedKeys, List validKeys, List failedKeys, List ignoredKeys) {
this.timestamp = timestamp;
this.originHash = originHash;
this.signatureHash = signatureHash;
this.missingKeys = missingKeys;
this.trustedKeys = trustedKeys;
this.validKeys = validKeys;
this.failedKeys = failedKeys;
this.ignoredKeys = ignoredKeys;
}
void applyTo(SignatureVerificationResultBuilder builder) {
if (missingKeys != null) {
for (String missingKey : missingKeys) {
builder.missingKey(missingKey);
}
}
if (trustedKeys != null) {
for (PGPPublicKey trustedKey : trustedKeys) {
builder.verified(trustedKey, true);
}
}
if (validKeys != null) {
for (PGPPublicKey validKey : validKeys) {
builder.verified(validKey, false);
}
}
if (failedKeys != null) {
for (PGPPublicKey failedKey : failedKeys) {
builder.failed(failedKey);
}
}
if (ignoredKeys != null) {
for (String ignoredKey : ignoredKeys) {
builder.ignored(ignoredKey);
}
}
}
public boolean updated(HashCode originHash, HashCode signatureHash) {
return !this.originHash.equals(originHash) ||
!this.signatureHash.equals(signatureHash);
}
}
private static class CacheEntrySerializer extends AbstractSerializer {
private final InterningStringSerializer stringSerializer;
private final PublicKeySerializer publicKeySerializer = new PublicKeySerializer();
private CacheEntrySerializer(InterningStringSerializer stringSerializer) {
this.stringSerializer = stringSerializer;
}
@Override
public CacheEntry read(Decoder decoder) throws Exception {
long timestamp = decoder.readLong();
HashCode originHash = HashCode.fromBytes(decoder.readBinary());
HashCode signatureHash = HashCode.fromBytes(decoder.readBinary());
List missingKeys = readStringKeys(decoder);
List trustedKeys = readKeys(decoder);
List validKeys = readKeys(decoder);
List failedKeys = readKeys(decoder);
List ignoredKeys = readStringKeys(decoder);
return new CacheEntry(timestamp, originHash, signatureHash, missingKeys, trustedKeys, validKeys, failedKeys, ignoredKeys);
}
private List readStringKeys(Decoder decoder) throws Exception {
int missingKeysLen = decoder.readSmallInt();
List missingKeys = null;
if (missingKeysLen > 0) {
missingKeys = Lists.newArrayListWithCapacity(missingKeysLen);
for (int i = 0; i < missingKeysLen; i++) {
missingKeys.add(stringSerializer.read(decoder));
}
}
return missingKeys;
}
private List readKeys(Decoder decoder) throws Exception {
int len = decoder.readSmallInt();
List keys = null;
if (len > 0) {
keys = Lists.newArrayListWithCapacity(len);
for (int i = 0; i < len; i++) {
keys.add(publicKeySerializer.read(decoder));
}
}
return keys;
}
@Override
public void write(Encoder encoder, CacheEntry value) throws Exception {
encoder.writeLong(value.timestamp);
encoder.writeBinary(value.originHash.toByteArray());
encoder.writeBinary(value.signatureHash.toByteArray());
writeStringKeys(encoder, value.missingKeys);
writeKeys(encoder, value.trustedKeys);
writeKeys(encoder, value.validKeys);
writeKeys(encoder, value.failedKeys);
writeStringKeys(encoder, value.ignoredKeys);
}
private void writeStringKeys(Encoder encoder, List keys) throws Exception {
if (keys == null) {
encoder.writeSmallInt(0);
} else {
encoder.writeSmallInt(keys.size());
for (String key : keys) {
stringSerializer.write(encoder, key);
}
}
}
private void writeKeys(Encoder encoder, List keys) throws Exception {
if (keys == null) {
encoder.writeSmallInt(0);
} else {
encoder.writeSmallInt(keys.size());
for (PGPPublicKey key : keys) {
publicKeySerializer.write(encoder, key);
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy