All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy