chacha20-neon-core.S 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /*
  2. * ChaCha20 256-bit cipher algorithm, RFC7539, ARM NEON functions
  3. *
  4. * Copyright (C) 2016 Linaro, Ltd. <ard.biesheuvel@linaro.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. *
  10. * Based on:
  11. * ChaCha20 256-bit cipher algorithm, RFC7539, x64 SSE3 functions
  12. *
  13. * Copyright (C) 2015 Martin Willi
  14. *
  15. * This program is free software; you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation; either version 2 of the License, or
  18. * (at your option) any later version.
  19. */
  20. /*
  21. * NEON doesn't have a rotate instruction. The alternatives are, more or less:
  22. *
  23. * (a) vshl.u32 + vsri.u32 (needs temporary register)
  24. * (b) vshl.u32 + vshr.u32 + vorr (needs temporary register)
  25. * (c) vrev32.16 (16-bit rotations only)
  26. * (d) vtbl.8 + vtbl.8 (multiple of 8 bits rotations only,
  27. * needs index vector)
  28. *
  29. * ChaCha20 has 16, 12, 8, and 7-bit rotations. For the 12 and 7-bit
  30. * rotations, the only choices are (a) and (b). We use (a) since it takes
  31. * two-thirds the cycles of (b) on both Cortex-A7 and Cortex-A53.
  32. *
  33. * For the 16-bit rotation, we use vrev32.16 since it's consistently fastest
  34. * and doesn't need a temporary register.
  35. *
  36. * For the 8-bit rotation, we use vtbl.8 + vtbl.8. On Cortex-A7, this sequence
  37. * is twice as fast as (a), even when doing (a) on multiple registers
  38. * simultaneously to eliminate the stall between vshl and vsri. Also, it
  39. * parallelizes better when temporary registers are scarce.
  40. *
  41. * A disadvantage is that on Cortex-A53, the vtbl sequence is the same speed as
  42. * (a), so the need to load the rotation table actually makes the vtbl method
  43. * slightly slower overall on that CPU (~1.3% slower ChaCha20). Still, it
  44. * seems to be a good compromise to get a more significant speed boost on some
  45. * CPUs, e.g. ~4.8% faster ChaCha20 on Cortex-A7.
  46. */
  47. #include <linux/linkage.h>
  48. .text
  49. .fpu neon
  50. .align 5
  51. ENTRY(chacha20_block_xor_neon)
  52. // r0: Input state matrix, s
  53. // r1: 1 data block output, o
  54. // r2: 1 data block input, i
  55. //
  56. // This function encrypts one ChaCha20 block by loading the state matrix
  57. // in four NEON registers. It performs matrix operation on four words in
  58. // parallel, but requireds shuffling to rearrange the words after each
  59. // round.
  60. //
  61. // x0..3 = s0..3
  62. add ip, r0, #0x20
  63. vld1.32 {q0-q1}, [r0]
  64. vld1.32 {q2-q3}, [ip]
  65. vmov q8, q0
  66. vmov q9, q1
  67. vmov q10, q2
  68. vmov q11, q3
  69. adr ip, .Lrol8_table
  70. mov r3, #10
  71. vld1.8 {d10}, [ip, :64]
  72. .Ldoubleround:
  73. // x0 += x1, x3 = rotl32(x3 ^ x0, 16)
  74. vadd.i32 q0, q0, q1
  75. veor q3, q3, q0
  76. vrev32.16 q3, q3
  77. // x2 += x3, x1 = rotl32(x1 ^ x2, 12)
  78. vadd.i32 q2, q2, q3
  79. veor q4, q1, q2
  80. vshl.u32 q1, q4, #12
  81. vsri.u32 q1, q4, #20
  82. // x0 += x1, x3 = rotl32(x3 ^ x0, 8)
  83. vadd.i32 q0, q0, q1
  84. veor q3, q3, q0
  85. vtbl.8 d6, {d6}, d10
  86. vtbl.8 d7, {d7}, d10
  87. // x2 += x3, x1 = rotl32(x1 ^ x2, 7)
  88. vadd.i32 q2, q2, q3
  89. veor q4, q1, q2
  90. vshl.u32 q1, q4, #7
  91. vsri.u32 q1, q4, #25
  92. // x1 = shuffle32(x1, MASK(0, 3, 2, 1))
  93. vext.8 q1, q1, q1, #4
  94. // x2 = shuffle32(x2, MASK(1, 0, 3, 2))
  95. vext.8 q2, q2, q2, #8
  96. // x3 = shuffle32(x3, MASK(2, 1, 0, 3))
  97. vext.8 q3, q3, q3, #12
  98. // x0 += x1, x3 = rotl32(x3 ^ x0, 16)
  99. vadd.i32 q0, q0, q1
  100. veor q3, q3, q0
  101. vrev32.16 q3, q3
  102. // x2 += x3, x1 = rotl32(x1 ^ x2, 12)
  103. vadd.i32 q2, q2, q3
  104. veor q4, q1, q2
  105. vshl.u32 q1, q4, #12
  106. vsri.u32 q1, q4, #20
  107. // x0 += x1, x3 = rotl32(x3 ^ x0, 8)
  108. vadd.i32 q0, q0, q1
  109. veor q3, q3, q0
  110. vtbl.8 d6, {d6}, d10
  111. vtbl.8 d7, {d7}, d10
  112. // x2 += x3, x1 = rotl32(x1 ^ x2, 7)
  113. vadd.i32 q2, q2, q3
  114. veor q4, q1, q2
  115. vshl.u32 q1, q4, #7
  116. vsri.u32 q1, q4, #25
  117. // x1 = shuffle32(x1, MASK(2, 1, 0, 3))
  118. vext.8 q1, q1, q1, #12
  119. // x2 = shuffle32(x2, MASK(1, 0, 3, 2))
  120. vext.8 q2, q2, q2, #8
  121. // x3 = shuffle32(x3, MASK(0, 3, 2, 1))
  122. vext.8 q3, q3, q3, #4
  123. subs r3, r3, #1
  124. bne .Ldoubleround
  125. add ip, r2, #0x20
  126. vld1.8 {q4-q5}, [r2]
  127. vld1.8 {q6-q7}, [ip]
  128. // o0 = i0 ^ (x0 + s0)
  129. vadd.i32 q0, q0, q8
  130. veor q0, q0, q4
  131. // o1 = i1 ^ (x1 + s1)
  132. vadd.i32 q1, q1, q9
  133. veor q1, q1, q5
  134. // o2 = i2 ^ (x2 + s2)
  135. vadd.i32 q2, q2, q10
  136. veor q2, q2, q6
  137. // o3 = i3 ^ (x3 + s3)
  138. vadd.i32 q3, q3, q11
  139. veor q3, q3, q7
  140. add ip, r1, #0x20
  141. vst1.8 {q0-q1}, [r1]
  142. vst1.8 {q2-q3}, [ip]
  143. bx lr
  144. ENDPROC(chacha20_block_xor_neon)
  145. .align 4
  146. .Lctrinc: .word 0, 1, 2, 3
  147. .Lrol8_table: .byte 3, 0, 1, 2, 7, 4, 5, 6
  148. .align 5
  149. ENTRY(chacha20_4block_xor_neon)
  150. push {r4-r5}
  151. mov r4, sp // preserve the stack pointer
  152. sub ip, sp, #0x20 // allocate a 32 byte buffer
  153. bic ip, ip, #0x1f // aligned to 32 bytes
  154. mov sp, ip
  155. // r0: Input state matrix, s
  156. // r1: 4 data blocks output, o
  157. // r2: 4 data blocks input, i
  158. //
  159. // This function encrypts four consecutive ChaCha20 blocks by loading
  160. // the state matrix in NEON registers four times. The algorithm performs
  161. // each operation on the corresponding word of each state matrix, hence
  162. // requires no word shuffling. The words are re-interleaved before the
  163. // final addition of the original state and the XORing step.
  164. //
  165. // x0..15[0-3] = s0..15[0-3]
  166. add ip, r0, #0x20
  167. vld1.32 {q0-q1}, [r0]
  168. vld1.32 {q2-q3}, [ip]
  169. adr r5, .Lctrinc
  170. vdup.32 q15, d7[1]
  171. vdup.32 q14, d7[0]
  172. vld1.32 {q4}, [r5, :128]
  173. vdup.32 q13, d6[1]
  174. vdup.32 q12, d6[0]
  175. vdup.32 q11, d5[1]
  176. vdup.32 q10, d5[0]
  177. vadd.u32 q12, q12, q4 // x12 += counter values 0-3
  178. vdup.32 q9, d4[1]
  179. vdup.32 q8, d4[0]
  180. vdup.32 q7, d3[1]
  181. vdup.32 q6, d3[0]
  182. vdup.32 q5, d2[1]
  183. vdup.32 q4, d2[0]
  184. vdup.32 q3, d1[1]
  185. vdup.32 q2, d1[0]
  186. vdup.32 q1, d0[1]
  187. vdup.32 q0, d0[0]
  188. adr ip, .Lrol8_table
  189. mov r3, #10
  190. b 1f
  191. .Ldoubleround4:
  192. vld1.32 {q8-q9}, [sp, :256]
  193. 1:
  194. // x0 += x4, x12 = rotl32(x12 ^ x0, 16)
  195. // x1 += x5, x13 = rotl32(x13 ^ x1, 16)
  196. // x2 += x6, x14 = rotl32(x14 ^ x2, 16)
  197. // x3 += x7, x15 = rotl32(x15 ^ x3, 16)
  198. vadd.i32 q0, q0, q4
  199. vadd.i32 q1, q1, q5
  200. vadd.i32 q2, q2, q6
  201. vadd.i32 q3, q3, q7
  202. veor q12, q12, q0
  203. veor q13, q13, q1
  204. veor q14, q14, q2
  205. veor q15, q15, q3
  206. vrev32.16 q12, q12
  207. vrev32.16 q13, q13
  208. vrev32.16 q14, q14
  209. vrev32.16 q15, q15
  210. // x8 += x12, x4 = rotl32(x4 ^ x8, 12)
  211. // x9 += x13, x5 = rotl32(x5 ^ x9, 12)
  212. // x10 += x14, x6 = rotl32(x6 ^ x10, 12)
  213. // x11 += x15, x7 = rotl32(x7 ^ x11, 12)
  214. vadd.i32 q8, q8, q12
  215. vadd.i32 q9, q9, q13
  216. vadd.i32 q10, q10, q14
  217. vadd.i32 q11, q11, q15
  218. vst1.32 {q8-q9}, [sp, :256]
  219. veor q8, q4, q8
  220. veor q9, q5, q9
  221. vshl.u32 q4, q8, #12
  222. vshl.u32 q5, q9, #12
  223. vsri.u32 q4, q8, #20
  224. vsri.u32 q5, q9, #20
  225. veor q8, q6, q10
  226. veor q9, q7, q11
  227. vshl.u32 q6, q8, #12
  228. vshl.u32 q7, q9, #12
  229. vsri.u32 q6, q8, #20
  230. vsri.u32 q7, q9, #20
  231. // x0 += x4, x12 = rotl32(x12 ^ x0, 8)
  232. // x1 += x5, x13 = rotl32(x13 ^ x1, 8)
  233. // x2 += x6, x14 = rotl32(x14 ^ x2, 8)
  234. // x3 += x7, x15 = rotl32(x15 ^ x3, 8)
  235. vld1.8 {d16}, [ip, :64]
  236. vadd.i32 q0, q0, q4
  237. vadd.i32 q1, q1, q5
  238. vadd.i32 q2, q2, q6
  239. vadd.i32 q3, q3, q7
  240. veor q12, q12, q0
  241. veor q13, q13, q1
  242. veor q14, q14, q2
  243. veor q15, q15, q3
  244. vtbl.8 d24, {d24}, d16
  245. vtbl.8 d25, {d25}, d16
  246. vtbl.8 d26, {d26}, d16
  247. vtbl.8 d27, {d27}, d16
  248. vtbl.8 d28, {d28}, d16
  249. vtbl.8 d29, {d29}, d16
  250. vtbl.8 d30, {d30}, d16
  251. vtbl.8 d31, {d31}, d16
  252. vld1.32 {q8-q9}, [sp, :256]
  253. // x8 += x12, x4 = rotl32(x4 ^ x8, 7)
  254. // x9 += x13, x5 = rotl32(x5 ^ x9, 7)
  255. // x10 += x14, x6 = rotl32(x6 ^ x10, 7)
  256. // x11 += x15, x7 = rotl32(x7 ^ x11, 7)
  257. vadd.i32 q8, q8, q12
  258. vadd.i32 q9, q9, q13
  259. vadd.i32 q10, q10, q14
  260. vadd.i32 q11, q11, q15
  261. vst1.32 {q8-q9}, [sp, :256]
  262. veor q8, q4, q8
  263. veor q9, q5, q9
  264. vshl.u32 q4, q8, #7
  265. vshl.u32 q5, q9, #7
  266. vsri.u32 q4, q8, #25
  267. vsri.u32 q5, q9, #25
  268. veor q8, q6, q10
  269. veor q9, q7, q11
  270. vshl.u32 q6, q8, #7
  271. vshl.u32 q7, q9, #7
  272. vsri.u32 q6, q8, #25
  273. vsri.u32 q7, q9, #25
  274. vld1.32 {q8-q9}, [sp, :256]
  275. // x0 += x5, x15 = rotl32(x15 ^ x0, 16)
  276. // x1 += x6, x12 = rotl32(x12 ^ x1, 16)
  277. // x2 += x7, x13 = rotl32(x13 ^ x2, 16)
  278. // x3 += x4, x14 = rotl32(x14 ^ x3, 16)
  279. vadd.i32 q0, q0, q5
  280. vadd.i32 q1, q1, q6
  281. vadd.i32 q2, q2, q7
  282. vadd.i32 q3, q3, q4
  283. veor q15, q15, q0
  284. veor q12, q12, q1
  285. veor q13, q13, q2
  286. veor q14, q14, q3
  287. vrev32.16 q15, q15
  288. vrev32.16 q12, q12
  289. vrev32.16 q13, q13
  290. vrev32.16 q14, q14
  291. // x10 += x15, x5 = rotl32(x5 ^ x10, 12)
  292. // x11 += x12, x6 = rotl32(x6 ^ x11, 12)
  293. // x8 += x13, x7 = rotl32(x7 ^ x8, 12)
  294. // x9 += x14, x4 = rotl32(x4 ^ x9, 12)
  295. vadd.i32 q10, q10, q15
  296. vadd.i32 q11, q11, q12
  297. vadd.i32 q8, q8, q13
  298. vadd.i32 q9, q9, q14
  299. vst1.32 {q8-q9}, [sp, :256]
  300. veor q8, q7, q8
  301. veor q9, q4, q9
  302. vshl.u32 q7, q8, #12
  303. vshl.u32 q4, q9, #12
  304. vsri.u32 q7, q8, #20
  305. vsri.u32 q4, q9, #20
  306. veor q8, q5, q10
  307. veor q9, q6, q11
  308. vshl.u32 q5, q8, #12
  309. vshl.u32 q6, q9, #12
  310. vsri.u32 q5, q8, #20
  311. vsri.u32 q6, q9, #20
  312. // x0 += x5, x15 = rotl32(x15 ^ x0, 8)
  313. // x1 += x6, x12 = rotl32(x12 ^ x1, 8)
  314. // x2 += x7, x13 = rotl32(x13 ^ x2, 8)
  315. // x3 += x4, x14 = rotl32(x14 ^ x3, 8)
  316. vld1.8 {d16}, [ip, :64]
  317. vadd.i32 q0, q0, q5
  318. vadd.i32 q1, q1, q6
  319. vadd.i32 q2, q2, q7
  320. vadd.i32 q3, q3, q4
  321. veor q15, q15, q0
  322. veor q12, q12, q1
  323. veor q13, q13, q2
  324. veor q14, q14, q3
  325. vtbl.8 d30, {d30}, d16
  326. vtbl.8 d31, {d31}, d16
  327. vtbl.8 d24, {d24}, d16
  328. vtbl.8 d25, {d25}, d16
  329. vtbl.8 d26, {d26}, d16
  330. vtbl.8 d27, {d27}, d16
  331. vtbl.8 d28, {d28}, d16
  332. vtbl.8 d29, {d29}, d16
  333. vld1.32 {q8-q9}, [sp, :256]
  334. // x10 += x15, x5 = rotl32(x5 ^ x10, 7)
  335. // x11 += x12, x6 = rotl32(x6 ^ x11, 7)
  336. // x8 += x13, x7 = rotl32(x7 ^ x8, 7)
  337. // x9 += x14, x4 = rotl32(x4 ^ x9, 7)
  338. vadd.i32 q10, q10, q15
  339. vadd.i32 q11, q11, q12
  340. vadd.i32 q8, q8, q13
  341. vadd.i32 q9, q9, q14
  342. vst1.32 {q8-q9}, [sp, :256]
  343. veor q8, q7, q8
  344. veor q9, q4, q9
  345. vshl.u32 q7, q8, #7
  346. vshl.u32 q4, q9, #7
  347. vsri.u32 q7, q8, #25
  348. vsri.u32 q4, q9, #25
  349. veor q8, q5, q10
  350. veor q9, q6, q11
  351. vshl.u32 q5, q8, #7
  352. vshl.u32 q6, q9, #7
  353. vsri.u32 q5, q8, #25
  354. vsri.u32 q6, q9, #25
  355. subs r3, r3, #1
  356. bne .Ldoubleround4
  357. // x0..7[0-3] are in q0-q7, x10..15[0-3] are in q10-q15.
  358. // x8..9[0-3] are on the stack.
  359. // Re-interleave the words in the first two rows of each block (x0..7).
  360. // Also add the counter values 0-3 to x12[0-3].
  361. vld1.32 {q8}, [r5, :128] // load counter values 0-3
  362. vzip.32 q0, q1 // => (0 1 0 1) (0 1 0 1)
  363. vzip.32 q2, q3 // => (2 3 2 3) (2 3 2 3)
  364. vzip.32 q4, q5 // => (4 5 4 5) (4 5 4 5)
  365. vzip.32 q6, q7 // => (6 7 6 7) (6 7 6 7)
  366. vadd.u32 q12, q8 // x12 += counter values 0-3
  367. vswp d1, d4
  368. vswp d3, d6
  369. vld1.32 {q8-q9}, [r0]! // load s0..7
  370. vswp d9, d12
  371. vswp d11, d14
  372. // Swap q1 and q4 so that we'll free up consecutive registers (q0-q1)
  373. // after XORing the first 32 bytes.
  374. vswp q1, q4
  375. // First two rows of each block are (q0 q1) (q2 q6) (q4 q5) (q3 q7)
  376. // x0..3[0-3] += s0..3[0-3] (add orig state to 1st row of each block)
  377. vadd.u32 q0, q0, q8
  378. vadd.u32 q2, q2, q8
  379. vadd.u32 q4, q4, q8
  380. vadd.u32 q3, q3, q8
  381. // x4..7[0-3] += s4..7[0-3] (add orig state to 2nd row of each block)
  382. vadd.u32 q1, q1, q9
  383. vadd.u32 q6, q6, q9
  384. vadd.u32 q5, q5, q9
  385. vadd.u32 q7, q7, q9
  386. // XOR first 32 bytes using keystream from first two rows of first block
  387. vld1.8 {q8-q9}, [r2]!
  388. veor q8, q8, q0
  389. veor q9, q9, q1
  390. vst1.8 {q8-q9}, [r1]!
  391. // Re-interleave the words in the last two rows of each block (x8..15).
  392. vld1.32 {q8-q9}, [sp, :256]
  393. vzip.32 q12, q13 // => (12 13 12 13) (12 13 12 13)
  394. vzip.32 q14, q15 // => (14 15 14 15) (14 15 14 15)
  395. vzip.32 q8, q9 // => (8 9 8 9) (8 9 8 9)
  396. vzip.32 q10, q11 // => (10 11 10 11) (10 11 10 11)
  397. vld1.32 {q0-q1}, [r0] // load s8..15
  398. vswp d25, d28
  399. vswp d27, d30
  400. vswp d17, d20
  401. vswp d19, d22
  402. // Last two rows of each block are (q8 q12) (q10 q14) (q9 q13) (q11 q15)
  403. // x8..11[0-3] += s8..11[0-3] (add orig state to 3rd row of each block)
  404. vadd.u32 q8, q8, q0
  405. vadd.u32 q10, q10, q0
  406. vadd.u32 q9, q9, q0
  407. vadd.u32 q11, q11, q0
  408. // x12..15[0-3] += s12..15[0-3] (add orig state to 4th row of each block)
  409. vadd.u32 q12, q12, q1
  410. vadd.u32 q14, q14, q1
  411. vadd.u32 q13, q13, q1
  412. vadd.u32 q15, q15, q1
  413. // XOR the rest of the data with the keystream
  414. vld1.8 {q0-q1}, [r2]!
  415. veor q0, q0, q8
  416. veor q1, q1, q12
  417. vst1.8 {q0-q1}, [r1]!
  418. vld1.8 {q0-q1}, [r2]!
  419. veor q0, q0, q2
  420. veor q1, q1, q6
  421. vst1.8 {q0-q1}, [r1]!
  422. vld1.8 {q0-q1}, [r2]!
  423. veor q0, q0, q10
  424. veor q1, q1, q14
  425. vst1.8 {q0-q1}, [r1]!
  426. vld1.8 {q0-q1}, [r2]!
  427. veor q0, q0, q4
  428. veor q1, q1, q5
  429. vst1.8 {q0-q1}, [r1]!
  430. vld1.8 {q0-q1}, [r2]!
  431. veor q0, q0, q9
  432. veor q1, q1, q13
  433. vst1.8 {q0-q1}, [r1]!
  434. vld1.8 {q0-q1}, [r2]!
  435. veor q0, q0, q3
  436. veor q1, q1, q7
  437. vst1.8 {q0-q1}, [r1]!
  438. vld1.8 {q0-q1}, [r2]
  439. mov sp, r4 // restore original stack pointer
  440. veor q0, q0, q11
  441. veor q1, q1, q15
  442. vst1.8 {q0-q1}, [r1]
  443. pop {r4-r5}
  444. bx lr
  445. ENDPROC(chacha20_4block_xor_neon)