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 }