text:
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build go1.7,amd64,!gccgo,!appengine

#include "textflag.h"

DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b
DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1
GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32

DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1
DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b
DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179
GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32

DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403
DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403
DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b
GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32

DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302
DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32

DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16

DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16

DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1
DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16

DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16

DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403
DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16

DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16

#define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39
#define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93
#define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e
#define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93
#define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39

#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \
	VPADDQ  m0, Y0, Y0;   \
	VPADDQ  Y1, Y0, Y0;   \
	VPXOR   Y0, Y3, Y3;   \
	VPSHUFD $-79, Y3, Y3; \
	VPADDQ  Y3, Y2, Y2;   \
	VPXOR   Y2, Y1, Y1;   \
	VPSHUFB c40, Y1, Y1;  \
	VPADDQ  m1, Y0, Y0;   \
	VPADDQ  Y1, Y0, Y0;   \
	VPXOR   Y0, Y3, Y3;   \
	VPSHUFB c48, Y3, Y3;  \
	VPADDQ  Y3, Y2, Y2;   \
	VPXOR   Y2, Y1, Y1;   \
	VPADDQ  Y1, Y1, t;    \
	VPSRLQ  $63, Y1, Y1;  \
	VPXOR   t, Y1, Y1;    \
	VPERMQ_0x39_Y1_Y1;    \
	VPERMQ_0x4E_Y2_Y2;    \
	VPERMQ_0x93_Y3_Y3;    \
	VPADDQ  m2, Y0, Y0;   \
	VPADDQ  Y1, Y0, Y0;   \
	VPXOR   Y0, Y3, Y3;   \
	VPSHUFD $-79, Y3, Y3; \
	VPADDQ  Y3, Y2, Y2;   \
	VPXOR   Y2, Y1, Y1;   \
	VPSHUFB c40, Y1, Y1;  \
	VPADDQ  m3, Y0, Y0;   \
	VPADDQ  Y1, Y0, Y0;   \
	VPXOR   Y0, Y3, Y3;   \
	VPSHUFB c48, Y3, Y3;  \
	VPADDQ  Y3, Y2, Y2;   \
	VPXOR   Y2, Y1, Y1;   \
	VPADDQ  Y1, Y1, t;    \
	VPSRLQ  $63, Y1, Y1;  \
	VPXOR   t, Y1, Y1;    \
	VPERMQ_0x39_Y3_Y3;    \
	VPERMQ_0x4E_Y2_Y2;    \
	VPERMQ_0x93_Y1_Y1

#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E
#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26
#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E
#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36
#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E

#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n
#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n
#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n
#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n
#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n

#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01
#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01
#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01
#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01
#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01

#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01

#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8
#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01

// load msg: Y12 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \
	VMOVQ_SI_X12(i0*8);           \
	VMOVQ_SI_X11(i2*8);           \
	VPINSRQ_1_SI_X12(i1*8);       \
	VPINSRQ_1_SI_X11(i3*8);       \
	VINSERTI128 $1, X11, Y12, Y12

// load msg: Y13 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \
	VMOVQ_SI_X13(i0*8);           \
	VMOVQ_SI_X11(i2*8);           \
	VPINSRQ_1_SI_X13(i1*8);       \
	VPINSRQ_1_SI_X11(i3*8);       \
	VINSERTI128 $1, X11, Y13, Y13

// load msg: Y14 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \
	VMOVQ_SI_X14(i0*8);           \
	VMOVQ_SI_X11(i2*8);           \
	VPINSRQ_1_SI_X14(i1*8);       \
	VPINSRQ_1_SI_X11(i3*8);       \
	VINSERTI128 $1, X11, Y14, Y14

// load msg: Y15 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \
	VMOVQ_SI_X15(i0*8);           \
	VMOVQ_SI_X11(i2*8);           \
	VPINSRQ_1_SI_X15(i1*8);       \
	VPINSRQ_1_SI_X11(i3*8);       \
	VINSERTI128 $1, X11, Y15, Y15

#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \
	VMOVQ_SI_X12_0;                   \
	VMOVQ_SI_X11(4*8);                \
	VPINSRQ_1_SI_X12(2*8);            \
	VPINSRQ_1_SI_X11(6*8);            \
	VINSERTI128 $1, X11, Y12, Y12;    \
	LOAD_MSG_AVX2_Y13(1, 3, 5, 7);    \
	LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \
	LOAD_MSG_AVX2_Y15(9, 11, 13, 15)

#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \
	LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \
	LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \
	VMOVQ_SI_X11(11*8);              \
	VPSHUFD     $0x4E, 0*8(SI), X14; \
	VPINSRQ_1_SI_X11(5*8);           \
	VINSERTI128 $1, X11, Y14, Y14;   \
	LOAD_MSG_AVX2_Y15(12, 2, 7, 3)

#define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \
	VMOVQ_SI_X11(5*8);              \
	VMOVDQU     11*8(SI), X12;      \
	VPINSRQ_1_SI_X11(15*8);         \
	VINSERTI128 $1, X11, Y12, Y12;  \
	VMOVQ_SI_X13(8*8);              \
	VMOVQ_SI_X11(2*8);              \
	VPINSRQ_1_SI_X13_0;             \
	VPINSRQ_1_SI_X11(13*8);         \
	VINSERTI128 $1, X11, Y13, Y13;  \
	LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \
	LOAD_MSG_AVX2_Y15(14, 6, 1, 4)

#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \
	LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \
	LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \
	LOAD_MSG_AVX2_Y14(2, 5, 4, 15);  \
	VMOVQ_SI_X15(6*8);               \
	VMOVQ_SI_X11_0;                  \
	VPINSRQ_1_SI_X15(10*8);          \
	VPINSRQ_1_SI_X11(8*8);           \
	VINSERTI128 $1, X11, Y15, Y15

#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \
	LOAD_MSG_AVX2_Y12(9, 5, 2, 10);  \
	VMOVQ_SI_X13_0;                  \
	VMOVQ_SI_X11(4*8);               \
	VPINSRQ_1_SI_X13(7*8);           \
	VPINSRQ_1_SI_X11(15*8);          \
	VINSERTI128 $1, X11, Y13, Y13;   \
	LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \
	LOAD_MSG_AVX2_Y15(1, 12, 8, 13)

#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \
	VMOVQ_SI_X12(2*8);                \
	VMOVQ_SI_X11_0;                   \
	VPINSRQ_1_SI_X12(6*8);            \
	VPINSRQ_1_SI_X11(8*8);            \
	VINSERTI128 $1, X11, Y12, Y12;    \
	LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \
	LOAD_MSG_AVX2_Y14(4, 7, 15, 1);   \
	LOAD_MSG_AVX2_Y15(13, 5, 14, 9)

#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \
	LOAD_MSG_AVX2_Y12(12, 1, 14, 4);  \
	LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \
	VMOVQ_SI_X14_0;                   \
	VPSHUFD     $0x4E, 8*8(SI), X11;  \
	VPINSRQ_1_SI_X14(6*8);            \
	VINSERTI128 $1, X11, Y14, Y14;    \
	LOAD_MSG_AVX2_Y15(7, 3, 2, 11)

#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \
	LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \
	LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \
	LOAD_MSG_AVX2_Y14(5, 15, 8, 2);  \
	VMOVQ_SI_X15_0;                  \
	VMOVQ_SI_X11(6*8);               \
	VPINSRQ_1_SI_X15(4*8);           \
	VPINSRQ_1_SI_X11(10*8);          \
	VINSERTI128 $1, X11, Y15, Y15

#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \
	VMOVQ_SI_X12(6*8);              \
	VMOVQ_SI_X11(11*8);             \
	VPINSRQ_1_SI_X12(14*8);         \
	VPINSRQ_1_SI_X11_0;             \
	VINSERTI128 $1, X11, Y12, Y12;  \
	LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \
	VMOVQ_SI_X11(1*8);              \
	VMOVDQU     12*8(SI), X14;      \
	VPINSRQ_1_SI_X11(10*8);         \
	VINSERTI128 $1, X11, Y14, Y14;  \
	VMOVQ_SI_X15(2*8);              \
	VMOVDQU     4*8(SI), X11;       \
	VPINSRQ_1_SI_X15(7*8);          \
	VINSERTI128 $1, X11, Y15, Y15

#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \
	LOAD_MSG_AVX2_Y12(10, 8, 7, 1);  \
	VMOVQ_SI_X13(2*8);               \
	VPSHUFD     $0x4E, 5*8(SI), X11; \
	VPINSRQ_1_SI_X13(4*8);           \
	VINSERTI128 $1, X11, Y13, Y13;   \
	LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \
	VMOVQ_SI_X15(11*8);              \
	VMOVQ_SI_X11(12*8);              \
	VPINSRQ_1_SI_X15(14*8);          \
	VPINSRQ_1_SI_X11_0;              \
	VINSERTI128 $1, X11, Y15, Y15

// func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
TEXT ·hashBlocksAVX2(SB), 4, $320-48 // frame size = 288 + 32 byte alignment
	MOVQ h+0(FP), AX
	MOVQ c+8(FP), BX
	MOVQ flag+16(FP), CX
	MOVQ blocks_base+24(FP), SI
	MOVQ blocks_len+32(FP), DI

	MOVQ SP, DX
	MOVQ SP, R9
	ADDQ $31, R9
	ANDQ $~31, R9
	MOVQ R9, SP

	MOVQ CX, 16(SP)
	XORQ CX, CX
	MOVQ CX, 24(SP)

	VMOVDQU ·AVX2_c40<>(SB), Y4
	VMOVDQU ·AVX2_c48<>(SB), Y5

	VMOVDQU 0(AX), Y8
	VMOVDQU 32(AX), Y9
	VMOVDQU ·AVX2_iv0<>(SB), Y6
	VMOVDQU ·AVX2_iv1<>(SB), Y7

	MOVQ 0(BX), R8
	MOVQ 8(BX), R9
	MOVQ R9, 8(SP)

loop:
	ADDQ $128, R8
	MOVQ R8, 0(SP)
	CMPQ R8, $128
	JGE  noinc
	INCQ R9
	MOVQ R9, 8(SP)

noinc:
	VMOVDQA Y8, Y0
	VMOVDQA Y9, Y1
	VMOVDQA Y6, Y2
	VPXOR   0(SP), Y7, Y3

	LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15()
	VMOVDQA Y12, 32(SP)
	VMOVDQA Y13, 64(SP)
	VMOVDQA Y14, 96(SP)
	VMOVDQA Y15, 128(SP)
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3()
	VMOVDQA Y12, 160(SP)
	VMOVDQA Y13, 192(SP)
	VMOVDQA Y14, 224(SP)
	VMOVDQA Y15, 256(SP)

	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
	LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0()
	ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)

	ROUND_AVX2(32(SP), 64(SP), 96(SP), 128(SP), Y10, Y4, Y5)
	ROUND_AVX2(160(SP), 192(SP), 224(SP), 256(SP), Y10, Y4, Y5)

	VPXOR Y0, Y8, Y8
	VPXOR Y1, Y9, Y9
	VPXOR Y2, Y8, Y8
	VPXOR Y3, Y9, Y9

	LEAQ 128(SI), SI
	SUBQ $128, DI
	JNE  loop

	MOVQ R8, 0(BX)
	MOVQ R9, 8(BX)

	VMOVDQU Y8, 0(AX)
	VMOVDQU Y9, 32(AX)
	VZEROUPPER

	MOVQ DX, SP
	RET

#define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA
#define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB
#define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF
#define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD
#define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE

#define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7
#define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF
#define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7
#define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF
#define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7
#define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7
#define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF
#define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF

#define SHUFFLE_AVX() \
	VMOVDQA X6, X13;         \
	VMOVDQA X2, X14;         \
	VMOVDQA X4, X6;          \
	VPUNPCKLQDQ_X13_X13_X15; \
	VMOVDQA X5, X4;          \
	VMOVDQA X6, X5;          \
	VPUNPCKHQDQ_X15_X7_X6;   \
	VPUNPCKLQDQ_X7_X7_X15;   \
	VPUNPCKHQDQ_X15_X13_X7;  \
	VPUNPCKLQDQ_X3_X3_X15;   \
	VPUNPCKHQDQ_X15_X2_X2;   \
	VPUNPCKLQDQ_X14_X14_X15; \
	VPUNPCKHQDQ_X15_X3_X3;   \

#define SHUFFLE_AVX_INV() \
	VMOVDQA X2, X13;         \
	VMOVDQA X4, X14;         \
	VPUNPCKLQDQ_X2_X2_X15;   \
	VMOVDQA X5, X4;          \
	VPUNPCKHQDQ_X15_X3_X2;   \
	VMOVDQA X14, X5;         \
	VPUNPCKLQDQ_X3_X3_X15;   \
	VMOVDQA X6, X14;         \
	VPUNPCKHQDQ_X15_X13_X3;  \
	VPUNPCKLQDQ_X7_X7_X15;   \
	VPUNPCKHQDQ_X15_X6_X6;   \
	VPUNPCKLQDQ_X14_X14_X15; \
	VPUNPCKHQDQ_X15_X7_X7;   \

#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
	VPADDQ  m0, v0, v0;   \
	VPADDQ  v2, v0, v0;   \
	VPADDQ  m1, v1, v1;   \
	VPADDQ  v3, v1, v1;   \
	VPXOR   v0, v6, v6;   \
	VPXOR   v1, v7, v7;   \
	VPSHUFD $-79, v6, v6; \
	VPSHUFD $-79, v7, v7; \
	VPADDQ  v6, v4, v4;   \
	VPADDQ  v7, v5, v5;   \
	VPXOR   v4, v2, v2;   \
	VPXOR   v5, v3, v3;   \
	VPSHUFB c40, v2, v2;  \
	VPSHUFB c40, v3, v3;  \
	VPADDQ  m2, v0, v0;   \
	VPADDQ  v2, v0, v0;   \
	VPADDQ  m3, v1, v1;   \
	VPADDQ  v3, v1, v1;   \
	VPXOR   v0, v6, v6;   \
	VPXOR   v1, v7, v7;   \
	VPSHUFB c48, v6, v6;  \
	VPSHUFB c48, v7, v7;  \
	VPADDQ  v6, v4, v4;   \
	VPADDQ  v7, v5, v5;   \
	VPXOR   v4, v2, v2;   \
	VPXOR   v5, v3, v3;   \
	VPADDQ  v2, v2, t0;   \
	VPSRLQ  $63, v2, v2;  \
	VPXOR   t0, v2, v2;   \
	VPADDQ  v3, v3, t0;   \
	VPSRLQ  $63, v3, v3;  \
	VPXOR   t0, v3, v3

