|
| 1 | +/* |
| 2 | + * Simplified subset of TweetNaCl cryptographic library |
| 3 | + * For testing constant-time properties with CBMC's dependence graph analysis |
| 4 | + * |
| 5 | + * TweetNaCl claims to have no data-dependent branches or array indices. |
| 6 | + * This test verifies these claims using goto-analyzer --dependence-graph |
| 7 | + * |
| 8 | + * Reference: https://github.com/diffblue/cbmc/issues/366 |
| 9 | + * Based on TweetNaCl: https://tweetnacl.cr.yp.to/ |
| 10 | + * Public domain code |
| 11 | + */ |
| 12 | + |
| 13 | +typedef unsigned char u8; |
| 14 | +typedef unsigned int u32; |
| 15 | +typedef unsigned long long u64; |
| 16 | + |
| 17 | +/* |
| 18 | + * Constant-time selection function (sel25519 from TweetNaCl) |
| 19 | + * Returns: b if c == 1, else returns a |
| 20 | + * Key property: uses bitwise mask instead of if-statement |
| 21 | + * No secret-dependent branches |
| 22 | + */ |
| 23 | +u64 sel25519(u64 a, u64 b, u64 c) |
| 24 | +{ |
| 25 | + u64 mask = ~(c - 1); |
| 26 | + return a ^ ((a ^ b) & mask); |
| 27 | +} |
| 28 | + |
| 29 | +/* |
| 30 | + * Constant-time byte comparison (crypto_verify_16 from TweetNaCl) |
| 31 | + * Returns: 0 if equal, non-zero otherwise |
| 32 | + * Key property: accumulates differences without early exit |
| 33 | + * No secret-dependent branches |
| 34 | + */ |
| 35 | +int crypto_verify_16(const u8 *x, const u8 *y) |
| 36 | +{ |
| 37 | + u32 d = 0; |
| 38 | + int i; |
| 39 | + for(i = 0; i < 16; i++) |
| 40 | + d |= x[i] ^ y[i]; |
| 41 | + return (1 & ((d - 1) >> 8)) - 1; |
| 42 | +} |
| 43 | + |
| 44 | +/* |
| 45 | + * Constant-time conditional swap (from Curve25519) |
| 46 | + * Key property: fixed number of operations regardless of swap bit |
| 47 | + * No secret-dependent branches |
| 48 | + */ |
| 49 | +void cswap(u64 p[32], u64 q[32], u8 b) |
| 50 | +{ |
| 51 | + int i; |
| 52 | + u64 mask = ~((u64)b - 1); |
| 53 | + for(i = 0; i < 32; i++) |
| 54 | + { |
| 55 | + u64 t = (p[i] ^ q[i]) & mask; |
| 56 | + p[i] ^= t; |
| 57 | + q[i] ^= t; |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +/* |
| 62 | + * Montgomery ladder iteration (core of Curve25519 scalar multiplication) |
| 63 | + * Key property: processes all 255 bits regardless of secret key value |
| 64 | + * No secret-dependent branches or array indices |
| 65 | + */ |
| 66 | +void montgomery_ladder_step(u64 x[32], u64 z[32], const u8 *n) |
| 67 | +{ |
| 68 | + int i, j; |
| 69 | + u8 bit; |
| 70 | + |
| 71 | + // Process bit 10 through bit 0 (simplified from 254 to 0) |
| 72 | + for(i = 10; i >= 0; i--) |
| 73 | + { |
| 74 | + // Extract bit without data-dependent branch |
| 75 | + bit = (n[i >> 3] >> (i & 7)) & 1; |
| 76 | + |
| 77 | + // Constant-time swap |
| 78 | + cswap(x, z, bit); |
| 79 | + |
| 80 | + // Point doubling and addition (simplified) |
| 81 | + for(j = 0; j < 32; j++) |
| 82 | + { |
| 83 | + u64 a = x[j] + z[j]; |
| 84 | + u64 b = x[j] - z[j]; |
| 85 | + x[j] = a * a; |
| 86 | + z[j] = b * b; |
| 87 | + } |
| 88 | + |
| 89 | + // Swap back |
| 90 | + cswap(x, z, bit); |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +/* |
| 95 | + * Salsa20 quarter-round (from TweetNaCl's Salsa20 implementation) |
| 96 | + * Key property: all operations are data-independent |
| 97 | + * No secret-dependent array indices or branches |
| 98 | + */ |
| 99 | +void salsa20_quarterround(u32 y[16], int a, int b, int c, int d) |
| 100 | +{ |
| 101 | + // All array indices are constants - no secret-dependent indexing |
| 102 | + y[b] ^= ((y[a] + y[d]) << 7) | ((y[a] + y[d]) >> 25); |
| 103 | + y[c] ^= ((y[b] + y[a]) << 9) | ((y[b] + y[a]) >> 23); |
| 104 | + y[d] ^= ((y[c] + y[b]) << 13) | ((y[c] + y[b]) >> 19); |
| 105 | + y[a] ^= ((y[d] + y[c]) << 18) | ((y[d] + y[c]) >> 14); |
| 106 | +} |
| 107 | + |
| 108 | +/* |
| 109 | + * Simplified Salsa20 core (fixed 20 rounds) |
| 110 | + * Key property: fixed iteration count, no early exits |
| 111 | + * No secret-dependent branches or array indices |
| 112 | + */ |
| 113 | +void salsa20_core(u32 out[16], const u32 in[16]) |
| 114 | +{ |
| 115 | + u32 x[16]; |
| 116 | + int i; |
| 117 | + |
| 118 | + // Copy input to working state |
| 119 | + for(i = 0; i < 16; i++) |
| 120 | + x[i] = in[i]; |
| 121 | + |
| 122 | + // 20 rounds (10 double-rounds) - fixed count |
| 123 | + for(i = 0; i < 10; i++) |
| 124 | + { |
| 125 | + // Column rounds - all indices are constant |
| 126 | + salsa20_quarterround(x, 0, 4, 8, 12); |
| 127 | + salsa20_quarterround(x, 5, 9, 13, 1); |
| 128 | + salsa20_quarterround(x, 10, 14, 2, 6); |
| 129 | + salsa20_quarterround(x, 15, 3, 7, 11); |
| 130 | + |
| 131 | + // Row rounds - all indices are constant |
| 132 | + salsa20_quarterround(x, 0, 1, 2, 3); |
| 133 | + salsa20_quarterround(x, 5, 6, 7, 4); |
| 134 | + salsa20_quarterround(x, 10, 11, 8, 9); |
| 135 | + salsa20_quarterround(x, 15, 12, 13, 14); |
| 136 | + } |
| 137 | + |
| 138 | + // Add back input |
| 139 | + for(i = 0; i < 16; i++) |
| 140 | + out[i] = x[i] + in[i]; |
| 141 | +} |
| 142 | + |
| 143 | +/* |
| 144 | + * Test entry point |
| 145 | + */ |
| 146 | +int main(void) |
| 147 | +{ |
| 148 | + // Test inputs (would be symbolic in actual verification) |
| 149 | + u8 secret_key[32]; |
| 150 | + u8 msg1[16], msg2[16]; |
| 151 | + u64 x[32], z[32]; |
| 152 | + u32 salsa_in[16], salsa_out[16]; |
| 153 | + |
| 154 | + int i; |
| 155 | + |
| 156 | + // Initialize test data |
| 157 | + for(i = 0; i < 32; i++) |
| 158 | + { |
| 159 | + secret_key[i] = i; |
| 160 | + x[i] = i; |
| 161 | + z[i] = i + 32; |
| 162 | + } |
| 163 | + |
| 164 | + for(i = 0; i < 16; i++) |
| 165 | + { |
| 166 | + msg1[i] = i; |
| 167 | + msg2[i] = i + 1; |
| 168 | + salsa_in[i] = i * 100; |
| 169 | + } |
| 170 | + |
| 171 | + // Test constant-time selection |
| 172 | + u64 sel_result = sel25519(42, 99, secret_key[0] & 1); |
| 173 | + |
| 174 | + // Test constant-time comparison |
| 175 | + int verify_result = crypto_verify_16(msg1, msg2); |
| 176 | + |
| 177 | + // Test constant-time swap |
| 178 | + cswap(x, z, secret_key[1] & 1); |
| 179 | + |
| 180 | + // Test Montgomery ladder |
| 181 | + montgomery_ladder_step(x, z, secret_key); |
| 182 | + |
| 183 | + // Test Salsa20 |
| 184 | + salsa20_core(salsa_out, salsa_in); |
| 185 | + |
| 186 | + // Use results to prevent optimization |
| 187 | + return (sel_result + verify_result + x[0] + salsa_out[0]) & 1; |
| 188 | +} |
0 commit comments