You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
12 KiB

4 years ago
  1. import binascii
  2. from . import ecdsa
  3. from . import der
  4. from . import rfc6979
  5. from .curves import NIST192p, find_curve
  6. from .util import string_to_number, number_to_string, randrange
  7. from .util import sigencode_string, sigdecode_string
  8. from .util import oid_ecPublicKey, encoded_oid_ecPublicKey
  9. from .six import PY3, b
  10. from hashlib import sha1
  11. class BadSignatureError(Exception):
  12. pass
  13. class BadDigestError(Exception):
  14. pass
  15. class VerifyingKey:
  16. def __init__(self, _error__please_use_generate=None):
  17. if not _error__please_use_generate:
  18. raise TypeError("Please use SigningKey.generate() to construct me")
  19. @classmethod
  20. def from_public_point(klass, point, curve=NIST192p, hashfunc=sha1):
  21. self = klass(_error__please_use_generate=True)
  22. self.curve = curve
  23. self.default_hashfunc = hashfunc
  24. self.pubkey = ecdsa.Public_key(curve.generator, point)
  25. self.pubkey.order = curve.order
  26. return self
  27. @classmethod
  28. def from_string(klass, string, curve=NIST192p, hashfunc=sha1,
  29. validate_point=True):
  30. order = curve.order
  31. assert len(string) == curve.verifying_key_length, \
  32. (len(string), curve.verifying_key_length)
  33. xs = string[:curve.baselen]
  34. ys = string[curve.baselen:]
  35. assert len(xs) == curve.baselen, (len(xs), curve.baselen)
  36. assert len(ys) == curve.baselen, (len(ys), curve.baselen)
  37. x = string_to_number(xs)
  38. y = string_to_number(ys)
  39. if validate_point:
  40. assert ecdsa.point_is_valid(curve.generator, x, y)
  41. from . import ellipticcurve
  42. point = ellipticcurve.Point(curve.curve, x, y, order)
  43. return klass.from_public_point(point, curve, hashfunc)
  44. @classmethod
  45. def from_pem(klass, string):
  46. return klass.from_der(der.unpem(string))
  47. @classmethod
  48. def from_der(klass, string):
  49. # [[oid_ecPublicKey,oid_curve], point_str_bitstring]
  50. s1,empty = der.remove_sequence(string)
  51. if empty != b(""):
  52. raise der.UnexpectedDER("trailing junk after DER pubkey: %s" %
  53. binascii.hexlify(empty))
  54. s2,point_str_bitstring = der.remove_sequence(s1)
  55. # s2 = oid_ecPublicKey,oid_curve
  56. oid_pk, rest = der.remove_object(s2)
  57. oid_curve, empty = der.remove_object(rest)
  58. if empty != b(""):
  59. raise der.UnexpectedDER("trailing junk after DER pubkey objects: %s" %
  60. binascii.hexlify(empty))
  61. assert oid_pk == oid_ecPublicKey, (oid_pk, oid_ecPublicKey)
  62. curve = find_curve(oid_curve)
  63. point_str, empty = der.remove_bitstring(point_str_bitstring)
  64. if empty != b(""):
  65. raise der.UnexpectedDER("trailing junk after pubkey pointstring: %s" %
  66. binascii.hexlify(empty))
  67. assert point_str.startswith(b("\x00\x04"))
  68. return klass.from_string(point_str[2:], curve)
  69. def to_string(self):
  70. # VerifyingKey.from_string(vk.to_string()) == vk as long as the
  71. # curves are the same: the curve itself is not included in the
  72. # serialized form
  73. order = self.pubkey.order
  74. x_str = number_to_string(self.pubkey.point.x(), order)
  75. y_str = number_to_string(self.pubkey.point.y(), order)
  76. return x_str + y_str
  77. def to_pem(self):
  78. return der.topem(self.to_der(), "PUBLIC KEY")
  79. def to_der(self):
  80. order = self.pubkey.order
  81. x_str = number_to_string(self.pubkey.point.x(), order)
  82. y_str = number_to_string(self.pubkey.point.y(), order)
  83. point_str = b("\x00\x04") + x_str + y_str
  84. return der.encode_sequence(der.encode_sequence(encoded_oid_ecPublicKey,
  85. self.curve.encoded_oid),
  86. der.encode_bitstring(point_str))
  87. def verify(self, signature, data, hashfunc=None, sigdecode=sigdecode_string):
  88. hashfunc = hashfunc or self.default_hashfunc
  89. digest = hashfunc(data).digest()
  90. return self.verify_digest(signature, digest, sigdecode)
  91. def verify_digest(self, signature, digest, sigdecode=sigdecode_string):
  92. if len(digest) > self.curve.baselen:
  93. raise BadDigestError("this curve (%s) is too short "
  94. "for your digest (%d)" % (self.curve.name,
  95. 8*len(digest)))
  96. number = string_to_number(digest)
  97. r, s = sigdecode(signature, self.pubkey.order)
  98. sig = ecdsa.Signature(r, s)
  99. if self.pubkey.verifies(number, sig):
  100. return True
  101. raise BadSignatureError
  102. class SigningKey:
  103. def __init__(self, _error__please_use_generate=None):
  104. if not _error__please_use_generate:
  105. raise TypeError("Please use SigningKey.generate() to construct me")
  106. @classmethod
  107. def generate(klass, curve=NIST192p, entropy=None, hashfunc=sha1):
  108. secexp = randrange(curve.order, entropy)
  109. return klass.from_secret_exponent(secexp, curve, hashfunc)
  110. # to create a signing key from a short (arbitrary-length) seed, convert
  111. # that seed into an integer with something like
  112. # secexp=util.randrange_from_seed__X(seed, curve.order), and then pass
  113. # that integer into SigningKey.from_secret_exponent(secexp, curve)
  114. @classmethod
  115. def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1):
  116. self = klass(_error__please_use_generate=True)
  117. self.curve = curve
  118. self.default_hashfunc = hashfunc
  119. self.baselen = curve.baselen
  120. n = curve.order
  121. assert 1 <= secexp < n
  122. pubkey_point = curve.generator*secexp
  123. pubkey = ecdsa.Public_key(curve.generator, pubkey_point)
  124. pubkey.order = n
  125. self.verifying_key = VerifyingKey.from_public_point(pubkey_point, curve,
  126. hashfunc)
  127. self.privkey = ecdsa.Private_key(pubkey, secexp)
  128. self.privkey.order = n
  129. return self
  130. @classmethod
  131. def from_string(klass, string, curve=NIST192p, hashfunc=sha1):
  132. assert len(string) == curve.baselen, (len(string), curve.baselen)
  133. secexp = string_to_number(string)
  134. return klass.from_secret_exponent(secexp, curve, hashfunc)
  135. @classmethod
  136. def from_pem(klass, string, hashfunc=sha1):
  137. # the privkey pem file has two sections: "EC PARAMETERS" and "EC
  138. # PRIVATE KEY". The first is redundant.
  139. if PY3 and isinstance(string, str):
  140. string = string.encode()
  141. privkey_pem = string[string.index(b("-----BEGIN EC PRIVATE KEY-----")):]
  142. return klass.from_der(der.unpem(privkey_pem), hashfunc)
  143. @classmethod
  144. def from_der(klass, string, hashfunc=sha1):
  145. # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1),
  146. # cont[1],bitstring])
  147. s, empty = der.remove_sequence(string)
  148. if empty != b(""):
  149. raise der.UnexpectedDER("trailing junk after DER privkey: %s" %
  150. binascii.hexlify(empty))
  151. one, s = der.remove_integer(s)
  152. if one != 1:
  153. raise der.UnexpectedDER("expected '1' at start of DER privkey,"
  154. " got %d" % one)
  155. privkey_str, s = der.remove_octet_string(s)
  156. tag, curve_oid_str, s = der.remove_constructed(s)
  157. if tag != 0:
  158. raise der.UnexpectedDER("expected tag 0 in DER privkey,"
  159. " got %d" % tag)
  160. curve_oid, empty = der.remove_object(curve_oid_str)
  161. if empty != b(""):
  162. raise der.UnexpectedDER("trailing junk after DER privkey "
  163. "curve_oid: %s" % binascii.hexlify(empty))
  164. curve = find_curve(curve_oid)
  165. # we don't actually care about the following fields
  166. #
  167. #tag, pubkey_bitstring, s = der.remove_constructed(s)
  168. #if tag != 1:
  169. # raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d"
  170. # % tag)
  171. #pubkey_str = der.remove_bitstring(pubkey_bitstring)
  172. #if empty != "":
  173. # raise der.UnexpectedDER("trailing junk after DER privkey "
  174. # "pubkeystr: %s" % binascii.hexlify(empty))
  175. # our from_string method likes fixed-length privkey strings
  176. if len(privkey_str) < curve.baselen:
  177. privkey_str = b("\x00")*(curve.baselen-len(privkey_str)) + privkey_str
  178. return klass.from_string(privkey_str, curve, hashfunc)
  179. def to_string(self):
  180. secexp = self.privkey.secret_multiplier
  181. s = number_to_string(secexp, self.privkey.order)
  182. return s
  183. def to_pem(self):
  184. # TODO: "BEGIN ECPARAMETERS"
  185. return der.topem(self.to_der(), "EC PRIVATE KEY")
  186. def to_der(self):
  187. # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1),
  188. # cont[1],bitstring])
  189. encoded_vk = b("\x00\x04") + self.get_verifying_key().to_string()
  190. return der.encode_sequence(der.encode_integer(1),
  191. der.encode_octet_string(self.to_string()),
  192. der.encode_constructed(0, self.curve.encoded_oid),
  193. der.encode_constructed(1, der.encode_bitstring(encoded_vk)),
  194. )
  195. def get_verifying_key(self):
  196. return self.verifying_key
  197. def sign_deterministic(self, data, hashfunc=None, sigencode=sigencode_string):
  198. hashfunc = hashfunc or self.default_hashfunc
  199. digest = hashfunc(data).digest()
  200. return self.sign_digest_deterministic(digest, hashfunc=hashfunc, sigencode=sigencode)
  201. def sign_digest_deterministic(self, digest, hashfunc=None, sigencode=sigencode_string):
  202. """
  203. Calculates 'k' from data itself, removing the need for strong
  204. random generator and producing deterministic (reproducible) signatures.
  205. See RFC 6979 for more details.
  206. """
  207. secexp = self.privkey.secret_multiplier
  208. k = rfc6979.generate_k(
  209. self.curve.generator.order(), secexp, hashfunc, digest)
  210. return self.sign_digest(digest, sigencode=sigencode, k=k)
  211. def sign(self, data, entropy=None, hashfunc=None, sigencode=sigencode_string, k=None):
  212. """
  213. hashfunc= should behave like hashlib.sha1 . The output length of the
  214. hash (in bytes) must not be longer than the length of the curve order
  215. (rounded up to the nearest byte), so using SHA256 with nist256p is
  216. ok, but SHA256 with nist192p is not. (In the 2**-96ish unlikely event
  217. of a hash output larger than the curve order, the hash will
  218. effectively be wrapped mod n).
  219. Use hashfunc=hashlib.sha1 to match openssl's -ecdsa-with-SHA1 mode,
  220. or hashfunc=hashlib.sha256 for openssl-1.0.0's -ecdsa-with-SHA256.
  221. """
  222. hashfunc = hashfunc or self.default_hashfunc
  223. h = hashfunc(data).digest()
  224. return self.sign_digest(h, entropy, sigencode, k)
  225. def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, k=None):
  226. if len(digest) > self.curve.baselen:
  227. raise BadDigestError("this curve (%s) is too short "
  228. "for your digest (%d)" % (self.curve.name,
  229. 8*len(digest)))
  230. number = string_to_number(digest)
  231. r, s = self.sign_number(number, entropy, k)
  232. return sigencode(r, s, self.privkey.order)
  233. def sign_number(self, number, entropy=None, k=None):
  234. # returns a pair of numbers
  235. order = self.privkey.order
  236. # privkey.sign() may raise RuntimeError in the amazingly unlikely
  237. # (2**-192) event that r=0 or s=0, because that would leak the key.
  238. # We could re-try with a different 'k', but we couldn't test that
  239. # code, so I choose to allow the signature to fail instead.
  240. # If k is set, it is used directly. In other cases
  241. # it is generated using entropy function
  242. if k is not None:
  243. _k = k
  244. else:
  245. _k = randrange(order, entropy)
  246. assert 1 <= _k < order
  247. sig = self.privkey.sign(number, _k)
  248. return sig.r, sig.s