ru.jts_dev.authserver.util.Encoder.java Source code

Java tutorial

Introduction

Here is the source code for ru.jts_dev.authserver.util.Encoder.java

Source

/*
 * Copyright (c) 2015, 2016, 2017 JTS-Team authors and/or its affiliates. All rights reserved.
 *
 * This file is part of JTS-V3 Project.
 *
 * JTS-V3 Project is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JTS-V3 Project 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 General Public License
 * along with JTS-V3 Project.  If not, see <http://www.gnu.org/licenses/>.
 */

package ru.jts_dev.authserver.util;

import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.Transformer;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import ru.jts_dev.authserver.model.AuthSession;
import ru.jts_dev.authserver.service.AuthSessionService;

import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.Objects;
import java.util.Random;

import static org.springframework.integration.ip.IpHeaders.CONNECTION_ID;

/**
 * @author Camelion
 * @since 02.12.15
 */
@Component
public class Encoder {
    private static final byte[] STATIC_BLOWFISH_KEY = { (byte) 0x6b, (byte) 0x60, (byte) 0xcb, (byte) 0x5b,
            (byte) 0x82, (byte) 0xce, (byte) 0x90, (byte) 0xb1, (byte) 0xcc, (byte) 0x2b, (byte) 0x6c, (byte) 0x55,
            (byte) 0x6c, (byte) 0x6c, (byte) 0x6c, (byte) 0x6c };
    public static final String STATIC_KEY_HEADER = "static_key";
    public static final int BLOWFISH_KEY_SIZE = 16;
    private static final Logger log = LoggerFactory.getLogger(Encoder.class);
    private static final int BLOWFISH_BLOCK_SIZE = 8;

    private final AuthSessionService authSessionService;

    private final Random random;

    @Autowired
    public Encoder(AuthSessionService authSessionService, Random random) {
        Assert.notNull(authSessionService, "AuthService must not be null!");
        Assert.notNull(random, "Random must not be null!");

        this.authSessionService = authSessionService;
        this.random = random;
    }

    public ByteBuf appendBlowFishPadding(ByteBuf buf) {
        int padding = BLOWFISH_BLOCK_SIZE - buf.readableBytes() % BLOWFISH_BLOCK_SIZE;
        byte[] stuff = new byte[padding];
        random.nextBytes(stuff);
        buf.writeBytes(stuff);

        return buf;
    }

    public ByteBuf validateChecksum(ByteBuf buf) {
        if (buf.readableBytes() % 4 != 0 || buf.readableBytes() <= 4) {
            throw new IndexOutOfBoundsException("ByteBuf size must be multiply of 4 and more, that 4");
        }

        long checksum = 0;
        long check;
        int i;

        for (i = 0; i < buf.readableBytes() - 4; i += 4) {
            check = buf.getInt(i);

            checksum ^= check;
        }

        check = buf.getInt(i);

        if (check != checksum) {
            throw new InvalidParameterException("Wrong checksum");
        }

        return buf.copy(0, buf.readableBytes() - 4);
    }

    public ByteBuf appendChecksum(ByteBuf buf) {
        if (buf.readableBytes() % 4 != 0) {
            throw new IndexOutOfBoundsException("ByteBuf size must be multiply of 4");
        }
        int checksum = 0;
        for (int i = 0; i < buf.readableBytes(); i += 4) {
            checksum ^= buf.getInt(i);
        }

        buf.writeInt(checksum);

        buf.writeZero(4); // for blowfish block

        return buf;
    }

    public ByteBuf encWithXor(ByteBuf buf) {
        if (buf.readableBytes() % 4 != 0) {
            throw new IndexOutOfBoundsException("ByteBuf size must be multiply of 4");
        }
        int edx;
        int ecx = 0; // Initial xor key

        buf.writeLong(random.nextLong()); // 8 bytes padding

        for (int pos = 4; pos < buf.readableBytes(); pos += 4) {
            edx = buf.getInt(pos);

            ecx += edx;
            edx ^= ecx;

            buf.setInt(pos, edx);
        }
        buf.writeInt(ecx);

        buf.writeInt(random.nextInt()); // 4 bytes for blowfish block

        return buf;
    }

    @Transformer
    public byte[] encrypt(byte[] data, @Header(CONNECTION_ID) String connectionId,
            // TODO: 08.12.15 spring integration bug with ignoring defaultValue in header
            @Header(value = STATIC_KEY_HEADER, required = false) String static_key) throws IOException {
        if (data.length % BLOWFISH_BLOCK_SIZE != 0)
            throw new IndexOutOfBoundsException("data.length must be multiply of " + BLOWFISH_BLOCK_SIZE);

        BlowfishEngine blowfishEngine = new BlowfishEngine();
        if (static_key != null && static_key.equals("true")) {
            blowfishEngine.init(STATIC_BLOWFISH_KEY);
        } else {
            AuthSession gameSession = authSessionService.getSessionBy(connectionId);

            // perform null check
            Objects.requireNonNull(gameSession, "gameSession is null for " + connectionId);

            blowfishEngine.init(gameSession.getBlowfishKey());
        }
        if (log.isTraceEnabled() && data.length > 0) {
            final StringBuilder leftStr = new StringBuilder("[");
            for (byte b : data) {
                leftStr.append(" ");
                leftStr.append(String.format("%02X", b));
            }
            leftStr.append(" ]");

            log.trace("Raw bytes before encrypt: " + leftStr);
        }

        for (int i = 0; i < data.length; i += BLOWFISH_BLOCK_SIZE) {
            blowfishEngine.encryptBlock(data, i, data, i);
        }

        return data;
    }

    @Transformer
    public byte[] decrypt(byte[] data, @Header(CONNECTION_ID) String connectionId) throws IOException {
        if (data.length % BLOWFISH_BLOCK_SIZE != 0)
            throw new IndexOutOfBoundsException("data.length must be multiply of " + BLOWFISH_BLOCK_SIZE);

        AuthSession gameSession = authSessionService.getSessionBy(connectionId);

        BlowfishEngine blowfishEngine = new BlowfishEngine();
        blowfishEngine.init(gameSession.getBlowfishKey());

        for (int i = 0; i < data.length; i += BLOWFISH_BLOCK_SIZE) {
            blowfishEngine.decryptBlock(data, i, data, i);
        }

        return data;
    }
}