// load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7)
// i0, i1, i2, i3, i4, i5, i6, i7 must not be 0
#define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \
	VMOVQ_SI_X12(i0*8);     \
	VMOVQ_SI_X13(i2*8);     \
	VMOVQ_SI_X14(i4*8);     \
	VMOVQ_SI_X15(i6*8);     \
	VPINSRQ_1_SI_X12(i1*8); \
	VPINSRQ_1_SI_X13(i3*8); \
	VPINSRQ_1_SI_X14(i5*8); \
	VPINSRQ_1_SI_X15(i7*8)

// load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7)
#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \
	VMOVQ_SI_X12_0;        \
	VMOVQ_SI_X13(4*8);     \
	VMOVQ_SI_X14(1*8);     \
	VMOVQ_SI_X15(5*8);     \
	VPINSRQ_1_SI_X12(2*8); \
	VPINSRQ_1_SI_X13(6*8); \
	VPINSRQ_1_SI_X14(3*8); \
	VPINSRQ_1_SI_X15(7*8)

// load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3)
#define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \
	VPSHUFD $0x4E, 0*8(SI), X12; \
	VMOVQ_SI_X13(11*8);          \
	VMOVQ_SI_X14(12*8);          \
	VMOVQ_SI_X15(7*8);           \
	VPINSRQ_1_SI_X13(5*8);       \
	VPINSRQ_1_SI_X14(2*8);       \
	VPINSRQ_1_SI_X15(3*8)

// load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13)
#define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \
	VMOVDQU 11*8(SI), X12;  \
	VMOVQ_SI_X13(5*8);      \
	VMOVQ_SI_X14(8*8);      \
	VMOVQ_SI_X15(2*8);      \
	VPINSRQ_1_SI_X13(15*8); \
	VPINSRQ_1_SI_X14_0;     \
	VPINSRQ_1_SI_X15(13*8)

// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8)
#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \
	VMOVQ_SI_X12(2*8);      \
	VMOVQ_SI_X13(4*8);      \
	VMOVQ_SI_X14(6*8);      \
	VMOVQ_SI_X15_0;         \
	VPINSRQ_1_SI_X12(5*8);  \
	VPINSRQ_1_SI_X13(15*8); \
	VPINSRQ_1_SI_X14(10*8); \
	VPINSRQ_1_SI_X15(8*8)

// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15)
#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \
	VMOVQ_SI_X12(9*8);      \
	VMOVQ_SI_X13(2*8);      \
	VMOVQ_SI_X14_0;         \
	VMOVQ_SI_X15(4*8);      \
	VPINSRQ_1_SI_X12(5*8);  \
	VPINSRQ_1_SI_X13(10*8); \
	VPINSRQ_1_SI_X14(7*8);  \
	VPINSRQ_1_SI_X15(15*8)

// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3)
#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \
	VMOVQ_SI_X12(2*8);      \
	VMOVQ_SI_X13_0;         \
	VMOVQ_SI_X14(12*8);     \
	VMOVQ_SI_X15(11*8);     \
	VPINSRQ_1_SI_X12(6*8);  \
	VPINSRQ_1_SI_X13(8*8);  \
	VPINSRQ_1_SI_X14(10*8); \
	VPINSRQ_1_SI_X15(3*8)

// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11)
#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \
	MOVQ    0*8(SI), X12;        \
	VPSHUFD $0x4E, 8*8(SI), X13; \
	MOVQ    7*8(SI), X14;        \
	MOVQ    2*8(SI), X15;        \
	VPINSRQ_1_SI_X12(6*8);       \
	VPINSRQ_1_SI_X14(3*8);       \
	VPINSRQ_1_SI_X15(11*8)

// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8)
#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \
	MOVQ 6*8(SI), X12;      \
	MOVQ 11*8(SI), X13;     \
	MOVQ 15*8(SI), X14;     \
	MOVQ 3*8(SI), X15;      \
	VPINSRQ_1_SI_X12(14*8); \
	VPINSRQ_1_SI_X13_0;     \
	VPINSRQ_1_SI_X14(9*8);  \
	VPINSRQ_1_SI_X15(8*8)

// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10)
#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \
	MOVQ 5*8(SI), X12;      \
	MOVQ 8*8(SI), X13;      \
	MOVQ 0*8(SI), X14;      \
	MOVQ 6*8(SI), X15;      \
	VPINSRQ_1_SI_X12(15*8); \
	VPINSRQ_1_SI_X13(2*8);  \
	VPINSRQ_1_SI_X14(4*8);  \
	VPINSRQ_1_SI_X15(10*8)

// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5)
#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \
	VMOVDQU 12*8(SI), X12;  \
	MOVQ    1*8(SI), X13;   \
	MOVQ    2*8(SI), X14;   \
	VPINSRQ_1_SI_X13(10*8); \
	VPINSRQ_1_SI_X14(7*8);  \
	VMOVDQU 4*8(SI), X15

// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0)
#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \
	MOVQ 15*8(SI), X12;     \
	MOVQ 3*8(SI), X13;      \
	MOVQ 11*8(SI), X14;     \
	MOVQ 12*8(SI), X15;     \
	VPINSRQ_1_SI_X12(9*8);  \
	VPINSRQ_1_SI_X13(13*8); \
	VPINSRQ_1_SI_X14(14*8); \
	VPINSRQ_1_SI_X15_0

// func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
	MOVQ h+0(FP), AX
	MOVQ c+8(FP), BX
	MOVQ flag+16(FP), CX
	MOVQ blocks_base+24(FP), SI
	MOVQ blocks_len+32(FP), DI

	MOVQ SP, BP
	MOVQ SP, R9
	ADDQ $15, R9
	ANDQ $~15, R9
	MOVQ R9, SP

	VMOVDQU ·AVX_c40<>(SB), X0
	VMOVDQU ·AVX_c48<>(SB), X1
	VMOVDQA X0, X8
	VMOVDQA X1, X9

	VMOVDQU ·AVX_iv3<>(SB), X0
	VMOVDQA X0, 0(SP)
	XORQ    CX, 0(SP)          // 0(SP) = ·AVX_iv3 ^ (CX || 0)

	VMOVDQU 0(AX), X10
	VMOVDQU 16(AX), X11
	VMOVDQU 32(AX), X2
	VMOVDQU 48(AX), X3

	MOVQ 0(BX), R8
	MOVQ 8(BX), R9

loop:
	ADDQ $128, R8
	CMPQ R8, $128
	JGE  noinc
	INCQ R9

noinc:
	VMOVQ_R8_X15
	VPINSRQ_1_R9_X15

	VMOVDQA X10, X0
	VMOVDQA X11, X1
	VMOVDQU ·AVX_iv0<>(SB), X4
	VMOVDQU ·AVX_iv1<>(SB), X5
	VMOVDQU ·AVX_iv2<>(SB), X6

	VPXOR   X15, X6, X6
	VMOVDQA 0(SP), X7

	LOAD_MSG_AVX_0_2_4_6_1_3_5_7()
	VMOVDQA X12, 16(SP)
	VMOVDQA X13, 32(SP)
	VMOVDQA X14, 48(SP)
	VMOVDQA X15, 64(SP)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15)
	VMOVDQA X12, 80(SP)
	VMOVDQA X13, 96(SP)
	VMOVDQA X14, 112(SP)
	VMOVDQA X15, 128(SP)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6)
	VMOVDQA X12, 144(SP)
	VMOVDQA X13, 160(SP)
	VMOVDQA X14, 176(SP)
	VMOVDQA X15, 192(SP)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX_1_0_11_5_12_2_7_3()
	VMOVDQA X12, 208(SP)
	VMOVDQA X13, 224(SP)
	VMOVDQA X14, 240(SP)
	VMOVDQA X15, 256(SP)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX_11_12_5_15_8_0_2_13()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX_2_5_4_15_6_10_0_8()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX_9_5_2_10_0_7_4_15()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX_2_6_0_8_12_10_11_3()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX_0_6_9_8_7_3_2_11()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX_5_15_8_2_0_4_6_10()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX_6_14_11_0_15_9_3_8()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX_12_13_1_10_2_7_4_5()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5)
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX()
	LOAD_MSG_AVX_15_9_3_13_11_14_12_0()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
	SHUFFLE_AVX_INV()

	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X15, X8, X9)
	SHUFFLE_AVX()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(SP), 96(SP), 112(SP), 128(SP), X15, X8, X9)
	SHUFFLE_AVX_INV()

	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(SP), 160(SP), 176(SP), 192(SP), X15, X8, X9)
	SHUFFLE_AVX()
	HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(SP), 224(SP), 240(SP), 256(SP), X15, X8, X9)
	SHUFFLE_AVX_INV()

	VMOVDQU 32(AX), X14
	VMOVDQU 48(AX), X15
	VPXOR   X0, X10, X10
	VPXOR   X1, X11, X11
	VPXOR   X2, X14, X14
	VPXOR   X3, X15, X15
	VPXOR   X4, X10, X10
	VPXOR   X5, X11, X11
	VPXOR   X6, X14, X2
	VPXOR   X7, X15, X3
	VMOVDQU X2, 32(AX)
	VMOVDQU X3, 48(AX)

	LEAQ 128(SI), SI
	SUBQ $128, DI
	JNE  loop

	VMOVDQU X10, 0(AX)
	VMOVDQU X11, 16(AX)

	MOVQ R8, 0(BX)
	MOVQ R9, 8(BX)
	VZEROUPPER

	MOVQ BP, SP
	RET

// func supportsAVX2() bool
TEXT ·supportsAVX2(SB), 4, $0-1
	MOVQ runtime·support_avx2(SB), AX
	MOVB AX, ret+0(FP)
	RET

// func supportsAVX() bool
TEXT ·supportsAVX(SB), 4, $0-1
	MOVQ runtime·support_avx(SB), AX
	MOVB AX, ret+0(FP)
	RET

repo_name: dan-v/dosxvpn

repo_language: Go

mime_type: text/x-shellscript


text:
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package otr

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"math/big"
	"os"
	"os/exec"
	"testing"
)

var isQueryTests = []struct {
	msg             string
	expectedVersion int
}{
	{"foo", 0},
	{"?OtR", 0},
	{"?OtR?", 0},
	{"?OTR?", 0},
	{"?OTRv?", 0},
	{"?OTRv1?", 0},
	{"?OTR?v1?", 0},
	{"?OTR?v?", 0},
	{"?OTR?v2?", 2},
	{"?OTRv2?", 2},
	{"?OTRv23?", 2},
	{"?OTRv23 ?", 0},
}

func TestIsQuery(t *testing.T) {
	for i, test := range isQueryTests {
		version := isQuery([]byte(test.msg))
		if version != test.expectedVersion {
			t.Errorf("#%d: got %d, want %d", i, version, test.expectedVersion)
		}
	}
}

var alicePrivateKeyHex = "000000000080c81c2cb2eb729b7e6fd48e975a932c638b3a9055478583afa46755683e30102447f6da2d8bec9f386bbb5da6403b0040fee8650b6ab2d7f32c55ab017ae9b6aec8c324ab5844784e9a80e194830d548fb7f09a0410df2c4d5c8bc2b3e9ad484e65412be689cf0834694e0839fb2954021521ffdffb8f5c32c14dbf2020b3ce7500000014da4591d58def96de61aea7b04a8405fe1609308d000000808ddd5cb0b9d66956e3dea5a915d9aba9d8a6e7053b74dadb2fc52f9fe4e5bcc487d2305485ed95fed026ad93f06ebb8c9e8baf693b7887132c7ffdd3b0f72f4002ff4ed56583ca7c54458f8c068ca3e8a4dfa309d1dd5d34e2a4b68e6f4338835e5e0fb4317c9e4c7e4806dafda3ef459cd563775a586dd91b1319f72621bf3f00000080b8147e74d8c45e6318c37731b8b33b984a795b3653c2cd1d65cc99efe097cb7eb2fa49569bab5aab6e8a1c261a27d0f7840a5e80b317e6683042b59b6dceca2879c6ffc877a465be690c15e4a42f9a7588e79b10faac11b1ce3741fcef7aba8ce05327a2c16d279ee1b3d77eb783fb10e3356caa25635331e26dd42b8396c4d00000001420bec691fea37ecea58a5c717142f0b804452f57"

var aliceFingerprintHex = "0bb01c360424522e94ee9c346ce877a1a4288b2f"

var bobPrivateKeyHex = "000000000080a5138eb3d3eb9c1d85716faecadb718f87d31aaed1157671d7fee7e488f95e8e0ba60ad449ec732710a7dec5190f7182af2e2f98312d98497221dff160fd68033dd4f3a33b7c078d0d9f66e26847e76ca7447d4bab35486045090572863d9e4454777f24d6706f63e02548dfec2d0a620af37bbc1d24f884708a212c343b480d00000014e9c58f0ea21a5e4dfd9f44b6a9f7f6a9961a8fa9000000803c4d111aebd62d3c50c2889d420a32cdf1e98b70affcc1fcf44d59cca2eb019f6b774ef88153fb9b9615441a5fe25ea2d11b74ce922ca0232bd81b3c0fcac2a95b20cb6e6c0c5c1ace2e26f65dc43c751af0edbb10d669890e8ab6beea91410b8b2187af1a8347627a06ecea7e0f772c28aae9461301e83884860c9b656c722f0000008065af8625a555ea0e008cd04743671a3cda21162e83af045725db2eb2bb52712708dc0cc1a84c08b3649b88a966974bde27d8612c2861792ec9f08786a246fcadd6d8d3a81a32287745f309238f47618c2bd7612cb8b02d940571e0f30b96420bcd462ff542901b46109b1e5ad6423744448d20a57818a8cbb1647d0fea3b664e0000001440f9f2eb554cb00d45a5826b54bfa419b6980e48"

