Mais um blog inútil.

Useless

Dezembro 20, 2009

Jardim do Torel - Lisboa

Arquivado em: lulz, serious-business, useless — devnull @ 02:50

Ir em busca de um produto em particular que se encontrava numa loja perto do Campo Mártires da Pátria… E uma extrema vontade de ir passear. Foi assim que se iniciou a desventura da descoberta de um jardim,  num sábado de inverno soalheiro e frio.

Levantar da cama às duas da tarde que está frio… E ter uma súbita sensação de querer passear! Agarrámos na SPOT e lá fomos, comemos uma sandes num fast food pelo caminho que agora escurece cedo e já se faz tarde… Comprar o bilhete de comboio para a SPOT e lá fomos. (Sim, um bilhete de comboio para uma cadela! Infelizmente não se pode comprar de ida e volta, irónico…)

Chegando ao Cais do Sodré, dirigimo-nos à Praça do Comércio para percorrermos a Rua Augusta e olhar de surra para as montras, bancadas, performances de rua e gente… MONTES DE GENTE!). Atestámos a barriga no inicio da Rua Augusta com umas waffles simples e quentinhas (que as com cobertura iam fazer com que nos cagássemos todos). Lá percorremos a bagunça de gente que ia para ali amontoada em busca de sabe-se lá o quê…  e fomos dar ao Rossio.

Sempre em frente, pelas portas de Sto. Antão até nos depararmos com um elevador e uma subida íngreme. Chegámos à rua da Lavra com os seus práticos elevadores.
Mas decididos a ser corajosos, fizemos a inclinação a pé de 22%… chegados lá acima e com os bofes de fora, virámos para o lado esquerdo e deparamo-nos com o Jardim do Torel.

Desconhecendo totalmente aquele jardim decidimos ir investigar mais um pouco…

Com uma vista magnifica, como muitos dos miradouros de Lisboa, ficámos surpresos por ver um jardim tão limpinho e com um ar tão novo… Não vou dizer o que se vê de lá, vão lá e descubram… ;-) Tem uns banquinhos pequeninos virados para a cidade e uns “cogumelos de ferro” que ainda nao percebi para que servem porém não destoam muito do ambiente de aristocracia e nobreza que as estátuas do jardim e os edificios à volta fazem transparecer. A descida de volta pode ser feita pelas escadas presentes no jardim (sim, dá para sair “por baixo”) indo dar no final da descida, depois da passagem pela escola primária da freguesia de S. José, a uma rua perpendicular à Avenida da Liberdade.

Infelizmente não levei nem máquina fotográfica nem câmara de filmar, não podendo presentear-vos com imagens… no entanto uma breve pesquisa no google e encontram montes de informação útil e imagens boas.

Ah e o produto estava do outro lado da cidade, no Bairro Alto…

Dezembro 16, 2009

HD nas consolas - it's a TRAP!!!

Arquivado em: drama, fail, useless — falso @ 12:22

Ora viva amigos!

Estava eu ontem a bater um vício de Need for Speed: Shift numa PissStation3 dum amigo meu, quando reparei na caixa do jogo e vi o seguinte: Resolução - 720p. O que me deixou indignado, ate porque no PC jogo o mesmo jogo a 1080 com os gráficos melhores que na PS3, nunca pensei que isto fosse assim, devido ao hype todo da PS3, CPU ultra potente com 50 mil cores, melhor que pão com tulicreme etc…
Fui ver o resto da colecção de jogos dele, e eram todos assim.
Então este hype todo do HD nas consolas era só para vender inicialmente? Só nos Blugays é que se tem 1080? Ou pelo que me dizem no IRC existem praí DOIS jogos em 1080 lol…
Depois a conversa seguiu-se para “A PS3 faz UPSCALING para 1080 - portanto é bom”, man, sendo assim eu também jogo jogos do ZX SPECTRUM a 1080 com o hq4x e afins.
Agora quando me vierem gozar da resolução da Wii levam com esta e EMBRULHAM. Continuo a dizer, jogos -> NO PC.

Já agora, estava sedento de fazer uma review do sapo codebits como toda a blogosfera fez, então aqui vai:
LOL

