changes from review

Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
This commit is contained in:
Lachlan Roberts 2024-07-23 17:42:07 +10:00
parent 52c6c88de6
commit 9ea7431c43
13 changed files with 178 additions and 218 deletions

View File

@ -188,14 +188,17 @@ public class EthereumAuthenticator extends LoginAuthenticator implements Dumpabl
}
/**
* This setting is only meaningful if a non-null {@link LoginService} has been set.
* Configures the behavior for authenticating users not found by a wrapped {@link LoginService}.
* <p>
* If set to true, any users not found by the {@link LoginService} will still
* be authenticated but with no roles, if set to false users will not be
* authenticated unless they are discovered by the wrapped {@link LoginService}.
* This setting is only meaningful if a wrapped {@link LoginService} has been set.
* </p>
* @param authenticateNewUsers whether to authenticate users not found by a wrapping LoginService
*/
* <p>
* If set to {@code true}, users not found by a wrapped {@link LoginService} will authenticated with no roles.
* If set to {@code false}, only users found by a wrapped {@link LoginService} will be authenticated.
* </p>
*
* @param authenticateNewUsers whether to authenticate users not found by the wrapped {@link LoginService}
**/
public void setAuthenticateNewUsers(boolean authenticateNewUsers)
{
this._authenticateNewUsers = authenticateNewUsers;
@ -817,4 +820,12 @@ public class EthereumAuthenticator extends LoginAuthenticator implements Dumpabl
return super.add(element);
}
}
public record SignedMessage(String message, String signature)
{
public String recoverAddress()
{
return EthereumUtil.recoverAddress(this);
}
}
}

View File

@ -1,24 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.security.siwe;
import org.eclipse.jetty.security.siwe.internal.EthereumSignatureVerifier;
public record SignedMessage(String message, String signature)
{
public String recoverAddress()
{
return EthereumSignatureVerifier.recoverAddress(this);
}
}

View File

