Os computadores quânticos disponíveis comercialmente podem quebrar secp5k1. Ainda estão cerca de 2 ordens de magnitude distantes... Por enquanto.
Steve Tippeconnic
Steve Tippeconnic27/06/2025
Quebrando uma Chave de Curva Elíptica de 5 Bits com um Computador Quântico de 133 Qubits ⚛️ Este experimento quebra uma chave criptográfica de curva elíptica de 5 bits usando o algoritmo de Shor. Executado no ibm_torino de 133 qubits da @IBM com @qiskit, um circuito de 15 qubits, composto por 10 qubits lógicos e 5 ancillas, interfere sobre ℤ₃₂ para extrair o escalar secreto k da relação da chave pública Q = kP, sem nunca codificar k diretamente no oráculo. De 16.384 tentativas, a interferência quântica revela uma crista diagonal no espaço de resultados da QFT 32x32. O circuito quântico, com mais de 67.000 camadas de profundidade, produziu padrões de interferência válidos apesar da profundidade extrema do circuito, e o pós-processamento clássico revelou k = 7 nos 100 melhores resultados invertíveis (a, b). Análise do Código 1. Codificação de Grupos Restringir a atenção ao subgrupo de ordem 32 ⟨𝑃⟩ de uma curva elíptica sobre 𝐹_p. Mapear pontos para inteiros: 0𝑃 -> 0,  1𝑃 -> 1, …, 31𝑃 -> 31. A lei do grupo torna-se adição modular: (𝑥𝑃) + (𝑦𝑃) = ((𝑥 + 𝑦) mod 32))𝑃. Este experimento usa uma curva elíptica sobre F_p​ com um subgrupo cíclico de ordem 32, mapeando P -> 1 e Q = 7P -> 23 em ℤ₃₂​. O código assume multiplicação escalar pré-computada, abstraindo coordenadas explícitas. 2. Registros Quânticos Registro a: cinco qubits para o expoente 𝑎 ∈ {0, …, 31}. Registro b: cinco qubits para 𝑏 ∈ {0, …, 31}. Registro p: cinco qubits inicializados em ∣0⟩ para manter o índice do ponto. Registro clássico c: um registro de 10 bits para registrar os valores medidos de a e b. 3. Preparação de Superposição Aplicar Hadamards a cada qubit em a e b: 31 1/32 ∑ ∣a⟩_a ∣b⟩_b ∣0⟩_p a, b=0 4. Construção do Oráculo U_f O objetivo é um mapeamento reversível: ∣a⟩ ∣b⟩ ∣0⟩ -> ∣a⟩ ∣b⟩ ∣aP + bQ⟩. Adicionar aP: para cada bit a_i (peso 2^𝑖), adicionar (2^𝑖 P) mod 32 Adicionar bQ: calcular (2^𝑖 𝑄) mod 32, depois adicionar controlado em 𝑏_𝑖. Esses usam portas de permutação controladas de 5 qubits. Todas as constantes são derivadas do gerador P da curva elíptica e do ponto público Q. Nenhuma porta faz referência direta ao secreto k. 5. Estado Global após o Oráculo O estado evolui para: 1/32 ∑ ∣a⟩∣b⟩∣f(a, b)⟩, onde f(a, b) = a + kb (mod32). a, b 6. Isolar o Registro do Ponto O algoritmo precisa apenas da relação de fase em 𝑎, 𝑏. Uma barreira isola p. 7. Transformada de Fourier Quântica (QFT) QFT 31 ∣a⟩ -> 1/√32 ∑ e^((2πi)/32 au) ∣u⟩, u=0 QFT 31 ∣b⟩ -> 1/√32 ∑ e^((2πi)/32 bv) ∣v⟩. v=0 8. Padrão de Interferência A amplitude conjunta para observar (u, v) é: 1/32 ∑_(a, b) e^((2πi/32)(au + bv)) δ_(a + kb ≡ 0) = 1/32 δ_(u + kv ≡ 0 (mod 32)), que forma uma crista diagonal na grade de resultados 32x32. 9. Medição Medir todos os dez qubits lógicos. Os resultados se concentram nos 32 pares distintos que satisfazem u + kv ≡ 0 (mod 32). 10. Pós-processamento Clássico As cadeias de bits são invertidas e analisadas em pares (a, b). Manter apenas linhas onde gcd⁡(b, 32) = 1, garantindo que b seja invertível. A chave candidata é computada como: k = (-a) b^(-1) mod 32 O script então: Extrai os 100 resultados invertíveis (a, b) com maior contagem. Computa k para cada um. Imprime cada par (a, b), k recuperado e contagem. Declara sucesso se k = 7 aparecer nos 100 melhores. 11. Verificação e Armazenamento O escalar correto k = 7 é confirmado se aparecer nos 100 melhores resultados invertíveis. Todas as contagens de cadeias de bits brutas, layout de qubits e metadados são salvos em JSON para visualização e análise adicionais. Resultados 2025-06-25 19:41:29,294 | INFO | Profundidade do circuito 67428, contagens de portas OrderedDict({'sx': 56613, 'cz': 34319, 'rz': 15355, 'x': 157, 'medir': 10, 'barreira': 1}) base_primitive._run:INFO:2025-06-25 19:41:29,994: Enviando trabalho usando opções {'options': {}, 'version': 2, 'support_qiskit': True} SUCESSO — k = 7 encontrado nos 100 melhores resultados Os 100 melhores pares invertíveis (a, b) e k recuperado: (a= 8, b=11) → k = 8 (contagem = 63) (a=12, b= 9) → k = 20 (contagem = 58) (a= 0, b= 3) → k = 0 (contagem = 54) (a= 1, b= 9) → k = 7 (contagem = 54) <<< (a=28, b= 1) → k = 4 (contagem = 53) (a= 0, b=11) → k = 0 (contagem = 53) (a= 8, b= 9) → k = 24 (contagem = 53) (a= 8, b= 3) → k = 8 (contagem = 53) ... (a=11, b= 3) → k = 7 (contagem = 41) <<< ... (a=25, b= 1) → k = 7 (contagem = 32) <<< Esta execução recuperou com sucesso o escalar secreto k = 7 usando uma chave ECC de 5 bits (subgrupo de ordem 32), estendendo meu ataque anterior de 4 bits para um espaço com 1024 combinações possíveis (a, b) e φ(32) = 16 valores b invertíveis. k = 7 aparece três vezes nos 100 melhores: (a = 1, b = 9) -> k = 7 com 54 contagens (a = 11, b = 3) -> k = 7 com 41 contagens (a =25, b = 1) -> k = 7 com 32 contagens Estes são estados de alta frequência, tornando-os vetores de ataque credíveis sob pós-processamento clássico. As contagens exibem concentração em torno de relações modulares estruturadas: a + kb ≡ 0 mod 32. Estas aparecem como cristas diagonais no espaço de medição 32x32. Vários valores de k se repetem frequentemente (k = 0, 24, 28), mostrando a sobreposição probabilística intrínseca à interferência quântica, alguns destes são falsos positivos provenientes de ruído ou aliasing com outras equivalências modulares. A profundidade do circuito foi de 67.428, com um total de 106.455 portas, refletindo uma rotina quântica grande e complexa para aritmética de índice modular controlada. Esta pode ser a maior quantidade de portas que já usei em um circuito. Contagens de portas para o circuito: sx: 56613 cz: 34319 rz: 15355 x: 157 medir: 10 barreira: 1 Total de portas: 106455 Profundidade: 67428 Largura: 133 qubits | 10 clbits Este experimento levou 54 segundos para ser concluído no 'ibm_torino'. O perfil de ruído é não uniforme, mas decrescente, o que significa que o sistema quântico provavelmente resolveu harmônicos dominantes na interferência, mas borrifou estruturas mais finas. A cauda da distribuição ainda contém candidatos válidos k = 7, isso apoia ataques quânticos estilo dicionário onde varreduras de resultados top-N (N = 100) são suficientes para recuperar a chave. A Mapa de Contagem Bruta (a vs b) acima (código completo no Qwork) mostra uma grade 32x32 que representa as contagens observadas para cada par (a, b) após a execução do circuito de Shor. O mapa de calor mostra uma distribuição em faixas e desigual, indicando cristas de interferência, não ruído. Certas linhas (valores de a) têm concentração visivelmente maior, sugerindo interferência construtiva ao longo de soluções específicas a + kb ≡ 0 mod 32. O Histograma de Valores k Recuperados acima (código completo no Qwork) agrega as contagens totais para cada chave escalar recuperada k ∈ Z₃₂, derivadas via k = −ab^(−1) mod 32. Um grande pico em k = 0 e outro em torno de k = 24 são dominantes. A chave correta k = 7 não é a mais alta, mas está bem representada (~54 + 41 + 32 = 127 contagens em múltiplos pares (a, b)). Isso mostra que hackers podem atacar dicionários com uma quantidade maior de resultados. O Rank de Cadeia de Bits vs Contagem (Escala Logarítmica) acima (código completo no Qwork) mostra um gráfico de rank tipo Zipf de todas as cadeias de bits por contagem decrescente. O eixo y em escala logarítmica mostra uma cauda exponencial, a maioria dos resultados ocorre <10 vezes. A cabeça (top ~50) das cadeias de bits tem probabilidade muito mais alta, indicando picos de interferência construtiva. Você poderia construir um ataque quântico heurístico de dicionário que colhe apenas as N cadeias de bits principais com retorno exponencial sobre o sinal. Isso valida que a estrutura de sinal-para-ruído quântica ainda está intacta mesmo com circuitos mais longos (67428 de profundidade). As Localizações de (a, b) Decodificando para k = 7 acima (código completo no Qwork) mostram que cada ponto é um par (a, b) que decodificou para k = 7. A intensidade da cor é igual ao número de vezes que este par ocorreu. O gráfico mostra uma distribuição relativamente uniforme em (a, b), mas com picos locais em (1, 9), (11, 3), (25, 1). Múltiplas combinações (a, b) convergem para k = 7 com multiplicidade não uniforme. De um ponto de vista criptoanalítico, isso valida que o algoritmo de Shor pode quebrar chaves ECC de forma confiável, mesmo quando o k correto não é o resultado número 1. A Máscara de Invertibilidade para o Registro b acima (código completo no Qwork) mostra um gráfico de barras das contagens para cada b ∈ {0, …, 31} que são coprimos com 32 (gcd⁡(b, 32) = 1, apenas estes são invertíveis módulo 32 e úteis para recuperar k via: k = (−a)b^(−1) mod 32. Os b invertíveis estão bem populados, mostrando que o circuito produziu candidatos recuperáveis. Mais uniformidade aqui aumentaria o poder do pós-processamento, idealmente, estes seriam planos. O Mapa de Frequência do Inverso Modular: b vs b^(−1) mod 32 acima (código completo no Qwork) mostra um mapa de calor de quão frequentemente cada b invertível mapeia para cada inverso modular correspondente b^(−1) mod 32, ponderado pelas contagens de resultados. A maioria dos pontos cai em uma linha bijetiva limpa, cada b mapeia claramente para um único b^(−1), confirmando a correção da inversão modular. Regiões mais brilhantes (perto do canto inferior esquerdo ou superior direito) mostram b's favorecidos pela amostragem quântica. Sem ruído ou agrupamento fora da diagonal, bom sinal de estrutura modular limpa preservada. O Mapa a + 7b mod 32 acima (código completo no Qwork) assume k = 7 e plota o valor de (a + 7b) mod 32 em todos os resultados. A distribuição é quase uniforme. Isso sugere que não há crista de interferência acentuada como no caso de 4 bits. Por quê? O circuito de 5 bits (com 10 qubits lógicos) espalha a amplitude mais fina em 1024 resultados, reduzindo o contraste. No entanto, a chave correta ainda era recuperável, indicando muitos caminhos fracamente construtivos, em vez de alguns dominantes. A Eficiência do Ataque ECC: b Válido vs Inválido acima (código completo no Qwork) mostra contagens totais de amostras com b invertível (útil para recuperação de chave) vs. b não invertível (inútil). Mais da metade dos resultados são desperdiçados em valores b inválidos (gcd⁡(b, 32) != 1). O próximo design para uma quebra de 6 bits poderia usar mascaramento de oráculo ou filtragem de pós-seleção em domínios b válidos. O Mapa de Calor: (a, b) com b Invertível (mod 32) acima (código completo no Qwork) foca apenas em pares (a, b) onde b é invertível módulo 32, uma condição necessária para recuperar k. Ele mostra onde a interferência quântica está se concentrando dentro do espaço de 1024 pontos. As regiões de alta intensidade revelam caminhos de interferência preferidos, sugerindo que o estado quântico evoluiu de forma não uniforme, possivelmente favorecendo certas órbitas sob multiplicação modular. Isso confirma que a coerência do sinal é forte o suficiente em algumas regiões para isolar candidatos válidos. A Distribuição de Ângulos de Crista de Fase acima (código completo no Qwork) agrupa os ângulos formados pelos vetores (-a, b) no plano, módulo π, que correspondem aproximadamente a cristas de fase no espaço QFT. Picos no histograma indicam fortes alinhamentos, ressonâncias, da forma u + kv ≡ 0, significando que o circuito codificou com sucesso k no padrão de interferência, mesmo que o espaço de estado completo seja vasto. Os múltiplos ângulos dominantes sugerem que harmônicos do deslocamento oculto estão presentes. O Mapa de Resíduo de a + 7b mod 32 acima (código completo no Qwork) visualiza o resíduo de saída para a chave alvo específica k = 7, em todo o espaço (a, b). Quaisquer faixas ou simetrias consistentes aqui indicam quão bem a interferência amplificou soluções válidas (onde este valor é igual a 0). Você pode observar quais regiões da rede 1024 correspondem a a + 7b ≡ 0, validando que a estrutura do oráculo levou a uma impressão de chave bem-sucedida. O Ruído: Variância da Contagem em b para a fixa acima (código completo no Qwork) mostra quão ruidosas ou estáveis foram as contagens de saída para cada a fixa enquanto variamos b. Alta variância significa que alguns valores de b levam a forte amplificação enquanto outros não, implicando sensibilidade do circuito ou ruído de backend para aquela linha a. Regiões suaves implicam coerência quântica, enquanto picos podem apontar para decoerência ou propagação de erro durante a avaliação do oráculo. No final, este experimento quebrou com sucesso uma chave de curva elíptica de 5 bits usando o algoritmo de Shor executado no processador quântico de 133 qubits da IBM, estendendo o resultado anterior de 4 bits para um espaço de interferência significativamente maior (32x32 = 1024 resultados). Este circuito codificou o oráculo sobre ℤ₃₂ sem nunca referenciar o escalar secreto k, e aproveitou a aritmética de grupo modular para entrelaçar o escalar na interferência de fase. O circuito quântico, com mais de 67.000 camadas de profundidade, produziu padrões de interferência válidos apesar da profundidade extrema do circuito, e o pós-processamento clássico revelou k = 7 nos 100 melhores resultados invertíveis (a, b). Através de visualizações, este experimento confirmou estruturas de crista diagonal, máscaras de invertibilidade e alinhamento harmônico de cristas de interferência, validando que a coerência quântica permaneceu forte o suficiente para amplificar a relação modular correta. Isso estabelece que o algoritmo de Shor continua a escalar sob regimes de circuito mais profundos e que estratégias de recuperação de chave baseadas em dicionário (enumeração dos 100 melhores) permanecem viáveis à medida que o comprimento dos bits aumenta, mostrando clara vantagem quântica mesmo sob condições reais ruidosas. Código # Circuito principal # Importações import logging, json from math import gcd import numpy as np from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile from qiskit.circuit.library import UnitaryGate, QFT from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 import pandas as pd # IBMQ TOKEN = "YOUR_IBMQ_API_KEY" INSTANCE = "YOUR_IBMQ_CRN" BACKEND = "ibm_torino" CAL_CSV = "/Users/steventippeconnic/Downloads/ibm_torino_calibrations_2025-06-26T02_21_07Z.csv" SHOTS = 16384 # Parâmetros da curva de brinquedo (subgrupo de ordem 32 de E(F_p)) ORDER = 32 # |E(F_p)| = 32 P_IDX = 1 # Gerador P -> índice 1 Q_IDX = 23 # Ponto público Q = kP, aqui “23 mod 32” para k = 7 # Helper de logging logging.basicConfig(level=logging .INFO, format="%(asctime)s | %(levelname)s | %(message)s") log = logging.getLogger(__name__) # Seleção de qubit baseada em calibração def best_qubits(csv_path: str, n: int) -> list[int]: df = pd .read_csv(csv_path) df.columns = df.columns.str.strip() winners = ( df.sort_values(["√x (sx) error", "T1 (us)", "T2 (us)"], ascending=[True, False, False]) ["Qubit"].head(n).tolist() ) log .info("Melhores qubits físicos: %s", winners) return winners N_Q = 5 N_Q_TOTAL = N_Q * 3 # a, b, ponto PHYSICAL = best_qubits(CAL_CSV, N_Q_TOTAL) # Adicionador constante módulo 32 como uma porta reutilizável def add_const_mod32_gate(c: int) -> UnitaryGate: """Retorna uma porta de 5 qubits que mapeia |x⟩ ↦ |x+c (mod 32)⟩.""" mat = np.zeros((32, 32)) for x in range(32): mat[(x + c) % 32, x] = 1 return UnitaryGate(mat, label=f"+{c}") ADDERS = {c: add_const_mod32_gate(c) for c in range(1, 32)} def controlled_add(qc: QuantumCircuit, ctrl_qubit, point_reg, constant): """Aplica |x⟩ → |x+constant (mod 32)⟩ controlado por um qubit.""" qc.append(ADDERS[constant].control(), [ctrl_qubit, *point_reg]) # Oráculo U_f : |a⟩|b⟩|0⟩ ⟶ |a⟩|b⟩|aP + bQ⟩ (aritmética de índice mod 32) def ecdlp_oracle(qc, a_reg, b_reg, point_reg): for i in range(N_Q): constant = (P_IDX * (1 << i)) % ORDER if constant: controlled_add(qc, a_reg[i], point_reg, constant) for i in range(N_Q): constant = (Q_IDX * (1 << i)) % ORDER if constant: controlled_add(qc, b_reg[i], point_reg, constant) # Construir o circuito completo de Shor def shor_ecdlp_circuit() -> QuantumCircuit: a = QuantumRegister(N_Q, "a") b = QuantumRegister(N_Q, "b") p = QuantumRegister(N_Q, "p") c = ClassicalRegister(N_Q * 2, "c") qc = QuantumCircuit(a, b, p, c, name="ECDLP_32pts") qc.h(a) qc.h(b) ecdlp_oracle(qc, a, b, p) qc.barrier() qc.append(QFT(N_Q, do_swaps=False), a) qc.append(QFT(N_Q, do_swaps=False), b) qc.measure(a, c[:N_Q]) qc.measure(b, c[N_Q:]) return qc # Execução do IBM Runtime service = QiskitRuntimeService(channel="ibm_cloud", token=TOKEN, instance=INSTANCE) backend = service.backend(BACKEND) log .info("Backend → %s", backend .name) qc_raw = shor_ecdlp_circuit() trans = transpile(qc_raw, backend=backend, initial_layout=PHYSICAL, optimization_level=3) log .info("Profundidade do circuito %d, contagens de portas %s", trans.depth(), trans.count_ops()) sampler = SamplerV2(mode=backend) job = sampler .run([trans], shots=SHOTS) result = job.result() # Pós-processamento clássico creg_name = trans.cregs[0].name counts_raw = result[0].data.__getattribute__(creg_name).get_counts() def bits_to_int(bs): return int(bs[::-1], 2) counts = {(bits_to_int(k[N_Q:]), bits_to_int(k[:N_Q])): v for k, v in counts_raw.items()} top = sorted(counts.items(), key=lambda kv: kv[1], reverse=True) # Critérios de sucesso. Verifique as 100 melhores linhas invertíveis para k = 7 top_invertibles = [] for (a_val, b_val), freq in top: if gcd(b_val, ORDER) != 1: continue inv_b = pow(b_val, -1, ORDER) k_candidate = (-a_val * inv_b) % ORDER top_invertibles.append(((a_val, b_val), k_candidate, freq)) if len(top_invertibles) == 100: break # Verifique o sucesso e imprima os resultados found_k7 = any(k == 7 for (_, k, _) in top_invertibles) if found_k7: print("\nSUCESSO — k = 7 encontrado nos 100 melhores resultados\n") else: print("\nAVISO — k = 7 NÃO encontrado nos 100 melhores resultados\n") print("Os 100 melhores pares invertíveis (a, b) e k recuperado:") for (a, b), k, count in top_invertibles: tag = " <<<" if k == 7 else "" print(f" (a={a:2}, b={b:2}) → k = {k:2} (contagem = {count}){tag}") # Salvar dados brutos out = { "experimento": "ECDLP_32pts_Shors", "backend": backend .name, "qubits_físicos": PHYSICAL, "shots": SHOTS, "contagens": counts_raw } JSON_PATH = "/Users/steventippeconnic/Documents/QC/Shors_ECC_5_Bit_Key_0.json" with open(JSON_PATH, "w") as fp: json.dump(out, fp, indent=4) log .info("Resultados salvos → %s", JSON_PATH) # Fim Código completo para todas as visualizações usando dados de execução no Qwork.
9,77K