Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/ecdsa/curves.py: 39%

147 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:16 +0000

1from __future__ import division 

2 

3from six import PY2 

4from . import der, ecdsa, ellipticcurve, eddsa 

5from .util import orderlen, number_to_string, string_to_number 

6from ._compat import normalise_bytes, bit_length 

7 

8 

9# orderlen was defined in this module previously, so keep it in __all__, 

10# will need to mark it as deprecated later 

11__all__ = [ 

12 "UnknownCurveError", 

13 "orderlen", 

14 "Curve", 

15 "SECP112r1", 

16 "SECP112r2", 

17 "SECP128r1", 

18 "SECP160r1", 

19 "NIST192p", 

20 "NIST224p", 

21 "NIST256p", 

22 "NIST384p", 

23 "NIST521p", 

24 "curves", 

25 "find_curve", 

26 "curve_by_name", 

27 "SECP256k1", 

28 "BRAINPOOLP160r1", 

29 "BRAINPOOLP192r1", 

30 "BRAINPOOLP224r1", 

31 "BRAINPOOLP256r1", 

32 "BRAINPOOLP320r1", 

33 "BRAINPOOLP384r1", 

34 "BRAINPOOLP512r1", 

35 "PRIME_FIELD_OID", 

36 "CHARACTERISTIC_TWO_FIELD_OID", 

37 "Ed25519", 

38 "Ed448", 

39] 

40 

41 

42PRIME_FIELD_OID = (1, 2, 840, 10045, 1, 1) 

43CHARACTERISTIC_TWO_FIELD_OID = (1, 2, 840, 10045, 1, 2) 

44 

45 

46class UnknownCurveError(Exception): 

47 pass 

48 

49 

50class Curve: 

51 def __init__(self, name, curve, generator, oid, openssl_name=None): 

52 self.name = name 

53 self.openssl_name = openssl_name # maybe None 

54 self.curve = curve 

55 self.generator = generator 

56 self.order = generator.order() 

57 if isinstance(curve, ellipticcurve.CurveEdTw): 

58 # EdDSA keys are special in that both private and public 

59 # are the same size (as it's defined only with compressed points) 

60 

61 # +1 for the sign bit and then round up 

62 self.baselen = (bit_length(curve.p()) + 1 + 7) // 8 

63 self.verifying_key_length = self.baselen 

64 else: 

65 self.baselen = orderlen(self.order) 

66 self.verifying_key_length = 2 * orderlen(curve.p()) 

67 self.signature_length = 2 * self.baselen 

68 self.oid = oid 

69 if oid: 

70 self.encoded_oid = der.encode_oid(*oid) 

71 

72 def __eq__(self, other): 

73 if isinstance(other, Curve): 

74 return ( 

75 self.curve == other.curve and self.generator == other.generator 

76 ) 

77 return NotImplemented 

78 

79 def __ne__(self, other): 

80 return not self == other 

81 

82 def __repr__(self): 

83 return self.name 

84 

85 def to_der(self, encoding=None, point_encoding="uncompressed"): 

86 """Serialise the curve parameters to binary string. 

87 

88 :param str encoding: the format to save the curve parameters in. 

89 Default is ``named_curve``, with fallback being the ``explicit`` 

90 if the OID is not set for the curve. 

91 :param str point_encoding: the point encoding of the generator when 

92 explicit curve encoding is used. Ignored for ``named_curve`` 

93 format. 

94 

95 :return: DER encoded ECParameters structure 

96 :rtype: bytes 

97 """ 

98 if encoding is None: 

99 if self.oid: 

100 encoding = "named_curve" 

101 else: 

102 encoding = "explicit" 

103 

104 if encoding not in ("named_curve", "explicit"): 

105 raise ValueError( 

106 "Only 'named_curve' and 'explicit' encodings supported" 

107 ) 

108 

109 if encoding == "named_curve": 

110 if not self.oid: 

111 raise UnknownCurveError( 

112 "Can't encode curve using named_curve encoding without " 

113 "associated curve OID" 

114 ) 

115 return der.encode_oid(*self.oid) 

116 elif isinstance(self.curve, ellipticcurve.CurveEdTw): 

117 assert encoding == "explicit" 

118 raise UnknownCurveError( 

119 "Twisted Edwards curves don't support explicit encoding" 

120 ) 

121 

122 # encode the ECParameters sequence 

123 curve_p = self.curve.p() 

124 version = der.encode_integer(1) 

125 field_id = der.encode_sequence( 

126 der.encode_oid(*PRIME_FIELD_OID), der.encode_integer(curve_p) 

127 ) 