@ -1,139 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.security.siwe.internal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECPoint;
import org.eclipse.jetty.security.siwe.SignedMessage;
import org.eclipse.jetty.util.StringUtil;
/**
* Used to recover an Ethereum address from a message and signature.
* <p>
* This uses algorithms and terminology defined in <a href="https://eips.ethereum.org/EIPS/eip-191">EIP-191</a> and
* <a href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">ECDSA</a>.
* </p>
*/
public class EthereumSignatureVerifier
{
public static final String PREFIX = "\u0019Ethereum Signed Message:\n";
private static final int ADDRESS_LENGTH_BYTES = 20;
private static final X9ECParameters SEC_P256K1_PARAMS = CustomNamedCurves.getByName("secp256k1");
private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(
SEC_P256K1_PARAMS.getCurve(), SEC_P256K1_PARAMS.getG(), SEC_P256K1_PARAMS.getN(), SEC_P256K1_PARAMS.getH());
private static final BigInteger PRIME = SEC_P256K1_PARAMS.getCurve().getField().getCharacteristic();
private static final X9IntegerConverter INT_CONVERTER = new X9IntegerConverter();
private static final Charset CHARSET = StandardCharsets.UTF_8;
private EthereumSignatureVerifier()
{
}
/**
* Recover the Ethereum Address from the {@link SignedMessage}.
* @param signedMessage the signed message used to recover the address.
* @return the ethereum address recovered from the signature.
*/
public static String recoverAddress(SignedMessage signedMessage)
{
String siweMessage = signedMessage.message();
String signatureHex = signedMessage.signature();
if (StringUtil.asciiStartsWithIgnoreCase(signatureHex, "0x"))
signatureHex = signatureHex.substring(2);
int messageLength = siweMessage.getBytes(CHARSET).length;
String prefixedMessage = PREFIX + messageLength + siweMessage;
byte[] messageHash = keccak256(prefixedMessage.getBytes(CHARSET));
byte[] signatureBytes = StringUtil.fromHexString(signatureHex);
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 0, 32));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 32, 64));
byte v = (byte)(signatureBytes[64] < 27 ? signatureBytes[64] : signatureBytes[64] - 27);
ECPoint qPoint = ecRecover(messageHash, v, r, s);
if (qPoint == null)
return null;
return toAddress(qPoint);
}
public static ECPoint ecRecover(byte[] hash, int v, BigInteger r, BigInteger s)
{
if (v < 0 || v >= 4)
throw new IllegalArgumentException("Invalid v value: " + v);
// Verify that r and s are integers in [1, n-1]. If not, the signature is invalid.
BigInteger n = DOMAIN_PARAMS.getN();
if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n.subtract(BigInteger.ONE)) > 0)
return null;
if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n.subtract(BigInteger.ONE)) > 0)
return null;
// Calculate the curve point R.
BigInteger x = r.add(BigInteger.valueOf(v / 2).multiply(n));
if (x.compareTo(PRIME) >= 0)
return null;
ECPoint rPoint = decodePoint(x, v);
if (!rPoint.multiply(n).isInfinity())
return null;
// Calculate the curve point Q = u1 * G + u2 * R, where u1=-zr^(-1)%n and u2=sr^(-1)%n.
// Note: for secp256k1 z=e as the hash is 256 bits and z is defined as the Ln leftmost bits of e.
BigInteger e = new BigInteger(1, hash);
BigInteger rInv = r.modInverse(n);
BigInteger u1 = e.negate().multiply(rInv).mod(n);
BigInteger u2 = s.multiply(rInv).mod(n);
return ECAlgorithms.sumOfTwoMultiplies(DOMAIN_PARAMS.getG(), u1, rPoint, u2);
}
public static String toAddress(ECPoint point)
{
// Remove the 1-byte prefix and return the public key as an ethereum address.
byte[] qBytes = point.getEncoded(false);
byte[] qHash = keccak256(qBytes, 1, qBytes.length - 1);
byte[] address = new byte[ADDRESS_LENGTH_BYTES];
System.arraycopy(qHash, qHash.length - ADDRESS_LENGTH_BYTES, address, 0, ADDRESS_LENGTH_BYTES);
return "0x" + StringUtil.toHexString(address);
}
public static ECPoint decodePoint(BigInteger p, int v)
{
byte[] encodedPoint = INT_CONVERTER.integerToBytes(p, 1 + INT_CONVERTER.getByteLength(DOMAIN_PARAMS.getCurve()));
encodedPoint[0] = (byte)((v % 2) == 0 ? 0x02 : 0x03);
return DOMAIN_PARAMS.getCurve().decodePoint(encodedPoint);
}
public static byte[] keccak256(byte[] bytes)
{
Keccak.Digest256 digest256 = new Keccak.Digest256();
return digest256.digest(bytes);
}
public static byte[] keccak256(byte[] buf, int offset, int len)
{
Keccak.Digest256 digest256 = new Keccak.Digest256();
digest256.update(buf, offset, len);
return digest256.digest();
}
}

View File

