Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4588708
version to 2.1.1-SNAPSHOT
hhund Jul 29, 2026
c918b95
backport from develop
hhund Jul 29, 2026
1812d95
throw bpmn error if translater is applied
wetret May 29, 2026
9b26057
fixed class and constructor visibility
hhund Jun 3, 2026
db2f39c
Simplified SendTaskErrorBoundaryEventTestThrow, added missing condition
hhund Jun 3, 2026
ac28656
Fix check for unsupported JWKS algorithm
EmteZogaf Jul 2, 2026
760c595
URL-encode client id and client secret for Basic Auth
EmteZogaf Jul 2, 2026
b62f60b
Fixed encoding as defined in RFC 6749 - Appendix B -> UTF-8
hhund Jul 4, 2026
6d802fa
new test to reproduce issue #535
hhund Jul 7, 2026
4985d70
special cases for null json values to fix issue #535
hhund Jul 7, 2026
b6735d6
fixed bad handling of input values with index 0, some code cleanup
hhund Jul 22, 2026
d08f97c
Merge remote-tracking branch 'origin/issue/560_Backport_Bugfixes' into
hhund Jul 29, 2026
d318dc1
new tests to verify conditional updates
hhund Jul 30, 2026
22743e1
added defensive check, fixed old/new resource mix-up
hhund Jul 30, 2026
a696810
Adds limits for max allowed plain text and crypt text stream lengths
hhund Jul 31, 2026
5ad2a82
new tests to verify $snapshot operations enforce authorization rules
hhund Jul 31, 2026
d29e157
improved $snapshot implementation, added missing authorization checks
hhund Jul 31, 2026
d8cc90c
code formatting
hhund Jul 31, 2026
703c9b4
javadoc fix
hhund Jul 31, 2026
08c32a8
Merge remote-tracking branch
hhund Aug 4, 2026
a36a03f
Merge remote-tracking branch
hhund Aug 4, 2026
8d5df49
Merge remote-tracking branch
hhund Aug 4, 2026
fb3877b
new security service and authorization rule unit tests
hhund Aug 2, 2026
662e336
additional draft task integration tests
hhund Aug 2, 2026
5ae983e
some refactoring and harmonization
hhund Aug 2, 2026
d4901e6
2.1.1 release
hhund Aug 4, 2026
3eac36a
fixed typo, decreased visibility
hhund Aug 4, 2026
3d55ab6
Merge branch 'hotfix/2.1.1' into main
hhund Aug 4, 2026
9aa72f4
Merge remote-tracking branch 'origin/main' into
hhund Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ preferred-citation:
doi: 10.3233/SHTI210060
type: proceedings
title: "Data Sharing Framework (DSF)"
version: 2.1.1
date-released: 2026-04-20
version: 2.2.0
date-released: 2026-08-25
url: https://dsf.dev
repository-code: https://github.com/datasharingframework/dsf
repository-artifact: https://github.com/datasharingframework/dsf/releases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,15 @@
import de.hsheilbronn.mi.utils.crypto.keypair.KeyPairGeneratorFactory;
import de.hsheilbronn.mi.utils.crypto.keypair.KeyPairValidator;
import de.hsheilbronn.mi.utils.crypto.keystore.KeyStoreCreator;
import dev.dsf.bpe.v2.service.stream.LimitedInputStream;

