Skip to content

Commit

Permalink
Add Tunnel-Password encryption, OptionalTaggedTextData, TaggedTextDat…
Browse files Browse the repository at this point in the history
…a, and FreeRADIUS dictionary attribute tagging support
  • Loading branch information
tsyd committed Mar 11, 2024
1 parent fc88483 commit 1d75920
Show file tree
Hide file tree
Showing 16 changed files with 1,264 additions and 137 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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;

/**
* Filter for transforming data within attribute {@link Data}.
*/
public interface DataFilter {

/**
* Decodes data.
*
* @param codecContext the codec context
* @param data the data to decode
*
* @return the decoded data or null if the given bytes can not be decoded
*/
byte[] decode(CodecContext codecContext, byte[] data);

/**
* Encodes data.
*
* @param codecContext the codec context
* @param data the data bytes to encode
*
* @return byte array of the encoded data
*/
byte[] encode(CodecContext codecContext, byte[] data);

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,34 @@ public static final class Codec implements DataCodec<IntegerData> {
*/
public static final Codec INSTANCE = new Codec();

private final DataFilter dataFilter;

/**
* Constructs a {@link TaggedStringData.Codec}.
*/
public Codec() {
this.dataFilter = null;
}

/**
* Constructs a {@link TaggedStringData.Codec} with the provided {@link DataFilter}.
*
* @param dataFilter the data filter
*/
public Codec(DataFilter dataFilter) {
this.dataFilter = dataFilter;
}

@Override
public IntegerData decode(CodecContext codecContext, AttributeType parentAttributeType, byte[] bytes) {
if (dataFilter != null) {
bytes = dataFilter.decode(codecContext, bytes);

if (bytes == null) {
return null;
}
}

if (bytes.length != 4) {
return null;
}
Expand All @@ -82,6 +108,10 @@ public byte[] encode(CodecContext codecContext, AttributeType parentAttributeTyp
bytes[2] = (byte) ((integerData.value & 0x0000ff00) >>> 8);
bytes[3] = (byte) (integerData.value & 0x000000ff);

if (dataFilter != null) {
bytes = dataFilter.encode(codecContext, bytes);
}

return bytes;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,34 @@ public static final class Codec implements DataCodec<Ipv4AddrData> {
*/
public static final Codec INSTANCE = new Codec();

private final DataFilter dataFilter;

/**
* Constructs a {@link Codec}.
*/
public Codec() {
this.dataFilter = null;
}

/**
* Constructs a {@link Codec} with the provided {@link DataFilter}.
*
* @param dataFilter the data filter
*/
public Codec(DataFilter dataFilter) {
this.dataFilter = dataFilter;
}

@Override
public Ipv4AddrData decode(CodecContext codecContext, AttributeType parentAttributeType, byte[] bytes) {
if (dataFilter != null) {
bytes = dataFilter.decode(codecContext, bytes);

if (bytes == null) {
return null;
}
}

if (bytes.length != 4) {
return null;
}
Expand All @@ -79,6 +105,10 @@ public Ipv4AddrData decode(CodecContext codecContext, AttributeType parentAttrib
public byte[] encode(CodecContext codecContext, AttributeType parentAttributeType, Data data) {
Ipv4AddrData ipv4AddrData = (Ipv4AddrData) data;

if (dataFilter != null) {
return dataFilter.encode(codecContext, ipv4AddrData.value.getAddress());
}

return ipv4AddrData.value.getAddress();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,57 @@ public static class Codec implements DataCodec<OptionalTaggedStringData> {
*/
public static final Codec INSTANCE = new Codec();

private final DataFilter dataFilter;

/**
* Constructs a {@link Codec}.
*/
public Codec() {
this.dataFilter = null;
}

/**
* Constructs a {@link Codec} with the provided {@link DataFilter}.
*
* @param dataFilter the data filter
*/
public Codec(DataFilter dataFilter) {
this.dataFilter = dataFilter;
}

@Override
public OptionalTaggedStringData decode(CodecContext codecContext, AttributeType parentAttributeType,
byte[] bytes) {
byte[] bytes)
{
if (bytes.length < 1) {
return new OptionalTaggedStringData(new byte[] {});
}

int tag = bytes[0] & 0xff;

if (tag > 31) {
if (dataFilter != null) {
bytes = dataFilter.decode(codecContext, bytes);

if (bytes == null) {
return null;
}
}

return new OptionalTaggedStringData(bytes);
}

return new OptionalTaggedStringData(Arrays.copyOfRange(bytes, 1, bytes.length), tag);
byte[] dataBytes = Arrays.copyOfRange(bytes, 1, bytes.length);

if (dataFilter != null) {
dataBytes = dataFilter.decode(codecContext, dataBytes);
}

if (dataBytes == null) {
return null;
}

return new OptionalTaggedStringData(dataBytes, tag);
}

@Override
Expand All @@ -114,9 +151,17 @@ public byte[] encode(CodecContext codecContext, AttributeType parentAttributeTyp
bytes[0] = optionalTaggedStringData.tag.byteValue();
System.arraycopy(optionalTaggedStringData.value, 0, bytes, 1, optionalTaggedStringData.value.length);

if (dataFilter != null) {
return dataFilter.encode(codecContext, bytes);
}

return bytes;
}

if (dataFilter != null) {
return dataFilter.encode(codecContext, optionalTaggedStringData.value);
}

return optionalTaggedStringData.value;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* 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 java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;

/**
* A "text" attribute data type containing an optional 1-byte tag. The text data is mapped to a {@link String}.
*/
public class OptionalTaggedTextData extends Data {

private final String value;

private final Integer tag;

/**
* Constructs tagged string data from a given byte array and tag.
*
* @param value the string
* @param tag the tag (int in range [0, 31])
*/
public OptionalTaggedTextData(String value, int tag) {
if (tag < 0 || tag > 31) {
throw new IllegalArgumentException("Tag must be in range [0, 31]");
}

this.tag = tag;
this.value = Objects.requireNonNull(value);
}

/**
* Constructs tagged string data from a given string. No tag is included in the data with this constructor.
*
* @param value the string
*/
public OptionalTaggedTextData(String value) {
this.tag = null;
this.value = Objects.requireNonNull(value);
}

@Override
public int length() {
return (tag == null ? 0 : 1) + value.getBytes(StandardCharsets.UTF_8).length;
}

/**
* Gets the text value as a String.
*
* @return the text data as a String
*/
public String getValue() {
return value;
}

/**
* Gets the optional tag.
*
* @return optional tag (Optional with Integer in range [0, 31])
*/
public Optional<Integer> getTag() {
return Optional.ofNullable(tag);
}

/**
* A codec for optional tagged "text" data.
*/
public static class Codec implements DataCodec<OptionalTaggedTextData> {

/**
* An instance of {@link Codec}.
*/
public static final Codec INSTANCE = new Codec();

private final DataFilter dataFilter;

/**
* Constructs a {@link Codec}.
*/
public Codec() {
this.dataFilter = null;
}

/**
* Constructs a {@link Codec} with the provided {@link DataFilter}.
*
* @param dataFilter the data filter
*/
public Codec(DataFilter dataFilter) {
this.dataFilter = dataFilter;
}

@Override
public OptionalTaggedTextData decode(CodecContext codecContext, AttributeType parentAttributeType,
byte[] bytes)
{
if (bytes.length < 1) {
return new OptionalTaggedTextData("");
}

int tag = bytes[0] & 0xff;

if (tag > 31) {
if (dataFilter != null) {
bytes = dataFilter.decode(codecContext, bytes);

if (bytes == null) {
return null;
}
}

return new OptionalTaggedTextData(new String(bytes, StandardCharsets.UTF_8));
}

byte[] dataBytes = Arrays.copyOfRange(bytes, 1, bytes.length);

if (dataFilter != null) {
dataBytes = dataFilter.decode(codecContext, dataBytes);
}

if (dataBytes == null) {
return null;
}

return new OptionalTaggedTextData(new String(dataBytes, StandardCharsets.UTF_8), tag);
}

@Override
public byte[] encode(CodecContext codecContext, AttributeType parentAttributeType, Data data) {
OptionalTaggedTextData optionalTaggedTextData = (OptionalTaggedTextData) data;

if (optionalTaggedTextData.tag != null) {
byte[] bytes = new byte[optionalTaggedTextData.length()];
byte[] dataBytes = optionalTaggedTextData.value.getBytes(StandardCharsets.UTF_8);

dataBytes[0] = optionalTaggedTextData.tag.byteValue();
System.arraycopy(dataBytes, 0, bytes, 1, dataBytes.length);

if (dataFilter != null) {
return dataFilter.encode(codecContext, bytes);
}

return bytes;
}

byte[] bytes = optionalTaggedTextData.value.getBytes(StandardCharsets.UTF_8);

if (dataFilter != null) {
return dataFilter.encode(codecContext, bytes);
}

return bytes;
}

}

}
Loading

0 comments on commit 1d75920

Please sign in to comment.