@ -13,17 +13,129 @@
package org.eclipse.jetty.security.siwe.internal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECPoint;
import org.eclipse.jetty.security.siwe.EthereumAuthenticator;
import org.eclipse.jetty.util.StringUtil;
public class EthereumUtil
{
public static final String PREFIX = "\u0019Ethereum Signed Message:\n";
private static final String NONCE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final SecureRandom RANDOM = new SecureRandom();
private static final int ADDRESS_LENGTH_BYTES = 20;
private static final X9ECParameters SEC_P256K1_PARAMS = CustomNamedCurves.getByName("secp256k1");
private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(
SEC_P256K1_PARAMS.getCurve(), SEC_P256K1_PARAMS.getG(), SEC_P256K1_PARAMS.getN(), SEC_P256K1_PARAMS.getH());
private static final BigInteger PRIME = SEC_P256K1_PARAMS.getCurve().getField().getCharacteristic();
private static final X9IntegerConverter INT_CONVERTER = new X9IntegerConverter();
private static final Charset CHARSET = StandardCharsets.UTF_8;
private EthereumUtil()
{
}
/**
* Recover the Ethereum Address from the {@link EthereumAuthenticator.SignedMessage}.
* <p>
* This uses algorithms and terminology defined in <a href="https://eips.ethereum.org/EIPS/eip-191">EIP-191</a> and
* <a href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">ECDSA</a>.
* </p>
* @param signedMessage the signed message used to recover the address.
* @return the ethereum address recovered from the signature.
*/
public static String recoverAddress(EthereumAuthenticator.SignedMessage signedMessage)
{
String siweMessage = signedMessage.message();
String signatureHex = signedMessage.signature();
if (StringUtil.asciiStartsWithIgnoreCase(signatureHex, "0x"))
signatureHex = signatureHex.substring(2);
int messageLength = siweMessage.getBytes(CHARSET).length;
String prefixedMessage = PREFIX + messageLength + siweMessage;
byte[] messageHash = keccak256(prefixedMessage.getBytes(CHARSET));
byte[] signatureBytes = StringUtil.fromHexString(signatureHex);
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 0, 32));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 32, 64));
byte v = (byte)(signatureBytes[64] < 27 ? signatureBytes[64] : signatureBytes[64] - 27);
ECPoint qPoint = ecRecover(messageHash, v, r, s);
if (qPoint == null)
return null;
return toAddress(qPoint);
}
public static ECPoint ecRecover(byte[] hash, int v, BigInteger r, BigInteger s)
{
if (v < 0 || v >= 4)
throw new IllegalArgumentException("Invalid v value: " + v);
// Verify that r and s are integers in [1, n-1]. If not, the signature is invalid.
BigInteger n = DOMAIN_PARAMS.getN();
if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n.subtract(BigInteger.ONE)) > 0)
return null;
if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n.subtract(BigInteger.ONE)) > 0)
return null;
// Calculate the curve point R.
BigInteger x = r.add(BigInteger.valueOf(v / 2).multiply(n));
if (x.compareTo(PRIME) >= 0)
return null;
ECPoint rPoint = decodePoint(x, v);
if (!rPoint.multiply(n).isInfinity())
return null;
// Calculate the curve point Q = u1 * G + u2 * R, where u1=-zr^(-1)%n and u2=sr^(-1)%n.
// Note: for secp256k1 z=e as the hash is 256 bits and z is defined as the Ln leftmost bits of e.
BigInteger e = new BigInteger(1, hash);
BigInteger rInv = r.modInverse(n);
BigInteger u1 = e.negate().multiply(rInv).mod(n);
BigInteger u2 = s.multiply(rInv).mod(n);
return ECAlgorithms.sumOfTwoMultiplies(DOMAIN_PARAMS.getG(), u1, rPoint, u2);
}
public static String toAddress(ECPoint point)
{
// Remove the 1-byte prefix and return the public key as an ethereum address.
byte[] qBytes = point.getEncoded(false);
byte[] qHash = keccak256(qBytes, 1, qBytes.length - 1);
byte[] address = new byte[ADDRESS_LENGTH_BYTES];
System.arraycopy(qHash, qHash.length - ADDRESS_LENGTH_BYTES, address, 0, ADDRESS_LENGTH_BYTES);
return "0x" + StringUtil.toHexString(address);
}
public static ECPoint decodePoint(BigInteger p, int v)
{
byte[] encodedPoint = INT_CONVERTER.integerToBytes(p, 1 + INT_CONVERTER.getByteLength(DOMAIN_PARAMS.getCurve()));
encodedPoint[0] = (byte)((v % 2) == 0 ? 0x02 : 0x03);
return DOMAIN_PARAMS.getCurve().decodePoint(encodedPoint);
}
public static byte[] keccak256(byte[] bytes)
{
Keccak.Digest256 digest256 = new Keccak.Digest256();
return digest256.digest(bytes);
}
public static byte[] keccak256(byte[] buf, int offset, int len)
{
Keccak.Digest256 digest256 = new Keccak.Digest256();
digest256.update(buf, offset, len);
return digest256.digest();
}
public static String createNonce()
{
StringBuilder builder = new StringBuilder(8);

View File

@ -18,7 +18,7 @@ import java.time.format.DateTimeFormatter;
import java.util.function.Predicate;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.siwe.SignedMessage;
import org.eclipse.jetty.security.siwe.EthereumAuthenticator;
import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.StringUtil;
@ -54,13 +54,13 @@ public record SignInWithEthereumToken(String scheme,
{
/**
* @param signedMessage the {@link SignedMessage}.
* @param signedMessage the {@link EthereumAuthenticator.SignedMessage}.
* @param validateNonce a {@link Predicate} used to validate the nonce.
* @param domains the {@link IncludeExcludeSet} used to validate the domain.
* @param chainIds the {@link IncludeExcludeSet} used to validate the chainId.
* @throws ServerAuthException if the {@link SignedMessage} fails validation.
* @throws ServerAuthException if the {@link EthereumAuthenticator.SignedMessage} fails validation.
*/
public void validate(SignedMessage signedMessage, Predicate<String> validateNonce,
public void validate(EthereumAuthenticator.SignedMessage signedMessage, Predicate<String> validateNonce,
IncludeExcludeSet<String, String> domains,
IncludeExcludeSet<String, String> chainIds) throws ServerAuthException
{

View File

@ -142,7 +142,7 @@ public class SignInWithEthereumTest
// Create ethereum credentials to login, and sign a login message.
String siweMessage = SignInWithEthereumGenerator.generateMessage(_connector.getLocalPort(), _credentials.getAddress(), nonce);
SignedMessage signedMessage = _credentials.signMessage(siweMessage);
EthereumAuthenticator.SignedMessage signedMessage = _credentials.signMessage(siweMessage);
// Send an Authentication request with the signed SIWE message, this should redirect back to initial request.
response = sendAuthRequest(signedMessage);
@ -190,7 +190,7 @@ public class SignInWithEthereumTest
// Create ethereum credentials to login, and sign a login message.
String siweMessage = SignInWithEthereumGenerator.generateMessage(_connector.getLocalPort(), _credentials.getAddress(), nonce);
SignedMessage signedMessage = _credentials.signMessage(siweMessage);
EthereumAuthenticator.SignedMessage signedMessage = _credentials.signMessage(siweMessage);
// Initial authentication should succeed because it has a valid nonce.
response = sendAuthRequest(signedMessage);
@ -249,7 +249,7 @@ public class SignInWithEthereumTest
assertThat(response.getContentAsString(), containsString("UserPrincipal: " + _credentials.getAddress()));
}
private ContentResponse sendAuthRequest(SignedMessage signedMessage) throws ExecutionException, InterruptedException, TimeoutException
private ContentResponse sendAuthRequest(EthereumAuthenticator.SignedMessage signedMessage) throws ExecutionException, InterruptedException, TimeoutException
{
MultiPartRequestContent content = new MultiPartRequestContent();
content.addPart(new MultiPart.ByteBufferPart("signature", null, null, BufferUtil.toBuffer(signedMessage.signature())));

View File

@ -50,7 +50,7 @@ public class SignInWithEthereumTokenTest
null, null, null, null
);
SignedMessage signedMessage = credentials.signMessage(message);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(message);
SignInWithEthereumToken siwe = SignInWithEthereumParser.parse(message);
assertNotNull(siwe);
@ -79,7 +79,7 @@ public class SignInWithEthereumTokenTest
null, null, null
);
SignedMessage signedMessage = credentials.signMessage(message);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(message);
SignInWithEthereumToken siwe = SignInWithEthereumParser.parse(message);
assertNotNull(siwe);
@ -109,7 +109,7 @@ public class SignInWithEthereumTokenTest
null, null
);
SignedMessage signedMessage = credentials.signMessage(message);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(message);
SignInWithEthereumToken siwe = SignInWithEthereumParser.parse(message);
assertNotNull(siwe);
@ -136,7 +136,7 @@ public class SignInWithEthereumTokenTest
null, null, null, null
);
SignedMessage signedMessage = credentials.signMessage(message);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(message);
SignInWithEthereumToken siwe = SignInWithEthereumParser.parse(message);
assertNotNull(siwe);
@ -166,7 +166,7 @@ public class SignInWithEthereumTokenTest
null, null, null, null
);
SignedMessage signedMessage = credentials.signMessage(message);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(message);
SignInWithEthereumToken siwe = SignInWithEthereumParser.parse(message);
assertNotNull(siwe);
@ -196,7 +196,7 @@ public class SignInWithEthereumTokenTest
null, null, null, null
);
SignedMessage signedMessage = credentials.signMessage(message);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(message);
SignInWithEthereumToken siwe = SignInWithEthereumParser.parse(message);
assertNotNull(siwe);
@ -224,7 +224,7 @@ public class SignInWithEthereumTokenTest
null, null, null, null
);
SignedMessage signedMessage = credentials.signMessage(message);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(message);
SignInWithEthereumToken siwe = SignInWithEthereumParser.parse(message);
assertNotNull(siwe);

