com.veraxsystems.vxipmi.coding.protocol.decoder.Protocolv20Decoder Maven / Gradle / Ivy
The newest version!
/*
* Protocolv20Decoder.java
* Created on 2011-07-21
*
* Copyright (c) Verax Systems 2011.
* All rights reserved.
*
* This software is furnished under a license. Use, duplication,
* disclosure and all other uses are restricted to the rights
* specified in the written license agreement.
*/
package com.veraxsystems.vxipmi.coding.protocol.decoder;
import com.veraxsystems.vxipmi.coding.protocol.AuthenticationType;
import com.veraxsystems.vxipmi.coding.protocol.IpmiMessage;
import com.veraxsystems.vxipmi.coding.protocol.Ipmiv20Message;
import com.veraxsystems.vxipmi.coding.protocol.PayloadType;
import com.veraxsystems.vxipmi.coding.rmcp.RmcpMessage;
import com.veraxsystems.vxipmi.coding.security.CipherSuite;
import com.veraxsystems.vxipmi.coding.security.ConfidentialityNone;
import com.veraxsystems.vxipmi.common.TypeConverter;
import org.apache.log4j.Logger;
import java.security.InvalidKeyException;
import java.util.Arrays;
/**
* Decodes IPMI v2.0 session header and retrieves encrypted payload.
*/
public class Protocolv20Decoder extends ProtocolDecoder {
private static Logger logger = Logger.getLogger(Protocolv20Decoder.class);
private CipherSuite cipherSuite;
/**
* Initiates IPMI v2.0 packet decoder.
*
* @param cipherSuite
* - {@link CipherSuite} that will be used to decode the message
*/
public Protocolv20Decoder(CipherSuite cipherSuite) {
super();
this.cipherSuite = cipherSuite;
}
/**
* Decodes IPMI v2.0 message fields.
*
* @param rmcpMessage
* - RMCP message to decode.
* @return decoded message
* @see Ipmiv20Message
* @throws IllegalArgumentException
* when delivered RMCP message does not contain encapsulated
* IPMI message or when AuthCode field is incorrect (integrity
* check fails).
* @throws InvalidKeyException
* - when initiation of the integrity algorithm fails
*/
@Override
public IpmiMessage decode(RmcpMessage rmcpMessage) throws InvalidKeyException {
Ipmiv20Message message = new Ipmiv20Message(
cipherSuite.getConfidentialityAlgorithm());
byte[] raw = rmcpMessage.getData();
message.setAuthenticationType(decodeAuthenticationType(raw[0]));
message.setPayloadEncrypted(decodeEncryption(raw[1]));
message.setPayloadAuthenticated(decodeAuthentication(raw[1]));
message.setPayloadType(decodePayloadType(raw[1]));
int offset = 2;
if (message.getPayloadType() == PayloadType.Oem) {
message.setOemIANA(decodeOEMIANA(raw));
offset += 4;
message.setOemPayloadID(decodeOEMPayloadId(raw, offset));
offset += 2;
}
message.setSessionID(decodeSessionID(raw, offset));
offset += 4;
message.setSessionSequenceNumber(decodeSessionSequenceNumber(raw,
offset));
offset += 4;
int payloadLength = decodePayloadLength(raw, offset);
offset += 2;
if (message.isPayloadEncrypted()) {
message.setPayload(decodePayload(raw, offset, payloadLength,
message.getConfidentialityAlgorithm(), message.getPayloadType()));
} else {
message.setPayload(decodePayload(raw, offset, payloadLength,
new ConfidentialityNone(), message.getPayloadType()));
}
offset += payloadLength;
if (message.getAuthenticationType() != AuthenticationType.None
&& !(message.getAuthenticationType() == AuthenticationType.RMCPPlus && !message
.isPayloadAuthenticated())
&& message.getSessionID() != 0) {
offset = skipIntegrityPAD(raw, offset);
message.setAuthCode(decodeAuthCode(raw, offset));
if (!validateAuthCode(raw, offset)) {
logger.warn("Integrity check failed");
}
}
return message;
}
/**
* Decodes first bit of Payload Type.
*
* @param payloadType
* @return True if payload is encrypted, false otherwise.
*/
private boolean decodeEncryption(byte payloadType) {
return (payloadType & TypeConverter.intToByte(0x80)) != 0;
}
/**
* Decodes second bit of Payload Type.
*
* @param payloadType
* @return True if payload is authenticated, false otherwise.
*/
public boolean decodeAuthentication(byte payloadType) {
return (payloadType & TypeConverter.intToByte(0x40)) != 0;
}
public static PayloadType decodePayloadType(byte payloadType) {
return PayloadType.parseInt(TypeConverter.intToByte(payloadType
& TypeConverter.intToByte(0x3f)));
}
/**
* Decodes OEM IANA.
*
* @param rawMessage
* - Byte array holding whole message data.
* @return OEM IANA number.
*/
private int decodeOEMIANA(byte[] rawMessage) {
byte[] oemIANA = new byte[4];
System.arraycopy(rawMessage, 3, oemIANA, 0, 3);
oemIANA[3] = 0;
return TypeConverter.littleEndianByteArrayToInt(oemIANA);
}
/**
* Decodes OEM payload ID. To implement manufacturer-specific OEM Payload ID
* decoding, override this function.
*
* @param rawMessage
* - Byte array holding whole message data.
* @param offset
* - Offset to OEM payload ID in header.
* @return Decoded OEM payload ID.
*/
protected Object decodeOEMPayloadId(byte[] rawMessage, int offset) {
byte[] oemPayload = new byte[2];
System.arraycopy(rawMessage, offset, oemPayload, 0, 2);
return oemPayload;
}
@Override
protected int decodePayloadLength(byte[] rawData, int offset) {
byte[] payloadLength = new byte[4];
System.arraycopy(rawData, offset, payloadLength, 0, 2);
payloadLength[2] = 0;
payloadLength[3] = 0;
return TypeConverter.littleEndianByteArrayToInt(payloadLength);
}
/**
* Skips the integrity pad and pad length fields.
*
* @param rawMessage
* - Byte array holding whole message data.
* @param offset
* - Offset to integrity pad.
* @return Offset to Auth Code
* @throws IndexOutOfBoundsException
* when message is corrupted and pad length does not appear
* after integrity pad or length is incorrect.
*/
private int skipIntegrityPAD(final byte[] rawMessage, final int offset) {
int skip = 0;
while (TypeConverter.byteToInt(rawMessage[offset + skip]) == 0xff) {
++skip;
}
int length = TypeConverter.byteToInt(rawMessage[offset + skip]);
if (length != skip) {
throw new IndexOutOfBoundsException("Message is corrupted.");
}
int currentOffset = offset + skip + 2; // skip pad length and next header fields
if (currentOffset >= rawMessage.length) {
throw new IndexOutOfBoundsException("Message is corrupted.");
}
return currentOffset;
}
/**
* Decodes the Auth Code.
*
* @param rawMessage
* - Byte array holding whole message data.
* @param offset
* - Offset to auth code.
* @return Auth Code
* @throws IndexOutOfBoundsException
* when message is corrupted and pad length does not appear
* after integrity pad or length is incorrect.
*/
private byte[] decodeAuthCode(byte[] rawMessage, int offset) {
byte[] authCode = new byte[rawMessage.length - offset];
System.arraycopy(rawMessage, offset, authCode, 0, authCode.length);
return authCode;
}
/**
* Checks if Auth Code of the received message is valid
*
* @param rawMessage
* - received message
* @param offset
* - offset to the AuthCode field in the message
* @return True if AuthCode is correct, false otherwise.
* @throws InvalidKeyException
* - when initiation of the integrity algorithm fails
*/
private boolean validateAuthCode(byte[] rawMessage, int offset) {
byte[] base = new byte[offset];
System.arraycopy(rawMessage, 0, base, 0, offset);
byte[] authCode = null;
if (rawMessage.length > offset) {
authCode = new byte[rawMessage.length - offset];
System.arraycopy(rawMessage, offset, authCode, 0, authCode.length);
}
return Arrays.equals(authCode, cipherSuite.getIntegrityAlgorithm()
.generateAuthCode(base));
}
/**
* Decodes session ID.
*
* @param message
* - message to get session ID from
* @return Session ID.
*/
public static int decodeSessionID(RmcpMessage message) {
int offset = 2;
if (decodePayloadType(message.getData()[1]) == PayloadType.Oem) {
offset += 6;
}
return decodeSessionID(message.getData(), offset);
}
}