128 curve = der.encode_sequence( 

129 der.encode_octet_string( 

130 number_to_string(self.curve.a() % curve_p, curve_p) 

131 ), 

132 der.encode_octet_string( 

133 number_to_string(self.curve.b() % curve_p, curve_p) 

134 ), 

135 ) 

136 base = der.encode_octet_string(self.generator.to_bytes(point_encoding)) 

137 order = der.encode_integer(self.generator.order()) 

138 seq_elements = [version, field_id, curve, base, order] 

139 if self.curve.cofactor(): 

140 cofactor = der.encode_integer(self.curve.cofactor()) 

141 seq_elements.append(cofactor) 

142 

143 return der.encode_sequence(*seq_elements) 

144 

145 def to_pem(self, encoding=None, point_encoding="uncompressed"): 

146 """ 

147 Serialise the curve parameters to the :term:`PEM` format. 

148 

149 :param str encoding: the format to save the curve parameters in. 

150 Default is ``named_curve``, with fallback being the ``explicit`` 

151 if the OID is not set for the curve. 

152 :param str point_encoding: the point encoding of the generator when 

153 explicit curve encoding is used. Ignored for ``named_curve`` 

154 format. 

155 

156 :return: PEM encoded ECParameters structure 

157 :rtype: str 

158 """ 

159 return der.topem( 

160 self.to_der(encoding, point_encoding), "EC PARAMETERS" 

161 ) 

162 

163 @staticmethod 

164 def from_der(data, valid_encodings=None): 

165 """Decode the curve parameters from DER file. 

166 

167 :param data: the binary string to decode the parameters from 

168 :type data: :term:`bytes-like object` 

169 :param valid_encodings: set of names of allowed encodings, by default 

170 all (set by passing ``None``), supported ones are ``named_curve`` 

171 and ``explicit`` 

172 :type valid_encodings: :term:`set-like object` 

173 """ 

174 if not valid_encodings: 

175 valid_encodings = set(("named_curve", "explicit")) 

176 if not all(i in ["named_curve", "explicit"] for i in valid_encodings): 

177 raise ValueError( 

178 "Only named_curve and explicit encodings supported" 

179 ) 

180 data = normalise_bytes(data) 

181 if not der.is_sequence(data): 

182 if "named_curve" not in valid_encodings: 

183 raise der.UnexpectedDER( 

184 "named_curve curve parameters not allowed" 

185 ) 

186 oid, empty = der.remove_object(data) 

187 if empty: 

188 raise der.UnexpectedDER("Unexpected data after OID") 

189 return find_curve(oid) 

190 

191 if "explicit" not in valid_encodings: 

192 raise der.UnexpectedDER("explicit curve parameters not allowed") 

193 

194 seq, empty = der.remove_sequence(data) 

195 if empty: 

196 raise der.UnexpectedDER( 

197 "Unexpected data after ECParameters structure" 

198 ) 

199 # decode the ECParameters sequence 

200 version, rest = der.remove_integer(seq) 

201 if version != 1: 

202 raise der.UnexpectedDER("Unknown parameter encoding format") 

203 field_id, rest = der.remove_sequence(rest) 

204 curve, rest = der.remove_sequence(rest) 

205 base_bytes, rest = der.remove_octet_string(rest) 

206 order, rest = der.remove_integer(rest) 

207 cofactor = None 

208 if rest: 

209 # the ASN.1 specification of ECParameters allows for future 

210 # extensions of the sequence, so ignore the remaining bytes 

211 cofactor, _ = der.remove_integer(rest) 

212 

213 # decode the ECParameters.fieldID sequence 

214 field_type, rest = der.remove_object(field_id) 

215 if field_type == CHARACTERISTIC_TWO_FIELD_OID: 

216 raise UnknownCurveError("Characteristic 2 curves unsupported") 

217 if field_type != PRIME_FIELD_OID: 

218 raise UnknownCurveError( 

219 "Unknown field type: {0}".format(field_type) 

220 ) 

221 prime, empty = der.remove_integer(rest) 

222 if empty: 

223 raise der.UnexpectedDER( 

224 "Unexpected data after ECParameters.fieldID.Prime-p element" 

225 ) 

226 

227 # decode the ECParameters.curve sequence 

228 curve_a_bytes, rest = der.remove_octet_string(curve) 

229 curve_b_bytes, rest = der.remove_octet_string(rest) 

230 # seed can be defined here, but we don't parse it, so ignore `rest` 

231 

232 curve_a = string_to_number(curve_a_bytes) 

