From 0fdbac74e69b94a8df023d8271255f908779b316 Mon Sep 17 00:00:00 2001 From: juerg Date: Thu, 28 Mar 2024 13:30:17 -0700 Subject: [PATCH] Add validation to HpkeUtil.intToByteArray. - The algorithm implementation used here doesn't work for capacity larger than 4, so we shouldn't allow this. - The algorithm definition, see https://www.rfc-editor.org/rfc/rfc3447.html#section-4.1, requires that 0 <= value < 256^capacity. And add unit tests. PiperOrigin-RevId: 620041132 --- .../crypto/tink/hybrid/internal/HpkeUtil.java | 8 +++ .../crypto/tink/hybrid/internal/BUILD.bazel | 11 +++ .../tink/hybrid/internal/HpkeUtilTest.java | 69 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 java_src/src/test/java/com/google/crypto/tink/hybrid/internal/HpkeUtilTest.java diff --git a/java_src/src/main/java/com/google/crypto/tink/hybrid/internal/HpkeUtil.java b/java_src/src/main/java/com/google/crypto/tink/hybrid/internal/HpkeUtil.java index aebd245dee..6d926df55f 100644 --- a/java_src/src/main/java/com/google/crypto/tink/hybrid/internal/HpkeUtil.java +++ b/java_src/src/main/java/com/google/crypto/tink/hybrid/internal/HpkeUtil.java @@ -64,6 +64,14 @@ public final class HpkeUtil { * @param value that should be represented as a byte array */ public static byte[] intToByteArray(int capacity, int value) { + if ((capacity > 4) || (capacity < 0)) { + throw new IllegalArgumentException("capacity must be between 0 and 4"); + } + // Check that 0 <= value < 256^capacity. + // For capacity == 4, all positive values are valid. + if (value < 0 || (capacity < 4 && (value >= 1 << (8 * capacity)))) { + throw new IllegalArgumentException("value too large"); + } final byte[] result = new byte[capacity]; for (int i = 0; i < capacity; i++) { result[i] = (byte) ((value >> (8 * (capacity - i - 1))) & 0xFF); diff --git a/java_src/src/test/java/com/google/crypto/tink/hybrid/internal/BUILD.bazel b/java_src/src/test/java/com/google/crypto/tink/hybrid/internal/BUILD.bazel index 43b5142df6..608aae5aa0 100644 --- a/java_src/src/test/java/com/google/crypto/tink/hybrid/internal/BUILD.bazel +++ b/java_src/src/test/java/com/google/crypto/tink/hybrid/internal/BUILD.bazel @@ -322,3 +322,14 @@ java_test( "@maven//:junit_junit", ], ) + +java_test( + name = "HpkeUtilTest", + size = "small", + srcs = ["HpkeUtilTest.java"], + deps = [ + "//src/main/java/com/google/crypto/tink/hybrid/internal:hpke_util", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + ], +) diff --git a/java_src/src/test/java/com/google/crypto/tink/hybrid/internal/HpkeUtilTest.java b/java_src/src/test/java/com/google/crypto/tink/hybrid/internal/HpkeUtilTest.java new file mode 100644 index 0000000000..809d8fd024 --- /dev/null +++ b/java_src/src/test/java/com/google/crypto/tink/hybrid/internal/HpkeUtilTest.java @@ -0,0 +1,69 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package com.google.crypto.tink.hybrid.internal; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class HpkeUtilTest { + + @Test + public void intToByteArray_works() { + assertThat(HpkeUtil.intToByteArray(0, 0)).isEqualTo(new byte[] {}); + assertThat(HpkeUtil.intToByteArray(1, 42)).isEqualTo(new byte[] {(byte) 42}); + assertThat(HpkeUtil.intToByteArray(2, 0x0102)).isEqualTo(new byte[] {(byte) 01, (byte) 0x02}); + + assertThat(HpkeUtil.intToByteArray(1, 0xaa)).isEqualTo(new byte[] {(byte) 0xaa}); + assertThat(HpkeUtil.intToByteArray(1, 256 - 1)).isEqualTo(new byte[] {(byte) 0xff}); + assertThat(HpkeUtil.intToByteArray(2, 0xaabb)).isEqualTo(new byte[] {(byte) 0xaa, (byte) 0xbb}); + assertThat(HpkeUtil.intToByteArray(2, 256 * 256 - 1)) + .isEqualTo(new byte[] {(byte) 0xff, (byte) 0xff}); + assertThat(HpkeUtil.intToByteArray(3, 0xaabbcc)) + .isEqualTo(new byte[] {(byte) 0xaa, (byte) 0xbb, (byte) 0xcc}); + assertThat(HpkeUtil.intToByteArray(3, 256 * 256 * 256 - 1)) + .isEqualTo(new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff}); + assertThat(HpkeUtil.intToByteArray(4, 0x0abbccdd)) + .isEqualTo(new byte[] {(byte) 0x0a, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd}); + assertThat(HpkeUtil.intToByteArray(4, Integer.MAX_VALUE)) + .isEqualTo(new byte[] {(byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff}); + } + + @Test + public void intToByteArray_failsWithInvalidCapacity() { + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(5, 0)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(-1, 0)); + } + + @Test + public void intToByteArray_valueTooLong_fails() { + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(0, 1)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(0, -1)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(1, 256)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(1, -1)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(2, 256 * 256)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(2, -1)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(3, 256 * 256 * 256)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(3, -1)); + assertThrows(IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(4, -1)); + assertThrows( + IllegalArgumentException.class, () -> HpkeUtil.intToByteArray(4, Integer.MIN_VALUE)); + } +}