func TestKeySerialization(t *testing.T) {
	var priv PrivateKey
	alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex)
	rest, ok := priv.Parse(alicePrivateKey)
	if !ok {
		t.Error("failed to parse private key")
	}
	if len(rest) > 0 {
		t.Error("data remaining after parsing private key")
	}

	out := priv.Serialize(nil)
	if !bytes.Equal(alicePrivateKey, out) {
		t.Errorf("serialization (%x) is not equal to original (%x)", out, alicePrivateKey)
	}

	aliceFingerprint, _ := hex.DecodeString(aliceFingerprintHex)
	fingerprint := priv.PublicKey.Fingerprint()
	if !bytes.Equal(aliceFingerprint, fingerprint) {
		t.Errorf("fingerprint (%x) is not equal to expected value (%x)", fingerprint, aliceFingerprint)
	}
}

const libOTRPrivateKey = `(privkeys
 (account
(name "foo@example.com")
(protocol prpl-jabber)
(private-key
 (dsa
  (p #00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB8C031D3561FECEE72EBB4A090D450A9B7A857#)
  (q #00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#)
  (g #535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57597766A2F9CE3857D7ACE3E1E3BC1FC6F26#)
  (y #0AC8670AD767D7A8D9D14CC1AC6744CD7D76F993B77FFD9E39DF01E5A6536EF65E775FCEF2A983E2A19BD6415500F6979715D9FD1257E1FE2B6F5E1E74B333079E7C880D39868462A93454B41877BE62E5EF0A041C2EE9C9E76BD1E12AE25D9628DECB097025DD625EF49C3258A1A3C0FF501E3DC673B76D7BABF349009B6ECF#)
  (x #14D0345A3562C480A039E3C72764F72D79043216#)
  )
 )
 )
)`

func TestParseLibOTRPrivateKey(t *testing.T) {
	var priv PrivateKey

	if !priv.Import([]byte(libOTRPrivateKey)) {
		t.Fatalf("Failed to import sample private key")
	}
}

func TestSignVerify(t *testing.T) {
	var priv PrivateKey
	alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex)
	_, ok := priv.Parse(alicePrivateKey)
	if !ok {
		t.Error("failed to parse private key")
	}

	var msg [32]byte
	rand.Reader.Read(msg[:])

	sig := priv.Sign(rand.Reader, msg[:])
	rest, ok := priv.PublicKey.Verify(msg[:], sig)
	if !ok {
		t.Errorf("signature (%x) of %x failed to verify", sig, msg[:])
	} else if len(rest) > 0 {
		t.Error("signature data remains after verification")
	}

	sig[10] ^= 80
	_, ok = priv.PublicKey.Verify(msg[:], sig)
	if ok {
		t.Errorf("corrupted signature (%x) of %x verified", sig, msg[:])
	}
}

func setupConversation(t *testing.T) (alice, bob *Conversation) {
	alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex)
	bobPrivateKey, _ := hex.DecodeString(bobPrivateKeyHex)

	alice, bob = new(Conversation), new(Conversation)

	alice.PrivateKey = new(PrivateKey)
	bob.PrivateKey = new(PrivateKey)
	alice.PrivateKey.Parse(alicePrivateKey)
	bob.PrivateKey.Parse(bobPrivateKey)
	alice.FragmentSize = 100
	bob.FragmentSize = 100

	if alice.IsEncrypted() {
		t.Error("Alice believes that the conversation is secure before we've started")
	}
	if bob.IsEncrypted() {
		t.Error("Bob believes that the conversation is secure before we've started")
	}

	performHandshake(t, alice, bob)
	return alice, bob
}

func performHandshake(t *testing.T, alice, bob *Conversation) {
	var alicesMessage, bobsMessage [][]byte
	var out []byte
	var aliceChange, bobChange SecurityChange
	var err error
	alicesMessage = append(alicesMessage, []byte(QueryMessage))

	for round := 0; len(alicesMessage) > 0 || len(bobsMessage) > 0; round++ {
		bobsMessage = nil
		for i, msg := range alicesMessage {
			out, _, bobChange, bobsMessage, err = bob.Receive(msg)
			if len(out) > 0 {
				t.Errorf("Bob generated output during key exchange, round %d, message %d", round, i)
			}
			if err != nil {
				t.Fatalf("Bob returned an error, round %d, message %d (%x): %s", round, i, msg, err)
			}
			if len(bobsMessage) > 0 && i != len(alicesMessage)-1 {
				t.Errorf("Bob produced output while processing a fragment, round %d, message %d", round, i)
			}
		}

		alicesMessage = nil
		for i, msg := range bobsMessage {
			out, _, aliceChange, alicesMessage, err = alice.Receive(msg)
			if len(out) > 0 {
				t.Errorf("Alice generated output during key exchange, round %d, message %d", round, i)
			}
			if err != nil {
				t.Fatalf("Alice returned an error, round %d, message %d (%x): %s", round, i, msg, err)
			}
			if len(alicesMessage) > 0 && i != len(bobsMessage)-1 {
				t.Errorf("Alice produced output while processing a fragment, round %d, message %d", round, i)
			}
		}
	}

	if aliceChange != NewKeys {
		t.Errorf("Alice terminated without signaling new keys")
	}
	if bobChange != NewKeys {
		t.Errorf("Bob terminated without signaling new keys")
	}

	if !bytes.Equal(alice.SSID[:], bob.SSID[:]) {
		t.Errorf("Session identifiers don't match. Alice has %x, Bob has %x", alice.SSID[:], bob.SSID[:])
	}

	if !alice.IsEncrypted() {
		t.Error("Alice doesn't believe that the conversation is secure")
	}
	if !bob.IsEncrypted() {
		t.Error("Bob doesn't believe that the conversation is secure")
	}
}

const (
	firstRoundTrip = iota
	subsequentRoundTrip
	noMACKeyCheck
)

func roundTrip(t *testing.T, alice, bob *Conversation, message []byte, macKeyCheck int) {
	alicesMessage, err := alice.Send(message)
	if err != nil {
		t.Errorf("Error from Alice sending message: %s", err)
	}

	if len(alice.oldMACs) != 0 {
		t.Errorf("Alice has not revealed all MAC keys")
	}

	for i, msg := range alicesMessage {
		out, encrypted, _, _, err := bob.Receive(msg)

		if err != nil {
			t.Errorf("Error generated while processing test message: %s", err.Error())
		}
		if len(out) > 0 {
			if i != len(alicesMessage)-1 {
				t.Fatal("Bob produced a message while processing a fragment of Alice's")
			}
			if !encrypted {
				t.Errorf("Message was not marked as encrypted")
			}
			if !bytes.Equal(out, message) {
				t.Errorf("Message corrupted: got %x, want %x", out, message)
			}
		}
	}

	switch macKeyCheck {
	case firstRoundTrip:
		if len(bob.oldMACs) != 0 {
			t.Errorf("Bob should not have MAC keys to reveal")
		}
	case subsequentRoundTrip:
		if len(bob.oldMACs) != 40 {
			t.Errorf("Bob has %d bytes of MAC keys to reveal, but should have 40", len(bob.oldMACs))
		}
	}

	bobsMessage, err := bob.Send(message)
	if err != nil {
		t.Errorf("Error from Bob sending message: %s", err)
	}

	if len(bob.oldMACs) != 0 {
		t.Errorf("Bob has not revealed all MAC keys")
	}

	for i, msg := range bobsMessage {
		out, encrypted, _, _, err := alice.Receive(msg)

		if err != nil {
			t.Errorf("Error generated while processing test message: %s", err.Error())
		}
		if len(out) > 0 {
			if i != len(bobsMessage)-1 {
				t.Fatal("Alice produced a message while processing a fragment of Bob's")
			}
			if !encrypted {
				t.Errorf("Message was not marked as encrypted")
			}
			if !bytes.Equal(out, message) {
				t.Errorf("Message corrupted: got %x, want %x", out, message)
			}
		}
	}

	switch macKeyCheck {
	case firstRoundTrip:
		if len(alice.oldMACs) != 20 {
			t.Errorf("Alice has %d bytes of MAC keys to reveal, but should have 20", len(alice.oldMACs))
		}
	case subsequentRoundTrip:
		if len(alice.oldMACs) != 40 {
			t.Errorf("Alice has %d bytes of MAC keys to reveal, but should have 40", len(alice.oldMACs))
		}
	}
}

func TestConversation(t *testing.T) {
	alice, bob := setupConversation(t)

	var testMessages = [][]byte{
		[]byte("hello"), []byte("bye"),
	}

	roundTripType := firstRoundTrip

	for _, testMessage := range testMessages {
		roundTrip(t, alice, bob, testMessage, roundTripType)
		roundTripType = subsequentRoundTrip
	}
}

func TestGoodSMP(t *testing.T) {
	var alice, bob Conversation

	alice.smp.secret = new(big.Int).SetInt64(42)
	bob.smp.secret = alice.smp.secret

	var alicesMessages, bobsMessages []tlv
	var aliceComplete, bobComplete bool
	var err error
	var out tlv

	alicesMessages = alice.startSMP("")
	for round := 0; len(alicesMessages) > 0 || len(bobsMessages) > 0; round++ {
		bobsMessages = bobsMessages[:0]
		for i, msg := range alicesMessages {
			out, bobComplete, err = bob.processSMP(msg)
			if err != nil {
				t.Errorf("Error from Bob in round %d: %s", round, err)
			}
			if bobComplete && i != len(alicesMessages)-1 {
				t.Errorf("Bob returned a completed signal before processing all of Alice's messages in round %d", round)
			}
			if out.typ != 0 {
				bobsMessages = append(bobsMessages, out)
			}
		}

		alicesMessages = alicesMessages[:0]
		for i, msg := range bobsMessages {
			out, aliceComplete, err = alice.processSMP(msg)
			if err != nil {
				t.Errorf("Error from Alice in round %d: %s", round, err)
			}
			if aliceComplete && i != len(bobsMessages)-1 {
				t.Errorf("Alice returned a completed signal before processing all of Bob's messages in round %d", round)
			}
			if out.typ != 0 {
				alicesMessages = append(alicesMessages, out)
			}
		}
	}

	if !aliceComplete || !bobComplete {
		t.Errorf("SMP completed without both sides reporting success: alice: %v, bob: %v\n", aliceComplete, bobComplete)
	}
}

func TestBadSMP(t *testing.T) {
	var alice, bob Conversation

	alice.smp.secret = new(big.Int).SetInt64(42)
	bob.smp.secret = new(big.Int).SetInt64(43)

	var alicesMessages, bobsMessages []tlv

	alicesMessages = alice.startSMP("")
	for round := 0; len(alicesMessages) > 0 || len(bobsMessages) > 0; round++ {
		bobsMessages = bobsMessages[:0]
		for _, msg := range alicesMessages {
			out, complete, _ := bob.processSMP(msg)
			if complete {
				t.Errorf("Bob signaled completion in round %d", round)
			}
			if out.typ != 0 {
				bobsMessages = append(bobsMessages, out)
			}
		}

		alicesMessages = alicesMessages[:0]
		for _, msg := range bobsMessages {
			out, complete, _ := alice.processSMP(msg)
			if complete {
				t.Errorf("Alice signaled completion in round %d", round)
			}
			if out.typ != 0 {
				alicesMessages = append(alicesMessages, out)
			}
		}
	}
}

func TestRehandshaking(t *testing.T) {
	alice, bob := setupConversation(t)
	roundTrip(t, alice, bob, []byte("test"), firstRoundTrip)
	roundTrip(t, alice, bob, []byte("test 2"), subsequentRoundTrip)
	roundTrip(t, alice, bob, []byte("test 3"), subsequentRoundTrip)
	roundTrip(t, alice, bob, []byte("test 4"), subsequentRoundTrip)
	roundTrip(t, alice, bob, []byte("test 5"), subsequentRoundTrip)
	roundTrip(t, alice, bob, []byte("test 6"), subsequentRoundTrip)
	roundTrip(t, alice, bob, []byte("test 7"), subsequentRoundTrip)
	roundTrip(t, alice, bob, []byte("test 8"), subsequentRoundTrip)
	performHandshake(t, alice, bob)
	roundTrip(t, alice, bob, []byte("test"), noMACKeyCheck)
	roundTrip(t, alice, bob, []byte("test 2"), noMACKeyCheck)
}

func TestAgainstLibOTR(t *testing.T) {
	// This test requires otr.c.test to be built as /tmp/a.out.
	// If enabled, this tests runs forever performing OTR handshakes in a
	// loop.
	return

	alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex)
	var alice Conversation
	alice.PrivateKey = new(PrivateKey)
	alice.PrivateKey.Parse(alicePrivateKey)

	cmd := exec.Command("/tmp/a.out")
	cmd.Stderr = os.Stderr

	out, err := cmd.StdinPipe()
	if err != nil {
		t.Fatal(err)
	}
	defer out.Close()
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatal(err)
	}
	in := bufio.NewReader(stdout)

	if err := cmd.Start(); err != nil {
		t.Fatal(err)
	}

	out.Write([]byte(QueryMessage))
	out.Write([]byte("\n"))
	var expectedText = []byte("test message")

	for {
		line, isPrefix, err := in.ReadLine()
		if isPrefix {
			t.Fatal("line from subprocess too long")
		}
		if err != nil {
			t.Fatal(err)
		}
		text, encrypted, change, alicesMessage, err := alice.Receive(line)
		if err != nil {
			t.Fatal(err)
		}
		for _, msg := range alicesMessage {
			out.Write(msg)
			out.Write([]byte("\n"))
		}
		if change == NewKeys {
			alicesMessage, err := alice.Send([]byte("Go -> libotr test message"))
			if err != nil {
				t.Fatalf("error sending message: %s", err.Error())
			} else {
				for _, msg := range alicesMessage {
					out.Write(msg)
					out.Write([]byte("\n"))
				}
			}
		}
		if len(text) > 0 {
			if !bytes.Equal(text, expectedText) {
				t.Fatalf("expected %x, but got %x", expectedText, text)
			}
			if !encrypted {
				t.Fatal("message wasn't encrypted")
			}
		}
	}
}


