content: /*
   * Copyright 2019 Web3 Labs Ltd.
   *
   * 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.
   */
  /*
   * Copyright 2024 Ant Group
   *
   * 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.
   */
  package com.alipay.antchain.bridge.commons.utils.crypto.secp256k1;
  
  import java.math.BigInteger;
  import java.security.KeyPair;
  import java.util.Arrays;
  
  import lombok.Getter;
  import org.bouncycastle.crypto.digests.SHA256Digest;
  import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
  import org.bouncycastle.crypto.signers.ECDSASigner;
  import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
  import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
  import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
  
  /** Elliptic Curve SECP-256k1 generated key pair. */
  @Getter
  public class ECKeyPair {
      private final BigInteger privateKey;
      private final BigInteger publicKey;
  
      public ECKeyPair(BigInteger privateKey, BigInteger publicKey) {
          this.privateKey = privateKey;
          this.publicKey = publicKey;
      }
  
      /**
       * Sign a hash with the private key of this key pair.
       *
       * @param transactionHash the hash to sign
       * @return An {@link ECDSASignature} of the hash
       */
      public ECDSASignature sign(byte[] transactionHash) {
          ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
  
          ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKey, Sign.CURVE);
          signer.init(true, privKey);
          BigInteger[] components = signer.generateSignature(transactionHash);
  
          return new ECDSASignature(components[0], components[1]).toCanonicalised();
      }
  
      public static ECKeyPair create(KeyPair keyPair) {
          BCECPrivateKey privateKey = (BCECPrivateKey) keyPair.getPrivate();
          BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic();
  
          BigInteger privateKeyValue = privateKey.getD();
  
          // Ethereum does not use encoded public keys like bitcoin - see
          // https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm for details
          // Additionally, as the first bit is a constant prefix (0x04) we ignore this value
          byte[] publicKeyBytes = publicKey.getQ().getEncoded(false);
          BigInteger publicKeyValue =
                  new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 1, publicKeyBytes.length));
  
          return new ECKeyPair(privateKeyValue, publicKeyValue);
      }
  
      public static ECKeyPair create(BigInteger privateKey) {
          return new ECKeyPair(privateKey, Sign.publicKeyFromPrivate(privateKey));
      }
  
      public static ECKeyPair create(byte[] privateKey) {
          return create(new BigInteger(1, privateKey));
      }
  
      @Override
      public boolean equals(Object o) {
          if (this == o) {
              return true;
          }
          if (o == null || getClass() != o.getClass()) {
              return false;
          }
  
          ECKeyPair ecKeyPair = (ECKeyPair) o;
  
          if (privateKey != null
                  ? !privateKey.equals(ecKeyPair.privateKey)
                  : ecKeyPair.privateKey != null) {
              return false;
          }
  
          return publicKey != null
                  ? publicKey.equals(ecKeyPair.publicKey)
                  : ecKeyPair.publicKey == null;
      }
  
      @Override
      public int hashCode() {
          int result = privateKey != null ? privateKey.hashCode() : 0;
          result = 31 * result + (publicKey != null ? publicKey.hashCode() : 0);
          return result;
      }
  }
  