class Ed25519DsaSigner implements DsaSigner {

  @Override
  @SuppressWarnings("squid:S00117")
  public Signature sign(final byte[] data) {
    if (!this.getKeyPair().hasPrivateKey()) {
      throw new CryptoException("cannot sign without private key");
    }

    Hasher hasher32 = Hashes::sha512;
    Hasher hasher64 = Hashes::sha512;

    // Hash the private key to improve randomness.
    final byte[] hash = hasher32.hash(this.getKeyPair().getPrivateKey().getBytes());

    // r = H(hash_b,...,hash_2b-1, data) where b=256.
    final Ed25519EncodedFieldElement r =
        new Ed25519EncodedFieldElement(
            hasher64.hash(
                Arrays.copyOfRange(hash, 32, 64), // only
                // include
                // the last
                // 32 bytes
                // of the
                // private
                // key hash
                data));

    // Reduce size of r since we are calculating mod group order anyway
    final Ed25519EncodedFieldElement rModQ = r.modQ();

    // R = rModQ * base point.
    final Ed25519GroupElement R = Ed25519Group.BASE_POINT.scalarMultiply(rModQ);
    final Ed25519EncodedGroupElement encodedR = R.encode();

    // S = (r + H(encodedR, encodedA, data) * a) mod group order where
    // encodedR and encodedA are the little endian encodings of the group element R
    // and the
    // public
    // key A and
    // a is the lower 32 bytes of hash after clamping.
    final Ed25519EncodedFieldElement h =
        new Ed25519EncodedFieldElement(
            hasher64.hash(encodedR.getRaw(), this.getKeyPair().getPublicKey().getBytes(), data));
    final Ed25519EncodedFieldElement hModQ = h.modQ();
    final Ed25519EncodedFieldElement encodedS =
        hModQ.multiplyAndAddModQ(
            Ed25519Utils.prepareForScalarMultiply(this.getKeyPair().getPrivateKey()), rModQ);

    // Signature is (encodedR, encodedS)
    final Signature signature = new Signature(encodedR.getRaw(), encodedS.getRaw());
    if (!this.isCanonicalSignature(signature)) {
      throw new CryptoException("Generated signature is not canonical");
    }

    return signature;
  }

  public  Ed25519DsaSigner(final KeyPair keyPair);

}