text:
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package otr implements the Off The Record protocol as specified in
// http://www.cypherpunks.ca/otr/Protocol-v2-3.1.0.html
package otr // import "golang.org/x/crypto/otr"

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/dsa"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"hash"
	"io"
	"math/big"
	"strconv"
)

// SecurityChange describes a change in the security state of a Conversation.
type SecurityChange int

const (
	NoChange SecurityChange = iota
	// NewKeys indicates that a key exchange has completed. This occurs
	// when a conversation first becomes encrypted, and when the keys are
	// renegotiated within an encrypted conversation.
	NewKeys
	// SMPSecretNeeded indicates that the peer has started an
	// authentication and that we need to supply a secret. Call SMPQuestion
	// to get the optional, human readable challenge and then Authenticate
	// to supply the matching secret.
	SMPSecretNeeded
	// SMPComplete indicates that an authentication completed. The identity
	// of the peer has now been confirmed.
	SMPComplete
	// SMPFailed indicates that an authentication failed.
	SMPFailed
	// ConversationEnded indicates that the peer ended the secure
	// conversation.
	ConversationEnded
)

// QueryMessage can be sent to a peer to start an OTR conversation.
var QueryMessage = "?OTRv2?"

// ErrorPrefix can be used to make an OTR error by appending an error message
// to it.
var ErrorPrefix = "?OTR Error:"

var (
	fragmentPartSeparator = []byte(",")
	fragmentPrefix        = []byte("?OTR,")
	msgPrefix             = []byte("?OTR:")
	queryMarker           = []byte("?OTR")
)

// isQuery attempts to parse an OTR query from msg and returns the greatest
// common version, or 0 if msg is not an OTR query.
func isQuery(msg []byte) (greatestCommonVersion int) {
	pos := bytes.Index(msg, queryMarker)
	if pos == -1 {
		return 0
	}
	for i, c := range msg[pos+len(queryMarker):] {
		if i == 0 {
			if c == '?' {
				// Indicates support for version 1, but we don't
				// implement that.
				continue
			}

			if c != 'v' {
				// Invalid message
				return 0
			}

			continue
		}

		if c == '?' {
			// End of message
			return
		}

		if c == ' ' || c == '\t' {
			// Probably an invalid message
			return 0
		}

		if c == '2' {
			greatestCommonVersion = 2
		}
	}

	return 0
}

const (
	statePlaintext = iota
	stateEncrypted
	stateFinished
)

const (
	authStateNone = iota
	authStateAwaitingDHKey
	authStateAwaitingRevealSig
	authStateAwaitingSig
)

const (
	msgTypeDHCommit  = 2
	msgTypeData      = 3
	msgTypeDHKey     = 10
	msgTypeRevealSig = 17
	msgTypeSig       = 18
)

const (
	// If the requested fragment size is less than this, it will be ignored.
	minFragmentSize = 18
	// Messages are padded to a multiple of this number of bytes.
	paddingGranularity = 256
	// The number of bytes in a Diffie-Hellman private value (320-bits).
	dhPrivateBytes = 40
	// The number of bytes needed to represent an element of the DSA
	// subgroup (160-bits).
	dsaSubgroupBytes = 20
	// The number of bytes of the MAC that are sent on the wire (160-bits).
	macPrefixBytes = 20
)

// These are the global, common group parameters for OTR.
var (
	p       *big.Int // group prime
	g       *big.Int // group generator
	q       *big.Int // group order
	pMinus2 *big.Int
)

func init() {
	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16)
	q, _ = new(big.Int).SetString("7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF", 16)
	g = new(big.Int).SetInt64(2)
	pMinus2 = new(big.Int).Sub(p, g)
}

// Conversation represents a relation with a peer. The zero value is a valid
// Conversation, although PrivateKey must be set.
//
// When communicating with a peer, all inbound messages should be passed to
// Conversation.Receive and all outbound messages to Conversation.Send. The
// Conversation will take care of maintaining the encryption state and
// negotiating encryption as needed.
type Conversation struct {
	// PrivateKey contains the private key to use to sign key exchanges.
	PrivateKey *PrivateKey

	// Rand can be set to override the entropy source. Otherwise,
	// crypto/rand will be used.
	Rand io.Reader
	// If FragmentSize is set, all messages produced by Receive and Send
	// will be fragmented into messages of, at most, this number of bytes.
	FragmentSize int

	// Once Receive has returned NewKeys once, the following fields are
	// valid.
	SSID           [8]byte
	TheirPublicKey PublicKey

	state, authState int

	r       [16]byte
	x, y    *big.Int
	gx, gy  *big.Int
	gxBytes []byte
	digest  [sha256.Size]byte

	revealKeys, sigKeys akeKeys

	myKeyId         uint32
	myCurrentDHPub  *big.Int
	myCurrentDHPriv *big.Int
	myLastDHPub     *big.Int
	myLastDHPriv    *big.Int

	theirKeyId        uint32
	theirCurrentDHPub *big.Int
	theirLastDHPub    *big.Int

	keySlots [4]keySlot

	myCounter    [8]byte
	theirLastCtr [8]byte
	oldMACs      []byte

	k, n int // fragment state
	frag []byte

	smp smpState
}

// A keySlot contains key material for a specific (their keyid, my keyid) pair.
type keySlot struct {
	// used is true if this slot is valid. If false, it's free for reuse.
	used                   bool
	theirKeyId             uint32
	myKeyId                uint32
	sendAESKey, recvAESKey []byte
	sendMACKey, recvMACKey []byte
	theirLastCtr           [8]byte
}

// akeKeys are generated during key exchange. There's one set for the reveal
// signature message and another for the signature message. In the protocol
// spec the latter are indicated with a prime mark.
type akeKeys struct {
	c      [16]byte
	m1, m2 [32]byte
}

func (c *Conversation) rand() io.Reader {
	if c.Rand != nil {
		return c.Rand
	}
	return rand.Reader
}

func (c *Conversation) randMPI(buf []byte) *big.Int {
	_, err := io.ReadFull(c.rand(), buf)
	if err != nil {
		panic("otr: short read from random source")
	}

	return new(big.Int).SetBytes(buf)
}

// tlv represents the type-length value from the protocol.
type tlv struct {
	typ, length uint16
	data        []byte
}

const (
	tlvTypePadding          = 0
	tlvTypeDisconnected     = 1
	tlvTypeSMP1             = 2
	tlvTypeSMP2             = 3
	tlvTypeSMP3             = 4
	tlvTypeSMP4             = 5
	tlvTypeSMPAbort         = 6
	tlvTypeSMP1WithQuestion = 7
)

// Receive handles a message from a peer. It returns a human readable message,
// an indicator of whether that message was encrypted, a hint about the
// encryption state and zero or more messages to send back to the peer.
// These messages do not need to be passed to Send before transmission.
func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change SecurityChange, toSend [][]byte, err error) {
	if bytes.HasPrefix(in, fragmentPrefix) {
		in, err = c.processFragment(in)
		if in == nil || err != nil {
			return
		}
	}

	if bytes.HasPrefix(in, msgPrefix) && in[len(in)-1] == '.' {
		in = in[len(msgPrefix) : len(in)-1]
	} else if version := isQuery(in); version > 0 {
		c.authState = authStateAwaitingDHKey
		c.reset()
		toSend = c.encode(c.generateDHCommit())
		return
	} else {
		// plaintext message
		out = in
		return
	}

	msg := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
	msgLen, err := base64.StdEncoding.Decode(msg, in)
	if err != nil {
		err = errors.New("otr: invalid base64 encoding in message")
		return
	}
	msg = msg[:msgLen]

	// The first two bytes are the protocol version (2)
	if len(msg) < 3 || msg[0] != 0 || msg[1] != 2 {
		err = errors.New("otr: invalid OTR message")
		return
	}

	msgType := int(msg[2])
	msg = msg[3:]

	switch msgType {
	case msgTypeDHCommit:
		switch c.authState {
		case authStateNone:
			c.authState = authStateAwaitingRevealSig
			if err = c.processDHCommit(msg); err != nil {
				return
			}
			c.reset()
			toSend = c.encode(c.generateDHKey())
			return
		case authStateAwaitingDHKey:
			// This is a 'SYN-crossing'. The greater digest wins.
			var cmp int
			if cmp, err = c.compareToDHCommit(msg); err != nil {
				return
			}
			if cmp > 0 {
				// We win. Retransmit DH commit.
				toSend = c.encode(c.serializeDHCommit())
				return
			} else {
				// They win. We forget about our DH commit.
				c.authState = authStateAwaitingRevealSig
				if err = c.processDHCommit(msg); err != nil {
					return
				}
				c.reset()
				toSend = c.encode(c.generateDHKey())
				return
			}
		case authStateAwaitingRevealSig:
			if err = c.processDHCommit(msg); err != nil {
				return
			}
			toSend = c.encode(c.serializeDHKey())
		case authStateAwaitingSig:
			if err = c.processDHCommit(msg); err != nil {
				return
			}
			c.reset()
			toSend = c.encode(c.generateDHKey())
			c.authState = authStateAwaitingRevealSig
		default:
			panic("bad state")
		}
	case msgTypeDHKey:
		switch c.authState {
		case authStateAwaitingDHKey:
			var isSame bool
			if isSame, err = c.processDHKey(msg); err != nil {
				return
			}
			if isSame {
				err = errors.New("otr: unexpected duplicate DH key")
				return
			}
			toSend = c.encode(c.generateRevealSig())
			c.authState = authStateAwaitingSig
		case authStateAwaitingSig:
			var isSame bool
			if isSame, err = c.processDHKey(msg); err != nil {
				return
			}
			if isSame {
				toSend = c.encode(c.serializeDHKey())
			}
		}
	case msgTypeRevealSig:
		if c.authState != authStateAwaitingRevealSig {
			return
		}
		if err = c.processRevealSig(msg); err != nil {
			return
		}
		toSend = c.encode(c.generateSig())
		c.authState = authStateNone
		c.state = stateEncrypted
		change = NewKeys
	case msgTypeSig:
		if c.authState != authStateAwaitingSig {
			return
		}
		if err = c.processSig(msg); err != nil {
			return
		}
		c.authState = authStateNone
		c.state = stateEncrypted
		change = NewKeys
	case msgTypeData:
		if c.state != stateEncrypted {
			err = errors.New("otr: encrypted message received without encrypted session established")
			return
		}
		var tlvs []tlv
		out, tlvs, err = c.processData(msg)
		encrypted = true

	EachTLV:
		for _, inTLV := range tlvs {
			switch inTLV.typ {
			case tlvTypeDisconnected:
				change = ConversationEnded
				c.state = stateFinished
				break EachTLV
			case tlvTypeSMP1, tlvTypeSMP2, tlvTypeSMP3, tlvTypeSMP4, tlvTypeSMPAbort, tlvTypeSMP1WithQuestion:
				var reply tlv
				var complete bool
				reply, complete, err = c.processSMP(inTLV)
				if err == smpSecretMissingError {
					err = nil
					change = SMPSecretNeeded
					c.smp.saved = &inTLV
					return
				}
				if err == smpFailureError {
					err = nil
					change = SMPFailed
				} else if complete {
					change = SMPComplete
				}
				if reply.typ != 0 {
					toSend = c.encode(c.generateData(nil, &reply))
				}
				break EachTLV
			default:
				// skip unknown TLVs
			}
		}
	default:
		err = errors.New("otr: unknown message type " + strconv.Itoa(msgType))
	}

	return
}

// Send takes a human readable message from the local user, possibly encrypts
// it and returns zero one or more messages to send to the peer.
func (c *Conversation) Send(msg []byte) ([][]byte, error) {
	switch c.state {
	case statePlaintext:
		return [][]byte{msg}, nil
	case stateEncrypted:
		return c.encode(c.generateData(msg, nil)), nil
	case stateFinished:
		return nil, errors.New("otr: cannot send message because secure conversation has finished")
	}

	return nil, errors.New("otr: cannot send message in current state")
}

// SMPQuestion returns the human readable challenge question from the peer.
// It's only valid after Receive has returned SMPSecretNeeded.
func (c *Conversation) SMPQuestion() string {
	return c.smp.question
}

// Authenticate begins an authentication with the peer. Authentication involves
// an optional challenge message and a shared secret. The authentication
// proceeds until either Receive returns SMPComplete, SMPSecretNeeded (which
// indicates that a new authentication is happening and thus this one was
// aborted) or SMPFailed.
func (c *Conversation) Authenticate(question string, mutualSecret []byte) (toSend [][]byte, err error) {
	if c.state != stateEncrypted {
		err = errors.New("otr: can't authenticate a peer without a secure conversation established")
		return
	}

	if c.smp.saved != nil {
		c.calcSMPSecret(mutualSecret, false /* they started it */)

		var out tlv
		var complete bool
		out, complete, err = c.processSMP(*c.smp.saved)
		if complete {
			panic("SMP completed on the first message")
		}
		c.smp.saved = nil
		if out.typ != 0 {
			toSend = c.encode(c.generateData(nil, &out))
		}
		return
	}

	c.calcSMPSecret(mutualSecret, true /* we started it */)
	outs := c.startSMP(question)
	for _, out := range outs {
		toSend = append(toSend, c.encode(c.generateData(nil, &out))...)
	}
	return
}