233 curve_b = string_to_number(curve_b_bytes) 

234 

235 curve_fp = ellipticcurve.CurveFp(prime, curve_a, curve_b, cofactor) 

236 

237 # decode the ECParameters.base point 

238 

239 base = ellipticcurve.PointJacobi.from_bytes( 

240 curve_fp, 

241 base_bytes, 

242 valid_encodings=("uncompressed", "compressed", "hybrid"), 

243 order=order, 

244 generator=True, 

245 ) 

246 tmp_curve = Curve("unknown", curve_fp, base, None) 

247 

248 # if the curve matches one of the well-known ones, use the well-known 

249 # one in preference, as it will have the OID and name associated 

250 for i in curves: 

251 if tmp_curve == i: 

252 return i 

253 return tmp_curve 

254 

255 @classmethod 

256 def from_pem(cls, string, valid_encodings=None): 

257 """Decode the curve parameters from PEM file. 

258 

259 :param str string: the text string to decode the parameters from 

260 :param valid_encodings: set of names of allowed encodings, by default 

261 all (set by passing ``None``), supported ones are ``named_curve`` 

262 and ``explicit`` 

263 :type valid_encodings: :term:`set-like object` 

264 """ 

265 if not PY2 and isinstance(string, str): # pragma: no branch 

266 string = string.encode() 

267 

268 ec_param_index = string.find(b"-----BEGIN EC PARAMETERS-----") 

269 if ec_param_index == -1: 

270 raise der.UnexpectedDER("EC PARAMETERS PEM header not found") 

271 

272 return cls.from_der( 

273 der.unpem(string[ec_param_index:]), valid_encodings 

274 ) 

275 

276 

277# the SEC curves 

278SECP112r1 = Curve( 

279 "SECP112r1", 

280 ecdsa.curve_112r1, 

281 ecdsa.generator_112r1, 

282 (1, 3, 132, 0, 6), 

283 "secp112r1", 

284) 

285 

286 

287SECP112r2 = Curve( 

288 "SECP112r2", 

289 ecdsa.curve_112r2, 

290 ecdsa.generator_112r2, 

291 (1, 3, 132, 0, 7), 

292 "secp112r2", 

293) 

294 

295 

296SECP128r1 = Curve( 

297 "SECP128r1", 

298 ecdsa.curve_128r1, 

299 ecdsa.generator_128r1, 

300 (1, 3, 132, 0, 28), 

301 "secp128r1", 

302) 

303 

304 

305SECP160r1 = Curve( 

306 "SECP160r1", 

307 ecdsa.curve_160r1, 

308 ecdsa.generator_160r1, 

309 (1, 3, 132, 0, 8), 

310 "secp160r1", 

311) 

312 

313 

314# the NIST curves 

315NIST192p = Curve( 

316 "NIST192p", 

317 ecdsa.curve_192, 

318 ecdsa.generator_192, 

319 (1, 2, 840, 10045, 3, 1, 1), 

320 "prime192v1", 

321) 

322 

323 

324NIST224p = Curve( 

325 "NIST224p", 

326 ecdsa.curve_224, 

327 ecdsa.generator_224, 

328 (1, 3, 132, 0, 33), 

329 "secp224r1", 

330) 

331 

332 

333NIST256p = Curve( 

334 "NIST256p", 

335 ecdsa.curve_256, 

336 ecdsa.generator_256, 

337 (1, 2, 840, 10045, 3, 1, 7), 

338 "prime256v1", 

339) 

340 

341 

342NIST384p = Curve( 

343 "NIST384p", 

344 ecdsa.curve_384, 

345 ecdsa.generator_384, 

346 (1, 3, 132, 0, 34), 

347 "secp384r1", 

348) 

349 

350 

351NIST521p = Curve( 

352 "NIST521p", 

353 ecdsa.curve_521, 

354 ecdsa.generator_521, 

355 (1, 3, 132, 0, 35), 

356 "secp521r1", 

357) 

358 

359 

360SECP256k1 = Curve( 

361 "SECP256k1", 

362 ecdsa.curve_secp256k1, 

363 ecdsa.generator_secp256k1, 

364 (1, 3, 132, 0, 10), 

365 "secp256k1", 

366) 

367 

368 

369BRAINPOOLP160r1 = Curve( 

370 "BRAINPOOLP160r1", 

371 ecdsa.curve_brainpoolp160r1, 

372 ecdsa.generator_brainpoolp160r1, 

373 (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), 

374 "brainpoolP160r1", 

375) 

376 

377 