Update: Devido ao facto do drama nos comentários aqui vai o belo do Manic Miner em HD, com upscaling, à-lá-PS3. Cliquem para aumentar!!!!

Como seria o MANIC MINER se tivesse saído para PS3
Como seria o MANIC MINER se tivesse saído para PS3

Outubro 2, 2009

Sites em C

Arquivado em: assembly, coding, useless — falso @ 02:23

Ora viva!

Já desde há uns tempos que se tinha falado nos mirques sobre fazer sites super optimizados em C à-lá-OpenBSD.

Então hoje num momento de aborrecimento decidi fazer um!

Teve de ser alta executável estático para poder ser executado dentro do chroot do Apache.

Basicamente, é uma pagina central mais dois requests que são feitos por ajax com ajuda do jQuery.

Até inclui alta inline assembly cortesia do dongs!

Vejam-no em acção aqui: lolajax (a extensão é .exe só para ser mais hacker, porque o que retorna é html ;-).

Setembro 16, 2009

Multiplicação de Polinómios

Arquivado em: coding, serious-business, useless — dcoder @ 20:17

Depois de um breve hiato, volto a este Blol colocar pensamentos inúteis. E hoje pensei na multiplicação de polinómios - em particular, polinómios com coeficientes mod 2, i.e.

$$\mathbb{F}_2[x]$$

. Como é que representamos estes polinómios?

Dado que cada coeficiente apenas pode ser 0 ou 1, podemos utilizar apenas um bit para estes. Assim, se tivermos um polinómio

$$x^3 + x + 1$$

, representamos o mesmo por 0b1011 ou 0x0B.

A adição de polinómios é trivial - consiste unicamente em aplicar a operação XOR, dado que XOR é adição mod 2. A multiplicação pode ser efectuada como efectuamos multiplicações de inteiros, excepto que ignoramos todos os transportes ( carries). Podemos então implementar uma multiplicação de polinómios como:

u32 polymul(u32 a, u32 b)
{
  u32 z = 0;
  while(b != 0)
  {
    if(b&1 != 0)
      z ^= a;
    b >>= 1; a <<= 1;
  }
  return z;
}

Isto, no entanto, contém branching e operações condicionais. Se usarmos C++, podemos criar uma função sem qualquer loop que nos permita efectuar a mesma operação:

template<u32 N>
inline u32 mul(u32 a, u32 b)
{
  return (a & -(b&1)) ^ mul<N-1>(a<<1, b>>1);
}

template<>
inline u32 mul<0>(u32 a, u32 b)
{
  return 0;
}

O parâmetro N, utilizado na instanciação da função, corresponde ao grau máximo dos polinómios de entrada. No caso de um inteiro de 32 bits, a soma dos graus de a e b não poderá exceder 32, de forma a evitar overflows.

Agosto 25, 2009

printf("oi lindinhos!\n");

Arquivado em: assembly, coding, cracking, useless — charlie-lindinho @ 20:06

há mil dias li um post do *xorl* intitulado de “Funny C programming interview questions”, achei uma certa piada ao raio do post e resolvi escrever aqui sobre isso.

no post o xorl apresenta a solução para uma das questões colocadas neste *site*, no qual nos é questionado se é possível fazer com que a variável “i” apresente outro valor, senão “2”, apenas adicionando código à função foo().

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

void foo(void);

int main (void)
{
	int i=2;
	foo();
	printf ("%d\n",i);
	return EXIT_SUCCESS;
}

void foo(void)
{
	// add code so that printf above prints different value of i (i.e. not 2)
} 

a primeira coisa que me ocorreu (em mil milésimos de segundo) foi modificar directamente o valor de “i” através da função “printf” e então fiz o seguinte :

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

void foo(void);

int main(void)
{
	int i = 2;
	foo();
	printf("%d\n", i);
	return EXIT_SUCCESS;
}

void foo(void)
{
	unsigned long addr;
	__asm("movl %%ebp, %0" : : "m"(addr));
	printf("%1$n", addr += 24);
}

