edwards25519_test.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright (c) 2019 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package edwards25519
  5. import (
  6. "crypto/ed25519/internal/edwards25519/field"
  7. "encoding/hex"
  8. "os"
  9. "reflect"
  10. "runtime"
  11. "strings"
  12. "testing"
  13. )
  14. var B = NewGeneratorPoint()
  15. var I = NewIdentityPoint()
  16. func checkOnCurve(t *testing.T, points ...*Point) {
  17. t.Helper()
  18. for i, p := range points {
  19. var XX, YY, ZZ, ZZZZ field.Element
  20. XX.Square(&p.x)
  21. YY.Square(&p.y)
  22. ZZ.Square(&p.z)
  23. ZZZZ.Square(&ZZ)
  24. // -x² + y² = 1 + dx²y²
  25. // -(X/Z)² + (Y/Z)² = 1 + d(X/Z)²(Y/Z)²
  26. // (-X² + Y²)/Z² = 1 + (dX²Y²)/Z⁴
  27. // (-X² + Y²)*Z² = Z⁴ + dX²Y²
  28. var lhs, rhs field.Element
  29. lhs.Subtract(&YY, &XX).Multiply(&lhs, &ZZ)
  30. rhs.Multiply(d, &XX).Multiply(&rhs, &YY).Add(&rhs, &ZZZZ)
  31. if lhs.Equal(&rhs) != 1 {
  32. t.Errorf("X, Y, and Z do not specify a point on the curve\nX = %v\nY = %v\nZ = %v", p.x, p.y, p.z)
  33. }
  34. // xy = T/Z
  35. lhs.Multiply(&p.x, &p.y)
  36. rhs.Multiply(&p.z, &p.t)
  37. if lhs.Equal(&rhs) != 1 {
  38. t.Errorf("point %d is not valid\nX = %v\nY = %v\nZ = %v", i, p.x, p.y, p.z)
  39. }
  40. }
  41. }
  42. func TestGenerator(t *testing.T) {
  43. // These are the coordinates of B from RFC 8032, Section 5.1, converted to
  44. // little endian hex.
  45. x := "1ad5258f602d56c9b2a7259560c72c695cdcd6fd31e2a4c0fe536ecdd3366921"
  46. y := "5866666666666666666666666666666666666666666666666666666666666666"
  47. if got := hex.EncodeToString(B.x.Bytes()); got != x {
  48. t.Errorf("wrong B.x: got %s, expected %s", got, x)
  49. }
  50. if got := hex.EncodeToString(B.y.Bytes()); got != y {
  51. t.Errorf("wrong B.y: got %s, expected %s", got, y)
  52. }
  53. if B.z.Equal(feOne) != 1 {
  54. t.Errorf("wrong B.z: got %v, expected 1", B.z)
  55. }
  56. // Check that t is correct.
  57. checkOnCurve(t, B)
  58. }
  59. func TestAddSubNegOnBasePoint(t *testing.T) {
  60. checkLhs, checkRhs := &Point{}, &Point{}
  61. checkLhs.Add(B, B)
  62. tmpP2 := new(projP2).FromP3(B)
  63. tmpP1xP1 := new(projP1xP1).Double(tmpP2)
  64. checkRhs.fromP1xP1(tmpP1xP1)
  65. if checkLhs.Equal(checkRhs) != 1 {
  66. t.Error("B + B != [2]B")
  67. }
  68. checkOnCurve(t, checkLhs, checkRhs)
  69. checkLhs.Subtract(B, B)
  70. Bneg := new(Point).Negate(B)
  71. checkRhs.Add(B, Bneg)
  72. if checkLhs.Equal(checkRhs) != 1 {
  73. t.Error("B - B != B + (-B)")
  74. }
  75. if I.Equal(checkLhs) != 1 {
  76. t.Error("B - B != 0")
  77. }
  78. if I.Equal(checkRhs) != 1 {
  79. t.Error("B + (-B) != 0")
  80. }
  81. checkOnCurve(t, checkLhs, checkRhs, Bneg)
  82. }
  83. func TestComparable(t *testing.T) {
  84. if reflect.TypeOf(Point{}).Comparable() {
  85. t.Error("Point is unexpectedly comparable")
  86. }
  87. }
  88. func TestInvalidEncodings(t *testing.T) {
  89. // An invalid point, that also happens to have y > p.
  90. invalid := "efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"
  91. p := NewGeneratorPoint()
  92. if out, err := p.SetBytes(decodeHex(invalid)); err == nil {
  93. t.Error("expected error for invalid point")
  94. } else if out != nil {
  95. t.Error("SetBytes did not return nil on an invalid encoding")
  96. } else if p.Equal(B) != 1 {
  97. t.Error("the Point was modified while decoding an invalid encoding")
  98. }
  99. checkOnCurve(t, p)
  100. }
  101. func TestNonCanonicalPoints(t *testing.T) {
  102. type test struct {
  103. name string
  104. encoding, canonical string
  105. }
  106. tests := []test{
  107. // Points with x = 0 and the sign bit set. With x = 0 the curve equation
  108. // gives y² = 1, so y = ±1. 1 has two valid encodings.
  109. {
  110. "y=1,sign-",
  111. "0100000000000000000000000000000000000000000000000000000000000080",
  112. "0100000000000000000000000000000000000000000000000000000000000000",
  113. },
  114. {
  115. "y=p+1,sign-",
  116. "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  117. "0100000000000000000000000000000000000000000000000000000000000000",
  118. },
  119. {
  120. "y=p-1,sign-",
  121. "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  122. "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  123. },
  124. // Non-canonical y encodings with values 2²⁵⁵-19 (p) to 2²⁵⁵-1 (p+18).
  125. {
  126. "y=p,sign+",
  127. "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  128. "0000000000000000000000000000000000000000000000000000000000000000",
  129. },
  130. {
  131. "y=p,sign-",
  132. "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  133. "0000000000000000000000000000000000000000000000000000000000000080",
  134. },
  135. {
  136. "y=p+1,sign+",
  137. "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  138. "0100000000000000000000000000000000000000000000000000000000000000",
  139. },
  140. // "y=p+1,sign-" is already tested above.
  141. // p+2 is not a valid y-coordinate.
  142. {
  143. "y=p+3,sign+",
  144. "f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  145. "0300000000000000000000000000000000000000000000000000000000000000",
  146. },
  147. {
  148. "y=p+3,sign-",
  149. "f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  150. "0300000000000000000000000000000000000000000000000000000000000080",
  151. },
  152. {
  153. "y=p+4,sign+",
  154. "f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  155. "0400000000000000000000000000000000000000000000000000000000000000",
  156. },
  157. {
  158. "y=p+4,sign-",
  159. "f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  160. "0400000000000000000000000000000000000000000000000000000000000080",
  161. },
  162. {
  163. "y=p+5,sign+",
  164. "f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  165. "0500000000000000000000000000000000000000000000000000000000000000",
  166. },
  167. {
  168. "y=p+5,sign-",
  169. "f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  170. "0500000000000000000000000000000000000000000000000000000000000080",
  171. },
  172. {
  173. "y=p+6,sign+",
  174. "f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  175. "0600000000000000000000000000000000000000000000000000000000000000",
  176. },
  177. {
  178. "y=p+6,sign-",
  179. "f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  180. "0600000000000000000000000000000000000000000000000000000000000080",
  181. },
  182. // p+7 is not a valid y-coordinate.
  183. // p+8 is not a valid y-coordinate.
  184. {
  185. "y=p+9,sign+",
  186. "f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  187. "0900000000000000000000000000000000000000000000000000000000000000",
  188. },
  189. {
  190. "y=p+9,sign-",
  191. "f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  192. "0900000000000000000000000000000000000000000000000000000000000080",
  193. },
  194. {
  195. "y=p+10,sign+",
  196. "f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  197. "0a00000000000000000000000000000000000000000000000000000000000000",
  198. },
  199. {
  200. "y=p+10,sign-",
  201. "f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  202. "0a00000000000000000000000000000000000000000000000000000000000080",
  203. },
  204. // p+11 is not a valid y-coordinate.
  205. // p+12 is not a valid y-coordinate.
  206. // p+13 is not a valid y-coordinate.
  207. {
  208. "y=p+14,sign+",
  209. "fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  210. "0e00000000000000000000000000000000000000000000000000000000000000",
  211. },
  212. {
  213. "y=p+14,sign-",
  214. "fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  215. "0e00000000000000000000000000000000000000000000000000000000000080",
  216. },
  217. {
  218. "y=p+15,sign+",
  219. "fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  220. "0f00000000000000000000000000000000000000000000000000000000000000",
  221. },
  222. {
  223. "y=p+15,sign-",
  224. "fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  225. "0f00000000000000000000000000000000000000000000000000000000000080",
  226. },
  227. {
  228. "y=p+16,sign+",
  229. "fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  230. "1000000000000000000000000000000000000000000000000000000000000000",
  231. },
  232. {
  233. "y=p+16,sign-",
  234. "fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  235. "1000000000000000000000000000000000000000000000000000000000000080",
  236. },
  237. // p+17 is not a valid y-coordinate.
  238. {
  239. "y=p+18,sign+",
  240. "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
  241. "1200000000000000000000000000000000000000000000000000000000000000",
  242. },
  243. {
  244. "y=p+18,sign-",
  245. "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  246. "1200000000000000000000000000000000000000000000000000000000000080",
  247. },
  248. }
  249. for _, tt := range tests {
  250. t.Run(tt.name, func(t *testing.T) {
  251. p1, err := new(Point).SetBytes(decodeHex(tt.encoding))
  252. if err != nil {
  253. t.Fatalf("error decoding non-canonical point: %v", err)
  254. }
  255. p2, err := new(Point).SetBytes(decodeHex(tt.canonical))
  256. if err != nil {
  257. t.Fatalf("error decoding canonical point: %v", err)
  258. }
  259. if p1.Equal(p2) != 1 {
  260. t.Errorf("equivalent points are not equal: %v, %v", p1, p2)
  261. }
  262. if encoding := hex.EncodeToString(p1.Bytes()); encoding != tt.canonical {
  263. t.Errorf("re-encoding does not match canonical; got %q, expected %q", encoding, tt.canonical)
  264. }
  265. checkOnCurve(t, p1, p2)
  266. })
  267. }
  268. }
  269. var testAllocationsSink byte
  270. func TestAllocations(t *testing.T) {
  271. if runtime.Compiler == "gccgo" {
  272. t.Skip("gofronted escape analysis not good enough")
  273. }
  274. if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-noopt") {
  275. t.Skip("skipping allocations test without relevant optimizations")
  276. }
  277. if allocs := testing.AllocsPerRun(100, func() {
  278. p := NewIdentityPoint()
  279. p.Add(p, NewGeneratorPoint())
  280. s := NewScalar()
  281. testAllocationsSink ^= s.Bytes()[0]
  282. testAllocationsSink ^= p.Bytes()[0]
  283. }); allocs > 0 {
  284. t.Errorf("expected zero allocations, got %0.1v", allocs)
  285. }
  286. }
  287. func decodeHex(s string) []byte {
  288. b, err := hex.DecodeString(s)
  289. if err != nil {
  290. panic(err)
  291. }
  292. return b
  293. }