1 // -*- mode: rust; -*- 2 // 3 // This file is part of curve25519-dalek. 4 // Copyright (c) 2016-2019 Isis Lovecruft, Henry de Valence 5 // See LICENSE for licensing information. 6 // 7 // Authors: 8 // - Isis Agora Lovecruft <isis@patternsinthevoid.net> 9 // - Henry de Valence <hdevalence@hdevalence.ca> 10 11 //! Internal curve representations which are not part of the public API. 12 //! 13 //! # Curve representations 14 //! 15 //! Internally, we use several different models for the curve. Here 16 //! is a sketch of the relationship between the models, following [a 17 //! post][smith-moderncrypto] 18 //! by Ben Smith on the `moderncrypto` mailing list. This is also briefly 19 //! discussed in section 2.5 of [_Montgomery curves and their 20 //! arithmetic_][costello-smith-2017] by Costello and Smith. 21 //! 22 //! Begin with the affine equation for the curve, 23 //! $$ 24 //! -x\^2 + y\^2 = 1 + dx\^2y\^2. 25 //! $$ 26 //! Next, pass to the projective closure \\(\mathbb P\^1 \times \mathbb 27 //! P\^1 \\) by setting \\(x=X/Z\\), \\(y=Y/T.\\) Clearing denominators 28 //! gives the model 29 //! $$ 30 //! -X\^2T\^2 + Y\^2Z\^2 = Z\^2T\^2 + dX\^2Y\^2. 31 //! $$ 32 //! In `curve25519-dalek`, this is represented as the `CompletedPoint` 33 //! struct. 34 //! To map from \\(\mathbb P\^1 \times \mathbb P\^1 \\), a product of 35 //! two lines, to \\(\mathbb P\^3\\), we use the [Segre 36 //! embedding](https://en.wikipedia.org/wiki/Segre_embedding) 37 //! $$ 38 //! \sigma : ((X:Z),(Y:T)) \mapsto (XY:XT:ZY:ZT). 39 //! $$ 40 //! Using coordinates \\( (W_0:W_1:W_2:W_3) \\) for \\(\mathbb P\^3\\), 41 //! the image \\(\sigma (\mathbb P\^1 \times \mathbb P\^1) \\) is the 42 //! surface defined by \\( W_0 W_3 = W_1 W_2 \\), and under \\( 43 //! \sigma\\), the equation above becomes 44 //! $$ 45 //! -W\_1\^2 + W\_2\^2 = W\_3\^2 + dW\_0\^2, 46 //! $$ 47 //! so that the curve is given by the pair of equations 48 //! $$ 49 //! \begin{aligned} 50 //! -W\_1\^2 + W\_2\^2 &= W\_3\^2 + dW\_0\^2, \\\\ W_0 W_3 &= W_1 W_2. 51 //! \end{aligned} 52 //! $$ 53 //! Up to variable naming, this is exactly the "extended" curve model 54 //! introduced in [_Twisted Edwards Curves 55 //! Revisited_][hisil-wong-carter-dawson-2008] by Hisil, Wong, Carter, 56 //! and Dawson. In `curve25519-dalek`, it is represented as the 57 //! `EdwardsPoint` struct. We can map from \\(\mathbb P\^3 \\) to 58 //! \\(\mathbb P\^2 \\) by sending \\( (W\_0:W\_1:W\_2:W\_3) \\) to \\( 59 //! (W\_1:W\_2:W\_3) \\). Notice that 60 //! $$ 61 //! \frac {W\_1} {W\_3} = \frac {XT} {ZT} = \frac X Z = x, 62 //! $$ 63 //! and 64 //! $$ 65 //! \frac {W\_2} {W\_3} = \frac {YZ} {ZT} = \frac Y T = y, 66 //! $$ 67 //! so this is the same as if we had started with the affine model 68 //! and passed to \\( \mathbb P\^2 \\) by setting \\( x = W\_1 / W\_3 69 //! \\), \\(y = W\_2 / W\_3 \\). 70 //! Up to variable naming, this is the projective representation 71 //! introduced in in [_Twisted Edwards 72 //! Curves_][bernstein-birkner-joye-lange-peters-2008] by Bernstein, 73 //! Birkner, Joye, Lange, and Peters. In `curve25519-dalek`, it is 74 //! represented by the `ProjectivePoint` struct. 75 //! 76 //! # Passing between curve models 77 //! 78 //! Although the \\( \mathbb P\^3 \\) model provides faster addition 79 //! formulas, the \\( \mathbb P\^2 \\) model provides faster doubling 80 //! formulas. Hisil, Wong, Carter, and Dawson therefore suggest mixing 81 //! coordinate systems for scalar multiplication, attributing the idea 82 //! to [a 1998 paper][cohen-miyaji-ono-1998] of Cohen, Miyagi, and Ono. 83 //! 84 //! Their suggestion is to vary the formulas used by context, using a 85 //! \\( \mathbb P\^2 \rightarrow \mathbb P\^2 \\) doubling formula when 86 //! a doubling is followed 87 //! by another doubling, a \\( \mathbb P\^2 \rightarrow \mathbb P\^3 \\) 88 //! doubling formula when a doubling is followed by an addition, and 89 //! computing point additions using a \\( \mathbb P\^3 \times \mathbb P\^3 90 //! \rightarrow \mathbb P\^2 \\) formula. 91 //! 92 //! The `ref10` reference implementation of [Ed25519][ed25519], by 93 //! Bernstein, Duif, Lange, Schwabe, and Yang, tweaks 94 //! this strategy, factoring the addition formulas through the 95 //! completion \\( \mathbb P\^1 \times \mathbb P\^1 \\), so that the 96 //! output of an addition or doubling always lies in \\( \mathbb P\^1 \times 97 //! \mathbb P\^1\\), and the choice of which formula to use is replaced 98 //! by a choice of whether to convert the result to \\( \mathbb P\^2 \\) 99 //! or \\(\mathbb P\^3 \\). However, this tweak is not described in 100 //! their paper, only in their software. 101 //! 102 //! Our naming for the `CompletedPoint` (\\(\mathbb P\^1 \times \mathbb 103 //! P\^1 \\)), `ProjectivePoint` (\\(\mathbb P\^2 \\)), and 104 //! `EdwardsPoint` (\\(\mathbb P\^3 \\)) structs follows the naming in 105 //! Adam Langley's [Golang ed25519][agl-ed25519] implementation, which 106 //! `curve25519-dalek` was originally derived from. 107 //! 108 //! Finally, to accelerate readditions, we use two cached point formats 109 //! in "Niels coordinates", named for Niels Duif, 110 //! one for the affine model and one for the \\( \mathbb P\^3 \\) model: 111 //! 112 //! * `AffineNielsPoint`: \\( (y+x, y-x, 2dxy) \\) 113 //! * `ProjectiveNielsPoint`: \\( (Y+X, Y-X, Z, 2dXY) \\) 114 //! 115 //! [smith-moderncrypto]: https://moderncrypto.org/mail-archive/curves/2016/000807.html 116 //! [costello-smith-2017]: https://eprint.iacr.org/2017/212 117 //! [hisil-wong-carter-dawson-2008]: https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf 118 //! [bernstein-birkner-joye-lange-peters-2008]: https://eprint.iacr.org/2008/013 119 //! [cohen-miyaji-ono-1998]: https://link.springer.com/content/pdf/10.1007%2F3-540-49649-1_6.pdf 120 //! [ed25519]: https://eprint.iacr.org/2011/368 121 //! [agl-ed25519]: https://github.com/agl/ed25519 122 123 #![allow(non_snake_case)] 124 125 use core::fmt::Debug; 126 use core::ops::{Add, Neg, Sub}; 127 128 use subtle::Choice; 129 use subtle::ConditionallySelectable; 130 131 use zeroize::Zeroize; 132 133 use constants; 134 135 use edwards::EdwardsPoint; 136 use field::FieldElement; 137 use traits::ValidityCheck; 138 139 // ------------------------------------------------------------------------ 140 // Internal point representations 141 // ------------------------------------------------------------------------ 142 143 /// A `ProjectivePoint` is a point \\((X:Y:Z)\\) on the \\(\mathbb 144 /// P\^2\\) model of the curve. 145 /// A point \\((x,y)\\) in the affine model corresponds to 146 /// \\((x:y:1)\\). 147 /// 148 /// More details on the relationships between the different curve models 149 /// can be found in the module-level documentation. 150 #[derive(Copy, Clone)] 151 pub struct ProjectivePoint { 152 pub X: FieldElement, 153 pub Y: FieldElement, 154 pub Z: FieldElement, 155 } 156 157 /// A `CompletedPoint` is a point \\(((X:Z), (Y:T))\\) on the \\(\mathbb 158 /// P\^1 \times \mathbb P\^1 \\) model of the curve. 159 /// A point (x,y) in the affine model corresponds to \\( ((x:1),(y:1)) 160 /// \\). 161 /// 162 /// More details on the relationships between the different curve models 163 /// can be found in the module-level documentation. 164 #[derive(Copy, Clone)] 165 #[allow(missing_docs)] 166 pub struct CompletedPoint { 167 pub X: FieldElement, 168 pub Y: FieldElement, 169 pub Z: FieldElement, 170 pub T: FieldElement, 171 } 172 173 /// A pre-computed point in the affine model for the curve, represented as 174 /// \\((y+x, y-x, 2dxy)\\) in "Niels coordinates". 175 /// 176 /// More details on the relationships between the different curve models 177 /// can be found in the module-level documentation. 178 // Safe to derive Eq because affine coordinates. 179 #[derive(Copy, Clone, Eq, PartialEq)] 180 #[allow(missing_docs)] 181 pub struct AffineNielsPoint { 182 pub y_plus_x: FieldElement, 183 pub y_minus_x: FieldElement, 184 pub xy2d: FieldElement, 185 } 186 187 impl Zeroize for AffineNielsPoint { zeroize(&mut self)188 fn zeroize(&mut self) { 189 self.y_plus_x.zeroize(); 190 self.y_minus_x.zeroize(); 191 self.xy2d.zeroize(); 192 } 193 } 194 195 /// A pre-computed point on the \\( \mathbb P\^3 \\) model for the 196 /// curve, represented as \\((Y+X, Y-X, Z, 2dXY)\\) in "Niels coordinates". 197 /// 198 /// More details on the relationships between the different curve models 199 /// can be found in the module-level documentation. 200 #[derive(Copy, Clone)] 201 pub struct ProjectiveNielsPoint { 202 pub Y_plus_X: FieldElement, 203 pub Y_minus_X: FieldElement, 204 pub Z: FieldElement, 205 pub T2d: FieldElement, 206 } 207 208 impl Zeroize for ProjectiveNielsPoint { zeroize(&mut self)209 fn zeroize(&mut self) { 210 self.Y_plus_X.zeroize(); 211 self.Y_minus_X.zeroize(); 212 self.Z.zeroize(); 213 self.T2d.zeroize(); 214 } 215 } 216 217 // ------------------------------------------------------------------------ 218 // Constructors 219 // ------------------------------------------------------------------------ 220 221 use traits::Identity; 222 223 impl Identity for ProjectivePoint { identity() -> ProjectivePoint224 fn identity() -> ProjectivePoint { 225 ProjectivePoint { 226 X: FieldElement::zero(), 227 Y: FieldElement::one(), 228 Z: FieldElement::one(), 229 } 230 } 231 } 232 233 impl Identity for ProjectiveNielsPoint { identity() -> ProjectiveNielsPoint234 fn identity() -> ProjectiveNielsPoint { 235 ProjectiveNielsPoint{ 236 Y_plus_X: FieldElement::one(), 237 Y_minus_X: FieldElement::one(), 238 Z: FieldElement::one(), 239 T2d: FieldElement::zero(), 240 } 241 } 242 } 243 244 impl Default for ProjectiveNielsPoint { default() -> ProjectiveNielsPoint245 fn default() -> ProjectiveNielsPoint { 246 ProjectiveNielsPoint::identity() 247 } 248 } 249 250 impl Identity for AffineNielsPoint { identity() -> AffineNielsPoint251 fn identity() -> AffineNielsPoint { 252 AffineNielsPoint{ 253 y_plus_x: FieldElement::one(), 254 y_minus_x: FieldElement::one(), 255 xy2d: FieldElement::zero(), 256 } 257 } 258 } 259 260 impl Default for AffineNielsPoint { default() -> AffineNielsPoint261 fn default() -> AffineNielsPoint { 262 AffineNielsPoint::identity() 263 } 264 } 265 266 // ------------------------------------------------------------------------ 267 // Validity checks (for debugging, not CT) 268 // ------------------------------------------------------------------------ 269 270 impl ValidityCheck for ProjectivePoint { is_valid(&self) -> bool271 fn is_valid(&self) -> bool { 272 // Curve equation is -x^2 + y^2 = 1 + d*x^2*y^2, 273 // homogenized as (-X^2 + Y^2)*Z^2 = Z^4 + d*X^2*Y^2 274 let XX = self.X.square(); 275 let YY = self.Y.square(); 276 let ZZ = self.Z.square(); 277 let ZZZZ = ZZ.square(); 278 let lhs = &(&YY - &XX) * &ZZ; 279 let rhs = &ZZZZ + &(&constants::EDWARDS_D * &(&XX * &YY)); 280 281 lhs == rhs 282 } 283 } 284 285 // ------------------------------------------------------------------------ 286 // Constant-time assignment 287 // ------------------------------------------------------------------------ 288 289 impl ConditionallySelectable for ProjectiveNielsPoint { conditional_select(a: &Self, b: &Self, choice: Choice) -> Self290 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 291 ProjectiveNielsPoint { 292 Y_plus_X: FieldElement::conditional_select(&a.Y_plus_X, &b.Y_plus_X, choice), 293 Y_minus_X: FieldElement::conditional_select(&a.Y_minus_X, &b.Y_minus_X, choice), 294 Z: FieldElement::conditional_select(&a.Z, &b.Z, choice), 295 T2d: FieldElement::conditional_select(&a.T2d, &b.T2d, choice), 296 } 297 } 298 conditional_assign(&mut self, other: &Self, choice: Choice)299 fn conditional_assign(&mut self, other: &Self, choice: Choice) { 300 self.Y_plus_X.conditional_assign(&other.Y_plus_X, choice); 301 self.Y_minus_X.conditional_assign(&other.Y_minus_X, choice); 302 self.Z.conditional_assign(&other.Z, choice); 303 self.T2d.conditional_assign(&other.T2d, choice); 304 } 305 } 306 307 impl ConditionallySelectable for AffineNielsPoint { conditional_select(a: &Self, b: &Self, choice: Choice) -> Self308 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 309 AffineNielsPoint { 310 y_plus_x: FieldElement::conditional_select(&a.y_plus_x, &b.y_plus_x, choice), 311 y_minus_x: FieldElement::conditional_select(&a.y_minus_x, &b.y_minus_x, choice), 312 xy2d: FieldElement::conditional_select(&a.xy2d, &b.xy2d, choice), 313 } 314 } 315 conditional_assign(&mut self, other: &Self, choice: Choice)316 fn conditional_assign(&mut self, other: &Self, choice: Choice) { 317 self.y_plus_x.conditional_assign(&other.y_plus_x, choice); 318 self.y_minus_x.conditional_assign(&other.y_minus_x, choice); 319 self.xy2d.conditional_assign(&other.xy2d, choice); 320 } 321 } 322 323 // ------------------------------------------------------------------------ 324 // Point conversions 325 // ------------------------------------------------------------------------ 326 327 impl ProjectivePoint { 328 /// Convert this point from the \\( \mathbb P\^2 \\) model to the 329 /// \\( \mathbb P\^3 \\) model. 330 /// 331 /// This costs \\(3 \mathrm M + 1 \mathrm S\\). to_extended(&self) -> EdwardsPoint332 pub fn to_extended(&self) -> EdwardsPoint { 333 EdwardsPoint { 334 X: &self.X * &self.Z, 335 Y: &self.Y * &self.Z, 336 Z: self.Z.square(), 337 T: &self.X * &self.Y, 338 } 339 } 340 } 341 342 impl CompletedPoint { 343 /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1 344 /// \\) model to the \\( \mathbb P\^2 \\) model. 345 /// 346 /// This costs \\(3 \mathrm M \\). to_projective(&self) -> ProjectivePoint347 pub fn to_projective(&self) -> ProjectivePoint { 348 ProjectivePoint { 349 X: &self.X * &self.T, 350 Y: &self.Y * &self.Z, 351 Z: &self.Z * &self.T, 352 } 353 } 354 355 /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1 356 /// \\) model to the \\( \mathbb P\^3 \\) model. 357 /// 358 /// This costs \\(4 \mathrm M \\). to_extended(&self) -> EdwardsPoint359 pub fn to_extended(&self) -> EdwardsPoint { 360 EdwardsPoint { 361 X: &self.X * &self.T, 362 Y: &self.Y * &self.Z, 363 Z: &self.Z * &self.T, 364 T: &self.X * &self.Y, 365 } 366 } 367 } 368 369 // ------------------------------------------------------------------------ 370 // Doubling 371 // ------------------------------------------------------------------------ 372 373 impl ProjectivePoint { 374 /// Double this point: return self + self double(&self) -> CompletedPoint375 pub fn double(&self) -> CompletedPoint { // Double() 376 let XX = self.X.square(); 377 let YY = self.Y.square(); 378 let ZZ2 = self.Z.square2(); 379 let X_plus_Y = &self.X + &self.Y; 380 let X_plus_Y_sq = X_plus_Y.square(); 381 let YY_plus_XX = &YY + &XX; 382 let YY_minus_XX = &YY - &XX; 383 384 CompletedPoint{ 385 X: &X_plus_Y_sq - &YY_plus_XX, 386 Y: YY_plus_XX, 387 Z: YY_minus_XX, 388 T: &ZZ2 - &YY_minus_XX 389 } 390 } 391 } 392 393 // ------------------------------------------------------------------------ 394 // Addition and Subtraction 395 // ------------------------------------------------------------------------ 396 397 // XXX(hdevalence) These were doc(hidden) so they don't appear in the 398 // public API docs. 399 // However, that prevents them being used with --document-private-items, 400 // so comment out the doc(hidden) for now until this is resolved 401 // 402 // upstream rust issue: https://github.com/rust-lang/rust/issues/46380 403 //#[doc(hidden)] 404 impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a EdwardsPoint { 405 type Output = CompletedPoint; 406 add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint407 fn add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint { 408 let Y_plus_X = &self.Y + &self.X; 409 let Y_minus_X = &self.Y - &self.X; 410 let PP = &Y_plus_X * &other.Y_plus_X; 411 let MM = &Y_minus_X * &other.Y_minus_X; 412 let TT2d = &self.T * &other.T2d; 413 let ZZ = &self.Z * &other.Z; 414 let ZZ2 = &ZZ + &ZZ; 415 416 CompletedPoint{ 417 X: &PP - &MM, 418 Y: &PP + &MM, 419 Z: &ZZ2 + &TT2d, 420 T: &ZZ2 - &TT2d 421 } 422 } 423 } 424 425 //#[doc(hidden)] 426 impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a EdwardsPoint { 427 type Output = CompletedPoint; 428 sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint429 fn sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint { 430 let Y_plus_X = &self.Y + &self.X; 431 let Y_minus_X = &self.Y - &self.X; 432 let PM = &Y_plus_X * &other.Y_minus_X; 433 let MP = &Y_minus_X * &other.Y_plus_X; 434 let TT2d = &self.T * &other.T2d; 435 let ZZ = &self.Z * &other.Z; 436 let ZZ2 = &ZZ + &ZZ; 437 438 CompletedPoint{ 439 X: &PM - &MP, 440 Y: &PM + &MP, 441 Z: &ZZ2 - &TT2d, 442 T: &ZZ2 + &TT2d 443 } 444 } 445 } 446 447 //#[doc(hidden)] 448 impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a EdwardsPoint { 449 type Output = CompletedPoint; 450 add(self, other: &'b AffineNielsPoint) -> CompletedPoint451 fn add(self, other: &'b AffineNielsPoint) -> CompletedPoint { 452 let Y_plus_X = &self.Y + &self.X; 453 let Y_minus_X = &self.Y - &self.X; 454 let PP = &Y_plus_X * &other.y_plus_x; 455 let MM = &Y_minus_X * &other.y_minus_x; 456 let Txy2d = &self.T * &other.xy2d; 457 let Z2 = &self.Z + &self.Z; 458 459 CompletedPoint{ 460 X: &PP - &MM, 461 Y: &PP + &MM, 462 Z: &Z2 + &Txy2d, 463 T: &Z2 - &Txy2d 464 } 465 } 466 } 467 468 //#[doc(hidden)] 469 impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a EdwardsPoint { 470 type Output = CompletedPoint; 471 sub(self, other: &'b AffineNielsPoint) -> CompletedPoint472 fn sub(self, other: &'b AffineNielsPoint) -> CompletedPoint { 473 let Y_plus_X = &self.Y + &self.X; 474 let Y_minus_X = &self.Y - &self.X; 475 let PM = &Y_plus_X * &other.y_minus_x; 476 let MP = &Y_minus_X * &other.y_plus_x; 477 let Txy2d = &self.T * &other.xy2d; 478 let Z2 = &self.Z + &self.Z; 479 480 CompletedPoint{ 481 X: &PM - &MP, 482 Y: &PM + &MP, 483 Z: &Z2 - &Txy2d, 484 T: &Z2 + &Txy2d 485 } 486 } 487 } 488 489 // ------------------------------------------------------------------------ 490 // Negation 491 // ------------------------------------------------------------------------ 492 493 impl<'a> Neg for &'a ProjectiveNielsPoint { 494 type Output = ProjectiveNielsPoint; 495 neg(self) -> ProjectiveNielsPoint496 fn neg(self) -> ProjectiveNielsPoint { 497 ProjectiveNielsPoint{ 498 Y_plus_X: self.Y_minus_X, 499 Y_minus_X: self.Y_plus_X, 500 Z: self.Z, 501 T2d: -(&self.T2d), 502 } 503 } 504 } 505 506 impl<'a> Neg for &'a AffineNielsPoint { 507 type Output = AffineNielsPoint; 508 neg(self) -> AffineNielsPoint509 fn neg(self) -> AffineNielsPoint { 510 AffineNielsPoint{ 511 y_plus_x: self.y_minus_x, 512 y_minus_x: self.y_plus_x, 513 xy2d: -(&self.xy2d) 514 } 515 } 516 } 517 518 // ------------------------------------------------------------------------ 519 // Debug traits 520 // ------------------------------------------------------------------------ 521 522 impl Debug for ProjectivePoint { fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result523 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 524 write!(f, "ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}", 525 &self.X, &self.Y, &self.Z) 526 } 527 } 528 529 impl Debug for CompletedPoint { fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result530 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 531 write!(f, "CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}", 532 &self.X, &self.Y, &self.Z, &self.T) 533 } 534 } 535 536 impl Debug for AffineNielsPoint { fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result537 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 538 write!(f, "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}", 539 &self.y_plus_x, &self.y_minus_x, &self.xy2d) 540 } 541 } 542 543 impl Debug for ProjectiveNielsPoint { fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result544 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 545 write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}", 546 &self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d) 547 } 548 } 549 550 551