o que fiz aqui foi calcular o endereço da variável “i” apartir do base pointer (EBP) e fazer com que a funçao printf escrevesse 0 bytes no endereço da mesma através do especificador de formato “%n”.

bash-3.1# gcc -o printf printf.c
bash-3.1# ./printf
0
bash-3.1# 

ainda assim existe outra solução óbvia da qual me lembrei também, que consiste em fechar o file descriptor utilizado pela função printf ( stdout ) depois da função foo().

void foo(void)
{
	fprintf(stderr, "%d\n", 0);
	close(1);
}

queria dedicar este post inútil ao cavalo que morreu hoje atropelado no brasil.

Agosto 21, 2009

oi lindinhos do craking

Arquivado em: coding, cracking, useless — charlie-lindinho @ 11:07

aqui há tempos ( há mil dias ) prometi ao david publicar umas merdas sobre anti-debugging
e ao pedro enviar um crackme, mas em vez disso ( e porque sou uma merda )
resolvi apresentar aqui um exemplo de fácil compreensão e a devida explicação.

aqui vai disto :

root@inferno:[~/dev/cracking]# gdb crackme
(no debugging symbols found)
(gdb) set disassembly-flavor intel
(gdb) set write
(gdb) show write
Writing into executable and core files is on.
(gdb) file crackme
Reading symbols from /root/dev/cracking/crackme…(no debugging symbols found)…done.

(gdb) disas _start
Dump of assembler code for function _start:
0x08048060 <_start+0>: pop eax
0x08048061 <_start+1>: dec eax
0x08048062 <_start+2>: and eax,eax
0x08048064 <_start+4>: je 0x8048080

o valor de argc é copiado para o registo EAX, em seguida este é decrementado,
pois se o crackme for executado sem argumentos o valor de argc será 1.

em seguida é realizada a operaçao lógica AND com o propósito de verificar
se o valor que reside em EAX ( argc ) é zero, caso seja, a função exit é então executada.

0x08048066 <_start+6>: mov bl,0x3
0x08048068 <_start+8>: inc eax
0x08048069 <_start+9>: and eax,ebx
0x0804806b <_start+11>: jne 0x8048080
End of assembler dump.

aqui é atribuido o valor 3 ao registo EBX
e o registo EAX, ao ser incrementado, contém agora o valor real de argc.

por fim é novamente realizada a operação lógica AND com os registos EAX e EBX.
se estes forem diferentes a função exit é executada, caso contrário
( se o numero de argumentos corresponder a 3 ) passamos à função done.

(gdb) disas done
Dump of assembler code for function done:
0x0804806d <done+0>: xor ebx,ebx
0x0804806f <done+2>: push 0xa
0x08048071 <done+4>: push 0x656e6f64
0x08048076 <done+9>: mov al,0x4
0x08048078 <done+11>: mov bl,0x1
0x0804807a <done+13>: mov dl,0x5
0x0804807c <done+15>: mov ecx,esp
0x0804807e <done+17>: int 0x80
End of assembler dump.
(gdb) disas exit
Dump of assembler code for function exit:
0x08048080 <exit+0>: push $0x1
0x08048082 <exit+2>: pop %eax
0x08048083 <exit+3>: int $0x80
End of assembler dump.

(gdb) r 1 2 3
Starting program: /root/dev/cracking/crackme 1 2 3
(no debugging symbols found)
done

Program exited with code 01.

(gdb) x/4b _start+4
0x8048064 <_start+4>: 0x74 0x1a 0xb3 0x03
(gdb) p 0x1a
$1 = 26

( onde 0x74 é o opcode da instrução JE e 0x1a, 26 em decimal. )

ou seja, para efectuar o “je done” em vez de “je exit”
basta-nos calcular a distância em bytes entre a instrução JE e a função done, que é de 7 bytes.

(gdb) p 11-4
$2 = 7

uma vez que temos permissão para escrever no executável,
vamos então modificar um byte no segmento de código do mesmo
e posteriormente executá-lo sem argumentos :

(gdb) set *0x08048064 = 0x03b30774

(gdb) x/4b 0x08048064
0x8048064 <_start+4>: 0x74 0x07 0xb3 0x03
(gdb) x/i 0x08048064
0x8048064 <_start+4>: je 0x804806d

