Skip to content

Commit 9a8ee88

Browse files
committed
added property for limiting Argon2 memory usage - relates to github #2283
1 parent 6925874 commit 9a8ee88

5 files changed

Lines changed: 108 additions & 12 deletions

File tree

core/src/main/java/org/bouncycastle/crypto/params/Argon2Parameters.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import org.bouncycastle.crypto.CharToByteConverter;
44
import org.bouncycastle.crypto.PasswordConverter;
55
import org.bouncycastle.util.Arrays;
6+
import org.bouncycastle.util.Properties;
67

78
public class Argon2Parameters
89
{
10+
public static final String MAX_MEMORY_EXP = "org.bouncycastle.argon2.max_memory_exp";
11+
912
public static final int ARGON2_d = 0x00;
1013
public static final int ARGON2_i = 0x01;
1114
public static final int ARGON2_id = 0x02;
@@ -31,6 +34,7 @@ public static class Builder
3134

3235
private int version;
3336
private final int type;
37+
private final int maxMemory;
3438

3539
private CharToByteConverter converter = PasswordConverter.UTF8;
3640

@@ -46,10 +50,19 @@ public Builder(int type)
4650
this.memory = 1 << DEFAULT_MEMORY_COST;
4751
this.iterations = DEFAULT_ITERATIONS;
4852
this.version = DEFAULT_VERSION;
53+
this.maxMemory = Properties.asInteger(MAX_MEMORY_EXP, 30);
54+
if (maxMemory < 3 || maxMemory > 30)
55+
{
56+
throw new IllegalStateException(MAX_MEMORY_EXP + " out of range");
57+
}
4958
}
5059

5160
public Builder withParallelism(int parallelism)
5261
{
62+
if (lanes < 1)
63+
{
64+
throw new IllegalArgumentException("lanes out of range");
65+
}
5366
this.lanes = parallelism;
5467
return this;
5568
}
@@ -78,16 +91,23 @@ public Builder withIterations(int iterations)
7891
return this;
7992
}
8093

81-
8294
public Builder withMemoryAsKB(int memory)
8395
{
96+
if (memory < 0 || memory > (1 << maxMemory))
97+
{
98+
throw new IllegalArgumentException("memory out of range");
99+
}
84100
this.memory = memory;
85101
return this;
86102
}
87103

88-
89104
public Builder withMemoryPowOfTwo(int memory)
90105
{
106+
// Actual range is supposed to be 31 - int's are signed here so cutoff is at 2**30
107+
if (memory < 0 || memory > maxMemory)
108+
{
109+
throw new IllegalArgumentException("memory exponent out of range");
110+
}
91111
this.memory = 1 << memory;
92112
return this;
93113
}

core/src/test/java/org/bouncycastle/crypto/test/Argon2Test.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.bouncycastle.crypto.test;
22

33

4+
import java.security.SecureRandom;
45
import java.util.ArrayList;
56
import java.util.List;
67

@@ -34,13 +35,14 @@ public void performTest()
3435

3536
testPermutations();
3637
testVectorsFromInternetDraft();
37-
38+
checkArgon2MaxMemoryExpValue();
39+
3840
int version = Argon2Parameters.ARGON2_VERSION_10;
3941

4042
/* Multiple test cases for various input values */
4143
hashTest(version, 2, 16, 1, "password", "somesalt",
4244
"f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694", DEFAULT_OUTPUTLEN);
43-
45+
4446
hashTest(version, 2, 20, 1, "password", "somesalt",
4547
"9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9",
4648
DEFAULT_OUTPUTLEN);
@@ -160,6 +162,49 @@ public void testPermutations()
160162
}
161163
}
162164

