package ch.ge.ve.offlineadmin.services;
/*-
* #%L
* Admin offline
* %%
* Copyright (C) 2015 - 2016 République et Canton de Genève
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import ch.ge.ve.commons.crypto.ballot.BallotCipherService;
import ch.ge.ve.commons.crypto.ballot.EncryptedBallotAndWrappedKey;
import ch.ge.ve.commons.crypto.exceptions.CryptoOperationRuntimeException;
import ch.ge.ve.commons.crypto.utils.SecureRandomFactory;
import ch.ge.ve.offlineadmin.util.ProgressTracker;
import com.google.common.base.Strings;
import org.apache.log4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Ballots box decryption service
*/
public class DecryptionService {
public static final int STEP_SIZE = 100;
private static final Logger LOGGER = Logger.getLogger(DecryptionService.class);
private final BallotCipherService ballotCipherService;
private final ProgressTracker progressTracker;
private AtomicInteger invalidCounter = new AtomicInteger();
/**
* @param ballotCipherService providing decryption service
* @param progressTracker providing tracking utility
*/
public DecryptionService(BallotCipherService ballotCipherService, ProgressTracker progressTracker) {
this.ballotCipherService = ballotCipherService;
this.progressTracker = progressTracker;
}
/**
* Decrypts a list of encrypted ballots
* @param encryptedBallots list of encrypted ballots
* @return list of decrypted ballots
*/
public List<String> decrypt(List<EncryptedBallotAndWrappedKey> encryptedBallots) {
AtomicInteger counter = new AtomicInteger();
invalidCounter = new AtomicInteger();
Stream<String> decryptedBallots = encryptedBallots.parallelStream().map(encryptedBallot -> decryptBallot(counter, encryptedBallot));
List<String> validDecryptedBallots = decryptedBallots.filter(ballot -> !Strings.isNullOrEmpty(ballot)).collect(Collectors.toList());
Collections.shuffle(validDecryptedBallots, SecureRandomFactory.createPRNG());
return validDecryptedBallots;
}
private String decryptBallot(AtomicInteger counter, EncryptedBallotAndWrappedKey encryptedBallot) {
try {
if (counter.incrementAndGet() % STEP_SIZE == 0) {
progressTracker.incrementStepCount();
}
return ballotCipherService.decryptBallot(encryptedBallot);
} catch (CryptoOperationRuntimeException e) {
LOGGER.error(e);
invalidCounter.incrementAndGet();
return null;
}
}
/**
* Getter for the number of invalid ballots. Those ballots generated an error when trying to decrypt them.
* @return the number of undecryptable ballots
*/
public int getInvalidCounter() {
return invalidCounter.get();
}
}