(gdb) set write off
(gdb) file crackme
Reading symbols from /root/dev/cracking/crackme…(no debugging symbols found)…done.
(gdb) run
Starting program: /root/dev/cracking/crackme
(no debugging symbols found)
done

Program exited with code 01.
(gdb) q
root@inferno:[~/dev/cracking]#

como podemos verificar, foi-nos apresentada a mensagem “done”, cumprindo assim o objectivo deste crackme.
deixo-vos aqui uma pequena merda que fiz em hepatite C para facilitar ainda mais a tarefa.

agora vou beber mil cervejas para esquecer que escrevi isto e vou dormir.

[ crackme-patch.c ]

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>FILE *fp;

int main(int argc, char **argv){

unsigned int offset;
char *file = argv[1];

struct stat st;

if(stat(file, &st))
return EXIT_FAILURE;

fp = fopen(file, "r+");
offset = st.st_size;

while (offset) {

fseek(fp, offset, SEEK_SET);

if (fgetc(fp) == 0x1a)
patch(offset);

offset--;

}

fclose(fp);

return EXIT_SUCCESS;
}

int patch(int num){

printf("patching ... ");

fseek(fp, num, SEEK_SET);
fputc(0x07, fp);

printf("done.\n");

}

root@inferno:[~/dev/cracking]# nasm -f elf crackme.asm && ld -o crackme crackme.o
root@inferno:[~/dev/cracking]# ./crackme
root@inferno:[~/dev/cracking]# gcc -o crackme-patch crackme-patch.c
root@inferno:[~/dev/cracking]# ./crackme-patch ./crackme
patching ... done.
root@inferno:[~/dev/cracking]# ./crackme
done
root@inferno:[~/dev/cracking]#

PS: Este post foi feito plo MaDiNfO, já que o o charlie é uma merda e nem login consegue fazer com o seu proprio nick... (firefail dramas)

Agosto 4, 2009

Arvorezinha - LISP take 2

Arquivado em: arvorezinha, coding, drama, fail, useless — falso @ 22:23

Oi!

