Шифр Цезаря на ассемблере семейства i686 + ММХ


Я написал следующую реализацию шифр Цезаря на старый компьютер Linux с чип Pentium ММХ. Код был написан с целью конструкции в виду:

  • код должен выполнить хорошо
  • чтобы избежать бокового канала время атаки, во время выполнения должны быть независимы от данных, используемых
  • код должен работать на Pentium ММХ

Пожалуйста, дайте мне знать о возможных усовершенствованиях и ловушки я упал.

    # MMX caeasar cipher implementation
    # for i586 with MMX
    # signature:
    # caesar(out, in, len, key)
    # key is between 0 and 25

    .section .text
    .globl caesar
    .type caesar,@function
    .align 16
caesar:
    push %ebp
    mov %esp,%ebp
    push %ebx
    push %edi
    push %esi

    mov 8(%ebp),%edi        # edi: destination
    mov 12(%ebp),%esi       # esi: source
    mov 16(%ebp),%edx       # edx: length
    and $~7,%edx            # only process full qwords
    test %edx,%edx
    jz 1f

    xor %ecx,%ecx           # ecx: index
    movd 20(%ebp),%mm5
    punpcklbw %mm5,%mm5
    punpcklwd %mm5,%mm5
    punpckldq %mm5,%mm5     # mm5: key bytes

    movq %mm5,%mm4
    psubb twentysix,%mm4        # mm4: key - 26

    movq Amask,%mm6
    psubb %mm4,%mm6         # mm6: 'A' - 1 - (key - 26)

    .align 16
0:  movq (%esi,%ecx),%mm0
    movq %mm0,%mm1
    pand ucmask,%mm1        # mm1: xmm0 in upper case

    movq %mm1,%mm3
    movq %mm1,%mm2

    pcmpgtb Amask,%mm2      # mm2: 0xff where 'A' <= buf[i]
    pcmpgtb Zmask,%mm3      # mm3: 0xff where 'Z' < buf[i]
    pcmpgtb %mm6,%mm1       # mm1: 0xff where 'A' + key <= buf[i]

    pandn %mm1,%mm3
    pand %mm4,%mm3          # mm3: key-26 where 'A' + (26 - key) <= buf[i] <= 'Z'

    pandn %mm2,%mm1
    pand %mm5,%mm1          # mm1: key where 'A' <= buf[i] < 'A' + (26 - key)

    por %mm3,%mm1           # mm1: 0/key/key-26
    paddb %mm1,%mm0
    movq %mm0,(%edi,%ecx)

    add $8,%ecx
    cmp %edx,%ecx
    jb 0b
    emms
    add %ecx,%edi
    add %ecx,%esi

1:  mov 16(%ebp),%edx       # length
    mov 20(%ebp),%ecx       # key
    and $7,%edx         # loop tail

    # process remaining bytes
    add %edx,%edi       # end of output buffer
    add %edx,%esi       # end of input buffer
    neg %edx        # index counts up to 0

    lea -26(%ecx),%eax  # al: key - 26
    mov $'A'+26-1,%ah
    sub %cl,%ah     # ah: threshold ('A' - 1 - (key - 26))

    .align 16
0:  mov (%esi,%edx),%bl
    and $~0x20,%bl      # bl: uppercase c
    cmp $'Z'+1,%bl      # cf: uc <= 'Z'
    sbb %ch,%ch     # ch: 0xff if uc <= 'Z'
    cmp %bl,%ah     # cf: thresh < uc
    sbb %bh,%bh     # bh: 0xff if thresh < uc
    and %bh,%ch
    and %al,%ch     # ch: key - 26 & isleZ & isgttthres
    not %bh         # bh: ~isgtthresh
    cmp $'A',%bl
    cmc         # cf: 'A' <= uc
    sbb %bl,%bl     # bl: 0xff if 'A' <= uc
    and %bh,%bl
    and %cl,%bl     # bl: key & isgeA & ~isgtthresh
    or %ch,%bl
    add (%esi,%edx),%bl
    mov %bl,(%edi,%edx)
    inc %edx
    jnz 0b

1:  pop %esi
    pop %edi
    pop %ebx
    leave
    ret
    .size caesar,.-caesar

    .section .rodata.cst8,"aM",@progbits,8
    .align 8
    .type ucmask,@object
ucmask: .fill 8, 1, ~0x20
    .size ucmask, 8
    .type Amask,@object
Amask:  .fill 8, 1, 'A' - 1
    .size Amask, 8
    .type Zmask,@object
Zmask:  .fill 8, 1, 'Z'
    .size Zmask, 8
    .type twentysix,@object
twentysix:
    .fill 8, 1, 26
    .size twentysix, 8

Вы можете использовать эту шлейку для проверки кода:

#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern void caesar(const char *out, char *in, size_t len, int key);

extern int
main(int argc, char *argv[])
{
    char *buf = NULL;
    size_t len = 0;
    ssize_t count;
    int key = 13;

    if (argc > 1)
        key = atoi(argv[1]) % 26;

    while (count = getline(&buf, &len, stdin), count != EOF) {
        caesar(buf, buf, strlen(buf), key);
        fputs(buf, stdout);
    }

    return (EXIT_SUCCESS);
}


206
9
задан 1 апреля 2018 в 06:04 Источник Поделиться
Комментарии
2 ответа

Большая опасность.