// End ends a secure conversation by generating a termination message for
// the peer and switches to unencrypted communication.
func (c *Conversation) End() (toSend [][]byte) {
	switch c.state {
	case statePlaintext:
		return nil
	case stateEncrypted:
		c.state = statePlaintext
		return c.encode(c.generateData(nil, &tlv{typ: tlvTypeDisconnected}))
	case stateFinished:
		c.state = statePlaintext
		return nil
	}
	panic("unreachable")
}

// IsEncrypted returns true if a message passed to Send would be encrypted
// before transmission. This result remains valid until the next call to
// Receive or End, which may change the state of the Conversation.
func (c *Conversation) IsEncrypted() bool {
	return c.state == stateEncrypted
}

var fragmentError = errors.New("otr: invalid OTR fragment")

// processFragment processes a fragmented OTR message and possibly returns a
// complete message. Fragmented messages look like "?OTR,k,n,msg," where k is
// the fragment number (starting from 1), n is the number of fragments in this
// message and msg is a substring of the base64 encoded message.
func (c *Conversation) processFragment(in []byte) (out []byte, err error) {
	in = in[len(fragmentPrefix):] // remove "?OTR,"
	parts := bytes.Split(in, fragmentPartSeparator)
	if len(parts) != 4 || len(parts[3]) != 0 {
		return nil, fragmentError
	}

	k, err := strconv.Atoi(string(parts[0]))
	if err != nil {
		return nil, fragmentError
	}

	n, err := strconv.Atoi(string(parts[1]))
	if err != nil {
		return nil, fragmentError
	}

	if k < 1 || n < 1 || k > n {
		return nil, fragmentError
	}

	if k == 1 {
		c.frag = append(c.frag[:0], parts[2]...)
		c.k, c.n = k, n
	} else if n == c.n && k == c.k+1 {
		c.frag = append(c.frag, parts[2]...)
		c.k++
	} else {
		c.frag = c.frag[:0]
		c.n, c.k = 0, 0
	}

	if c.n > 0 && c.k == c.n {
		c.n, c.k = 0, 0
		return c.frag, nil
	}

	return nil, nil
}

func (c *Conversation) generateDHCommit() []byte {
	_, err := io.ReadFull(c.rand(), c.r[:])
	if err != nil {
		panic("otr: short read from random source")
	}

	var xBytes [dhPrivateBytes]byte
	c.x = c.randMPI(xBytes[:])
	c.gx = new(big.Int).Exp(g, c.x, p)
	c.gy = nil
	c.gxBytes = appendMPI(nil, c.gx)

	h := sha256.New()
	h.Write(c.gxBytes)
	h.Sum(c.digest[:0])

	aesCipher, err := aes.NewCipher(c.r[:])
	if err != nil {
		panic(err.Error())
	}

	var iv [aes.BlockSize]byte
	ctr := cipher.NewCTR(aesCipher, iv[:])
	ctr.XORKeyStream(c.gxBytes, c.gxBytes)

	return c.serializeDHCommit()
}

func (c *Conversation) serializeDHCommit() []byte {
	var ret []byte
	ret = appendU16(ret, 2) // protocol version
	ret = append(ret, msgTypeDHCommit)
	ret = appendData(ret, c.gxBytes)
	ret = appendData(ret, c.digest[:])
	return ret
}

func (c *Conversation) processDHCommit(in []byte) error {
	var ok1, ok2 bool
	c.gxBytes, in, ok1 = getData(in)
	digest, in, ok2 := getData(in)
	if !ok1 || !ok2 || len(in) > 0 {
		return errors.New("otr: corrupt DH commit message")
	}
	copy(c.digest[:], digest)
	return nil
}

func (c *Conversation) compareToDHCommit(in []byte) (int, error) {
	_, in, ok1 := getData(in)
	digest, in, ok2 := getData(in)
	if !ok1 || !ok2 || len(in) > 0 {
		return 0, errors.New("otr: corrupt DH commit message")
	}
	return bytes.Compare(c.digest[:], digest), nil
}

func (c *Conversation) generateDHKey() []byte {
	var yBytes [dhPrivateBytes]byte
	c.y = c.randMPI(yBytes[:])
	c.gy = new(big.Int).Exp(g, c.y, p)
	return c.serializeDHKey()
}

func (c *Conversation) serializeDHKey() []byte {
	var ret []byte
	ret = appendU16(ret, 2) // protocol version
	ret = append(ret, msgTypeDHKey)
	ret = appendMPI(ret, c.gy)
	return ret
}

func (c *Conversation) processDHKey(in []byte) (isSame bool, err error) {
	gy, in, ok := getMPI(in)
	if !ok {
		err = errors.New("otr: corrupt DH key message")
		return
	}
	if gy.Cmp(g) < 0 || gy.Cmp(pMinus2) > 0 {
		err = errors.New("otr: DH value out of range")
		return
	}
	if c.gy != nil {
		isSame = c.gy.Cmp(gy) == 0
		return
	}
	c.gy = gy
	return
}

func (c *Conversation) generateEncryptedSignature(keys *akeKeys, xFirst bool) ([]byte, []byte) {
	var xb []byte
	xb = c.PrivateKey.PublicKey.Serialize(xb)

	var verifyData []byte
	if xFirst {
		verifyData = appendMPI(verifyData, c.gx)
		verifyData = appendMPI(verifyData, c.gy)
	} else {
		verifyData = appendMPI(verifyData, c.gy)
		verifyData = appendMPI(verifyData, c.gx)
	}
	verifyData = append(verifyData, xb...)
	verifyData = appendU32(verifyData, c.myKeyId)

	mac := hmac.New(sha256.New, keys.m1[:])
	mac.Write(verifyData)
	mb := mac.Sum(nil)

	xb = appendU32(xb, c.myKeyId)
	xb = append(xb, c.PrivateKey.Sign(c.rand(), mb)...)

	aesCipher, err := aes.NewCipher(keys.c[:])
	if err != nil {
		panic(err.Error())
	}
	var iv [aes.BlockSize]byte
	ctr := cipher.NewCTR(aesCipher, iv[:])
	ctr.XORKeyStream(xb, xb)

	mac = hmac.New(sha256.New, keys.m2[:])
	encryptedSig := appendData(nil, xb)
	mac.Write(encryptedSig)

	return encryptedSig, mac.Sum(nil)
}

func (c *Conversation) generateRevealSig() []byte {
	s := new(big.Int).Exp(c.gy, c.x, p)
	c.calcAKEKeys(s)
	c.myKeyId++

	encryptedSig, mac := c.generateEncryptedSignature(&c.revealKeys, true /* gx comes first */)

	c.myCurrentDHPub = c.gx
	c.myCurrentDHPriv = c.x
	c.rotateDHKeys()
	incCounter(&c.myCounter)

	var ret []byte
	ret = appendU16(ret, 2)
	ret = append(ret, msgTypeRevealSig)
	ret = appendData(ret, c.r[:])
	ret = append(ret, encryptedSig...)
	ret = append(ret, mac[:20]...)
	return ret
}

func (c *Conversation) processEncryptedSig(encryptedSig, theirMAC []byte, keys *akeKeys, xFirst bool) error {
	mac := hmac.New(sha256.New, keys.m2[:])
	mac.Write(appendData(nil, encryptedSig))
	myMAC := mac.Sum(nil)[:20]

	if len(myMAC) != len(theirMAC) || subtle.ConstantTimeCompare(myMAC, theirMAC) == 0 {
		return errors.New("bad signature MAC in encrypted signature")
	}

	aesCipher, err := aes.NewCipher(keys.c[:])
	if err != nil {
		panic(err.Error())
	}
	var iv [aes.BlockSize]byte
	ctr := cipher.NewCTR(aesCipher, iv[:])
	ctr.XORKeyStream(encryptedSig, encryptedSig)

	sig := encryptedSig
	sig, ok1 := c.TheirPublicKey.Parse(sig)
	keyId, sig, ok2 := getU32(sig)
	if !ok1 || !ok2 {
		return errors.New("otr: corrupt encrypted signature")
	}

	var verifyData []byte
	if xFirst {
		verifyData = appendMPI(verifyData, c.gx)
		verifyData = appendMPI(verifyData, c.gy)
	} else {
		verifyData = appendMPI(verifyData, c.gy)
		verifyData = appendMPI(verifyData, c.gx)
	}
	verifyData = c.TheirPublicKey.Serialize(verifyData)
	verifyData = appendU32(verifyData, keyId)

	mac = hmac.New(sha256.New, keys.m1[:])
	mac.Write(verifyData)
	mb := mac.Sum(nil)

	sig, ok1 = c.TheirPublicKey.Verify(mb, sig)
	if !ok1 {
		return errors.New("bad signature in encrypted signature")
	}
	if len(sig) > 0 {
		return errors.New("corrupt encrypted signature")
	}

	c.theirKeyId = keyId
	zero(c.theirLastCtr[:])
	return nil
}

func (c *Conversation) processRevealSig(in []byte) error {
	r, in, ok1 := getData(in)
	encryptedSig, in, ok2 := getData(in)
	theirMAC := in
	if !ok1 || !ok2 || len(theirMAC) != 20 {
		return errors.New("otr: corrupt reveal signature message")
	}

	aesCipher, err := aes.NewCipher(r)
	if err != nil {
		return errors.New("otr: cannot create AES cipher from reveal signature message: " + err.Error())
	}
	var iv [aes.BlockSize]byte
	ctr := cipher.NewCTR(aesCipher, iv[:])
	ctr.XORKeyStream(c.gxBytes, c.gxBytes)
	h := sha256.New()
	h.Write(c.gxBytes)
	digest := h.Sum(nil)
	if len(digest) != len(c.digest) || subtle.ConstantTimeCompare(digest, c.digest[:]) == 0 {
		return errors.New("otr: bad commit MAC in reveal signature message")
	}
	var rest []byte
	c.gx, rest, ok1 = getMPI(c.gxBytes)
	if !ok1 || len(rest) > 0 {
		return errors.New("otr: gx corrupt after decryption")
	}
	if c.gx.Cmp(g) < 0 || c.gx.Cmp(pMinus2) > 0 {
		return errors.New("otr: DH value out of range")
	}
	s := new(big.Int).Exp(c.gx, c.y, p)
	c.calcAKEKeys(s)

	if err := c.processEncryptedSig(encryptedSig, theirMAC, &c.revealKeys, true /* gx comes first */); err != nil {
		return errors.New("otr: in reveal signature message: " + err.Error())
	}

	c.theirCurrentDHPub = c.gx
	c.theirLastDHPub = nil

	return nil
}

func (c *Conversation) generateSig() []byte {
	c.myKeyId++

	encryptedSig, mac := c.generateEncryptedSignature(&c.sigKeys, false /* gy comes first */)

	c.myCurrentDHPub = c.gy
	c.myCurrentDHPriv = c.y
	c.rotateDHKeys()
	incCounter(&c.myCounter)

	var ret []byte
	ret = appendU16(ret, 2)
	ret = append(ret, msgTypeSig)
	ret = append(ret, encryptedSig...)
	ret = append(ret, mac[:macPrefixBytes]...)
	return ret
}

func (c *Conversation) processSig(in []byte) error {
	encryptedSig, in, ok1 := getData(in)
	theirMAC := in
	if !ok1 || len(theirMAC) != macPrefixBytes {
		return errors.New("otr: corrupt signature message")
	}

	if err := c.processEncryptedSig(encryptedSig, theirMAC, &c.sigKeys, false /* gy comes first */); err != nil {
		return errors.New("otr: in signature message: " + err.Error())
	}

	c.theirCurrentDHPub = c.gy
	c.theirLastDHPub = nil

	return nil
}

func (c *Conversation) rotateDHKeys() {
	// evict slots using our retired key id
	for i := range c.keySlots {
		slot := &c.keySlots[i]
		if slot.used && slot.myKeyId == c.myKeyId-1 {
			slot.used = false
			c.oldMACs = append(c.oldMACs, slot.recvMACKey...)
		}
	}

	c.myLastDHPriv = c.myCurrentDHPriv
	c.myLastDHPub = c.myCurrentDHPub

	var xBytes [dhPrivateBytes]byte
	c.myCurrentDHPriv = c.randMPI(xBytes[:])
	c.myCurrentDHPub = new(big.Int).Exp(g, c.myCurrentDHPriv, p)
	c.myKeyId++
}

