Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Random access InputStream using AES CTR mode in android

I am unable to find any working example for AES CTR encryption for random access. Can someone guide me how to use the counter in CTR MODE also how to implement jumping to a specific position in the stream?

The default stream implementation (CipherInputStream) doesn't skip the stream and it corrupts the plain text.

I am trying to decrypt encrypted Video file stored on sdcard in Android. An embedded HTTP file server decrypts it on the fly. Everything is working fine until a user performs a seek within the video: the video stops immediately because it receives corrupt video stream.

I am using following code to initialize and encrypt/ decrypt stream (for sake of simplicity i hard coded the keys. it will not be hard coded in production)

    ByteBuffer bb = ByteBuffer.allocate(16);
    bb.put("1234567891230000".getBytes());
    byte[] ivString = bb.array();

    // INITIALISATION
    String keyString = "1234567812345678";
    IvParameterSpec iv = new IvParameterSpec(ivString);
    SecretKeySpec keySpec = new SecretKeySpec(keyString.getBytes(), "AES");

    // FOR ENCRYPTION
    Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivString));
    Inputstream encrypted_is = new CipherInputStream(in, cipher);

    // FOR DECRYPTION
    cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivString));
            Inputstream decrypted_is = new CipherInputStream(in, cipher);
like image 846
hawx Avatar asked May 19 '14 17:05

hawx


1 Answers

You should not implement this using a stream. A stream is used for sequential access to data. Skipping should only be used for jumping short distances forward and mark/reset only for jumping small distances back.

Using a file map is probably the most efficient. For a slightly easier approach but less efficient approach you could use RandomAccessFile instead. You should furthermore use Cipher.getInstance("AES/CTR/NoPadding") using an "IV" that is set to the counter you expect at the location you start within the file.


Sample code for using CTR with offset:

private static final int AES_BLOCK_SIZE = 16;

public static final void jumpToOffset(final Cipher c,
        final SecretKey aesKey, final IvParameterSpec iv, final long offset) {
    if (!c.getAlgorithm().toUpperCase().startsWith("AES/CTR")) {
        throw new IllegalArgumentException(
                "Invalid algorithm, only AES/CTR mode supported");
    }

    if (offset < 0) {
        throw new IllegalArgumentException("Invalid offset");
    }

    final int skip = (int) (offset % AES_BLOCK_SIZE);
    final IvParameterSpec calculatedIVForOffset = calculateIVForOffset(iv,
            offset - skip);
    try {
        c.init(Cipher.ENCRYPT_MODE, aesKey, calculatedIVForOffset);
        final byte[] skipBuffer = new byte[skip];
        c.update(skipBuffer, 0, skip, skipBuffer);
        Arrays.fill(skipBuffer, (byte) 0);
    } catch (ShortBufferException | InvalidKeyException
            | InvalidAlgorithmParameterException e) {
        throw new IllegalStateException(e);
    }
}

private static IvParameterSpec calculateIVForOffset(final IvParameterSpec iv,
        final long blockOffset) {
    final BigInteger ivBI = new BigInteger(1, iv.getIV());
    final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset
            / AES_BLOCK_SIZE));

    final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
    final IvParameterSpec ivForOffset;
    if (ivForOffsetBA.length >= AES_BLOCK_SIZE) {
        ivForOffset = new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - AES_BLOCK_SIZE,
                AES_BLOCK_SIZE);
    } else {
        final byte[] ivForOffsetBASized = new byte[AES_BLOCK_SIZE];
        System.arraycopy(ivForOffsetBA, 0, ivForOffsetBASized, AES_BLOCK_SIZE
                - ivForOffsetBA.length, ivForOffsetBA.length);
        ivForOffset = new IvParameterSpec(ivForOffsetBASized);
    }

    return ivForOffset;
}
like image 133
Maarten Bodewes Avatar answered Sep 28 '22 20:09

Maarten Bodewes