public class CryptoServiceImpl implements CryptoService
{
public static final class KemDelegate implements Kem
{
protected static final long ENCRYPT_LIMIT = 250 * 1024 * 1024; // 250 MiB
protected static final long DECRYPT_LIMIT = (250 * 1024 * 1024) + 1024; // 250 MiB + 1024 Bytes

private final AbstractKemAesGcm delegate;

public KemDelegate(AbstractKemAesGcm delegate)
Expand All @@ -63,15 +67,15 @@ public KemDelegate(AbstractKemAesGcm delegate)
public InputStream encrypt(InputStream data, PublicKey publicKey) throws NoSuchAlgorithmException,
InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException
{
return delegate.encrypt(data, publicKey);
return delegate.encrypt(new LimitedInputStream(data, ENCRYPT_LIMIT), publicKey);
}

@Override
public InputStream decrypt(InputStream encrypted, PrivateKey privateKey)
throws IOException, NoSuchAlgorithmException, InvalidKeyException, DecapsulateException,
NoSuchPaddingException, InvalidAlgorithmParameterException
{
return delegate.decrypt(encrypted, privateKey);
return delegate.decrypt(new LimitedInputStream(encrypted, DECRYPT_LIMIT), privateKey);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Copyright 2018-2025 Heilbronn University of Applied Sciences
*
* 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 dev.dsf.bpe.v2.service.stream;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

/**
* An InputStream wrapper that exposes at most a configured number of bytes from the underlying stream.
*
* <p>
* Unlike a regular end-of-stream condition, exhausting the configured limit is treated as a resource limit violation.
* Once the configured limit has been exhausted, every subsequent call to {@link #read()} or {@link #skip(long)} throws
* {@link IOException}, regardless of whether the underlying stream has reached end-of-stream.
* </p>
*
* <p>
* If the underlying stream supports mark/reset, this stream supports it as well. The remaining byte limit is restored
* when {@link #reset()} is called.
* </p>
*
* This class is not safe for concurrent use by multiple threads.
*/
public final class LimitedInputStream extends FilterInputStream
{
private final long maxBytes;

private long remainingBytes;
private long markedRemaining;

/**
* Creates a stream that allows reading at most {@code maxBytes} bytes.
*
* @param in
* the underlying input stream
* @param maxBytes
* the maximum number of bytes that may be read
* @throws NullPointerException
* if {@code in} is null
* @throws IllegalArgumentException
* if {@code maxBytes} is negative
*/
public LimitedInputStream(InputStream in, long maxBytes)
{
super(Objects.requireNonNull(in, "in"));

if (maxBytes < 0)
throw new IllegalArgumentException("maxBytes must be >= 0");

this.maxBytes = maxBytes;

this.remainingBytes = maxBytes;
this.markedRemaining = maxBytes;
}

/**
* Returns the number of bytes that may still be consumed.
*
* @return remaining byte allowance
*/
public long getRemainingBytes()
{
return remainingBytes;
}

@Override
public int read() throws IOException
{
ensureRemaining();

int b = super.read();
if (b != -1)
remainingBytes--;

return b;
}

@Override
public int read(byte[] b, int off, int len) throws IOException
{
Objects.checkFromIndexSize(off, len, b.length);

if (len == 0)
return 0;

ensureRemaining();

int allowed = (int) Math.min(len, remainingBytes);
int count = super.read(b, off, allowed);

if (count > 0)
remainingBytes -= count;

return count;
}

/**
* Skipping bytes beyond the configured limit is treated as an attempt to consume data beyond the allowed resource
* boundary.
*
* @throws IOException
* if the limit has already been exhausted
*/
@Override
public long skip(long n) throws IOException
{
if (n <= 0)
return 0;

ensureRemaining();

long skipped = super.skip(Math.min(n, remainingBytes));
remainingBytes -= skipped;

return skipped;
}

/**
* Returns the smaller of the underlying available bytes and the remaining byte allowance. This value represents
* bytes available within this wrapper's limit, not necessarily bytes immediately readable.
*/
@Override
public int available() throws IOException
{
return (int) Math.min(super.available(), remainingBytes);
}

@Override
public synchronized void mark(int readlimit)
{
if (markSupported())
{
super.mark(readlimit);
markedRemaining = remainingBytes;
}
}

@Override
public synchronized void reset() throws IOException
{
super.reset();
remainingBytes = markedRemaining;
}

private void ensureRemaining() throws IOException
{
if (remainingBytes == 0)
throw new IOException("Stream limit of " + maxBytes + " byte" + (maxBytes != 1 ? "s" : "") + " exceeded");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright 2018-2025 Heilbronn University of Applied Sciences
*
* 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 dev.dsf.bpe.v2.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyPair;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

import de.hsheilbronn.mi.utils.crypto.kem.AbstractKemAesGcm;
import de.hsheilbronn.mi.utils.crypto.kem.EcDhKemAesGcm;
import de.hsheilbronn.mi.utils.crypto.kem.RsaKemAesGcm;
import de.hsheilbronn.mi.utils.crypto.keypair.KeyPairGeneratorFactory;
import dev.dsf.bpe.v2.service.CryptoService.Kem;
import dev.dsf.bpe.v2.service.CryptoServiceImpl.KemDelegate;

@RunWith(Parameterized.class)
public class CryptoServiceTest
{
private static final class ZeroInputStream extends InputStream
{
private final long size;
private long position = 0;

public ZeroInputStream(long size)
{
this.size = size;
}

@Override
public int read() throws IOException
{
if (position >= size)
return -1;

position++;
return 0;
}

@Override
public int read(byte[] b, int off, int len) throws IOException
{
if (position >= size)
return -1;

int bytesToRead = (int) Math.min(len, size - position);

for (int i = 0; i < bytesToRead; i++)
b[off + i] = 0;

position += bytesToRead;
return bytesToRead;
}

@Override
public long skip(long n) throws IOException
{
long skipped = Math.min(n, size - position);
position += skipped;
return skipped;
}

@Override
public int available() throws IOException
{
long remaining = size - position;
return remaining > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) remaining;
}

@Override
public void close() throws IOException
{
// nothing to do
}
}

@Parameters
public static Object[][] data()
{
CryptoService cryptoService = new CryptoServiceImpl();

return new Object[][] {
{ cryptoService.createKeyPairGeneratorX25519AndInitialize().genKeyPair(), cryptoService.createEcDhKem(),
new EcDhKemAesGcm() },
{ cryptoService.createKeyPairGeneratorX448AndInitialize().genKeyPair(), cryptoService.createEcDhKem(),
new EcDhKemAesGcm() },
{ KeyPairGeneratorFactory.rsa1024().initialize().generateKeyPair(), cryptoService.createRsaKem(),
new RsaKemAesGcm() },
{ cryptoService.createKeyPairGeneratorRsa4096AndInitialize().generateKeyPair(),
cryptoService.createRsaKem(), new RsaKemAesGcm() } };
}

@Parameter(0)
public KeyPair keyPair;

@Parameter(1)
public Kem kem;

@Parameter(2)
public AbstractKemAesGcm noLimitKem;

@Test
public void testEncryptionLimit() throws Exception
{
try
{
kem.encrypt(new ZeroInputStream(KemDelegate.ENCRYPT_LIMIT + 1), keyPair.getPublic())
.transferTo(OutputStream.nullOutputStream());

fail("Expected IOException");
}
catch (IOException e)
{
assertEquals("Stream limit of " + KemDelegate.ENCRYPT_LIMIT + " bytes exceeded", e.getMessage());
}
}

@Test
public void testDecryptLimit() throws Exception
{
try
{
InputStream encrypted = noLimitKem.encrypt(new ZeroInputStream(KemDelegate.DECRYPT_LIMIT + 1),
keyPair.getPublic());

kem.decrypt(encrypted, keyPair.getPrivate()).transferTo(OutputStream.nullOutputStream());

fail("Expected IOException");
}
catch (IOException e)
{
assertEquals("Stream limit of " + KemDelegate.DECRYPT_LIMIT + " bytes exceeded", e.getMessage());
}
}
}
Loading
Loading