changes from review
Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
This commit is contained in:
parent
52c6c88de6
commit
9ea7431c43
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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())));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
34
pom.xml
|
@ -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>
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue