Coverage Summary for Class: ECDSASignature (org.ethereum.crypto.signature)
Class |
Class, %
|
Method, %
|
Line, %
|
ECDSASignature |
100%
(1/1)
|
55.6%
(10/18)
|
32.8%
(19/58)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package org.ethereum.crypto.signature;
21
22 import org.ethereum.config.Constants;
23 import org.ethereum.crypto.ECKey;
24
25 import java.math.BigInteger;
26
27 import static org.ethereum.util.BIUtil.isLessThan;
28
29 /**
30 * Groups the two components that make up a signature, and provides a way to encode to Base64 form, which is
31 * how ECDSA signatures are represented when embedded in other data structures in the Ethereum protocol. The raw
32 * components can be useful for doing further EC maths on them.
33 */
34 public class ECDSASignature {
35 /**
36 * The two components of the signature.
37 */
38 private final BigInteger r;
39 private final BigInteger s;
40 private byte v;
41
42 /**
43 * Constructs a signature with the given components. Does NOT automatically canonicalise the signature.
44 *
45 * @param r -
46 * @param s -
47 */
48 public ECDSASignature(BigInteger r, BigInteger s) {
49 this.r = r;
50 this.s = s;
51 }
52
53 /**
54 * Constructs a signature with the given components. Does NOT automatically canonicalise the signature.
55 *
56 * @param r
57 * @param s
58 * @param v
59 */
60 public ECDSASignature(BigInteger r, BigInteger s, byte v) {
61 this.r = r;
62 this.s = s;
63 this.v = v;
64 }
65
66 /**
67 * Warning: Used in Fed Node.
68 *
69 * @param r -
70 * @param s -
71 * @param hash - the hash used to compute this signature
72 * @param pub - public key bytes, used to calculate the recovery byte 'v'
73 * @return -
74 */
75 public static ECDSASignature fromComponentsWithRecoveryCalculation(byte[] r, byte[] s, byte[] hash, byte[] pub) {
76 byte v = calculateRecoveryByte(r, s, hash, pub);
77 return fromComponents(r, s, v);
78 }
79
80 private static byte calculateRecoveryByte(byte[] r, byte[] s, byte[] hash, byte[] pub) {
81 ECDSASignature sig = ECDSASignature.fromComponents(r, s);
82 ECKey pubKey = ECKey.fromPublicOnly(pub);
83
84 // Now we have to work backwards to figure out the recId needed to recover the signature.
85 int recId = -1;
86 for (int i = 0; i < 4; i++) {
87 ECKey k = Secp256k1.getInstance().recoverFromSignature(i, sig, hash, false);
88 if (k != null && k.equalsPub(pubKey)) {
89 recId = i;
90 break;
91 }
92 }
93
94 if (recId == -1) {
95 throw new RuntimeException("Could not construct a recoverable key. This should never happen.");
96 }
97
98 return (byte) (recId + 27);
99 }
100
101 /**
102 * Only for compatibility until we could finally remove old {@link org.ethereum.crypto.ECKey.ECDSASignature}.
103 *
104 * @param sign
105 * @return
106 */
107 public static ECDSASignature fromSignature(ECKey.ECDSASignature sign) {
108 return ECDSASignature.fromComponents(sign.r.toByteArray(), sign.s.toByteArray(), sign.v);
109 }
110
111 public BigInteger getR() {
112 return r;
113 }
114
115 public BigInteger getS() {
116 return s;
117 }
118
119 public byte getV() {
120 return v;
121 }
122
123 public void setV(byte v) {
124 this.v = v;
125 }
126
127 /**
128 * With no recovery byte "v"
129 * @param r
130 * @param s
131 * @return -
132 */
133 public static ECDSASignature fromComponents(byte[] r, byte[] s) {
134 return new ECDSASignature(new BigInteger(1, r), new BigInteger(1, s));
135 }
136
137 /**
138 *
139 * @param r -
140 * @param s -
141 * @param v -
142 * @return -
143 */
144 public static ECDSASignature fromComponents(byte[] r, byte[] s, byte v) {
145 ECDSASignature signature = fromComponents(r, s);
146 signature.v = v;
147 return signature;
148 }
149
150 public boolean validateComponents() {
151 return validateComponents(r, s, v);
152 }
153
154 public boolean validateComponentsWithoutV() {
155 return validateComponents(r, s);
156 }
157
158 public static boolean validateComponents(BigInteger r, BigInteger s, byte v) {
159
160 if (v != 27 && v != 28) {
161 return false;
162 }
163 return validateComponents(r, s);
164 }
165
166 private static boolean validateComponents(BigInteger r, BigInteger s) {
167 if (isLessThan(r, BigInteger.ONE)) {
168 return false;
169 }
170
171 if (isLessThan(s, BigInteger.ONE)) {
172 return false;
173 }
174
175 if (!isLessThan(r, Constants.getSECP256K1N())) {
176 return false;
177 }
178
179 return isLessThan(s, Constants.getSECP256K1N());
180 }
181
182 /**
183 * Will automatically adjust the S component to be less than or equal to half the curve order, if necessary.
184 * This is required because for every signature (r,s) the signature (r, -s (mod N)) is a valid signature of
185 * the same message. However, we dislike the ability to modify the bits of a Ethereum transaction after it's
186 * been signed, as that violates various assumed invariants. Thus in future only one of those forms will be
187 * considered legal and the other will be banned.
188 *
189 * @return -
190 */
191 public ECDSASignature toCanonicalised() {
192 if (s.compareTo(ECKey.HALF_CURVE_ORDER) > 0) {
193 // The order of the curve is the number of valid points that exist on that curve. If S is in the upper
194 // half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that
195 // N = 10
196 // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions.
197 // 10 - 8 == 2, giving us always the latter solution, which is canonical.
198 return new ECDSASignature(r, ECKey.CURVE.getN().subtract(s));
199 } else {
200 return this;
201 }
202 }
203
204 @Override
205 public boolean equals(Object o) {
206 if (this == o) {
207 return true;
208 }
209
210 if (o == null || getClass() != o.getClass()) {
211 return false;
212 }
213
214 ECDSASignature signature = (ECDSASignature) o;
215
216 if (!r.equals(signature.r)) {
217 return false;
218 }
219
220 return s.equals(signature.s);
221 }
222
223 @Override
224 public int hashCode() {
225 int result = r.hashCode();
226 result = 31 * result + s.hashCode();
227 return result;
228 }
229 }