aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/gnunet/util/crypto/EddsaPrivateKey.java
blob: 973706f0a1bb10520a83c531b25a953f4534e7e6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package org.gnunet.util.crypto;

import org.gnunet.construct.FixedSizeIntegerArray;
import org.gnunet.construct.Message;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class EddsaPrivateKey implements Message {
    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
    public byte[] d;

    public EddsaSignature sign(EddsaPublicKey publicKey, int purpose, byte[] m) {
        MessageDigest sha512;
        try {
            sha512 = MessageDigest.getInstance("SHA-512");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("crypto algorithm required but not provided");
        }
        byte[] h = sha512.digest(d);
        BigInteger a = BigInteger.valueOf(2).pow(Ed25519.b-2);
        for (int i = 3; i < (Ed25519.b - 2); i++) {
            a = a.add(BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(bit(h,i))));
        }
        ByteBuffer rsub = ByteBuffer.allocate((Ed25519.b/8)+m.length);
        rsub.put(h, Ed25519.b/8, Ed25519.b/4-Ed25519.b/8).put(m);
        BigInteger r = Ed25519.Hint(rsub.array());
        Ed25519 R = Ed25519.B.scalarmult(r);

        byte[] encodedPublicKey = publicKey.asPoint().encode();
        ByteBuffer buf = ByteBuffer.allocate(32 + encodedPublicKey.length + m.length);
        buf.put(R.encode()).put(encodedPublicKey).put(m);

        BigInteger S = r.add(Ed25519.Hint(buf.array()).multiply(a)).mod(Ed25519.l);

        return new EddsaSignature(R, S);
    }

    /**
     * Get return the i-th bit in the given array of bytes h.
     *
     * @param h array of bytes
     * @param i bit index
     * @return i-th bit in h
     */
    private static int bit(byte[] h, int i) {
        return h[i/8] >> (i%8) & 1;
    }


    private BigInteger computePublicKeyCoefficient() {
        MessageDigest sha512;
        try {
            sha512 = MessageDigest.getInstance("SHA-512");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("crypto algorithm required but not provided");
        }
        byte[] h = sha512.digest(d);
        BigInteger a = BigInteger.valueOf(2).pow(Ed25519.b - 2);
        for (int i=3; i < (Ed25519.b - 2); i++) {
            BigInteger apart = BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(bit(h,i)));
            a = a.add(apart);
        }
        return a;
    }

    public EddsaPublicKey getPublicKey() {
        BigInteger a = computePublicKeyCoefficient();
        Ed25519 A = Ed25519.B.scalarmult(a);
        return new EddsaPublicKey(A);
    }

    public static EddsaPrivateKey createRandom() {
        SecureRandom sr = new SecureRandom();
        EddsaPrivateKey privateKey = new EddsaPrivateKey();
        privateKey.d = new byte[32];
        sr.nextBytes(privateKey.d);
        return privateKey;
    }
}