Segundo a LISP Police, vim a saber que a primeira arvorezinha em LISP do _Lone_Wolf_ não cumpria os standards definidos pelo rfc da arvorezinha, e ele para não sofrer as consequências redimiu-se com uma versão totalmente standard. Aqui vai ela:

  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8
  I  \ `+' /  I      8         8           8     8        8    8
   \  `-+-'  /       8         8           8      ooooo   8oooo
    `-__|__-'        8         8           8           8  8
        |            8     o   8           8     o     8  8
  ------+------       ooooo    8oooooo  ooo8ooo   ooooo   8
Welcome to GNU CLISP 2.47 (2008-10-23) <http://clisp.cons.org/>
Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2008
Type :h and hit Enter for context help.
[1]> (loop
    (loop for i from 1 to 4 do
        (loop for j from 1 to i do
            (write-char #\*)
        )
        (terpri)
    )
    (return '*****)
)
*
**
***
****
*****

Julho 31, 2009

kgme1

Arquivado em: assembly, cracking, linux, useless — falso @ 01:32

Ora viva!

Já há uns tempos que tinha falado com o Dongs para fazer um KeygenMe para eu tentar keygennar porque nunca o tinha feito. Ele então fez um e eu pus mãos à obra.

[ kgme.tar.bz2 ]

$ ./kgme
Name: falso
Serial: 12345
Bad serial!

Abri então o ficheiro no IDA e andei a procurar por “Bad serial!”, e então que achei. Vai aqui um dump comentado:

0804830C	movzx   ecx, byte ptr [esi] ; ESI contem o "User"
0804830C                                ; e ECX contem o primeiro char do "User"
0804830F	test    ecx, ecx        ; Se (ECX != 0x0) ZF=0 ELSE ZF=1
08048311	jz      short loc_8048333 ; Se ZF=1 Salta pro drama
08048313	lea     edx, [ebp-10Fh] ; EDX fica com "User" sem o primeiro char
08048319	mov     ebx, 0DEADh
0804831E	db      66h
0804831E	nop                     ; No Operation
08048320
08048320 loc_8048320:
08048320	mov     eax, ebx        ; EAX passa a tar 0xDEAD tambem
08048322	shl     eax, 5          ; Shift Logical Left 5 vezes
08048325	add     eax, ecx        ; Adiciona o valor do char do "User" (ECX) a EAX
08048327	movzx   ecx, byte ptr [edx] ; le o proximo char pra ECX
0804832A	add     edx, 1          ; Add
0804832D	add     ebx, eax        ; Add
0804832F	test    ecx, ecx        ; Logical Compare
08048331	jnz     short loc_8048320 ; Jump if Not Zero (ZF=0)
08048333
08048333 loc_8048333:
08048333	xor     edx, edx        ; Logical Exclusive OR
08048335	mov     eax, offset aBadSerial ; "Bad serial!"
0804833A	cmp     [ebp-10h], ebx  ; Compare Two Operands
0804833D	setz    dl              ; Set Byte if Zero (ZF=1)
08048340	xor     eax, offset aGoodSerial ; "Good serial!"
08048345	neg     edx             ; Two's Complement Negation
08048347	and     eax, edx        ; Logical AND
08048349	xor     eax, offset aBadSerial ; "Bad serial!"

Então o funcionamento da coisa é mais ou menos o seguinte:

/\* inicialização(?) */
\* Le o Name para o registo ESI
\* Mete o primeiro char do Name no registo ECX
\* EDX fica com o Name sem o primeiro char
\* EBX fica com o valor 0xDEAD

/\* ciclo central */
\* Copia o valor de EBX para EAX
\* Faz um shift left 5 vezes a EAX
\* Adiciona o valor do char em ECX a EAX
\* Le o proximo char do Name para ECX
\* Adiciona 1 a EDX
\* Adiciona EAX a EBX
\* Se ECX nao for diferente de 0 salta para o inicio do ciclo

Vou tentar dar um exemplo:

User: Jim

/\* inicialização */
\* ESI = Jim
\* ECX = J (0x4a)
\* EDX = im
\* EBX = 0xdead

( valores dos registos )
-- ECX = J | EDX = im | EBX = 0xdead –

/\* ciclo central */
( Primeiro ciclo - J )
\* EAX = EBX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0xdead –
\* shift left 5 vezes a EAX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0x1bd5a0 –
\* adiciona o valor do char em ECX (J - 0x4a) a EAX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0x1bd5ea –
\* Le o proximo char do Name pra ECX
-- ECX = i | EDX = im | EBX = 0xdead | EAX = 0x1bd5ea –
\* Adiciona 1 a EDX
-- ECX = i | EDX = m | EBX = 0xdead | EAX = 0x1bd5ea –
\* Adiciona EAX a EBX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x1bd5ea –
\* Se ECX é diferente de 0 então do inicio de novo!

( Segundo ciclo - i )
\* EAX = EBX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x1cb497 –
\* shift left 5 vezes a EAX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x39692e0 –
\* adiciona o valor do char em ECX (i - 0x69) a EAX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Le o proximo char do Name pra ECX
-- ECX = m | EDX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Adiciona 1 a EDX (fica limpo)
-- ECX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Adiciona EAX a EBX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x3969349 –
\* Se ECX é diferente de 0 então do inicio de novo!

( Terceiro ciclo - m )
\* EAX = EBX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x3b347e0 –
\* shift left 5 vezes a EAX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x7668fc00 –
\* adiciona o valor do char em ECX (m - 0x6d) a EAX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Le o proximo char do Name pra ECX (fica limpo)
-- EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Adiciona 1 a EDX (continua limpo)
-- EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Adiciona EAX a EBX
-- EBX = 0x7a1c444d | EAX = 0x7668fc6d –
\* ECX é zero, então temos a serial em EBX

$ kgme
Name: Jim
Serial: 7a1c444d
Good serial!

Fiz uma implementação em C++ do keygen, que a única coisa que usa de c++ é o cout mas mesmo assim, aqui vai:

//============================================================================
// Name        : lolkg.cpp
// Author      : falso
// Version     : 0.1-pre1-beta2-rc4
// Copyright   : WTFPL
// Description : Keygen para o kgme do dongs
//============================================================================

#include <iostream>
using namespace std;

int main() {

	char name[50];

	cout << "|         |    |\n";
	cout << "|    ,---.|    |__/ ,---.\n";
	cout << "|    |   ||    |  \\ |   |\n";
	cout << "`---'`---'`---'`   ``---|\n";
	cout << "                    `---'\n";

	cout << "Name: " << flush;
	cin >> name;

	int ebx = 0xdead;
	int eax = ebx;
	int ecx = name[0];

	int i = 1;

	while (ecx != 0x0) {
		eax = ebx;
		eax = eax << 5;
		eax = eax + ecx;
		ecx = name[i];
		i++;
		ebx = ebx + eax;
	}

	printf("Serial: %x\n",ebx);

	return 0;
}

