Skip to content

Commit f4f8071

Browse files
Improved Curve25519 Public Key Handling (#959)
- Modified Curve25519 negotiation to determine algorithm identifier length based on PublicKey.getEncoded() length instead of hard-coded value of 44 - Runtime length determination avoids differences in X25519 implementations on Java 11 Co-authored-by: Jeroen van Erp <[email protected]>
1 parent f525ed0 commit f4f8071

File tree

2 files changed

+41
-17
lines changed

2 files changed

+41
-17
lines changed

src/main/java/net/schmizz/sshj/transport/kex/Curve25519DH.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ public class Curve25519DH extends DHBase {
3434

3535
private static final String ALGORITHM = "X25519";
3636

37-
private static final int ENCODED_ALGORITHM_ID_KEY_LENGTH = 44;
37+
private static final int KEY_LENGTH = 32;
3838

39-
private static final int ALGORITHM_ID_LENGTH = 12;
39+
private int encodedKeyLength;
4040

41-
private static final int KEY_LENGTH = 32;
41+
private int algorithmIdLength;
4242

43-
private final byte[] algorithmId = new byte[ALGORITHM_ID_LENGTH];
43+
// Algorithm Identifier is set on Key Agreement Initialization
44+
private byte[] algorithmId = new byte[KEY_LENGTH];
4445

4546
public Curve25519DH() {
4647
super(ALGORITHM, ALGORITHM);
@@ -81,23 +82,24 @@ public void init(final AlgorithmParameterSpec params, final Factory<Random> rand
8182
private void setPublicKey(final PublicKey publicKey) {
8283
final byte[] encoded = publicKey.getEncoded();
8384

85+
// Set key and algorithm identifier lengths based on initialized Public Key
86+
encodedKeyLength = encoded.length;
87+
algorithmIdLength = encodedKeyLength - KEY_LENGTH;
88+
algorithmId = new byte[algorithmIdLength];
89+
8490
// Encoded public key consists of the algorithm identifier and public key
85-
if (encoded.length == ENCODED_ALGORITHM_ID_KEY_LENGTH) {
86-
final byte[] publicKeyEncoded = new byte[KEY_LENGTH];
87-
System.arraycopy(encoded, ALGORITHM_ID_LENGTH, publicKeyEncoded, 0, KEY_LENGTH);
88-
setE(publicKeyEncoded);
89-
90-
// Save Algorithm Identifier byte array
91-
System.arraycopy(encoded, 0, algorithmId, 0, ALGORITHM_ID_LENGTH);
92-
} else {
93-
throw new IllegalArgumentException(String.format("X25519 unsupported public key length [%d]", encoded.length));
94-
}
91+
final byte[] publicKeyEncoded = new byte[KEY_LENGTH];
92+
System.arraycopy(encoded, algorithmIdLength, publicKeyEncoded, 0, KEY_LENGTH);
93+
setE(publicKeyEncoded);
94+
95+
// Save Algorithm Identifier byte array
96+
System.arraycopy(encoded, 0, algorithmId, 0, algorithmIdLength);
9597
}
9698

9799
private KeySpec getPeerPublicKeySpec(final byte[] peerPublicKey) {
98-
final byte[] encodedKeySpec = new byte[ENCODED_ALGORITHM_ID_KEY_LENGTH];
99-
System.arraycopy(algorithmId, 0, encodedKeySpec, 0, ALGORITHM_ID_LENGTH);
100-
System.arraycopy(peerPublicKey, 0, encodedKeySpec, ALGORITHM_ID_LENGTH, KEY_LENGTH);
100+
final byte[] encodedKeySpec = new byte[encodedKeyLength];
101+
System.arraycopy(algorithmId, 0, encodedKeySpec, 0, algorithmIdLength);
102+
System.arraycopy(peerPublicKey, 0, encodedKeySpec, algorithmIdLength, KEY_LENGTH);
101103
return new X509EncodedKeySpec(encodedKeySpec);
102104
}
103105
}

src/test/java/net/schmizz/sshj/transport/kex/Curve25519DHTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,24 @@
1515
*/
1616
package net.schmizz.sshj.transport.kex;
1717

18+
import net.schmizz.sshj.common.SecurityUtils;
1819
import net.schmizz.sshj.transport.random.JCERandom;
20+
import org.junit.jupiter.api.BeforeEach;
1921
import org.junit.jupiter.api.Test;
2022

2123
import java.math.BigInteger;
2224
import java.security.GeneralSecurityException;
25+
import java.security.KeyPairGenerator;
26+
import java.security.Provider;
27+
import java.security.Security;
2328

2429
import static org.junit.jupiter.api.Assertions.assertEquals;
2530
import static org.junit.jupiter.api.Assertions.assertNotNull;
2631

2732
public class Curve25519DHTest {
2833

34+
private static final String ALGORITHM_FILTER = "KeyPairGenerator.X25519";
35+
2936
private static final int KEY_LENGTH = 32;
3037

3138
private static final byte[] PEER_PUBLIC_KEY = {
@@ -35,8 +42,16 @@ public class Curve25519DHTest {
3542
1, 2, 3, 4, 5, 6, 7, 8
3643
};
3744

45+
@BeforeEach
46+
public void clearSecurityProvider() {
47+
SecurityUtils.setSecurityProvider(null);
48+
}
49+
3850
@Test
3951
public void testInitPublicKeyLength() throws GeneralSecurityException {
52+
final boolean bouncyCastleRegistrationRequired = isAlgorithmUnsupported();
53+
SecurityUtils.setRegisterBouncyCastle(bouncyCastleRegistrationRequired);
54+
4055
final Curve25519DH dh = new Curve25519DH();
4156
dh.init(null, new JCERandom.Factory());
4257

@@ -48,6 +63,8 @@ public void testInitPublicKeyLength() throws GeneralSecurityException {
4863

4964
@Test
5065
public void testInitComputeSharedSecretKey() throws GeneralSecurityException {
66+
SecurityUtils.setRegisterBouncyCastle(true);
67+
5168
final Curve25519DH dh = new Curve25519DH();
5269
dh.init(null, new JCERandom.Factory());
5370

@@ -57,4 +74,9 @@ public void testInitComputeSharedSecretKey() throws GeneralSecurityException {
5774
assertNotNull(sharedSecretKey);
5875
assertEquals(BigInteger.ONE.signum(), sharedSecretKey.signum());
5976
}
77+
78+
private boolean isAlgorithmUnsupported() {
79+
final Provider[] providers = Security.getProviders(ALGORITHM_FILTER);
80+
return providers == null || providers.length == 0;
81+
}
6082
}

0 commit comments

Comments
 (0)