aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java
blob: 1e5e3e7ca68a0911bc11dcdb5e3ee690afe51a1b (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 This file is part of GNUnet.
  Copyright (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.HashCode;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOError;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;

/**
 * Private key for elliptic curve DSA.
 */
public class EcdsaPrivateKey implements Message {
    /**
     * Private key byte string, in big endian form.
     */
    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
    public byte[] d;

    /**
     * Create a private key without allocating the key data.
     */
    public EcdsaPrivateKey() {
        // empty, needed by org.gnunet.construct.*
    }

    /**
     * Get the anonymous private key.  Corresponds to '1'.
     *
     * @return the anonymous private ECDSA key
     */
    public static EcdsaPrivateKey getAnonymous() {
        EcdsaPrivateKey privateKey = new EcdsaPrivateKey();
        privateKey.d = new byte[32];
        privateKey.d[31] = 1;
        return privateKey;
    }

    public EcdsaSignature sign(byte[] data, int purpose) {
        return sign(data, purpose, getPublicKey());
    }

    public EcdsaSignature sign(byte[] data, int purpose, EcdsaPublicKey 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 signRaw(publicKey, os.toByteArray());
    }

    /**
     * Sign the given data with this private key.
     *
     * @param data data to signRaw
     * @return the signature over both the data and the purpose
     */
    public EcdsaSignature signRaw(byte[] data) {
        return signRaw(getPublicKey(), data);
    }

    /**
     * Sign the given data with this private key.
     *
     * @param publicKey public key corresponding to this private key, supplying this parameter
     *                  leads to better performance as the public key does not have to be derived
     * @param data data to signRaw
     * @return the signature over both the data and the purpose
     */
    public EcdsaSignature signRaw(EcdsaPublicKey publicKey, byte[] data) {
        EcdsaSignature signature = new EcdsaSignature();
        DsaPrng prng = new DsaPrng(d, data);
        HashCode h = HashCode.hash(data);
        BigInteger z = new BigInteger(1, h.data);
        BigInteger dCoeff = this.asCoefficient();

        while (true) {
            BigInteger k = prng.nextK();
            Ed25519 P = Ed25519.B.scalarmult(k);
            BigInteger r = P.P0.mod(Ed25519.l);
            if (r.equals(BigInteger.ZERO))
                continue;
            BigInteger kInv = k.modInverse(Ed25519.l);
            BigInteger v = z.add(r.multiply(dCoeff));
            BigInteger s = kInv.multiply(v).mod(Ed25519.l);
            if (s.equals(BigInteger.ZERO))
                continue;
            signature.r = Ed25519.encodeScalar(r);
            signature.s = Ed25519.encodeScalar(s);
            return signature;
        }
    }

    /**
     * Get the public key for this private key.
     *
     * @return the public key for this private key
     */
    public EcdsaPublicKey getPublicKey() {
        Ed25519 A = Ed25519.B.scalarmult(this.asCoefficient());
        return new EcdsaPublicKey(A);
    }

    /**
     * Create a randomly generated private ecdsa key.
     *
     * @return a freshly generated key
     */
    public static EcdsaPrivateKey createRandom() {
        SecureRandom sr = new SecureRandom();
        EcdsaPrivateKey privateKey = new EcdsaPrivateKey();
        privateKey.d = new byte[32];
        sr.nextBytes(privateKey.d);
        return privateKey;
    }

    public BigInteger asCoefficient() {
        return new BigInteger(1, d);
    }

    /**
     * Key that is set to all zeros.
     *
     * @return key that is set to all zeros
     */
    public static EcdsaPrivateKey zeroKey() {
        EcdsaPrivateKey pk = new EcdsaPrivateKey();
        pk.d = new byte[32];
        return pk;
    }
}