SECPPublicKey.java
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.crypto;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.math.BigInteger;
import java.util.Arrays;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.MutableBytes;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
/** The Secp public key. */
public class SECPPublicKey implements java.security.PublicKey {
/** The constant BYTE_LENGTH. */
public static final int BYTE_LENGTH = 64;
/** Encoded Bytes */
private final Bytes encoded;
/** Algorithm */
private final String algorithm;
/**
* Create secp public key.
*
* @param key the key
* @param algorithm the algorithm
* @return the secp public key
*/
public static SECPPublicKey create(final BigInteger key, final String algorithm) {
checkNotNull(key);
return create(toBytes64(key.toByteArray()), algorithm);
}
/**
* Create secp public key.
*
* @param encoded the encoded
* @param algorithm the algorithm
* @return the secp public key
*/
public static SECPPublicKey create(final Bytes encoded, final String algorithm) {
return new SECPPublicKey(encoded, algorithm);
}
/**
* Create secp public key.
*
* @param privateKey the private key
* @param curve the curve
* @param algorithm the algorithm
* @return the secp public key
*/
public static SECPPublicKey create(
final SECPPrivateKey privateKey, final ECDomainParameters curve, final String algorithm) {
BigInteger privKey = privateKey.getEncodedBytes().toUnsignedBigInteger();
/*
* TODO: FixedPointCombMultiplier currently doesn't support scalars longer than the group
* order, but that could change in future versions.
*/
if (privKey.bitLength() > curve.getN().bitLength()) {
privKey = privKey.mod(curve.getN());
}
final ECPoint point = new FixedPointCombMultiplier().multiply(curve.getG(), privKey);
return SECPPublicKey.create(
Bytes.wrap(Arrays.copyOfRange(point.getEncoded(false), 1, 65)), algorithm);
}
private static Bytes toBytes64(final byte[] backing) {
if (backing.length == BYTE_LENGTH) {
return Bytes.wrap(backing);
} else if (backing.length > BYTE_LENGTH) {
return Bytes.wrap(backing, backing.length - BYTE_LENGTH, BYTE_LENGTH);
} else {
final MutableBytes res = MutableBytes.create(BYTE_LENGTH);
Bytes.wrap(backing).copyTo(res, BYTE_LENGTH - backing.length);
return res;
}
}
private SECPPublicKey(final Bytes encoded, final String algorithm) {
checkNotNull(encoded);
checkNotNull(algorithm);
checkArgument(
encoded.size() == BYTE_LENGTH,
"Encoding must be %s bytes long, got %s",
BYTE_LENGTH,
encoded.size());
this.encoded = encoded;
this.algorithm = algorithm;
}
/**
* Returns this public key as an {@link ECPoint} of Bouncy Castle, to facilitate cryptographic
* operations.
*
* @param curve The elliptic curve (e.g. SECP256K1) represented as its domain parameters
* @return This public key represented as an Elliptic Curve point.
*/
public ECPoint asEcPoint(final ECDomainParameters curve) {
// 0x04 is the prefix for uncompressed keys.
final Bytes val = Bytes.concatenate(Bytes.of(0x04), encoded);
return curve.getCurve().decodePoint(val.toArrayUnsafe());
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof SECPPublicKey)) {
return false;
}
final SECPPublicKey that = (SECPPublicKey) other;
return this.encoded.equals(that.encoded) && this.algorithm.equals(that.algorithm);
}
@Override
public byte[] getEncoded() {
return encoded.toArrayUnsafe();
}
/**
* Gets encoded bytes.
*
* @return the encoded bytes
*/
public Bytes getEncodedBytes() {
return encoded;
}
@Override
public String getAlgorithm() {
return algorithm;
}
@Override
public String getFormat() {
return null;
}
@Override
public int hashCode() {
return encoded.hashCode();
}
@Override
public String toString() {
return encoded.toString();
}
}