func (c *Conversation) processData(in []byte) (out []byte, tlvs []tlv, err error) {
	origIn := in
	flags, in, ok1 := getU8(in)
	theirKeyId, in, ok2 := getU32(in)
	myKeyId, in, ok3 := getU32(in)
	y, in, ok4 := getMPI(in)
	counter, in, ok5 := getNBytes(in, 8)
	encrypted, in, ok6 := getData(in)
	macedData := origIn[:len(origIn)-len(in)]
	theirMAC, in, ok7 := getNBytes(in, macPrefixBytes)
	_, in, ok8 := getData(in)
	if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 || !ok6 || !ok7 || !ok8 || len(in) > 0 {
		err = errors.New("otr: corrupt data message")
		return
	}

	ignoreErrors := flags&1 != 0

	slot, err := c.calcDataKeys(myKeyId, theirKeyId)
	if err != nil {
		if ignoreErrors {
			err = nil
		}
		return
	}

	mac := hmac.New(sha1.New, slot.recvMACKey)
	mac.Write([]byte{0, 2, 3})
	mac.Write(macedData)
	myMAC := mac.Sum(nil)
	if len(myMAC) != len(theirMAC) || subtle.ConstantTimeCompare(myMAC, theirMAC) == 0 {
		if !ignoreErrors {
			err = errors.New("otr: bad MAC on data message")
		}
		return
	}

	if bytes.Compare(counter, slot.theirLastCtr[:]) <= 0 {
		err = errors.New("otr: counter regressed")
		return
	}
	copy(slot.theirLastCtr[:], counter)

	var iv [aes.BlockSize]byte
	copy(iv[:], counter)
	aesCipher, err := aes.NewCipher(slot.recvAESKey)
	if err != nil {
		panic(err.Error())
	}
	ctr := cipher.NewCTR(aesCipher, iv[:])
	ctr.XORKeyStream(encrypted, encrypted)
	decrypted := encrypted

	if myKeyId == c.myKeyId {
		c.rotateDHKeys()
	}
	if theirKeyId == c.theirKeyId {
		// evict slots using their retired key id
		for i := range c.keySlots {
			slot := &c.keySlots[i]
			if slot.used && slot.theirKeyId == theirKeyId-1 {
				slot.used = false
				c.oldMACs = append(c.oldMACs, slot.recvMACKey...)
			}
		}

		c.theirLastDHPub = c.theirCurrentDHPub
		c.theirKeyId++
		c.theirCurrentDHPub = y
	}

	if nulPos := bytes.IndexByte(decrypted, 0); nulPos >= 0 {
		out = decrypted[:nulPos]
		tlvData := decrypted[nulPos+1:]
		for len(tlvData) > 0 {
			var t tlv
			var ok1, ok2, ok3 bool

			t.typ, tlvData, ok1 = getU16(tlvData)
			t.length, tlvData, ok2 = getU16(tlvData)
			t.data, tlvData, ok3 = getNBytes(tlvData, int(t.length))
			if !ok1 || !ok2 || !ok3 {
				err = errors.New("otr: corrupt tlv data")
				return
			}
			tlvs = append(tlvs, t)
		}
	} else {
		out = decrypted
	}

	return
}

func (c *Conversation) generateData(msg []byte, extra *tlv) []byte {
	slot, err := c.calcDataKeys(c.myKeyId-1, c.theirKeyId)
	if err != nil {
		panic("otr: failed to generate sending keys: " + err.Error())
	}

	var plaintext []byte
	plaintext = append(plaintext, msg...)
	plaintext = append(plaintext, 0)

	padding := paddingGranularity - ((len(plaintext) + 4) % paddingGranularity)
	plaintext = appendU16(plaintext, tlvTypePadding)
	plaintext = appendU16(plaintext, uint16(padding))
	for i := 0; i < padding; i++ {
		plaintext = append(plaintext, 0)
	}

	if extra != nil {
		plaintext = appendU16(plaintext, extra.typ)
		plaintext = appendU16(plaintext, uint16(len(extra.data)))
		plaintext = append(plaintext, extra.data...)
	}

	encrypted := make([]byte, len(plaintext))

	var iv [aes.BlockSize]byte
	copy(iv[:], c.myCounter[:])
	aesCipher, err := aes.NewCipher(slot.sendAESKey)
	if err != nil {
		panic(err.Error())
	}
	ctr := cipher.NewCTR(aesCipher, iv[:])
	ctr.XORKeyStream(encrypted, plaintext)

	var ret []byte
	ret = appendU16(ret, 2)
	ret = append(ret, msgTypeData)
	ret = append(ret, 0 /* flags */)
	ret = appendU32(ret, c.myKeyId-1)
	ret = appendU32(ret, c.theirKeyId)
	ret = appendMPI(ret, c.myCurrentDHPub)
	ret = append(ret, c.myCounter[:]...)
	ret = appendData(ret, encrypted)

	mac := hmac.New(sha1.New, slot.sendMACKey)
	mac.Write(ret)
	ret = append(ret, mac.Sum(nil)[:macPrefixBytes]...)
	ret = appendData(ret, c.oldMACs)
	c.oldMACs = nil
	incCounter(&c.myCounter)

	return ret
}

func incCounter(counter *[8]byte) {
	for i := 7; i >= 0; i-- {
		counter[i]++
		if counter[i] > 0 {
			break
		}
	}
}

// calcDataKeys computes the keys used to encrypt a data message given the key
// IDs.
func (c *Conversation) calcDataKeys(myKeyId, theirKeyId uint32) (slot *keySlot, err error) {
	// Check for a cache hit.
	for i := range c.keySlots {
		slot = &c.keySlots[i]
		if slot.used && slot.theirKeyId == theirKeyId && slot.myKeyId == myKeyId {
			return
		}
	}

	// Find an empty slot to write into.
	slot = nil
	for i := range c.keySlots {
		if !c.keySlots[i].used {
			slot = &c.keySlots[i]
			break
		}
	}
	if slot == nil {
		return nil, errors.New("otr: internal error: no more key slots")
	}

	var myPriv, myPub, theirPub *big.Int

	if myKeyId == c.myKeyId {
		myPriv = c.myCurrentDHPriv
		myPub = c.myCurrentDHPub
	} else if myKeyId == c.myKeyId-1 {
		myPriv = c.myLastDHPriv
		myPub = c.myLastDHPub
	} else {
		err = errors.New("otr: peer requested keyid " + strconv.FormatUint(uint64(myKeyId), 10) + " when I'm on " + strconv.FormatUint(uint64(c.myKeyId), 10))
		return
	}

	if theirKeyId == c.theirKeyId {
		theirPub = c.theirCurrentDHPub
	} else if theirKeyId == c.theirKeyId-1 && c.theirLastDHPub != nil {
		theirPub = c.theirLastDHPub
	} else {
		err = errors.New("otr: peer requested keyid " + strconv.FormatUint(uint64(myKeyId), 10) + " when they're on " + strconv.FormatUint(uint64(c.myKeyId), 10))
		return
	}

	var sendPrefixByte, recvPrefixByte [1]byte

	if myPub.Cmp(theirPub) > 0 {
		// we're the high end
		sendPrefixByte[0], recvPrefixByte[0] = 1, 2
	} else {
		// we're the low end
		sendPrefixByte[0], recvPrefixByte[0] = 2, 1
	}

	s := new(big.Int).Exp(theirPub, myPriv, p)
	sBytes := appendMPI(nil, s)

	h := sha1.New()
	h.Write(sendPrefixByte[:])
	h.Write(sBytes)
	slot.sendAESKey = h.Sum(slot.sendAESKey[:0])[:16]

	h.Reset()
	h.Write(slot.sendAESKey)
	slot.sendMACKey = h.Sum(slot.sendMACKey[:0])

	h.Reset()
	h.Write(recvPrefixByte[:])
	h.Write(sBytes)
	slot.recvAESKey = h.Sum(slot.recvAESKey[:0])[:16]

	h.Reset()
	h.Write(slot.recvAESKey)
	slot.recvMACKey = h.Sum(slot.recvMACKey[:0])

	slot.theirKeyId = theirKeyId
	slot.myKeyId = myKeyId
	slot.used = true

	zero(slot.theirLastCtr[:])
	return
}

func (c *Conversation) calcAKEKeys(s *big.Int) {
	mpi := appendMPI(nil, s)
	h := sha256.New()

	var cBytes [32]byte
	hashWithPrefix(c.SSID[:], 0, mpi, h)

	hashWithPrefix(cBytes[:], 1, mpi, h)
	copy(c.revealKeys.c[:], cBytes[:16])
	copy(c.sigKeys.c[:], cBytes[16:])

	hashWithPrefix(c.revealKeys.m1[:], 2, mpi, h)
	hashWithPrefix(c.revealKeys.m2[:], 3, mpi, h)
	hashWithPrefix(c.sigKeys.m1[:], 4, mpi, h)
	hashWithPrefix(c.sigKeys.m2[:], 5, mpi, h)
}

func hashWithPrefix(out []byte, prefix byte, in []byte, h hash.Hash) {
	h.Reset()
	var p [1]byte
	p[0] = prefix
	h.Write(p[:])
	h.Write(in)
	if len(out) == h.Size() {
		h.Sum(out[:0])
	} else {
		digest := h.Sum(nil)
		copy(out, digest)
	}
}

func (c *Conversation) encode(msg []byte) [][]byte {
	b64 := make([]byte, base64.StdEncoding.EncodedLen(len(msg))+len(msgPrefix)+1)
	base64.StdEncoding.Encode(b64[len(msgPrefix):], msg)
	copy(b64, msgPrefix)
	b64[len(b64)-1] = '.'

	if c.FragmentSize < minFragmentSize || len(b64) <= c.FragmentSize {
		// We can encode this in a single fragment.
		return [][]byte{b64}
	}

	// We have to fragment this message.
	var ret [][]byte
	bytesPerFragment := c.FragmentSize - minFragmentSize
	numFragments := (len(b64) + bytesPerFragment) / bytesPerFragment

	for i := 0; i < numFragments; i++ {
		frag := []byte("?OTR," + strconv.Itoa(i+1) + "," + strconv.Itoa(numFragments) + ",")
		todo := bytesPerFragment
		if todo > len(b64) {
			todo = len(b64)
		}
		frag = append(frag, b64[:todo]...)
		b64 = b64[todo:]
		frag = append(frag, ',')
		ret = append(ret, frag)
	}

	return ret
}

func (c *Conversation) reset() {
	c.myKeyId = 0

	for i := range c.keySlots {
		c.keySlots[i].used = false
	}
}

type PublicKey struct {
	dsa.PublicKey
}

func (pk *PublicKey) Parse(in []byte) ([]byte, bool) {
	var ok bool
	var pubKeyType uint16

	if pubKeyType, in, ok = getU16(in); !ok || pubKeyType != 0 {
		return nil, false
	}
	if pk.P, in, ok = getMPI(in); !ok {
		return nil, false
	}
	if pk.Q, in, ok = getMPI(in); !ok {
		return nil, false
	}
	if pk.G, in, ok = getMPI(in); !ok {
		return nil, false
	}
	if pk.Y, in, ok = getMPI(in); !ok {
		return nil, false
	}

	return in, true
}

func (pk *PublicKey) Serialize(in []byte) []byte {
	in = appendU16(in, 0)
	in = appendMPI(in, pk.P)
	in = appendMPI(in, pk.Q)
	in = appendMPI(in, pk.G)
	in = appendMPI(in, pk.Y)
	return in
}

// Fingerprint returns the 20-byte, binary fingerprint of the PublicKey.
func (pk *PublicKey) Fingerprint() []byte {
	b := pk.Serialize(nil)
	h := sha1.New()
	h.Write(b[2:])
	return h.Sum(nil)
}

func (pk *PublicKey) Verify(hashed, sig []byte) ([]byte, bool) {
	if len(sig) != 2*dsaSubgroupBytes {
		return nil, false
	}
	r := new(big.Int).SetBytes(sig[:dsaSubgroupBytes])
	s := new(big.Int).SetBytes(sig[dsaSubgroupBytes:])
	ok := dsa.Verify(&pk.PublicKey, hashed, r, s)
	return sig[dsaSubgroupBytes*2:], ok
}

type PrivateKey struct {
	PublicKey
	dsa.PrivateKey
}

func (priv *PrivateKey) Sign(rand io.Reader, hashed []byte) []byte {
	r, s, err := dsa.Sign(rand, &priv.PrivateKey, hashed)
	if err != nil {
		panic(err.Error())
	}
	rBytes := r.Bytes()
	sBytes := s.Bytes()
	if len(rBytes) > dsaSubgroupBytes || len(sBytes) > dsaSubgroupBytes {
		panic("DSA signature too large")
	}

	out := make([]byte, 2*dsaSubgroupBytes)
	copy(out[dsaSubgroupBytes-len(rBytes):], rBytes)
	copy(out[len(out)-len(sBytes):], sBytes)
	return out
}

func (priv *PrivateKey) Serialize(in []byte) []byte {
	in = priv.PublicKey.Serialize(in)
	in = appendMPI(in, priv.PrivateKey.X)
	return in
}

func (priv *PrivateKey) Parse(in []byte) ([]byte, bool) {
	in, ok := priv.PublicKey.Parse(in)
	if !ok {
		return in, ok
	}
	priv.PrivateKey.PublicKey = priv.PublicKey.PublicKey
	priv.PrivateKey.X, in, ok = getMPI(in)
	return in, ok
}

func (priv *PrivateKey) Generate(rand io.Reader) {
	if err := dsa.GenerateParameters(&priv.PrivateKey.PublicKey.Parameters, rand, dsa.L1024N160); err != nil {
		panic(err.Error())
	}
	if err := dsa.GenerateKey(&priv.PrivateKey, rand); err != nil {
		panic(err.Error())
	}
	priv.PublicKey.PublicKey = priv.PrivateKey.PublicKey
}

func notHex(r rune) bool {
	if r >= '0' && r <= '9' ||
		r >= 'a' && r <= 'f' ||
		r >= 'A' && r <= 'F' {
		return false
	}

	return true
}

// Import parses the contents of a libotr private key file.
func (priv *PrivateKey) Import(in []byte) bool {
	mpiStart := []byte(" #")

	mpis := make([]*big.Int, 5)

	for i := 0; i < len(mpis); i++ {
		start := bytes.Index(in, mpiStart)
		if start == -1 {
			return false
		}
		in = in[start+len(mpiStart):]
		end := bytes.IndexFunc(in, notHex)
		if end == -1 {
			return false
		}
		hexBytes := in[:end]
		in = in[end:]

		if len(hexBytes)&1 != 0 {
			return false
		}

		mpiBytes := make([]byte, len(hexBytes)/2)
		if _, err := hex.Decode(mpiBytes, hexBytes); err != nil {
			return false
		}

		mpis[i] = new(big.Int).SetBytes(mpiBytes)
	}

	for _, mpi := range mpis {
		if mpi.Sign() <= 0 {
			return false
		}
	}

	priv.PrivateKey.P = mpis[0]
	priv.PrivateKey.Q = mpis[1]
	priv.PrivateKey.G = mpis[2]
	priv.PrivateKey.Y = mpis[3]
	priv.PrivateKey.X = mpis[4]
	priv.PublicKey.PublicKey = priv.PrivateKey.PublicKey

	a := new(big.Int).Exp(priv.PrivateKey.G, priv.PrivateKey.X, priv.PrivateKey.P)
	return a.Cmp(priv.PrivateKey.Y) == 0
}