165+
private void checkArgon2MaxMemoryExpValue()
166+
throws Exception
167+
{
168+
System.setProperty("org.bouncycastle.argon2.max_memory_exp", "10");
169+
170+
byte[] salt = new byte[16];
171+
new SecureRandom().nextBytes(salt);
172+
173+
try
174+
{
175+
Argon2Parameters parameters = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
176+
.withSalt(salt)
177+
.withIterations(1)
178+
.withParallelism(4)
179+
.withMemoryPowOfTwo(30) // 1 << 22 = 4 GiB, no guard
180+
.withVersion(Argon2Parameters.ARGON2_VERSION_13)
181+
.build();
182+
fail("no exception");
183+
}
184+
catch (IllegalArgumentException e)
185+
{
186+
isEquals("memory exponent out of range", e.getMessage());
187+
}
188+
189+
try
190+
{
191+
Argon2Parameters parameters = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
192+
.withSalt(salt)
193+
.withIterations(1)
194+
.withParallelism(4)
195+
.withMemoryAsKB(1 << 25)
196+
.withVersion(Argon2Parameters.ARGON2_VERSION_13)
197+
.build();
198+
fail("no exception");
199+
}
200+
catch (IllegalArgumentException e)
201+
{
202+
isEquals("memory out of range", e.getMessage());
203+
}
204+
205+
System.setProperty("org.bouncycastle.argon2.max_memory_exp", "30");
206+
}
207+
163208
private void swap(byte[] buf, int i, int j)
164209
{
165210
byte b = buf[i];

pg/src/main/java/org/bouncycastle/bcpg/S2K.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import java.security.SecureRandom;
77

88
import org.bouncycastle.crypto.CryptoServicesRegistrar;
9+
import org.bouncycastle.crypto.params.Argon2Parameters;
910
import org.bouncycastle.util.Integers;
11+
import org.bouncycastle.util.Properties;
1012

1113

1214
/**
@@ -157,6 +159,11 @@ public class S2K
157159
passes = dIn.read();
158160
parallelism = dIn.read();
159161
memorySizeExponent = dIn.read();
162+
// TODO: should really be 3 + log2(parallelism)
163+
if (memorySizeExponent < 3 || memorySizeExponent > Properties.asInteger(Argon2Parameters.MAX_MEMORY_EXP, 30))
164+
{
165+
throw new IOException("memory size exponent out of range");
166+
}
160167
break;
161168

162169
case GNU_DUMMY_S2K:

pg/src/test/java/org/bouncycastle/openpgp/test/Argon2S2KTest.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010

1111
import org.bouncycastle.bcpg.ArmoredInputStream;
1212
import org.bouncycastle.bcpg.ArmoredOutputStream;
13+
import org.bouncycastle.bcpg.BCPGInputStream;
1314
import org.bouncycastle.bcpg.BCPGOutputStream;
1415
import org.bouncycastle.bcpg.S2K;
1516
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
17+
import org.bouncycastle.bcpg.SymmetricKeyEncSessionPacket;
1618
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
1719
import org.bouncycastle.openpgp.PGPEncryptedDataList;
1820
import org.bouncycastle.openpgp.PGPException;
@@ -61,14 +63,14 @@ public class Argon2S2KTest
6163

6264
// https://www.rfc-editor.org/rfc/rfc9580.html#name-v4-skesk-using-argon2-with-ae
6365
private static final String TEST_MSG_AES256 = "-----BEGIN PGP MESSAGE-----\n" +
64-
"Comment: Encrypted using AES with 256-bit key\n" +
65-
"Comment: Session key: BBEDA55B9AAE63DAC45D4F49D89DACF4AF37FEF...\n" +
66-
"Comment: Session key: ...C13BAB2F1F8E18FB74580D8B0\n" +
67-
"\n" +
68-
"wzcECQS4eJUgIG/3mcaILEJFpmJ8AQQVnZ9l7KtagdClm9UaQ/Z6M/5roklSGpGu\n" +
69-
"623YmaXezGj80j4B+Ku1sgTdJo87X1Wrup7l0wJypZls21Uwd67m9koF60eefH/K\n" +
70-
"95D1usliXOEm8ayQJQmZrjf6K6v9PWwqMQ==\n" +
71-
"-----END PGP MESSAGE-----";
66+
"Comment: Encrypted using AES with 256-bit key\n" +
67+
"Comment: Session key: BBEDA55B9AAE63DAC45D4F49D89DACF4AF37FEF...\n" +
68+
"Comment: Session key: ...C13BAB2F1F8E18FB74580D8B0\n" +
69+
"\n" +
70+
"wzcECQS4eJUgIG/3mcaILEJFpmJ8AQQVnZ9l7KtagdClm9UaQ/Z6M/5roklSGpGu\n" +
71+
"623YmaXezGj80j4B+Ku1sgTdJo87X1Wrup7l0wJypZls21Uwd67m9koF60eefH/K\n" +
72+
"95D1usliXOEm8ayQJQmZrjf6K6v9PWwqMQ==\n" +
73+
"-----END PGP MESSAGE-----";
7274

7375
static final String TEST_MSG_PLAIN = "Hello, world!";
7476

@@ -96,6 +98,7 @@ public void performTest()
9698
testDecryptAES256Message();
9799
// dynamic round-trip
98100
testEncryptAndDecryptMessageWithArgon2();
101+
checkArgon2MaxMemoryExpValue();
99102
}
100103

101104
public void encodingTest()
@@ -155,6 +158,27 @@ public void testEncryptAndDecryptMessageWithArgon2()
155158
isEquals(TEST_MSG_PLAIN, plaintext);
156159
}
157160

161+
private void checkArgon2MaxMemoryExpValue()
162+
throws Exception
163+
{
164+
System.setProperty("org.bouncycastle.argon2.max_memory_exp", "10");
165+
166+
BCPGInputStream pgpIn = new BCPGInputStream(
167+
getClass().getResourceAsStream("poc_argon2_s2k.pgp"));
168+
try
169+
{
170+
SymmetricKeyEncSessionPacket skesk =
171+
(SymmetricKeyEncSessionPacket)pgpIn.readPacket();
172+
fail("no exception");
173+
}
174+
catch (IOException e)
175+
{
176+
isEquals("memory size exponent out of range", e.getMessage());
177+
}
178+
179+
System.setProperty("org.bouncycastle.argon2.max_memory_exp", "30");
180+
}
181+
158182
private String decryptSymmetricallyEncryptedMessage(String message, String password)
159183
throws IOException, PGPException
160184
{
Binary file not shown.

0 commit comments

Comments
 (0)