connectbot/sshlib

View on GitHub
src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java

Summary

Maintainability
A
35 mins
Test Coverage
C
76%

package com.trilead.ssh2.crypto.cipher;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * CipherOutputStream.
 *
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: CipherOutputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 */
public class CipherOutputStream
{
    private BlockCipher currentCipher;
    private final BufferedOutputStream bo;
    private byte[] buffer;
    private byte[] enc;
    private int blockSize;
    private int pos;
    private boolean recordingOutput;
    private final ByteArrayOutputStream recordingOutputStream = new ByteArrayOutputStream();

    public CipherOutputStream(BlockCipher tc, OutputStream bo)
    {
        if (bo instanceof BufferedOutputStream) {
            this.bo = (BufferedOutputStream) bo;
        } else {
            this.bo = new BufferedOutputStream(bo);
        }
        changeCipher(tc);
    }

    public void flush() throws IOException
    {
        if (pos != 0)
            throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");

        bo.flush();
    }

    public void changeCipher(BlockCipher bc)
    {
        this.currentCipher = bc;
        blockSize = bc.getBlockSize();
        buffer = new byte[blockSize];
        enc = new byte[blockSize];
        pos = 0;
    }

    public void startRecording() {
        recordingOutput = true;
    }

    public byte[] getRecordedOutput() {
        recordingOutput = false;
        byte[] recordedOutput = recordingOutputStream.toByteArray();
        recordingOutputStream.reset();
        return recordedOutput;
    }

    private void writeBlock() throws IOException
    {
        try
        {
            currentCipher.transformBlock(buffer, 0, enc, 0);
        }
        catch (Exception e)
        {
            throw new IOException("Error while decrypting block.", e);
        }

        bo.write(enc, 0, blockSize);
        pos = 0;

        if (recordingOutput) {
            recordingOutputStream.write(enc, 0, blockSize);
        }
    }

    public void write(byte[] src, int off, int len) throws IOException
    {
        while (len > 0)
        {
            int avail = blockSize - pos;
            int copy = Math.min(avail, len);

            System.arraycopy(src, off, buffer, pos, copy);
            pos += copy;
            off += copy;
            len -= copy;

            if (pos >= blockSize)
                writeBlock();
        }
    }

    public void write(int b) throws IOException
    {
        buffer[pos++] = (byte) b;
        if (pos >= blockSize)
            writeBlock();
    }

    public void writePlain(int b) throws IOException
    {
        if (pos != 0)
            throw new IOException("Cannot write plain since crypto buffer is not aligned.");
        bo.write(b);
    }

    public void writePlain(byte[] b, int off, int len) throws IOException
    {
        if (pos != 0)
            throw new IOException("Cannot write plain since crypto buffer is not aligned.");
        bo.write(b, off, len);
    }
}