/**
* Copyright 2013-2015 Seagate Technology LLC.
*
* This Source Code Form is subject to the terms of the Mozilla
* Public License, v. 2.0. If a copy of the MPL was not
* distributed with this file, You can obtain one at
* https://mozilla.org/MP:/2.0/.
*
* This program is distributed in the hope that it will be useful,
* but is provided AS-IS, WITHOUT ANY WARRANTY; including without
* the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or
* FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public
* License for more details.
*
* See www.openkinetic.org for more project information
*/
package com.seagate.kinetic.simulator.internal;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import kinetic.client.KineticException;
import com.google.protobuf.ByteString;
import com.seagate.kinetic.common.lib.KineticMessage;
import com.seagate.kinetic.proto.Kinetic.Command;
import com.seagate.kinetic.proto.Kinetic.Command.PinOperation.PinOpType;
import com.seagate.kinetic.proto.Kinetic.Command.MessageType;
import com.seagate.kinetic.proto.Kinetic.Command.Setup;
import com.seagate.kinetic.proto.Kinetic.Command.Status.StatusCode;
import com.seagate.kinetic.proto.Kinetic.Message.AuthType;
import com.seagate.kinetic.proto.Kinetic.Message;
/**
*
* Pin operation handler.
*
*/
public abstract class PinOperationHandler {
private final static Logger logger = Logger.getLogger(PinOperationHandler.class
.getName());
public static void handleOperation(KineticMessage request,
KineticMessage respond, SimulatorEngine engine)
throws KVStoreException, KineticException {
Message.Builder messageBuilder = (Message.Builder) respond.getMessage();
// set pin auth
messageBuilder.setAuthType(AuthType.PINAUTH);
Command.Builder commandBuilder = (Command.Builder) respond.getCommand();
// set reply type
commandBuilder.getHeaderBuilder().setMessageType(
MessageType.PINOP_RESPONSE);
// set ack sequence
commandBuilder.getHeaderBuilder().setAckSequence(
request.getCommand().getHeader().getSequence());
// request pin
ByteString requestPin = request.getMessage().getPinAuth().getPin();
// request pin op type
PinOpType pinOpType = request.getCommand().getBody().getPinOp()
.getPinOpType();
try {
// check if met TLS requirement
checkSecureChannel(request);
switch (pinOpType) {
case LOCK_PINOP:
// check if not empty
checkRequestPin (requestPin);
// check if has permission
comparePin(requestPin, engine.getSecurityPin().getLockPin());
// lock device
engine.setDeviceLocked(true);
logger.info("Device locked ...");
break;
case UNLOCK_PINOP:
// check if not empty
checkRequestPin (requestPin);
// check if has permission
comparePin(requestPin, engine.getSecurityPin().getLockPin());
// unlock device
engine.setDeviceLocked(false);
logger.info("Device unlocked ...");
break;
case ERASE_PINOP:
// Both erase operations will return
// the device to an as manufactured state removing all
// user data and configuration settings.
// Erase the device. This may be secure
// or not. The implication is that it may be faster
// than the secure operation.
comparePin(requestPin, engine.getSecurityPin().getErasePin());
// do erase
doErase (engine);
break;
case SECURE_ERASE_PINOP:
// Erase the device in a way that will
// physical access and disassembly of the device
// will not
comparePin(requestPin, engine.getSecurityPin().getErasePin());
// do erase
doErase (engine);
break;
case INVALID_PINOP:
throw new InvalidRequestException("Invalid Pin Op Type: "
+ pinOpType);
default:
throw new InvalidRequestException("Invalid Pin Op Type: "
+ pinOpType);
}
} catch (KVSecurityException se) {
commandBuilder.getStatusBuilder()
.setCode(StatusCode.NOT_AUTHORIZED);
commandBuilder.getStatusBuilder().setStatusMessage(se.getMessage());
logger.warning("unauthorized pin opeartion request");
} catch (InvalidRequestException ire) {
commandBuilder.getStatusBuilder().setCode(
StatusCode.INVALID_REQUEST);
commandBuilder.getStatusBuilder()
.setStatusMessage(ire.getMessage());
}
}
/**
* Perform secure erase pin operation.
*
* @param engine
* @throws KVStoreException
* @throws KineticException
*/
public static void doErase(SimulatorEngine engine) throws KVStoreException,
KineticException {
// reset store
engine.getStore().reset();
// reset setup
resetSetup(engine);
// reset security
SecurityHandler.resetSecurity(engine);
}
private static void resetSetup(SimulatorEngine engine) {
Setup.Builder sb = Setup.newBuilder();
sb.setNewClusterVersion(0);
try {
SetupHandler.persistSetup(sb.build().toByteArray(), engine.getKineticHome());
engine.setClusterVersion(0);
} catch (IOException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
/**
* compare if request pin is equal to the device pin.
*
* @param requestPin pin in the request message
* @param devicePin device pin.
* @return true if the same, otherwise return false
*/
private static void comparePin (ByteString requestPin, ByteString devicePin) throws KVSecurityException {
/**
* if not set, simply returns.
*/
if (devicePin == null || devicePin.isEmpty()) {
return;
}
/**
* compare if pins are equal.
*/
if (devicePin.equals(requestPin) == false) {
throw new KVSecurityException ("pin does not match., requestPin=" + requestPin);
}
}
/**
* Check if the request op is under TLS channel.
*
* @param request
* @throws InvalidRequestException
*/
private static void checkSecureChannel(KineticMessage request)
throws InvalidRequestException {
boolean hasPermission = request.getIsSecureChannel();
if (hasPermission == false) {
throw new InvalidRequestException(
"TLS channel is required for Pin operation");
}
}
/**
* Check if the pin is not null or empty.
*
* @param pin pin to be validated.
*
* @throws InvalidRequestException if pin is null or empty.
*/
private static void checkRequestPin (ByteString pin) throws InvalidRequestException {
if (pin.isEmpty()) {
throw new InvalidRequestException ("Pin cannot be empty");
}
}
}