View File

@ -27,7 +27,7 @@ public class SignatureVerificationTest
public void testSignatureVerification() throws Exception
{
String siweMessage = "hello world";
SignedMessage signedMessage = credentials.signMessage(siweMessage);
EthereumAuthenticator.SignedMessage signedMessage = credentials.signMessage(siweMessage);
String address = credentials.getAddress();
String recoveredAddress = signedMessage.recoverAddress();
assertThat(recoveredAddress, equalToIgnoringCase(address));

View File

@ -28,10 +28,10 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.eclipse.jetty.security.siwe.SignedMessage;
import org.eclipse.jetty.security.siwe.internal.EthereumSignatureVerifier;
import org.eclipse.jetty.security.siwe.EthereumAuthenticator;
import org.eclipse.jetty.security.siwe.internal.EthereumUtil;
import static org.eclipse.jetty.security.siwe.internal.EthereumSignatureVerifier.keccak256;
import static org.eclipse.jetty.security.siwe.internal.EthereumUtil.keccak256;
public class EthereumCredentials
{
@ -50,7 +50,7 @@ public class EthereumCredentials
KeyPair keyPair = keyPairGenerator.generateKeyPair();
this.privateKey = keyPair.getPrivate();
this.publicKey = keyPair.getPublic();
this.address = EthereumSignatureVerifier.toAddress(((BCECPublicKey)publicKey).getQ());
this.address = EthereumUtil.toAddress(((BCECPublicKey)publicKey).getQ());
}
catch (Exception e)
{
@ -63,7 +63,7 @@ public class EthereumCredentials
return address;
}
public SignedMessage signMessage(String message) throws Exception
public EthereumAuthenticator.SignedMessage signMessage(String message) throws Exception
{
byte[] messageBytes = message.getBytes(StandardCharsets.ISO_8859_1);
String prefix = "\u0019Ethereum Signed Message:\n" + messageBytes.length + message;
@ -80,7 +80,7 @@ public class EthereumCredentials
System.arraycopy(r, 0, signature, 0, 32);
System.arraycopy(s, 0, signature, 32, 32);
signature[64] = (byte)(calculateV(messageHash, r, s) + 27);
return new SignedMessage(message, Hex.toHexString(signature));
return new EthereumAuthenticator.SignedMessage(message, Hex.toHexString(signature));
}
private byte[] getR(byte[] encodedSignature)
@ -117,7 +117,7 @@ public class EthereumCredentials
ECPoint publicKeyPoint = ((BCECPublicKey)publicKey).getQ();
for (int v = 0; v < 4; v++)
{
ECPoint qPoint = EthereumSignatureVerifier.ecRecover(hash, v, new BigInteger(1, r), new BigInteger(1, s));
ECPoint qPoint = EthereumUtil.ecRecover(hash, v, new BigInteger(1, r), new BigInteger(1, s));
if (qPoint != null && qPoint.equals(publicKeyPoint))
return (byte)v;
}

View File

@ -16,7 +16,7 @@ package org.eclipse.jetty.security.siwe.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class SignInWithEthereumGenerator
public class SignInWithEthereumGenerator
{
private SignInWithEthereumGenerator()
{

34
pom.xml
View File

@ -169,8 +169,8 @@
<asciidoctorj.version>3.0.0-alpha.2</asciidoctorj.version>
<asm.version>9.7</asm.version>
<awaitility.version>4.2.1</awaitility.version>
<bouncycastle.version>1.78.1</bouncycastle.version>
<bndlib.version>7.0.0</bndlib.version>
<bouncycastle.version>1.78.1</bouncycastle.version>
<build-helper.maven.plugin.version>3.6.0</build-helper.maven.plugin.version>
<build-support.version>1.5</build-support.version>
<buildnumber.maven.plugin.version>3.2.0</buildnumber.maven.plugin.version>
@ -619,6 +619,22 @@
<artifactId>awaitility</artifactId>
<version>${awaitility.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcutil-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-classworlds</artifactId>
@ -1304,22 +1320,6 @@
<artifactId>wildfly-elytron-sasl-scram</artifactId>
<version>${wildfly.elytron.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcutil-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -25,7 +25,7 @@ import org.eclipse.jetty.ee10.tests.distribution.siwe.EthereumCredentials;
import org.eclipse.jetty.ee10.tests.distribution.siwe.SignInWithEthereumGenerator;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.security.siwe.SignedMessage;
import org.eclipse.jetty.security.siwe.EthereumAuthenticator;
import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest;
import org.eclipse.jetty.tests.testers.JettyHomeTester;
import org.eclipse.jetty.tests.testers.Tester;
@ -137,7 +137,7 @@ public class SiweTests extends AbstractJettyHomeTest
private FormRequestContent getAuthRequestContent(int port, String nonce) throws Exception
{
SignedMessage signedMessage = _credentials.signMessage(
EthereumAuthenticator.SignedMessage signedMessage = _credentials.signMessage(
SignInWithEthereumGenerator.generateMessage(port, _credentials.getAddress(), nonce));
Fields fields = new Fields();
fields.add("signature", signedMessage.signature());

View File

@ -28,10 +28,10 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.eclipse.jetty.security.siwe.SignedMessage;
import org.eclipse.jetty.security.siwe.internal.EthereumSignatureVerifier;
import org.eclipse.jetty.security.siwe.EthereumAuthenticator;
import org.eclipse.jetty.security.siwe.internal.EthereumUtil;
import static org.eclipse.jetty.security.siwe.internal.EthereumSignatureVerifier.keccak256;
import static org.eclipse.jetty.security.siwe.internal.EthereumUtil.keccak256;
public class EthereumCredentials
{
@ -50,7 +50,7 @@ public class EthereumCredentials
KeyPair keyPair = keyPairGenerator.generateKeyPair();
this.privateKey = keyPair.getPrivate();
this.publicKey = keyPair.getPublic();
this.address = EthereumSignatureVerifier.toAddress(((BCECPublicKey)publicKey).getQ());
this.address = EthereumUtil.toAddress(((BCECPublicKey)publicKey).getQ());
}
catch (Exception e)
{
@ -63,7 +63,7 @@ public class EthereumCredentials
return address;
}
public SignedMessage signMessage(String message) throws Exception
public EthereumAuthenticator.SignedMessage signMessage(String message) throws Exception
{
byte[] messageBytes = message.getBytes(StandardCharsets.ISO_8859_1);
String prefix = "\u0019Ethereum Signed Message:\n" + messageBytes.length + message;
@ -80,7 +80,7 @@ public class EthereumCredentials
System.arraycopy(r, 0, signature, 0, 32);
System.arraycopy(s, 0, signature, 32, 32);
signature[64] = (byte)(calculateV(messageHash, r, s) + 27);
return new SignedMessage(message, Hex.toHexString(signature));
return new EthereumAuthenticator.SignedMessage(message, Hex.toHexString(signature));
}
private byte[] getR(byte[] encodedSignature)
@ -117,7 +117,7 @@ public class EthereumCredentials
ECPoint publicKeyPoint = ((BCECPublicKey)publicKey).getQ();
for (int v = 0; v < 4; v++)
{
ECPoint qPoint = EthereumSignatureVerifier.ecRecover(hash, v, new BigInteger(1, r), new BigInteger(1, s));
ECPoint qPoint = EthereumUtil.ecRecover(hash, v, new BigInteger(1, r), new BigInteger(1, s));
if (qPoint != null && qPoint.equals(publicKeyPoint))
return (byte)v;
}