/* This file is part of GNUnet. (C) 2012, 2013 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gnunet.util.crypto; import org.gnunet.construct.FixedSizeIntegerArray; import org.gnunet.construct.Message; import org.gnunet.util.Strings; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOError; import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.security.SecureRandom; public class EddsaSignature implements Message { /** * R value of the signature in compressed form. * The number is stored as little endian. */ @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32) public byte[] r; /** * S-value of the signature. * The number is stored as little endian. */ @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32) public byte[] s; public EddsaSignature() { this.r = new byte[32]; this.s = new byte[32]; } public EddsaSignature(Ed25519 r, BigInteger s) { this.r = r.encode(); this.s = Ed25519.encodeScalar(s); } /** * Verify the signature on a message with given purpose. * * @param m the message signed by this signature * @param publicKey public key of the signer * @return true if the signature is valid, false otherwise */ public boolean verifyRaw(byte[] m, EddsaPublicKey publicKey) { Ed25519 R = Ed25519.decode(r); if (!R.isOnCurve()) return false; Ed25519 A = publicKey.asPoint(); BigInteger S = Ed25519.decodeScalar(s); ByteBuffer Stemp = ByteBuffer.allocate(32 + 32 + m.length); Stemp.put(R.encode()).put(A.encode()).put(m); BigInteger h = Ed25519.Hint(Stemp.array()); Ed25519 ra = Ed25519.B.scalarmult(S); Ed25519 rb = R.add(A.scalarmult(h)); if (!A.isOnCurve()) { throw new AssertionError(); } if (!R.isOnCurve()) { throw new AssertionError(); } if (!ra.isOnCurve()) { throw new AssertionError(); } if (!rb.isOnCurve()) { throw new AssertionError(); } return ra.equals(rb); } public static EddsaSignature fromString(String value) { byte[] data = new byte[64]; if (!Strings.stringToData(value, data)) { throw new AssertionError(); } EddsaSignature sig = new EddsaSignature(); System.arraycopy(data, 0, sig.r, 0, 32); System.arraycopy(data, 32, sig.s, 0, 32); return sig; } public boolean verify(byte[] data, int purpose, EddsaPublicKey publicKey) { ByteArrayOutputStream os = new ByteArrayOutputStream(data.length + 8); DataOutputStream dos = new DataOutputStream(os); try { dos.writeInt(data.length); dos.writeInt(purpose); dos.write(data); } catch (IOException e) { throw new IOError(e); } return verifyRaw(os.toByteArray(), publicKey); } /** * Return a signature that is invalid with very, very high probability. * * @return signature with random garbage */ public static EddsaSignature randomGarbage() { EddsaSignature sig = new EddsaSignature(); SecureRandom r = new SecureRandom(); r.nextBytes(sig.r); r.nextBytes(sig.s); return sig; } @Override public String toString() { byte[] sigData = new byte[64]; System.arraycopy(r, 0, sigData, 0, 32); System.arraycopy(s, 0, sigData, 32, 32); return Strings.dataToString(sigData); } }