blob: 735921e22ad9fb56ceca501977ace5b36f78f011 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2021 MariaDB Corporation Ab
package org.mariadb.jdbc.plugin.authentication.standard;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.Arrays;
import org.mariadb.jdbc.Configuration;
import org.mariadb.jdbc.client.Context;
import org.mariadb.jdbc.client.ReadableByteBuf;
import org.mariadb.jdbc.client.socket.Reader;
import org.mariadb.jdbc.client.socket.Writer;
import org.mariadb.jdbc.plugin.AuthenticationPlugin;
import org.mariadb.jdbc.plugin.authentication.standard.ed25519.math.GroupElement;
import org.mariadb.jdbc.plugin.authentication.standard.ed25519.math.ed25519.Ed25519ScalarOps;
import org.mariadb.jdbc.plugin.authentication.standard.ed25519.spec.EdDSANamedCurveTable;
import org.mariadb.jdbc.plugin.authentication.standard.ed25519.spec.EdDSAParameterSpec;
/** ED25519 password plugin */
public class Ed25519PasswordPlugin implements AuthenticationPlugin {
private String authenticationData;
private byte[] seed;
/**
* Sign password
*
* @param password password
* @param seed server seed
* @return encrypted value
* @throws SQLException if any error occurs
*/
private static byte[] ed25519SignWithPassword(final String password, final byte[] seed)
throws SQLException {
try {
byte[] bytePwd = password.getBytes(StandardCharsets.UTF_8);
MessageDigest hash = MessageDigest.getInstance("SHA-512");
int mlen = seed.length;
final byte[] sm = new byte[64 + mlen];
byte[] az = hash.digest(bytePwd);
az[0] &= 248;
az[31] &= 63;
az[31] |= 64;
System.arraycopy(seed, 0, sm, 64, mlen);
System.arraycopy(az, 32, sm, 32, 32);
byte[] buff = Arrays.copyOfRange(sm, 32, 96);
hash.reset();
byte[] nonce = hash.digest(buff);
Ed25519ScalarOps scalar = new Ed25519ScalarOps();
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
GroupElement elementAvalue = spec.getB().scalarMultiply(az);
byte[] elementAarray = elementAvalue.toByteArray();
System.arraycopy(elementAarray, 0, sm, 32, elementAarray.length);
nonce = scalar.reduce(nonce);
GroupElement elementRvalue = spec.getB().scalarMultiply(nonce);
byte[] elementRarray = elementRvalue.toByteArray();
System.arraycopy(elementRarray, 0, sm, 0, elementRarray.length);
hash.reset();
byte[] hram = hash.digest(sm);
hram = scalar.reduce(hram);
byte[] tt = scalar.multiplyAndAdd(hram, az, nonce);
System.arraycopy(tt, 0, sm, 32, tt.length);
return Arrays.copyOfRange(sm, 0, 64);
} catch (NoSuchAlgorithmException e) {
throw new SQLException("Could not use SHA-512, failing", e);
}
}
@Override
public String type() {
return "client_ed25519";
}
/**
* Initialization.
*
* @param authenticationData authentication data (password/token)
* @param seed server provided seed
* @param conf Connection string options
*/
public void initialize(String authenticationData, byte[] seed, Configuration conf) {
this.seed = seed;
this.authenticationData = authenticationData;
}
/**
* Process Ed25519 password plugin authentication. see
* https://mariadb.com/kb/en/library/authentication-plugin-ed25519/
*
* @param out out stream
* @param in in stream
* @param context connection context
* @return response packet
* @throws IOException if socket error
*/
public ReadableByteBuf process(Writer out, Reader in, Context context)
throws SQLException, IOException {
if (authenticationData == null) {
out.writeEmptyPacket();
} else {
out.writeBytes(ed25519SignWithPassword(authenticationData, seed));
out.flush();
}
return in.readReusablePacket();
}
}