x25519ell2.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Copyright (c) 2021 Yawning Angel <yawning at schwanenlied dot me>
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. // Package x25519ell2 implements obfuscated X25519 ECDH, via the Elligator2
  16. // mapping.
  17. package x25519ell2 // import "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/lyrebird/internal/x25519ell2"
  18. import (
  19. "encoding/binary"
  20. "filippo.io/edwards25519"
  21. "filippo.io/edwards25519/field"
  22. "gitlab.com/yawning/edwards25519-extra/elligator2"
  23. )
  24. // The corrected version of this that solves the implementation errors
  25. // present in the historical implementation by agl is derived from
  26. // Monocypher (CC-0 or BSD-2) by Loup Vaillant. Without their efforts
  27. // and prodding, this would likely have stayed broken forever.
  28. var (
  29. feOne = new(field.Element).One()
  30. feNegTwo = mustFeFromBytes([]byte{
  31. 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  32. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
  33. })
  34. feA = mustFeFromUint64(486662)
  35. feSqrtM1 = mustFeFromBytes([]byte{
  36. 0xb0, 0xa0, 0x0e, 0x4a, 0x27, 0x1b, 0xee, 0xc4, 0x78, 0xe4, 0x2f, 0xad, 0x06, 0x18, 0x43, 0x2f,
  37. 0xa7, 0xd7, 0xfb, 0x3d, 0x99, 0x00, 0x4d, 0x2b, 0x0b, 0xdf, 0xc1, 0x4f, 0x80, 0x24, 0x83, 0x2b,
  38. })
  39. // Low order point Edwards x-coordinate `sqrt((sqrt(d + 1) + 1) / d)`.
  40. feLopX = mustFeFromBytes([]byte{
  41. 0x4a, 0xd1, 0x45, 0xc5, 0x46, 0x46, 0xa1, 0xde, 0x38, 0xe2, 0xe5, 0x13, 0x70, 0x3c, 0x19, 0x5c,
  42. 0xbb, 0x4a, 0xde, 0x38, 0x32, 0x99, 0x33, 0xe9, 0x28, 0x4a, 0x39, 0x06, 0xa0, 0xb9, 0xd5, 0x1f,
  43. })
  44. // Low order point Edwards y-coordinate `-lop_x * sqrtm1`
  45. feLopY = mustFeFromBytes([]byte{
  46. 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
  47. 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05,
  48. })
  49. )
  50. func mustFeFromBytes(b []byte) *field.Element {
  51. fe, err := new(field.Element).SetBytes(b)
  52. if err != nil {
  53. panic("internal/x25519ell2: failed to deserialize constant: " + err.Error())
  54. }
  55. return fe
  56. }
  57. func mustFeFromUint64(x uint64) *field.Element {
  58. var b [32]byte
  59. binary.LittleEndian.PutUint64(b[:], x)
  60. return mustFeFromBytes(b[:])
  61. }
  62. func selectLowOrderPoint(out, x, k *field.Element, cofactor uint8) {
  63. out.Zero()
  64. out.Select(k, out, int((cofactor>>1)&1)) // bit 1
  65. out.Select(x, out, int((cofactor>>0)&1)) // bit 0
  66. var tmp field.Element
  67. tmp.Negate(out)
  68. out.Select(&tmp, out, int((cofactor>>2)&1)) // bit 2
  69. }
  70. func scalarBaseMultDirty(privateKey *[32]byte) *field.Element {
  71. // Compute clean scalar multiplication
  72. scalar, err := new(edwards25519.Scalar).SetBytesWithClamping(privateKey[:])
  73. if err != nil {
  74. panic("internal/x25519ell2: failed to deserialize scalar: " + err.Error())
  75. }
  76. pk := new(edwards25519.Point).ScalarBaseMult(scalar)
  77. // Compute low order point
  78. var lopX, lopY, lopT field.Element
  79. selectLowOrderPoint(&lopX, feLopX, feSqrtM1, privateKey[0])
  80. selectLowOrderPoint(&lopY, feLopY, feOne, privateKey[0]+2)
  81. // Z = one
  82. lopT.Multiply(&lopX, &lopY)
  83. lop, err := new(edwards25519.Point).SetExtendedCoordinates(&lopX, &lopY, feOne, &lopT)
  84. if err != nil {
  85. panic("interal/x25519ell2: failed to create edwards point from x, y: " + err.Error())
  86. }
  87. // Add low order point to the public key
  88. pk.Add(pk, lop)
  89. // Convert to Montgomery u coordinate (we ignore the sign)
  90. _, yExt, zExt, _ := pk.ExtendedCoordinates()
  91. var t1, t2 field.Element
  92. t1.Add(zExt, yExt)
  93. t2.Subtract(zExt, yExt)
  94. t2.Invert(&t2)
  95. t1.Multiply(&t1, &t2)
  96. return &t1
  97. }
  98. func uToRepresentative(representative *[32]byte, u *field.Element, tweak byte) bool {
  99. t1 := new(field.Element).Set(u)
  100. t2 := new(field.Element).Add(t1, feA)
  101. t3 := new(field.Element).Multiply(t1, t2)
  102. t3.Multiply(t3, feNegTwo)
  103. if _, isSquare := t3.SqrtRatio(feOne, t3); isSquare == 1 {
  104. t1.Select(t2, t1, int(tweak&1))
  105. t3.Multiply(t1, t3)
  106. t1.Mult32(t3, 2)
  107. t2.Negate(t3)
  108. tmp := t1.Bytes()
  109. t3.Select(t2, t3, int(tmp[0]&1))
  110. copy(representative[:], t3.Bytes())
  111. // Pad with two random bits
  112. representative[31] |= tweak & 0xc0
  113. return true
  114. }
  115. return false
  116. }
  117. // ScalarBaseMult computes a curve25519 public key from a private
  118. // key and also a uniform representative for that public key.
  119. // Note that this function will fail and return false for about
  120. // half of private keys.
  121. //
  122. // The `privateKey` input MUST be the full 32-bytes of entropy
  123. // (X25519-style "clamping" will result in non-uniformly distributed
  124. // representatives).
  125. //
  126. // WARNING: The underlying scalar multiply explicitly does not clear
  127. // the cofactor, and thus the public keys will be different from
  128. // those produced by normal implementations.
  129. func ScalarBaseMult(publicKey, representative, privateKey *[32]byte, tweak byte) bool {
  130. u := scalarBaseMultDirty(privateKey)
  131. if !uToRepresentative(representative, u, tweak) {
  132. // No representative.
  133. return false
  134. }
  135. copy(publicKey[:], u.Bytes())
  136. return true
  137. }
  138. // RepresentativeToPublicKey converts a uniform representative value for
  139. // a curve25519 public key, as produced by ScalarBaseMult, to a curve25519
  140. // public key.
  141. func RepresentativeToPublicKey(publicKey, representative *[32]byte) {
  142. // Representatives are encoded in 254 bits.
  143. var clamped [32]byte
  144. copy(clamped[:], representative[:])
  145. clamped[31] &= 63
  146. var fe field.Element
  147. if _, err := fe.SetBytes(clamped[:]); err != nil {
  148. // Panic is fine, the only way this fails is if the representative
  149. // is not 32-bytes.
  150. panic("internal/x25519ell2: failed to deserialize representative: " + err.Error())
  151. }
  152. u, _ := elligator2.MontgomeryFlavor(&fe)
  153. copy(publicKey[:], u.Bytes())
  154. }