Skip to content

Commit

Permalink
Fix Accounting-Request authenticator
Browse files Browse the repository at this point in the history
  • Loading branch information
tsyd committed Aug 21, 2024
1 parent dae395e commit bef3063
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
*/
public final class PacketCodec {

private static final int ACCOUNTING_REQUEST_CODE = 4;

private static final int MESSAGE_AUTHENTICATOR_TYPE = 80;

private final Dictionary dictionary;
Expand Down Expand Up @@ -74,7 +76,7 @@ public PacketCodec(Dictionary dictionary, RandomProvider randomProvider) {

/**
* Constructs a packet codec with the given dictionary, random provider, and packet identifier generator.
*
*
* @param dictionary the dictionary to use
* @param randomProvider the random provider to use
* @param packetIdGenerator the packet identifier generator to use
Expand Down Expand Up @@ -110,13 +112,23 @@ private static Mac getHmacMd5Instance() {
*
* @param request the request packet to encode
* @param secret the shared secret
*
* @param requestAuthenticator the request authenticator (must be 16 bytes in length); populated by this method for
* Accounting-Request packets
*
* @return byte array of the encoded request packet
*
*
* @throws PacketCodecException if there's a problem encoding the packet
*/
public byte[] encodeRequest(Packet request, byte[] secret, byte[] requestAuthenticator)
throws PacketCodecException {
if (requestAuthenticator.length != 16) {
throw new IllegalArgumentException("requestAuthenticator length must be 16");
}

if (request.getCode() == ACCOUNTING_REQUEST_CODE) {
Arrays.fill(requestAuthenticator, (byte) 0x00);
}

CodecContext codecContext = new CodecContext(dictionary, secret, requestAuthenticator, randomProvider);

List<RawAttribute> rawAttributes = encodeAttributes(codecContext, request.getAttributes());
Expand Down Expand Up @@ -177,6 +189,16 @@ public byte[] encodeRequest(Packet request, byte[] secret, byte[] requestAuthent
System.arraycopy(messageAuthenticator, 0, bytes, messageAuthenticatorPosition + 2, 16);
}

if (request.getCode() == ACCOUNTING_REQUEST_CODE) {
MessageDigest md5 = getMd5Instance();
md5.update(bytes);
md5.update(secret);
byte[] accountingRequestAuthenticator = md5.digest();

System.arraycopy(accountingRequestAuthenticator, 0, bytes, 4, 16);
System.arraycopy(accountingRequestAuthenticator, 0, requestAuthenticator, 0, 16);
}

return bytes;
}

Expand All @@ -187,13 +209,17 @@ public byte[] encodeRequest(Packet request, byte[] secret, byte[] requestAuthent
* @param secret the shared secret
* @param requestId the request identifier
* @param requestAuthenticator the request authenticator
*
*
* @return byte array of the encoded response packet
*
*
* @throws PacketCodecException if there's a problem encoding the packet
*/
public byte[] encodeResponse(Packet response, byte[] secret, int requestId, byte[] requestAuthenticator)
throws PacketCodecException {
if (requestAuthenticator.length != 16) {
throw new IllegalArgumentException("requestAuthenticator length must be 16");
}

CodecContext codecContext = new CodecContext(dictionary, secret, requestAuthenticator, randomProvider);

List<RawAttribute> rawAttributes = encodeAttributes(codecContext, response.getAttributes());
Expand Down Expand Up @@ -269,9 +295,9 @@ public byte[] encodeResponse(Packet response, byte[] secret, int requestId, byte
*
* @param bytes byte array of the encoded request packet
* @param secret the shared secret
*
*
* @return a packet object
*
*
* @throws PacketCodecException if the packet is malformed or there's a problem decoding the request packet
*/
public Packet decodeRequest(byte[] bytes, byte[] secret) throws PacketCodecException {
Expand All @@ -291,6 +317,19 @@ public Packet decodeRequest(byte[] bytes, byte[] secret) throws PacketCodecExcep
byte[] authenticatorBytes = new byte[16];
System.arraycopy(bytes, 4, authenticatorBytes, 0, 16);

if (code == ACCOUNTING_REQUEST_CODE) {
Arrays.fill(bytes, 4, 4 + 16, (byte) 0x00);

MessageDigest md5 = getMd5Instance();
md5.update(bytes);
md5.update(secret);
byte[] calculatedAccountingRequestAuthenticator = md5.digest();

if (!Arrays.equals(authenticatorBytes, calculatedAccountingRequestAuthenticator)) {
throw new PacketCodecException("Invalid Accounting-Request packet authenticator");
}
}

CodecContext codecContext = new CodecContext(dictionary, secret, authenticatorBytes, randomProvider);

List<RawAttribute> rawAttributes = new ArrayList<>();
Expand Down Expand Up @@ -372,9 +411,9 @@ public Packet decodeRequest(byte[] bytes, byte[] secret) throws PacketCodecExcep
* @param bytes byte array of the encoded request packet
* @param secret the shared secret
* @param requestAuthenticator the request authenticator
*
*
* @return a packet object
*
*
* @throws PacketCodecException if the packet is malformed or there's a problem decoding the request packet
*/
public Packet decodeResponse(byte[] bytes, byte[] secret, byte[] requestAuthenticator) throws PacketCodecException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,67 @@ void decodeResponse() throws PacketCodecException {

}

@Nested
@DisplayName("Accounting packet encoding and decoding")
class AccountingPackets {

@Test
@DisplayName("Accounting-Request packet is encoded successfully")
void encodeRequest() throws PacketCodecException {
when(mockedPacketIdGenerator.nextId()).thenReturn(42);

Packet requestPacket = new Packet(4, List.of(
new RawAttribute(32, fromHex("5353494431")),
new RawAttribute(40, fromHex("00000001")),
new RawAttribute(44, fromHex("616263313233"))));

byte[] accountingRequestAuthenticator = new byte[16];

byte[] actual = packetCodec.encodeRequest(requestPacket, "abc123".getBytes(US_ASCII),
accountingRequestAuthenticator);

assertEquals("042a00295129abcd7a107c329bee6866d2887782200753534944312806000000" +
"012c08616263313233",
toHex(actual));

assertEquals("5129abcd7a107c329bee6866d2887782",
toHex(accountingRequestAuthenticator));
}

@Test
@DisplayName("Accounting-Request packet is decoded successfully")
void decodeRequest() throws PacketCodecException {
byte[] encoded = fromHex("042a00295129abcd7a107c329bee6866d2887782200753534944312806000000" +
"012c08616263313233");

Packet requestPacket = packetCodec.decodeRequest(encoded, "abc123".getBytes(US_ASCII));

assertEquals(4, requestPacket.getCode());
assertEquals(42, requestPacket.getReceivedFields().getIdentifier());
assertEquals("5129abcd7a107c329bee6866d2887782",
toHex(requestPacket.getReceivedFields().getAuthenticator()));
assertEquals(3, requestPacket.getAttributes().size());
assertEquals(new RawAttribute(32, fromHex("5353494431")),
requestPacket.getAttributes().get(0));
assertEquals(new RawAttribute(40, fromHex("00000001")),
requestPacket.getAttributes().get(1));
assertEquals(new RawAttribute(44, fromHex("616263313233")),
requestPacket.getAttributes().get(2));
}

@Test
@DisplayName("Accounting-Request packet with invalid request authenticator throws codec exception")
void decodeRequestWithInvalidRequestAuthenticatorThrows() throws PacketCodecException {
byte[] encoded = fromHex("042a0029ffffabcd7a107c329bee6866d2887782200753534944312806000000" +
"012c08616263313233");

assertThrows(PacketCodecException.class, () -> {
packetCodec.decodeRequest(encoded, "abc123".getBytes(US_ASCII));
});
}

}

@Nested
@DisplayName("Malformed packets")
class MalformedPackets {
Expand Down

0 comments on commit bef3063

Please sign in to comment.