E já agora, uma em MIPS assembly pro PCSpim:

# this asm can never fail
#
# lolkg
.data
us: .asciiz "User: "
u: .space 50

.text
.globl main
main:

# print user..
li $v0,4
la $a0,us
syscall

# get string
li $v0,8
la $a0,u
syscall

# variavel inicial do algoritmo
li $s0,0xdead # s0 - ebx
move $s1,$s0 # s1 - eax

la $s5,u # tmp
#apanha o primeiro char do Username
lb $s2,0($s5) # s2 - ecx

_while:
move $s1,$s0
sll $s1,$s1,5
add $s1,$s1,$s2

addi $s5,$s5,1
lb $s2,0($s5) # s2 - ecx

addu $s0,$s0,$s1
bne $s2,0xa,_while

# imprime o serial, mas em decimal ;-(
# nao sei como imprimir em hex, pois o serial é em HEX
li $v0,1
move $a0,$s0
syscall

#sai
li $v0,10
syscall

Espero que todo este conhecimento inútil sirva para alguém, nem que seja pra despertar um bichozinho pequenino pro cracking! Fiquem bem, e crackem muito!

Junho 25, 2009

R.I.P Michael Jackson

Arquivado em: drama, useless — madinfo @ 23:09

Pois é… segundos após a morte apareceram nas internets quase mil gif’s a lolar com o SR… tá mal e achei que devia blolar sobre isto.

Junho 9, 2009

Curvas Elípticas

Arquivado em: drama, serious-business, useless — dcoder @ 23:41

Nos últimos anos, uma grande parte da investigação feita em criptografia de chave pública tem-se desviado dos grupos definidos sobre os números inteiros mod m, i.e.

$$\mathbb{Z}/m\mathbb{Z}$$

, para os grupos definidos por curvas elípticas sobre um corpo finito K, este geralmente

$$\mathbb{F}_p$$

ou

$$\mathbb{F}_{2^m}$$

— E/K. Não só a investigação, mas também a própria indústria tem-se virado para as curvas elípticas em detrimento dos algoritmos mais clássicos - o NIST definiu em 1999 uma série de curvas e corpos standard e em 2006 anunciou a Suite B, um conjunto de algoritmos aprovados para proteger dados até ao nível ‘Top Secret’.

A que é que se deve esta mudança?

Tem a ver com a estrutura, ou falta dela, existente nas curvas elípticas. No caso do Diffie-Hellman ou RSA clássico, estamos a trabalhar nos números inteiros mod N, e o problema fundamental a resolver é a factorização de um produto de dois primos ou o logaritmo discreto. Ambos estes problemas podem ser resolvidos com essencialmente o mesmo algoritmo — NFS. A complexidade deste é:

$$O(e^{\frac{64}{9}^{1/3} (\log n)^{1/3} (\log \log n)^{2/3}})$$

A razão pela qual este algoritmo funciona em tempo sub-exponencial relativamente a n está relacionada com a estrutura de

$$\mathbb{Z}$$

