From 7665c2a2da0a7384a8171a19ac4db9370d801da1 Mon Sep 17 00:00:00 2001 From: Daniel Raniz Raneland Date: Fri, 20 Oct 2017 21:42:04 +0200 Subject: [PATCH] Iv hex encoding (#51) * Correct writing of hexadecimal values Because WriteUtil.writeHexadecimal used Integer.toHexString which converts each byte into an integer and then writes that integer using the minimum required number of hexadecimal digits results would vary and negative bytes would be written using 8 hexadecimal digits with the first 6 being ffffff and positive bytes between 0 and 15 would only be written using 1 hexadecimal digit. This would cause the IV in EXT-X-KEY tags to be corrupted. Using String.format("%02x", b) instead means all bytes will be written correctly. They won't take up more than 2 characters since no integer-conversion is performed and if only 1 digit is required they will be zero-padded. * Added test for WriteUtil#writeHexadecimal * Correct parsing of hexadecimal values * Fixed failing testEXT_X_KEY The IV was too short and the expected value was wrong --- .../java/com/iheartradio/m3u8/ParseUtil.java | 13 +++-- .../java/com/iheartradio/m3u8/WriteUtil.java | 2 +- .../m3u8/MediaPlaylistLineParserTest.java | 8 +-- .../m3u8/ParseUtilParseHexadecimalTest.java | 49 +++++++++++++++++++ .../m3u8/WriteUtilWriteHexadecimalTest.java | 49 +++++++++++++++++++ 5 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 src/test/java/com/iheartradio/m3u8/ParseUtilParseHexadecimalTest.java create mode 100644 src/test/java/com/iheartradio/m3u8/WriteUtilWriteHexadecimalTest.java diff --git a/src/main/java/com/iheartradio/m3u8/ParseUtil.java b/src/main/java/com/iheartradio/m3u8/ParseUtil.java index 71c5394..3898c7e 100644 --- a/src/main/java/com/iheartradio/m3u8/ParseUtil.java +++ b/src/main/java/com/iheartradio/m3u8/ParseUtil.java @@ -25,7 +25,7 @@ public static > T parseEnum(String string, Class enumType, throw ParseException.create(ParseExceptionType.NOT_JAVA_ENUM, tag, string); } } - + public static String parseDateTime(String string, String tag) throws ParseException { Matcher matcher = Constants.EXT_X_PROGRAM_DATE_TIME_PATTERN.matcher(string); @@ -50,9 +50,12 @@ public static List parseHexadecimal(String hexString, String tag) throws P if (matcher.matches()) { String valueString = matcher.group(1); + if (valueString.length() % 2 != 0) { + throw ParseException.create(ParseExceptionType.INVALID_HEXADECIMAL_STRING, tag, hexString); + } - for (char c : valueString.toCharArray()) { - bytes.add(hexCharToByte(c)); + for (int i = 0; i < valueString.length(); i += 2) { + bytes.add((byte)(Short.parseShort(valueString.substring(i, i+2), 16) & 0xFF)); } return bytes; @@ -60,7 +63,7 @@ public static List parseHexadecimal(String hexString, String tag) throws P throw ParseException.create(ParseExceptionType.INVALID_HEXADECIMAL_STRING, tag, hexString); } } - + private static byte hexCharToByte(char hex) { if (hex >= 'A') { return (byte) ((hex & 0xF) + 9); @@ -185,7 +188,7 @@ public static List parseAttributeList(String line, String tag) throws //Even Apple playlists have sometimes spaces after a , final String name = string.substring(0, separator).trim(); final String value = string.substring(separator + 1); - + if (name.isEmpty()) { throw ParseException.create(ParseExceptionType.MISSING_ATTRIBUTE_NAME, tag, attributes.toString()); } diff --git a/src/main/java/com/iheartradio/m3u8/WriteUtil.java b/src/main/java/com/iheartradio/m3u8/WriteUtil.java index e7a5849..f66779e 100644 --- a/src/main/java/com/iheartradio/m3u8/WriteUtil.java +++ b/src/main/java/com/iheartradio/m3u8/WriteUtil.java @@ -25,7 +25,7 @@ public static String writeHexadecimal(List hex) { StringBuilder builder = new StringBuilder(hex.size() + prefix.length()); builder.append(prefix); for(Byte b : hex) { - builder.append(Integer.toHexString(b)); + builder.append(String.format("%02x", b)); } return builder.toString(); } diff --git a/src/test/java/com/iheartradio/m3u8/MediaPlaylistLineParserTest.java b/src/test/java/com/iheartradio/m3u8/MediaPlaylistLineParserTest.java index c55ee62..6c31a05 100644 --- a/src/test/java/com/iheartradio/m3u8/MediaPlaylistLineParserTest.java +++ b/src/test/java/com/iheartradio/m3u8/MediaPlaylistLineParserTest.java @@ -46,7 +46,7 @@ public void testEXT_X_KEY() throws Exception { final String line = "#" + tag + ":METHOD=AES-128" + ",URI=\"" + uri + "\"" + - ",IV=0x1234abcd5678EF90" + + ",IV=0x1234abcd5678EF90aabbccddeeff0011" + ",KEYFORMAT=\"" + format + "\"" + ",KEYFORMATVERSIONS=\"1/2/3\""; @@ -58,8 +58,10 @@ public void testEXT_X_KEY() throws Exception { assertEquals(uri, encryptionData.getUri()); assertEquals( - Arrays.asList((byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 10, (byte) 11, (byte) 12, (byte) 13, - (byte) 5, (byte) 6, (byte) 7, (byte) 8, (byte) 14, (byte) 15, (byte) 9, (byte) 0), + Arrays.asList((byte) 0x12, (byte) 0x34, (byte) 0xAB, (byte) 0xCD, + (byte) 0x56, (byte) 0x78, (byte) 0xEF, (byte) 0x90, + (byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, + (byte) 0xEE, (byte) 0xFF, (byte) 0x00, (byte) 0x11), encryptionData.getInitializationVector()); assertEquals(format, encryptionData.getKeyFormat()); diff --git a/src/test/java/com/iheartradio/m3u8/ParseUtilParseHexadecimalTest.java b/src/test/java/com/iheartradio/m3u8/ParseUtilParseHexadecimalTest.java new file mode 100644 index 0000000..a31f9b0 --- /dev/null +++ b/src/test/java/com/iheartradio/m3u8/ParseUtilParseHexadecimalTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, Spiideo + */ + +package com.iheartradio.m3u8; + +import java.util.Arrays; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * @author Raniz + * @since 02/08/17. + */ +@RunWith(Parameterized.class) +public class ParseUtilParseHexadecimalTest { + + @Parameterized.Parameters(name = "{index}: {1}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {Arrays.asList((byte) 0), "0x00"}, + {Arrays.asList((byte) 1), "0x01"}, + {Arrays.asList((byte) -1), "0xff"}, + {Arrays.asList((byte) -16), "0xf0"}, + {Arrays.asList((byte) 0, (byte) 1), "0x0001"}, + {Arrays.asList((byte) 1, (byte) 1), "0x0101"}, + {Arrays.asList((byte) -1, (byte) -1), "0xffff"}, + {Arrays.asList((byte) -121, (byte) -6), "0x87fa"}, + {Arrays.asList((byte) 75, (byte) 118), "0x4b76"}, + }); + } + + private final List expected; + private final String input; + + public ParseUtilParseHexadecimalTest(final List expected, final String input) { + this.expected = expected; + this.input = input; + } + + @Test + public void parseHexadecimal() throws Exception { + Assert.assertEquals(expected, ParseUtil.parseHexadecimal(input, "")); + } + +} diff --git a/src/test/java/com/iheartradio/m3u8/WriteUtilWriteHexadecimalTest.java b/src/test/java/com/iheartradio/m3u8/WriteUtilWriteHexadecimalTest.java new file mode 100644 index 0000000..b8cc66b --- /dev/null +++ b/src/test/java/com/iheartradio/m3u8/WriteUtilWriteHexadecimalTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, Spiideo + */ + +package com.iheartradio.m3u8; + +import java.util.Arrays; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * @author Raniz + * @since 02/08/17. + */ +@RunWith(Parameterized.class) +public class WriteUtilWriteHexadecimalTest { + + @Parameterized.Parameters(name = "{index}: {1}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {Arrays.asList((byte) 0), "0x00"}, + {Arrays.asList((byte) 1), "0x01"}, + {Arrays.asList((byte) -1), "0xff"}, + {Arrays.asList((byte) -16), "0xf0"}, + {Arrays.asList((byte) 0, (byte) 1), "0x0001"}, + {Arrays.asList((byte) 1, (byte) 1), "0x0101"}, + {Arrays.asList((byte) -1, (byte) -1), "0xffff"}, + {Arrays.asList((byte) -121, (byte) -6), "0x87fa"}, + {Arrays.asList((byte) 75, (byte) 118), "0x4b76"}, + }); + } + + private final List input; + private final String expected; + + public WriteUtilWriteHexadecimalTest(final List input, final String expected) { + this.input = input; + this.expected = expected; + } + + @Test + public void writeHexadecimal() throws Exception { + Assert.assertEquals(expected, WriteUtil.writeHexadecimal(input)); + } + +} \ No newline at end of file