| 1 | module edwards25519 |
| 2 | |
| 3 | import os |
| 4 | import rand |
| 5 | import encoding.hex |
| 6 | import math.big |
| 7 | |
| 8 | const github_job = os.getenv('GITHUB_JOB') |
| 9 | |
| 10 | fn testsuite_begin() { |
| 11 | if github_job != '' { |
| 12 | // ensure that the CI does not run flaky tests: |
| 13 | rand.seed([u32(0xffff24), 0xabcd]) |
| 14 | } |
| 15 | } |
| 16 | |
| 17 | fn test_scalar_equal() { |
| 18 | assert sc_one.equal(sc_minus_one) != 1 |
| 19 | |
| 20 | assert sc_minus_one.equal(sc_minus_one) != 0 |
| 21 | } |
| 22 | |
| 23 | fn test_scalar_non_adjacent_form() { |
| 24 | mut s := Scalar{ |
| 25 | s: [u8(0x1a), 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, |
| 26 | 0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f, |
| 27 | 0x23, 0x76, 0xef, 0x09]! |
| 28 | } |
| 29 | expected_naf := [i8(0), 13, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, -11, |
| 30 | 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, 3, 0, 0, |
| 31 | 0, 0, 11, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 9, 0, 0, 0, |
| 32 | 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, -15, 0, 0, 0, 0, -7, 0, 0, |
| 33 | 0, 0, -9, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, -11, 0, 0, 0, |
| 34 | 0, -7, 0, 0, 0, 0, -13, 0, 0, 0, 0, 11, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, |
| 35 | -15, 0, 0, 0, 0, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 13, 0, 0, |
| 36 | 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, |
| 37 | 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, |
| 38 | 0, 0, 0, 0, 1, 0, 0, 0, 0] |
| 39 | |
| 40 | snaf := s.non_adjacent_form(5) |
| 41 | for i := 0; i < 256; i++ { |
| 42 | assert expected_naf[i] == snaf[i] |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | fn addlike_subneg(x Scalar, y Scalar) bool { |
| 47 | // Compute t1 = x - y |
| 48 | mut t1 := Scalar{} |
| 49 | t1.subtract(x, y) |
| 50 | |
| 51 | // Compute t2 = -y + x |
| 52 | mut t2 := Scalar{} |
| 53 | t2.negate(y) |
| 54 | t2.add(t2, x) |
| 55 | |
| 56 | return t1 == t2 && is_reduced(t1) |
| 57 | } |
| 58 | |
| 59 | fn test_scalar_add_like_subneg() { |
| 60 | for i in 0 .. 15 { |
| 61 | x := generate_scalar(1000) or { panic(err) } |
| 62 | y := generate_scalar(1000) or { panic(err) } |
| 63 | assert addlike_subneg(x, y) == true |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | fn fg(sc Scalar) bool { |
| 68 | return is_reduced(sc) |
| 69 | } |
| 70 | |
| 71 | fn test_scalar_generate() { |
| 72 | for i in 0 .. 15 { |
| 73 | sc := generate_scalar(1000) or { panic(err) } |
| 74 | |
| 75 | assert fg(sc) == true |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | // |
| 80 | fn test_scalar_set_canonical_bytes() { |
| 81 | for i in 0 .. 10 { |
| 82 | mut buf := rand.bytes(32) or { panic(err) } |
| 83 | mut sc := generate_scalar(1000) or { panic(err) } |
| 84 | buf[buf.len - 1] &= (1 << 4) - 1 |
| 85 | sc = sc.set_canonical_bytes(buf) or { panic(err) } |
| 86 | |
| 87 | assert buf[..] == sc.bytes() |
| 88 | assert is_reduced(sc) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | fn test_scalar_set_canonical_bytes_round_trip() { |
| 93 | for i in 0 .. 10 { |
| 94 | mut sc1 := generate_scalar(2)! |
| 95 | mut sc2 := generate_scalar(6)! |
| 96 | sc2.set_canonical_bytes(sc1.bytes()) or { panic(err) } |
| 97 | |
| 98 | assert sc1 == sc2 |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | const sc_error = Scalar{ |
| 103 | s: [32]u8{init: (u8(-1))} |
| 104 | } |
| 105 | |
| 106 | fn test_scalar_set_canonical_bytes_on_noncanonical_value() { |
| 107 | mut b := sc_minus_one.s |
| 108 | b[31] += 1 |
| 109 | |
| 110 | mut s := sc_one |
| 111 | out := |
| 112 | s.set_canonical_bytes(b[..]) or { sc_error } // set_canonical_bytes shouldn't worked on a non-canonical value" |
| 113 | assert out == sc_error |
| 114 | assert s == sc_one |
| 115 | } |
| 116 | |
| 117 | fn test_scalar_set_uniform_bytes() { |
| 118 | // mod, _ := new(big.Integer).SetString("27742317777372353535851937790883648493", 10) |
| 119 | mut mod := big.integer_from_string('27742317777372353535851937790883648493')! |
| 120 | // mod.Add(mod, new(big.Integer).Lsh(big.NewInt(1), 252)) |
| 121 | mod = mod + big.integer_from_i64(1).left_shift(252) |
| 122 | |
| 123 | mut sc := generate_scalar(100)! |
| 124 | inp := rand.bytes(64)! |
| 125 | |
| 126 | sc.set_uniform_bytes(inp[..])! |
| 127 | assert is_reduced(sc) == true |
| 128 | |
| 129 | scbig := bigint_from_le_bytes(sc.s[..]) |
| 130 | inbig := bigint_from_le_bytes(inp) |
| 131 | // return inbig.Mod(inbig, mod).Cmp(scbig) == 0 |
| 132 | _, m := inbig.div_mod(mod) |
| 133 | assert m.abs_cmp(scbig) == 0 // NEED FIX |
| 134 | } |
| 135 | |
| 136 | fn bigint_from_le_bytes(b []u8) big.Integer { |
| 137 | mut bc := b.clone() |
| 138 | buf := swap_endianness(mut bc) // WITHOUT THIS, some test would fail |
| 139 | bg := big.integer_from_bytes(buf) |
| 140 | return bg |
| 141 | } |
| 142 | |
| 143 | fn test_scalar_set_bytes_with_clamping() { |
| 144 | // Generated with libsodium.js 1.0.18 crypto_scalarmult_ed25519_base. |
| 145 | /* |
| 146 | random := "633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e" |
| 147 | s, _ := new(Scalar).SetBytesWithClamping(decodeHex(random)) |
| 148 | p := new(Point).ScalarBaseMult(s) |
| 149 | want := "1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94" |
| 150 | if got := hex.EncodeToString(p.Bytes()); got != want { |
| 151 | t.Errorf("random: got %q, want %q", got, want) |
| 152 | }*/ |
| 153 | random := '633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e' |
| 154 | random_bytes := hex.decode(random) or { panic(err) } |
| 155 | |
| 156 | mut s0 := Scalar{} |
| 157 | s0.set_bytes_with_clamping(random_bytes) or { panic(err) } |
| 158 | |
| 159 | mut p0 := Point{} |
| 160 | p0.scalar_base_mult(mut s0) |
| 161 | |
| 162 | want0 := '1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94' |
| 163 | got0 := hex.encode(p0.bytes()) |
| 164 | |
| 165 | assert got0 == want0 |
| 166 | |
| 167 | zero := '0000000000000000000000000000000000000000000000000000000000000000' |
| 168 | mut s1 := Scalar{} |
| 169 | zero_bytes := hex.decode(zero) or { panic(err) } |
| 170 | s1.set_bytes_with_clamping(zero_bytes) or { panic(err) } |
| 171 | mut p1 := Point{} |
| 172 | p1.scalar_base_mult(mut s1) |
| 173 | |
| 174 | want1 := '693e47972caf527c7883ad1b39822f026f47db2ab0e1919955b8993aa04411d1' |
| 175 | got1 := hex.encode(p1.bytes()) |
| 176 | assert want1 == got1 |
| 177 | |
| 178 | one := 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' |
| 179 | mut s2 := Scalar{} |
| 180 | mut one_bytes := hex.decode(one) or { panic(err) } |
| 181 | s2.set_bytes_with_clamping(one_bytes) or { panic(err) } |
| 182 | mut p2 := Point{} |
| 183 | p2.scalar_base_mult(mut s2) |
| 184 | |
| 185 | want2 := '12e9a68b73fd5aacdbcaf3e88c46fea6ebedb1aa84eed1842f07f8edab65e3a7' |
| 186 | got2 := hex.encode(p2.bytes()) |
| 187 | |
| 188 | assert want2 == got2 |
| 189 | } |
| 190 | |
| 191 | fn test_scalar_multiply_distributes_over_add() { |
| 192 | x := generate_scalar(100)! |
| 193 | y := generate_scalar(100)! |
| 194 | z := generate_scalar(100)! |
| 195 | |
| 196 | // Compute t1 = (x+y)*z |
| 197 | mut t1 := Scalar{} |
| 198 | t1.add(x, y) |
| 199 | t1.multiply(t1, z) |
| 200 | |
| 201 | // Compute t2 = x*z + y*z |
| 202 | mut t2 := Scalar{} |
| 203 | mut t3 := Scalar{} |
| 204 | t2.multiply(x, z) |
| 205 | t3.multiply(y, z) |
| 206 | t2.add(t2, t3) |
| 207 | |
| 208 | assert t1 == t2 && is_reduced(t1) && is_reduced(t3) |
| 209 | } |
| 210 | |