private static byte[] generateChecksum(final byte[] input) { // step 1: sha3 hash of (input final byte[] sha3StepThreeHash = Hashes.sha3_256(input); // step 2: get the first X bytes of (1) return Arrays.copyOfRange(sha3StepThreeHash, 0, NUM_CHECKSUM_BYTES); }
@Override public Signature sign(final byte[] data) { if (!this.getKeyPair().hasPrivateKey()) { throw new CryptoException("cannot sign without private key"); } // Hash the private key to improve randomness. final byte[] hash = Hashes.sha3_512(ArrayUtils.toByteArray(this.getKeyPair().getPrivateKey().getRaw(), 32)); // r = H(hash_b,...,hash_2b-1, data) where b=256. final Ed25519EncodedFieldElement r = new Ed25519EncodedFieldElement( Hashes.sha3_512( 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( Hashes.sha3_512(encodedR.getRaw(), this.getKeyPair().getPublicKey().getRaw(), 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; }
private static String generateEncoded(final byte version, final byte[] publicKey) { // step 1: sha3 hash of the public key final byte[] sha3PublicKeyHash = Hashes.sha3_256(publicKey); // step 2: ripemd160 hash of (1) final byte[] ripemd160StepOneHash = Hashes.ripemd160(sha3PublicKeyHash); // step 3: store version byte in front of (2) final byte[] versionPrefixedRipemd160Hash = ArrayUtils.concat(new byte[] {version}, ripemd160StepOneHash); // step 4: get the checksum of (3) final byte[] stepThreeChecksum = generateChecksum(versionPrefixedRipemd160Hash); // step 5: concatenate (3) and (4) final byte[] concatStepThreeAndStepSix = ArrayUtils.concat(versionPrefixedRipemd160Hash, stepThreeChecksum); // step 6: base32 encode (5) return Base32Encoder.getString(concatStepThreeAndStepSix); }
@Override public boolean verify(final byte[] data, final Signature signature) { if (!this.isCanonicalSignature(signature)) { return false; } if (1 == ArrayUtils.isEqualConstantTime( this.getKeyPair().getPublicKey().getRaw(), new byte[32])) { return false; } // h = H(encodedR, encodedA, data). final byte[] rawEncodedR = signature.getBinaryR(); final byte[] rawEncodedA = this.getKeyPair().getPublicKey().getRaw(); final Ed25519EncodedFieldElement h = new Ed25519EncodedFieldElement(Hashes.sha3_512(rawEncodedR, rawEncodedA, data)); // hReduced = h mod group order final Ed25519EncodedFieldElement hModQ = h.modQ(); Ed25519GroupElement A = this.getKeyPair().getPublicKey().getAsGroupElement(); if (null == A) { // Must compute A. A = new Ed25519EncodedGroupElement(rawEncodedA).decode(); A.precomputeForDoubleScalarMultiplication(); } // R = encodedS * B - H(encodedR, encodedA, data) * A final Ed25519GroupElement calculatedR = Ed25519Group.BASE_POINT.doubleScalarMultiplyVariableTime( A, hModQ, new Ed25519EncodedFieldElement(signature.getBinaryS())); // Compare calculated R to given R. final byte[] encodedCalculatedR = calculatedR.encode().getRaw(); final int result = ArrayUtils.isEqualConstantTime(encodedCalculatedR, rawEncodedR); return 1 == result; }