378BRAINPOOLP192r1 = Curve( 

379 "BRAINPOOLP192r1", 

380 ecdsa.curve_brainpoolp192r1, 

381 ecdsa.generator_brainpoolp192r1, 

382 (1, 3, 36, 3, 3, 2, 8, 1, 1, 3), 

383 "brainpoolP192r1", 

384) 

385 

386 

387BRAINPOOLP224r1 = Curve( 

388 "BRAINPOOLP224r1", 

389 ecdsa.curve_brainpoolp224r1, 

390 ecdsa.generator_brainpoolp224r1, 

391 (1, 3, 36, 3, 3, 2, 8, 1, 1, 5), 

392 "brainpoolP224r1", 

393) 

394 

395 

396BRAINPOOLP256r1 = Curve( 

397 "BRAINPOOLP256r1", 

398 ecdsa.curve_brainpoolp256r1, 

399 ecdsa.generator_brainpoolp256r1, 

400 (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), 

401 "brainpoolP256r1", 

402) 

403 

404 

405BRAINPOOLP320r1 = Curve( 

406 "BRAINPOOLP320r1", 

407 ecdsa.curve_brainpoolp320r1, 

408 ecdsa.generator_brainpoolp320r1, 

409 (1, 3, 36, 3, 3, 2, 8, 1, 1, 9), 

410 "brainpoolP320r1", 

411) 

412 

413 

414BRAINPOOLP384r1 = Curve( 

415 "BRAINPOOLP384r1", 

416 ecdsa.curve_brainpoolp384r1, 

417 ecdsa.generator_brainpoolp384r1, 

418 (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), 

419 "brainpoolP384r1", 

420) 

421 

422 

423BRAINPOOLP512r1 = Curve( 

424 "BRAINPOOLP512r1", 

425 ecdsa.curve_brainpoolp512r1, 

426 ecdsa.generator_brainpoolp512r1, 

427 (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), 

428 "brainpoolP512r1", 

429) 

430 

431 

432Ed25519 = Curve( 

433 "Ed25519", 

434 eddsa.curve_ed25519, 

435 eddsa.generator_ed25519, 

436 (1, 3, 101, 112), 

437) 

438 

439 

440Ed448 = Curve( 

441 "Ed448", 

442 eddsa.curve_ed448, 

443 eddsa.generator_ed448, 

444 (1, 3, 101, 113), 

445) 

446 

447 

448# no order in particular, but keep previously added curves first 

449curves = [ 

450 NIST192p, 

451 NIST224p, 

452 NIST256p, 

453 NIST384p, 

454 NIST521p, 

455 SECP256k1, 

456 BRAINPOOLP160r1, 

457 BRAINPOOLP192r1, 

458 BRAINPOOLP224r1, 

459 BRAINPOOLP256r1, 

460 BRAINPOOLP320r1, 

461 BRAINPOOLP384r1, 

462 BRAINPOOLP512r1, 

463 SECP112r1, 

464 SECP112r2, 

465 SECP128r1, 

466 SECP160r1, 

467 Ed25519, 

468 Ed448, 

469] 

470 

471 

472def find_curve(oid_curve): 

473 """Select a curve based on its OID 

474 

475 :param tuple[int,...] oid_curve: ASN.1 Object Identifier of the 

476 curve to return, like ``(1, 2, 840, 10045, 3, 1, 7)`` for ``NIST256p``. 

477 

478 :raises UnknownCurveError: When the oid doesn't match any of the supported 

479 curves 

480 

481 :rtype: ~ecdsa.curves.Curve 

482 """ 

483 for c in curves: 

484 if c.oid == oid_curve: 

485 return c 

486 raise UnknownCurveError( 

487 "I don't know about the curve with oid %s." 

488 "I only know about these: %s" % (oid_curve, [c.name for c in curves]) 

489 ) 

490 

491 

492def curve_by_name(name): 

493 """Select a curve based on its name. 

494 

495 Returns a :py:class:`~ecdsa.curves.Curve` object with a ``name`` name. 

496 Note that ``name`` is case-sensitve. 

497 

498 :param str name: Name of the curve to return, like ``NIST256p`` or 

499 ``prime256v1`` 

500 

501 :raises UnknownCurveError: When the name doesn't match any of the supported 

502 curves 

503 

504 :rtype: ~ecdsa.curves.Curve 

505 """ 

506 for c in curves: 

507 if name == c.name or (c.openssl_name and name == c.openssl_name): 

508 return c 

509 raise UnknownCurveError( 

510 "Curve with name {0!r} unknown, only curves supported: {1}".format( 

511 name, [c.name for c in curves] 

512 ) 

513 )