Skip to content

Commit

Permalink
Fixed User-Password encoding for passwords larger than 16 characters
Browse files Browse the repository at this point in the history
  • Loading branch information
tsyd committed Nov 14, 2023
1 parent ea00c50 commit 57c87f7
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,13 @@ public D decode(CodecContext codecContext, AttributeType parentAttributeType, by
public byte[] encode(CodecContext codecContext, AttributeType parentAttributeType, Data data) {
byte[] password = dataCodec.encode(codecContext, parentAttributeType, data);

if (password.length > 128) {
throw new IllegalArgumentException("Password length must be in range [0, 128]");
}

MessageDigest md5 = getMd5Instance();

byte[] paddedPassword = new byte[Math.min(16, password.length + ((16 - password.length) % 16))];
byte[] paddedPassword = new byte[password.length - ((password.length - 1) % 16) + 15];
System.arraycopy(password, 0, paddedPassword, 0, password.length);

byte[] hiddenPassword = new byte[paddedPassword.length];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright 2020 The AAA4J-RADIUS Authors
*
* 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 org.aaa4j.radius.core.attribute;

import org.aaa4j.radius.core.attribute.attributes.UserPassword;
import org.aaa4j.radius.core.dictionary.dictionaries.StandardDictionary;
import org.aaa4j.radius.core.util.SecureRandomProvider;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;

import static org.aaa4j.radius.core.Utils.fromHex;
import static org.aaa4j.radius.core.Utils.toHex;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

@DisplayName("UserPasswordDataCodec")
class UserPasswordDataCodecTest {

@Test
@DisplayName("UserPassword data is decoded successfully")
void testDecode() {
UserPasswordDataCodec<StringData> userPasswordDataCodec =
new UserPasswordDataCodec<>(StringData.Codec.INSTANCE);

CodecContext codecContext = new CodecContext(new StandardDictionary(),
fromHex("d955e791c15fe6996434be163c8c019d21cd901b867600c2662e8a4628c5bff3"),
fromHex("9fa4ee463dbfd1b0c99a209490c52cb6"),
new SecureRandomProvider());

{
byte[] hiddenPassword = fromHex("b04ef795318fb58d6da1ccc94ff1552c");

StringData passwordStringData =
userPasswordDataCodec.decode(codecContext, UserPassword.TYPE, hiddenPassword);

String password = new String(passwordStringData.getValue(), StandardCharsets.UTF_8);

assertEquals("", password);
}
{
byte[] hiddenPassword = fromHex("d12c9495318fb58d6da1ccc94ff1552c");

StringData passwordStringData =
userPasswordDataCodec.decode(codecContext, UserPassword.TYPE, hiddenPassword);

String password = new String(passwordStringData.getValue(), StandardCharsets.UTF_8);

assertEquals("abc", password);
}
{
byte[] hiddenPassword = fromHex("d12c94f154e9d2e504cba7a5229f3a5c");

StringData passwordStringData =
userPasswordDataCodec.decode(codecContext, UserPassword.TYPE, hiddenPassword);

String password = new String(passwordStringData.getValue(), StandardCharsets.UTF_8);

assertEquals("abcdefghijklmnop", password);
}
{
byte[] hiddenPassword = fromHex("d12c94f154e9d2e504cba7a5229f3a5cf3e675578cc3abb486e814c9a97dd5d0" +
"657fd2d428d79fa68b9be651ac0893a440e94a20e238962bc3de4b9184ccbb7c" +
"72dfdb01a95d8c1d473daad70b763494a65dc6c705e276634f8040ae9af5d0b3" +
"a501bb4ce27950968a2ce5926dfd2f1a5c642f59e615b4188389cfe99e518ea9");

StringData passwordStringData =
userPasswordDataCodec.decode(codecContext, UserPassword.TYPE, hiddenPassword);

String password = new String(passwordStringData.getValue(), StandardCharsets.UTF_8);

assertEquals("abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwx",
password);
}
}

@Test
@DisplayName("UserPassword is encoded successfully")
void testEncode() {
UserPasswordDataCodec<StringData> userPasswordDataCodec =
new UserPasswordDataCodec<>(StringData.Codec.INSTANCE);

CodecContext codecContext = new CodecContext(new StandardDictionary(),
fromHex("d955e791c15fe6996434be163c8c019d21cd901b867600c2662e8a4628c5bff3"),
fromHex("9fa4ee463dbfd1b0c99a209490c52cb6"),
new SecureRandomProvider());

{
byte[] password = "".getBytes(StandardCharsets.UTF_8);

byte[] hiddenPassword =
userPasswordDataCodec.encode(codecContext, UserPassword.TYPE, new StringData(password));

assertEquals("b04ef795318fb58d6da1ccc94ff1552c", toHex(hiddenPassword));
}
{
byte[] password = "abc".getBytes(StandardCharsets.UTF_8);

byte[] hiddenPassword =
userPasswordDataCodec.encode(codecContext, UserPassword.TYPE, new StringData(password));

assertEquals("d12c9495318fb58d6da1ccc94ff1552c", toHex(hiddenPassword));
}
{
byte[] password = "abcdefghijklmnop".getBytes(StandardCharsets.UTF_8);

byte[] hiddenPassword =
userPasswordDataCodec.encode(codecContext, UserPassword.TYPE, new StringData(password));

assertEquals("d12c94f154e9d2e504cba7a5229f3a5c", toHex(hiddenPassword));
}
{
byte[] password = ("abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwx").getBytes(StandardCharsets.UTF_8);

byte[] hiddenPassword =
userPasswordDataCodec.encode(codecContext, UserPassword.TYPE, new StringData(password));

assertEquals("d12c94f154e9d2e504cba7a5229f3a5cf3e675578cc3abb486e814c9a97dd5d0" +
"657fd2d428d79fa68b9be651ac0893a440e94a20e238962bc3de4b9184ccbb7c" +
"72dfdb01a95d8c1d473daad70b763494a65dc6c705e276634f8040ae9af5d0b3" +
"a501bb4ce27950968a2ce5926dfd2f1a5c642f59e615b4188389cfe99e518ea9",
toHex(hiddenPassword));
}
}

@Test
@DisplayName("Invalid vsa data is decoded into null")
void testEncodeInvalid() {
UserPasswordDataCodec<StringData> userPasswordDataCodec =
new UserPasswordDataCodec<>(StringData.Codec.INSTANCE);

CodecContext codecContext = new CodecContext(new StandardDictionary(),
fromHex("d955e791c15fe6996434be163c8c019d21cd901b867600c2662e8a4628c5bff3"),
fromHex("9fa4ee463dbfd1b0c99a209490c52cb6"),
new SecureRandomProvider());

{
// Too many bytes (> 128)
assertThrows(IllegalArgumentException.class, () -> {
new EvsData(32473, -1, fromHex("abcd"));
});
}
}

}

0 comments on commit 57c87f7

Please sign in to comment.