func getU8(in []byte) (uint8, []byte, bool) {
	if len(in) < 1 {
		return 0, in, false
	}
	return in[0], in[1:], true
}

func getU16(in []byte) (uint16, []byte, bool) {
	if len(in) < 2 {
		return 0, in, false
	}
	r := uint16(in[0])<<8 | uint16(in[1])
	return r, in[2:], true
}

func getU32(in []byte) (uint32, []byte, bool) {
	if len(in) < 4 {
		return 0, in, false
	}
	r := uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3])
	return r, in[4:], true
}

func getMPI(in []byte) (*big.Int, []byte, bool) {
	l, in, ok := getU32(in)
	if !ok || uint32(len(in)) < l {
		return nil, in, false
	}
	r := new(big.Int).SetBytes(in[:l])
	return r, in[l:], true
}

func getData(in []byte) ([]byte, []byte, bool) {
	l, in, ok := getU32(in)
	if !ok || uint32(len(in)) < l {
		return nil, in, false
	}
	return in[:l], in[l:], true
}

func getNBytes(in []byte, n int) ([]byte, []byte, bool) {
	if len(in) < n {
		return nil, in, false
	}
	return in[:n], in[n:], true
}

func appendU16(out []byte, v uint16) []byte {
	out = append(out, byte(v>>8), byte(v))
	return out
}

func appendU32(out []byte, v uint32) []byte {
	out = append(out, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
	return out
}

func appendData(out, v []byte) []byte {
	out = appendU32(out, uint32(len(v)))
	out = append(out, v...)
	return out
}

func appendMPI(out []byte, v *big.Int) []byte {
	vBytes := v.Bytes()
	out = appendU32(out, uint32(len(vBytes)))
	out = append(out, vBytes...)
	return out
}

func appendMPIs(out []byte, mpis ...*big.Int) []byte {
	for _, mpi := range mpis {
		out = appendMPI(out, mpi)
	}
	return out
}

func zero(b []byte) {
	for i := range b {
		b[i] = 0
	}
}


text:
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements the Socialist Millionaires Protocol as described in
// http://www.cypherpunks.ca/otr/Protocol-v2-3.1.0.html. The protocol
// specification is required in order to understand this code and, where
// possible, the variable names in the code match up with the spec.

package otr

import (
	"bytes"
	"crypto/sha256"
	"errors"
	"hash"
	"math/big"
)

type smpFailure string

func (s smpFailure) Error() string {
	return string(s)
}

var smpFailureError = smpFailure("otr: SMP protocol failed")
var smpSecretMissingError = smpFailure("otr: mutual secret needed")

const smpVersion = 1

const (
	smpState1 = iota
	smpState2
	smpState3
	smpState4
)

type smpState struct {
	state                  int
	a2, a3, b2, b3, pb, qb *big.Int
	g2a, g3a               *big.Int
	g2, g3                 *big.Int
	g3b, papb, qaqb, ra    *big.Int
	saved                  *tlv
	secret                 *big.Int
	question               string
}

func (c *Conversation) startSMP(question string) (tlvs []tlv) {
	if c.smp.state != smpState1 {
		tlvs = append(tlvs, c.generateSMPAbort())
	}
	tlvs = append(tlvs, c.generateSMP1(question))
	c.smp.question = ""
	c.smp.state = smpState2
	return
}

func (c *Conversation) resetSMP() {
	c.smp.state = smpState1
	c.smp.secret = nil
	c.smp.question = ""
}

func (c *Conversation) processSMP(in tlv) (out tlv, complete bool, err error) {
	data := in.data

	switch in.typ {
	case tlvTypeSMPAbort:
		if c.smp.state != smpState1 {
			err = smpFailureError
		}
		c.resetSMP()
		return
	case tlvTypeSMP1WithQuestion:
		// We preprocess this into a SMP1 message.
		nulPos := bytes.IndexByte(data, 0)
		if nulPos == -1 {
			err = errors.New("otr: SMP message with question didn't contain a NUL byte")
			return
		}
		c.smp.question = string(data[:nulPos])
		data = data[nulPos+1:]
	}

	numMPIs, data, ok := getU32(data)
	if !ok || numMPIs > 20 {
		err = errors.New("otr: corrupt SMP message")
		return
	}

	mpis := make([]*big.Int, numMPIs)
	for i := range mpis {
		var ok bool
		mpis[i], data, ok = getMPI(data)
		if !ok {
			err = errors.New("otr: corrupt SMP message")
			return
		}
	}

	switch in.typ {
	case tlvTypeSMP1, tlvTypeSMP1WithQuestion:
		if c.smp.state != smpState1 {
			c.resetSMP()
			out = c.generateSMPAbort()
			return
		}
		if c.smp.secret == nil {
			err = smpSecretMissingError
			return
		}
		if err = c.processSMP1(mpis); err != nil {
			return
		}
		c.smp.state = smpState3
		out = c.generateSMP2()
	case tlvTypeSMP2:
		if c.smp.state != smpState2 {
			c.resetSMP()
			out = c.generateSMPAbort()
			return
		}
		if out, err = c.processSMP2(mpis); err != nil {
			out = c.generateSMPAbort()
			return
		}
		c.smp.state = smpState4
	case tlvTypeSMP3:
		if c.smp.state != smpState3 {
			c.resetSMP()
			out = c.generateSMPAbort()
			return
		}
		if out, err = c.processSMP3(mpis); err != nil {
			return
		}
		c.smp.state = smpState1
		c.smp.secret = nil
		complete = true
	case tlvTypeSMP4:
		if c.smp.state != smpState4 {
			c.resetSMP()
			out = c.generateSMPAbort()
			return
		}
		if err = c.processSMP4(mpis); err != nil {
			out = c.generateSMPAbort()
			return
		}
		c.smp.state = smpState1
		c.smp.secret = nil
		complete = true
	default:
		panic("unknown SMP message")
	}

	return
}

func (c *Conversation) calcSMPSecret(mutualSecret []byte, weStarted bool) {
	h := sha256.New()
	h.Write([]byte{smpVersion})
	if weStarted {
		h.Write(c.PrivateKey.PublicKey.Fingerprint())
		h.Write(c.TheirPublicKey.Fingerprint())
	} else {
		h.Write(c.TheirPublicKey.Fingerprint())
		h.Write(c.PrivateKey.PublicKey.Fingerprint())
	}
	h.Write(c.SSID[:])
	h.Write(mutualSecret)
	c.smp.secret = new(big.Int).SetBytes(h.Sum(nil))
}

func (c *Conversation) generateSMP1(question string) tlv {
	var randBuf [16]byte
	c.smp.a2 = c.randMPI(randBuf[:])
	c.smp.a3 = c.randMPI(randBuf[:])
	g2a := new(big.Int).Exp(g, c.smp.a2, p)
	g3a := new(big.Int).Exp(g, c.smp.a3, p)
	h := sha256.New()

	r2 := c.randMPI(randBuf[:])
	r := new(big.Int).Exp(g, r2, p)
	c2 := new(big.Int).SetBytes(hashMPIs(h, 1, r))
	d2 := new(big.Int).Mul(c.smp.a2, c2)
	d2.Sub(r2, d2)
	d2.Mod(d2, q)
	if d2.Sign() < 0 {
		d2.Add(d2, q)
	}

	r3 := c.randMPI(randBuf[:])
	r.Exp(g, r3, p)
	c3 := new(big.Int).SetBytes(hashMPIs(h, 2, r))
	d3 := new(big.Int).Mul(c.smp.a3, c3)
	d3.Sub(r3, d3)
	d3.Mod(d3, q)
	if d3.Sign() < 0 {
		d3.Add(d3, q)
	}

	var ret tlv
	if len(question) > 0 {
		ret.typ = tlvTypeSMP1WithQuestion
		ret.data = append(ret.data, question...)
		ret.data = append(ret.data, 0)
	} else {
		ret.typ = tlvTypeSMP1
	}
	ret.data = appendU32(ret.data, 6)
	ret.data = appendMPIs(ret.data, g2a, c2, d2, g3a, c3, d3)
	return ret
}

func (c *Conversation) processSMP1(mpis []*big.Int) error {
	if len(mpis) != 6 {
		return errors.New("otr: incorrect number of arguments in SMP1 message")
	}
	g2a := mpis[0]
	c2 := mpis[1]
	d2 := mpis[2]
	g3a := mpis[3]
	c3 := mpis[4]
	d3 := mpis[5]
	h := sha256.New()

	r := new(big.Int).Exp(g, d2, p)
	s := new(big.Int).Exp(g2a, c2, p)
	r.Mul(r, s)
	r.Mod(r, p)
	t := new(big.Int).SetBytes(hashMPIs(h, 1, r))
	if c2.Cmp(t) != 0 {
		return errors.New("otr: ZKP c2 incorrect in SMP1 message")
	}
	r.Exp(g, d3, p)
	s.Exp(g3a, c3, p)
	r.Mul(r, s)
	r.Mod(r, p)
	t.SetBytes(hashMPIs(h, 2, r))
	if c3.Cmp(t) != 0 {
		return errors.New("otr: ZKP c3 incorrect in SMP1 message")
	}

	c.smp.g2a = g2a
	c.smp.g3a = g3a
	return nil
}

func (c *Conversation) generateSMP2() tlv {
	var randBuf [16]byte
	b2 := c.randMPI(randBuf[:])
	c.smp.b3 = c.randMPI(randBuf[:])
	r2 := c.randMPI(randBuf[:])
	r3 := c.randMPI(randBuf[:])
	r4 := c.randMPI(randBuf[:])
	r5 := c.randMPI(randBuf[:])
	r6 := c.randMPI(randBuf[:])

	g2b := new(big.Int).Exp(g, b2, p)
	g3b := new(big.Int).Exp(g, c.smp.b3, p)

	r := new(big.Int).Exp(g, r2, p)
	h := sha256.New()
	c2 := new(big.Int).SetBytes(hashMPIs(h, 3, r))
	d2 := new(big.Int).Mul(b2, c2)
	d2.Sub(r2, d2)
	d2.Mod(d2, q)
	if d2.Sign() < 0 {
		d2.Add(d2, q)
	}

	r.Exp(g, r3, p)
	c3 := new(big.Int).SetBytes(hashMPIs(h, 4, r))
	d3 := new(big.Int).Mul(c.smp.b3, c3)
	d3.Sub(r3, d3)
	d3.Mod(d3, q)
	if d3.Sign() < 0 {
		d3.Add(d3, q)
	}

	c.smp.g2 = new(big.Int).Exp(c.smp.g2a, b2, p)
	c.smp.g3 = new(big.Int).Exp(c.smp.g3a, c.smp.b3, p)
	c.smp.pb = new(big.Int).Exp(c.smp.g3, r4, p)
	c.smp.qb = new(big.Int).Exp(g, r4, p)
	r.Exp(c.smp.g2, c.smp.secret, p)
	c.smp.qb.Mul(c.smp.qb, r)
	c.smp.qb.Mod(c.smp.qb, p)

	s := new(big.Int)
	s.Exp(c.smp.g2, r6, p)
	r.Exp(g, r5, p)
	s.Mul(r, s)
	s.Mod(s, p)
	r.Exp(c.smp.g3, r5, p)
	cp := new(big.Int).SetBytes(hashMPIs(h, 5, r, s))

	// D5 = r5 - r4 cP mod q and D6 = r6 - y cP mod q

	s.Mul(r4, cp)
	r.Sub(r5, s)
	d5 := new(big.Int).Mod(r, q)
	if d5.Sign() < 0 {
		d5.Add(d5, q)
	}

	s.Mul(c.smp.secret, cp)
	r.Sub(r6, s)
	d6 := new(big.Int).Mod(r, q)
	if d6.Sign() < 0 {
		d6.Add(d6, q)
	}

	var ret tlv
	ret.typ = tlvTypeSMP2
	ret.data = appendU32(ret.data, 11)
	ret.data = appendMPIs(ret.data, g2b, c2, d2, g3b, c3, d3, c.smp.pb, c.smp.qb, cp, d5, d6)
	return ret
}

func (c *Conversation) processSMP2(mpis []*big.Int) (out tlv, err error) {
	if len(mpis) != 11 {
		err = errors.New("otr: incorrect number of arguments in SMP2 message")
		return
	}
	g2b := mpis[0]
	c2 := mpis[1]
	d2 := mpis[2]
	g3b := mpis[3]
	c3 := mpis[4]
	d3 := mpis[5]
	pb := mpis[6]
	qb := mpis[7]
	cp := mpis[8]
	d5 := mpis[9]
	d6 := mpis[10]
	h := sha256.New()

	r := new(big.Int).Exp(g, d2, p)
	s := new(big.Int).Exp(g2b, c2, p)
	r.Mul(r, s)
	r.Mod(r, p)
	s.SetBytes(hashMPIs(h, 3, r))
	if c2.Cmp(s) != 0 {
		err = errors.New("otr: ZKP c2 failed in SMP2 message")
		return
	}

	r.Exp(g, d3, p)
	s.Exp(g3b, c3, p)
	r.Mul(r, s)
	r.Mod(r, p)
	s.SetBytes(hashMPIs(h, 4, r))
	if c3.Cmp(s) != 0 {
		err = errors.New("otr: ZKP c3 failed in SMP2 message")
		return
	}

	c.smp.g2 = new(big.Int).Exp(g2b, c.smp.a2, p)
	c.smp.g3 = new(big.Int).Exp(g3b, c.smp.a3, p)

	r.Exp(g, d5, p)
	s.Exp(c.smp.g2, d6, p)
	r.Mul(r, s)
	s.Exp(qb, cp, p)
	r.Mul(r, s)
	r.Mod(r, p)

	s.Exp(c.smp.g3, d5, p)
	t := new(big.Int).Exp(pb, cp, p)
	s.Mul(s, t)
	s.Mod(s, p)
	t.SetBytes(hashMPIs(h, 5, s, r))
	if cp.Cmp(t) != 0 {
		err = errors.New("otr: ZKP cP failed in SMP2 message")
		return
	}

	var randBuf [16]byte
	r4 := c.randMPI(randBuf[:])
	r5 := c.randMPI(randBuf[:])
	r6 := c.randMPI(randBuf[:])
	r7 := c.randMPI(randBuf[:])

	pa := new(big.Int).Exp(c.smp.g3, r4, p)
	r.Exp(c.smp.g2, c.smp.secret, p)
	qa := new(big.Int).Exp(g, r4, p)
	qa.Mul(qa, r)
	qa.Mod(qa, p)

	r.Exp(g, r5, p)
	s.Exp(c.smp.g2, r6, p)
	r.Mul(r, s)
	r.Mod(r, p)

	s.Exp(c.smp.g3, r5, p)
	cp.SetBytes(hashMPIs(h, 6, s, r))

	r.Mul(r4, cp)
	d5 = new(big.Int).Sub(r5, r)
	d5.Mod(d5, q)
	if d5.Sign() < 0 {
		d5.Add(d5, q)
	}

	r.Mul(c.smp.secret, cp)
	d6 = new(big.Int).Sub(r6, r)
	d6.Mod(d6, q)
	if d6.Sign() < 0 {
		d6.Add(d6, q)
	}

	r.ModInverse(qb, p)
	qaqb := new(big.Int).Mul(qa, r)
	qaqb.Mod(qaqb, p)

	ra := new(big.Int).Exp(qaqb, c.smp.a3, p)
	r.Exp(qaqb, r7, p)
	s.Exp(g, r7, p)
	cr := new(big.Int).SetBytes(hashMPIs(h, 7, s, r))

	r.Mul(c.smp.a3, cr)
	d7 := new(big.Int).Sub(r7, r)
	d7.Mod(d7, q)
	if d7.Sign() < 0 {
		d7.Add(d7, q)
	}

	c.smp.g3b = g3b
	c.smp.qaqb = qaqb

	r.ModInverse(pb, p)
	c.smp.papb = new(big.Int).Mul(pa, r)
	c.smp.papb.Mod(c.smp.papb, p)
	c.smp.ra = ra

	out.typ = tlvTypeSMP3
	out.data = appendU32(out.data, 8)
	out.data = appendMPIs(out.data, pa, qa, cp, d5, d6, ra, cr, d7)
	return
}

func (c *Conversation) processSMP3(mpis []*big.Int) (out tlv, err error) {
	if len(mpis) != 8 {
		err = errors.New("otr: incorrect number of arguments in SMP3 message")
		return
	}
	pa := mpis[0]
	qa := mpis[1]
	cp := mpis[2]
	d5 := mpis[3]
	d6 := mpis[4]
	ra := mpis[5]
	cr := mpis[6]
	d7 := mpis[7]
	h := sha256.New()

	r := new(big.Int).Exp(g, d5, p)
	s := new(big.Int).Exp(c.smp.g2, d6, p)
	r.Mul(r, s)
	s.Exp(qa, cp, p)
	r.Mul(r, s)
	r.Mod(r, p)

	s.Exp(c.smp.g3, d5, p)
	t := new(big.Int).Exp(pa, cp, p)
	s.Mul(s, t)
	s.Mod(s, p)
	t.SetBytes(hashMPIs(h, 6, s, r))
	if t.Cmp(cp) != 0 {
		err = errors.New("otr: ZKP cP failed in SMP3 message")
		return
	}

	r.ModInverse(c.smp.qb, p)
	qaqb := new(big.Int).Mul(qa, r)
	qaqb.Mod(qaqb, p)

	r.Exp(qaqb, d7, p)
	s.Exp(ra, cr, p)
	r.Mul(r, s)
	r.Mod(r, p)

	s.Exp(g, d7, p)
	t.Exp(c.smp.g3a, cr, p)
	s.Mul(s, t)
	s.Mod(s, p)
	t.SetBytes(hashMPIs(h, 7, s, r))
	if t.Cmp(cr) != 0 {
		err = errors.New("otr: ZKP cR failed in SMP3 message")
		return
	}

	var randBuf [16]byte
	r7 := c.randMPI(randBuf[:])
	rb := new(big.Int).Exp(qaqb, c.smp.b3, p)

	r.Exp(qaqb, r7, p)
	s.Exp(g, r7, p)
	cr = new(big.Int).SetBytes(hashMPIs(h, 8, s, r))

	r.Mul(c.smp.b3, cr)
	d7 = new(big.Int).Sub(r7, r)
	d7.Mod(d7, q)
	if d7.Sign() < 0 {
		d7.Add(d7, q)
	}

	out.typ = tlvTypeSMP4
	out.data = appendU32(out.data, 3)
	out.data = appendMPIs(out.data, rb, cr, d7)

	r.ModInverse(c.smp.pb, p)
	r.Mul(pa, r)
	r.Mod(r, p)
	s.Exp(ra, c.smp.b3, p)
	if r.Cmp(s) != 0 {
		err = smpFailureError
	}

	return
}

func (c *Conversation) processSMP4(mpis []*big.Int) error {
	if len(mpis) != 3 {
		return errors.New("otr: incorrect number of arguments in SMP4 message")
	}
	rb := mpis[0]
	cr := mpis[1]
	d7 := mpis[2]
	h := sha256.New()

	r := new(big.Int).Exp(c.smp.qaqb, d7, p)
	s := new(big.Int).Exp(rb, cr, p)
	r.Mul(r, s)
	r.Mod(r, p)

	s.Exp(g, d7, p)
	t := new(big.Int).Exp(c.smp.g3b, cr, p)
	s.Mul(s, t)
	s.Mod(s, p)
	t.SetBytes(hashMPIs(h, 8, s, r))
	if t.Cmp(cr) != 0 {
		return errors.New("otr: ZKP cR failed in SMP4 message")
	}

	r.Exp(rb, c.smp.a3, p)
	if r.Cmp(c.smp.papb) != 0 {
		return smpFailureError
	}

	return nil
}

func (c *Conversation) generateSMPAbort() tlv {
	return tlv{typ: tlvTypeSMPAbort}
}

func hashMPIs(h hash.Hash, magic byte, mpis ...*big.Int) []byte {
	if h != nil {
		h.Reset()
	} else {
		h = sha256.New()
	}

	h.Write([]byte{magic})
	for _, mpi := range mpis {
		h.Write(appendMPI(nil, mpi))
	}
	return h.Sum(nil)
}


text:
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This code can be compiled and used to test the otr package against libotr.
// See otr_test.go.

// +build ignore

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <proto.h>
#include <message.h>
#include <privkey.h>

static int g_session_established = 0;

OtrlPolicy policy(void *opdata, ConnContext *context) {
  return OTRL_POLICY_ALWAYS;
}

int is_logged_in(void *opdata, const char *accountname, const char *protocol,
                 const char *recipient) {
  return 1;
}

void inject_message(void *opdata, const char *accountname, const char *protocol,
                    const char *recipient, const char *message) {
  printf("%s\n", message);
  fflush(stdout);
  fprintf(stderr, "libotr helper sent: %s\n", message);
}

void update_context_list(void *opdata) {}

void new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
                     const char *protocol, const char *username,
                     unsigned char fingerprint[20]) {
  fprintf(stderr, "NEW FINGERPRINT\n");
  g_session_established = 1;
}