— todos os inteiros podem ser representados como um produto de factores primos. Associando esta propriedade às propriedades dos logaritmos e exponenciações e à distribuição dos números primos, conseguimos resolver logaritmos discretos em menos tempo do que os métodos genéricos, como o Pollard’s Rho. Nos grupos das curvas elípticas sobre um corpo, para parâmetros razoavelmente bem escolhidos, não são conhecidos algoritmos sub-exponenciais ou polinomiais para resolver o problema onde reside a segurança do algoritmo.

Antes de mais, vamos definir uma curva elíptica. Na prática, uma curva elíptica é definida pela equação de Weierstrass num corpo finito arbitrário:

$$y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6$$

Os coeficientes

$$a_1 \ldots a_6$$

definem, portanto, a curva. Tipicamente, quando a curva é definida sobre o corpo finito dos inteiros mod p, p primo, apenas

$$a_4$$

e

$$a_6$$

são diferentes de 0 (uma excepção sendo a Curve25519 do djb).

Naturalmente, os elementos deste grupo serão as soluções da equação acima — dado que existem duas soluções para cada x, os elementos são definidos como pontos P = (x, y) na curva. Ainda nos falta, no entanto, a operação de grupo - a adição. Esta é ligeiramente diferente para o caso em que P != Q e P = Q, quando queremos obter P+Q = (x3, y3), P=(x1, y1), Q=(x2, y2):

Adição

$$x3 = \frac{y2-y1}{x2-x1}^2 - x1 - x2 \\ y3 = \frac{y2-y1}{x2-x1}(x1-x3)-y1$$

Duplicação

$$x3 = \frac{3x1 + a_4}{2y1}^2 - 2x1 \\ y3 = \frac{3x1 + a_4}{2y1}(x1-x3)-y1$$

Existe ainda um ponto especial,

$$\infty$$

, que representa a identidade relativamente à adição, i.e.

$$P+\infty = \infty+P = P$$

.

Tendo o grupo definido, surge a operação relevante para fins criptográficos: a multiplicação. Esta é definida como:

$$nP = P+P+\ldots +P$$

i.e. n vezes o ponto P.

Outro conceito importante é a ordem de uma curva — o número total de pontos que nela existem — #E. Este valor deve ser sempre ou primo ou bastante próximo de um primo, de forma a evitar ataques conhecidos.

Uma propriedade importante da multiplicação em curvas elípticas é que é bastante fácil calcular

$$n P$$

, mas dado

$$P$$

e

$$n P$$

é extremamente difícil obter

$$n$$

. Este é o designado problema do logaritmo discreto em curvas elípticas. O melhor algoritmo conhecido para calcular este logaritmo é o Pollard’s Rho, que tem uma complexidade de

$$O({\#E}^{1/2})$$

A diferença entre o logaritmo discreto nas curvas elípticas e nos inteiros mod n é esta - não existe conceito de divisibilidade entre pontos, i.e. não existem pontos ‘primos’ nos quais seja possível decompor um ponto arbitrário. Esta (falta de) estrutura provê uma grande vantagem a nivel de segurança relativamente aos grupos mais clássicos.

Para fazer uma comparação directa entre os tamanhos dos corpos finitos necessários para uma segurança de 80 bits, podemos aplicar directamente as complexidades acima. Com curvas elípticas, precisamos de um corpo finito de tamanho

$${2^{80}}^2 = 2^{160}$$

, i.e. 160 bits. Com e.g. Diffie-Hellman, precisamos de pelo menos 896 bits de chave —

$$e^{\frac{64}{9}^{1/3} (\log 2^{896})^{1/3} (\log \log 2^{896})^{2/3}} \approx 2^{81.85}$$

. Note-se que isto são apenas complexidades assimptóticas; o custo real é muito diferente e geralmente muito, muito maior que o apresentado nestas fórmulas simplificadas.

Portanto, podemos ver como as curvas elípticas podem ser apelativas para quem implementa sistemas criptográficos — maior segurança por tamanho de chave, maior rapidez (derivado das chaves menores), menor estrutura dentro do grupo, minimizando as vias de ataque. Até agora parece ser em quase todos os sentidos uma opção superior ao RSA, ElGamal, DH tradicionais.

PS. Este post foi essencialmente elaborado para testar o

$$\LaTeX$$

no blol.