Когда лен параметра, случается, нескольким из 8 и все полный qwords были обработаны, не будет и оставшиеся байты в процесс.
Ваш код вычисляет количество оставшихся байтов, используя

mov 16(%ebp),%edx     # length
...
and $7,%edx # loop tail

но забывает выйти, если это производит ноль. Цикла, который следует начат в любом случае и внизу, где он читает

inc %edx
jnz 0b

приращение %edx будет продолжать производить НЗ в течение очень долгого времени!

Устранить эту выход если and инструкция устанавливает ZF=1.

mov 16(%ebp), %edx    # length
...
and $7, %edx # loop tail
jz 1f


Небольшая оптимизация


and $~7,%edx        # only process full qwords
test %edx,%edx
jz 1f

Потому что and инструкция определяет нулевой флаг (ЗФ), не нужно писать test %edx, %edx.


Позвольте мне


mov $'A'+26-1,%ah

Это ударило меня как являющийся немного слишком много осложнений. Почему бы просто не писать

mov $'Z', %ah

Теперь, если это было ваше намерение приблизиться к комментарию, который следует в следующей строке
# ah: threshold ('A' - 1 - (key - 26))
потом писать

lea -26(%ecx), %eax   # al: key - 26
mov $'A'-1, %ah
sub %al, %ah # ah: threshold ('A' - 1 - (key - 26))

пригвоздили бы его.

5
ответ дан 8 апреля 2018 в 03:04 Источник Поделиться

После применения советов, который я получил, это окончательная версия кода:

    # MMX caeasar chiffre implementation
# for i686 with MMX
# signature:
# caesar(out, in, len, key)
# key is between 0 and 25

.section .text
.globl caesar
.type caesar,@function
.align 16
caesar:
push %ebp
mov %esp,%ebp
push %ebx
push %edi
push %esi

mov 8(%ebp),%edi # edi: destination
mov 12(%ebp),%esi # esi: source
mov 16(%ebp),%edx # edx: length
and $~7,%edx # only process full qwords
test %edx,%edx
jz 1f

xor %ecx,%ecx # ecx: index
movd 20(%ebp),%mm5
punpcklbw %mm5,%mm5
punpcklwd %mm5,%mm5
punpckldq %mm5,%mm5 # mm5: key bytes

movq Amask,%mm7 # for later use

movq %mm5,%mm4
psubb twentysix,%mm4 # mm4: key - 26

movq %mm7,%mm6
psubb %mm4,%mm6 # mm6: 'A' - 1 - (key - 26)

.align 16
0: movq (%esi,%ecx),%mm0
movq %mm0,%mm1
pand ucmask,%mm1 # mm1: xmm0 in upper case

movq %mm1,%mm3
movq %mm1,%mm2

pcmpgtb %mm7,%mm2 # mm2: 0xff where 'A' <= buf[i]
pcmpgtb Zmask,%mm3 # mm3: 0xff where 'Z' < buf[i]
pcmpgtb %mm6,%mm1 # mm1: 0xff where 'A' + key <= buf[i]

pandn %mm1,%mm3
pand %mm4,%mm3 # mm3: key-26 where 'A' + (26 - key) <= buf[i] <= 'Z'

pandn %mm2,%mm1
pand %mm5,%mm1 # mm1: key where 'A' <= buf[i] < 'A' + (26 - key)

por %mm3,%mm1 # mm1: 0/key/key-26
paddb %mm1,%mm0
movq %mm0,(%edi,%ecx)

add $8,%ecx
cmp %edx,%ecx
jb 0b
emms
add %ecx,%edi
add %ecx,%esi

1: mov 16(%ebp),%edx # length
mov 20(%ebp),%ecx # key
and $7,%edx # loop tail
jz 1f # all bytes processed alread?

# process remaining bytes
add %edx,%edi # end of output buffer
add %edx,%esi # end of input buffer
neg %edx # index counts up to 0

lea -26(%ecx),%ebx # bl: key - 26
mov $'A'-1,%ah
sub %bl,%ah # ah: threshold ('A' - 1 - (key - 26))

.align 16
0: mov (%esi,%edx),%al
and $~0x20,%al # al: uppercase c
cmp $'Z'+1,%al # cf: uc <= 'Z'
sbb %ch,%ch # ch: 0xff if uc <= 'Z'
cmp %al,%ah # cf: thresh < uc
sbb %bh,%bh # bh: 0xff if thresh < uc
and %bh,%ch
and %al,%ch # ch: key - 26 & isleZ & isgttthres
not %bh # bh: ~isgtthresh
cmp $'A',%al
cmc # cf: 'A' <= uc
sbb %al,%al # al: 0xff if 'A' <= uc
and %bh,%al
and %cl,%al # al: key & isgeA & ~isgtthresh
or %ch,%al
add (%esi,%edx),%al
mov %al,(%edi,%edx)
inc %edx
jnz 0b

1: pop %esi
pop %edi
pop %ebx
leave
ret
.size caesar,.-caesar

.section .rodata.cst8,"aM",@progbits,8
.align 8
.type ucmask,@object
ucmask: .fill 8, 1, ~0x20
.size ucmask, 8
.type Amask,@object
Amask: .fill 8, 1, 'A' - 1
.size Amask, 8
.type Zmask,@object
Zmask: .fill 8, 1, 'Z'
.size Zmask, 8
.type twentysix,@object
twentysix:
.fill 8, 1, 26
.size twentysix, 8

0
ответ дан 18 декабря 2018 в 08:12 Источник Поделиться