void write_fingerprints(void *opdata) {}

void gone_secure(void *opdata, ConnContext *context) {}

void gone_insecure(void *opdata, ConnContext *context) {}

void still_secure(void *opdata, ConnContext *context, int is_reply) {}

int max_message_size(void *opdata, ConnContext *context) { return 99999; }

const char *account_name(void *opdata, const char *account,
                         const char *protocol) {
  return "ACCOUNT";
}

void account_name_free(void *opdata, const char *account_name) {}

const char *error_message(void *opdata, ConnContext *context,
                          OtrlErrorCode err_code) {
  return "ERR";
}

void error_message_free(void *opdata, const char *msg) {}

void resent_msg_prefix_free(void *opdata, const char *prefix) {}

void handle_smp_event(void *opdata, OtrlSMPEvent smp_event,
                      ConnContext *context, unsigned short progress_event,
                      char *question) {}

void handle_msg_event(void *opdata, OtrlMessageEvent msg_event,
                      ConnContext *context, const char *message,
                      gcry_error_t err) {
  fprintf(stderr, "msg event: %d %s\n", msg_event, message);
}

OtrlMessageAppOps uiops = {
    policy,
    NULL,
    is_logged_in,
    inject_message,
    update_context_list,
    new_fingerprint,
    write_fingerprints,
    gone_secure,
    gone_insecure,
    still_secure,
    max_message_size,
    account_name,
    account_name_free,
    NULL, /* received_symkey */
    error_message,
    error_message_free,
    NULL, /* resent_msg_prefix */
    resent_msg_prefix_free,
    handle_smp_event,
    handle_msg_event,
    NULL /* create_instag */,
    NULL /* convert_msg */,
    NULL /* convert_free */,
    NULL /* timer_control */,
};

static const char kPrivateKeyData[] =
    "(privkeys (account (name \"account\") (protocol proto) (private-key (dsa "
    "(p "
    "#00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F"
    "30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E"
    "5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB"
    "8C031D3561FECEE72EBB4A090D450A9B7A857#) (q "
    "#00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#) (g "
    "#535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F"
    "1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F"
    "6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57"
    "597766A2F9CE3857D7ACE3E1E3BC1FC6F26#) (y "
    "#0AC8670AD767D7A8D9D14CC1AC6744CD7D76F993B77FFD9E39DF01E5A6536EF65E775FCEF"
    "2A983E2A19BD6415500F6979715D9FD1257E1FE2B6F5E1E74B333079E7C880D39868462A93"
    "454B41877BE62E5EF0A041C2EE9C9E76BD1E12AE25D9628DECB097025DD625EF49C3258A1A"
    "3C0FF501E3DC673B76D7BABF349009B6ECF#) (x "
    "#14D0345A3562C480A039E3C72764F72D79043216#)))))\n";

int main() {
  OTRL_INIT;

  // We have to write the private key information to a file because the libotr
  // API demands a filename to read from.
  const char *tmpdir = "/tmp";
  if (getenv("TMP")) {
    tmpdir = getenv("TMP");
  }

  char private_key_file[256];
  snprintf(private_key_file, sizeof(private_key_file),
           "%s/libotr_test_helper_privatekeys-XXXXXX", tmpdir);
  int fd = mkstemp(private_key_file);
  if (fd == -1) {
    perror("creating temp file");
  }
  write(fd, kPrivateKeyData, sizeof(kPrivateKeyData) - 1);
  close(fd);

  OtrlUserState userstate = otrl_userstate_create();
  otrl_privkey_read(userstate, private_key_file);
  unlink(private_key_file);

  fprintf(stderr, "libotr helper started\n");

  char buf[4096];

  for (;;) {
    char *message = fgets(buf, sizeof(buf), stdin);
    if (strlen(message) == 0) {
      break;
    }
    message[strlen(message) - 1] = 0;
    fprintf(stderr, "libotr helper got: %s\n", message);

    char *newmessage = NULL;
    OtrlTLV *tlvs;
    int ignore_message = otrl_message_receiving(
        userstate, &uiops, NULL, "account", "proto", "peer", message,
        &newmessage, &tlvs, NULL, NULL, NULL);
    if (tlvs) {
      otrl_tlv_free(tlvs);
    }

    if (newmessage != NULL) {
      fprintf(stderr, "libotr got: %s\n", newmessage);
      otrl_message_free(newmessage);

      gcry_error_t err;
      char *newmessage = NULL;

      err = otrl_message_sending(userstate, &uiops, NULL, "account", "proto",
                                 "peer", 0, "test message", NULL, &newmessage,
                                 OTRL_FRAGMENT_SEND_SKIP, NULL, NULL, NULL);
      if (newmessage == NULL) {
        fprintf(stderr, "libotr didn't encrypt message\n");
        return 1;
      }
      write(1, newmessage, strlen(newmessage));
      write(1, "\n", 1);
      fprintf(stderr, "libotr sent: %s\n", newmessage);
      otrl_message_free(newmessage);

      g_session_established = 0;
      write(1, "?OTRv2?\n", 8);
      fprintf(stderr, "libotr sent: ?OTRv2\n");
    }
  }

  return 0;
}


text:
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ripemd160

// Test vectors are from:
// http://homes.esat.kuleuven.be/~bosselae/ripemd160.html

import (
	"fmt"
	"io"
	"testing"
)

type mdTest struct {
	out string
	in  string
}

var vectors = [...]mdTest{
	{"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""},
	{"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"},
	{"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"},
	{"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"},
	{"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"},
	{"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
	{"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
	{"9b752e45573d4b39f4dbd3323cab82bf63326bfb", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
}

func TestVectors(t *testing.T) {
	for i := 0; i < len(vectors); i++ {
		tv := vectors[i]
		md := New()
		for j := 0; j < 3; j++ {
			if j < 2 {
				io.WriteString(md, tv.in)
			} else {
				io.WriteString(md, tv.in[0:len(tv.in)/2])
				md.Sum(nil)
				io.WriteString(md, tv.in[len(tv.in)/2:])
			}
			s := fmt.Sprintf("%x", md.Sum(nil))
			if s != tv.out {
				t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out)
			}
			md.Reset()
		}
	}
}

func millionA() string {
	md := New()
	for i := 0; i < 100000; i++ {
		io.WriteString(md, "aaaaaaaaaa")
	}
	return fmt.Sprintf("%x", md.Sum(nil))
}

func TestMillionA(t *testing.T) {
	const out = "52783243c1697bdbe16d37f97f68f08325dc1528"
	if s := millionA(); s != out {
		t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out)
	}
}

func BenchmarkMillionA(b *testing.B) {
	for i := 0; i < b.N; i++ {
		millionA()
	}
}