Skip to content

Commit

Permalink
Fixed AesAudioDecrypt + do not copy buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
devgianlu committed Jul 28, 2020
1 parent 17ba408 commit b71af83
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,7 @@ private Streamer(@NotNull StreamId streamId, @NotNull SuperAudioFormat format, @
available = new boolean[chunks];
requested = new boolean[chunks];

buffer = new byte[chunks][CHUNK_SIZE];
buffer[chunks - 1] = new byte[size % CHUNK_SIZE];
buffer = new byte[chunks][];

this.internalStream = new InternalStream(session.configuration().retryOnChunkError);
writeChunk(firstChunk, 0, fromCache);
Expand All @@ -262,7 +261,8 @@ public void writeChunk(@NotNull byte[] chunk, int chunkIndex, boolean cached) th

LOGGER.trace("Chunk {}/{} completed, cached: {}, stream: {}", chunkIndex, chunks, cached, describe());

audioDecrypt.decryptChunk(chunkIndex, chunk, buffer[chunkIndex]);
buffer[chunkIndex] = chunk;
audioDecrypt.decryptChunk(chunkIndex, chunk);
internalStream.notifyChunkAvailable(chunkIndex);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package xyz.gianlu.librespot.audio.decrypt;

import org.jetbrains.annotations.NotNull;
import xyz.gianlu.librespot.common.Utils;

import javax.crypto.Cipher;
Expand All @@ -19,33 +20,35 @@
public final class AesAudioDecrypt implements AudioDecrypt {
private static final byte[] AUDIO_AES_IV = new byte[]{(byte) 0x72, (byte) 0xe0, (byte) 0x67, (byte) 0xfb, (byte) 0xdd, (byte) 0xcb, (byte) 0xcf, (byte) 0x77, (byte) 0xeb, (byte) 0xe8, (byte) 0xbc, (byte) 0x64, (byte) 0x3f, (byte) 0x63, (byte) 0x0d, (byte) 0x93};
private final static BigInteger IV_INT = new BigInteger(1, AUDIO_AES_IV);
private static final BigInteger IV_DIFF = BigInteger.valueOf(0x100);
private final SecretKeySpec secretKeySpec;
private final Cipher cipher;
private int decryptCount = 0;
private long decryptTotalTime = 0;

public AesAudioDecrypt(byte[] key) {
this.secretKeySpec = new SecretKeySpec(key, "AES");
try {
this.secretKeySpec = new SecretKeySpec(key, "AES");
this.cipher = Cipher.getInstance("AES/CTR/NoPadding");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(); // This should never happen
} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
throw new IllegalStateException(ex); // This should never happen
}
}

@Override
public synchronized void decryptChunk(int chunkIndex, byte[] in, byte[] out) throws IOException {
int pos = CHUNK_SIZE * chunkIndex;

public synchronized void decryptChunk(int chunkIndex, @NotNull byte[] buffer) throws IOException {
BigInteger iv = IV_INT.add(BigInteger.valueOf(CHUNK_SIZE * chunkIndex / 16));
try {
long start = System.nanoTime();
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(Utils.toByteArray(IV_INT.add(BigInteger.valueOf(pos / 16)))));
for (int i = 0; i < buffer.length; i += 4096) {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(Utils.toByteArray(iv)));

int count = Math.min(4096, buffer.length - i);
int processed = cipher.doFinal(buffer, i, count, buffer, i);
if (count != processed)
throw new IOException(String.format("Couldn't process all data, actual: %d, expected: %d", processed, count));

for (int i = 0; i < in.length; i += 4096) {
int endBytes = Math.min(i + 4096, in.length);
int count = cipher.doFinal(in, 0, endBytes, out, 0);
if (count != endBytes)
throw new IOException(String.format("Couldn't process all data, actual: %d, expected: %d", count, endBytes));
iv = iv.add(IV_DIFF);
}

decryptTotalTime += System.nanoTime() - start;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* @author Gianlu
*/
public interface AudioDecrypt {
void decryptChunk(int chunkIndex, byte[] in, byte[] out) throws IOException;
void decryptChunk(int chunkIndex, byte[] buffer) throws IOException;

int decryptTimeMs();
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
package xyz.gianlu.librespot.audio.decrypt;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
* @author Gianlu
*/
public final class NoopAudioDecrypt implements AudioDecrypt {
private final static Logger LOGGER = LogManager.getLogger(NoopAudioDecrypt.class);

@Override
public void decryptChunk(int chunkIndex, byte[] in, byte[] out) {
int length = in.length;
if (in.length != out.length) {
length = Math.min(in.length, out.length);
LOGGER.warn("Buffers have different lengths! {index: {}, in: {}, out: {}}", chunkIndex, in.length, out.length);
}

System.arraycopy(in, 0, out, 0, length);
public void decryptChunk(int chunkIndex, byte[] buffer) {
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static xyz.gianlu.librespot.audio.storage.ChannelManager.CHUNK_SIZE;

/**
* @author Gianlu
*/
Expand Down Expand Up @@ -170,8 +168,7 @@ private class ChunksBuffer implements Closeable {

ChunksBuffer(int size, int chunks) {
this.size = size;
this.buffer = new byte[chunks][CHUNK_SIZE];
this.buffer[chunks - 1] = new byte[size % CHUNK_SIZE];
this.buffer = new byte[chunks][];
this.available = new boolean[chunks];
this.requested = new boolean[chunks];
this.audioDecrypt = new AesAudioDecrypt(key);
Expand All @@ -184,7 +181,8 @@ void writeChunk(@NotNull byte[] chunk, int chunkIndex) throws IOException {
if (chunk.length != buffer[chunkIndex].length)
throw new IllegalArgumentException(String.format("Buffer size mismatch, required: %d, received: %d, index: %d", buffer[chunkIndex].length, chunk.length, chunkIndex));

audioDecrypt.decryptChunk(chunkIndex, chunk, buffer[chunkIndex]);
buffer[chunkIndex] = chunk;
audioDecrypt.decryptChunk(chunkIndex, chunk);
internalStream.notifyChunkAvailable(chunkIndex);
}

Expand Down

0 comments on commit b71af83

Please sign in to comment.