Então, enviamos agentes super secretos Alice e Bob para um país inimigo disfarçado. Durante a missão, eles terão que entrar em contato e trabalhar juntos, trocar informações, assuntos comuns de espionagem. Obviamente, tudo isso deve ser feito em conformidade com todas as regras e procedimentos de segurança possíveis.
De fato, no último turno, queremos revelá-los: tanto a própria missão quanto os próprios agentes e toda a segurança nacional estão em risco. Portanto, é do nosso interesse fornecer aos espiões as informações mínimas necessárias. Em particular, quanto menos eles se conhecerem e sobre técnicas de comunicação, melhor.
Mas como eles identificarão o camarada da sede?

TL; DR - inventando um mecanismo de autenticação de usuário usando esteganografia para uma agência imaginária de três caracteres de um país inexistente.
Sobre lobos e peles de ovelhas
Uma capa é uma capa, então nem Alice nem Bob devem levantar suspeitas por nenhuma de suas ações. O planejamento adequado implica paranóia sobre o monitoramento constante deles em todos os níveis possíveis. Esta publicação não abordará a tarefa de troca direta de informações (ela merece uma série separada), mas apenas uma maneira de garantir que ela seja transmitida por quem precisa e para quem precisa.
Muito provavelmente, ambos os espiões terão uma história no formato de cidadãos comuns, além disso, de forma alguma conectados um ao outro. Portanto, você precisa vetar imediatamente o uso de ferramentas criptográficas clássicas e canais seguros - cada agente de contrainteligência sabe que pessoas honestas que não têm um relacionamento próximo não têm nada a esconder.
O que fazer
Obviamente, essa tarefa não é nova, existia felizmente e foi resolvida muito antes do advento desses na Internet. E não apenas foi decidido, como algumas decisões foram fortalecidas na cultura e ainda são encontradas em livros, filmes e jogos.
Vejamos exatamente essa cena: duas pessoas em casacos longos convergem para um local público e trocam frases muito estranhas. Se a frase e a resposta iniciais estiverem corretas, a autenticação foi bem-sucedida e as pessoas trocam pastas marcadas com "Top Secret" e divergem em direções desconhecidas.
A desvantagem de tal esquema é imediatamente óbvia - as frases devem ser mantidas em segredo e muitas vezes alteradas , o que não é muito simples no território inimigo. Ao mesmo tempo, para não serem falados aleatoriamente e não levarem ao caso do KDPV, eles se tornam bastante proeminentes e aleatórios, o que significa que podem distribuir agentes que os pronunciam.

Nós, na era da tecnologia digital, não gostamos desse método. Especialmente se você se lembrar de que quase todos os canais de comunicação são controlados por alguém e aproveitados por motivos bons e ruins. E não importa o que eles nos assegurem, a vida das pessoas não deve ser confiável na política de privacidade de qualquer Facebook.

Esteganografia (de novo?)
Hedgehog, é claro que a capacidade de se esconder em tal situação parece mais atraente do que nunca. De fato, mesmo o método descrito é sua subespécie - as frases de código podem ser consideradas como contêineres com apenas um bit de informação.
O mesmo mamífero do desapego dos insetívoros entende que não se trata simplesmente de atirar contêineres uns aos outros. Essa troca causará quase mais suspeitas do que alguma criptografia PGP comum, por isso não estamos interessados.
E então?
Diferentemente dos criptogramas, os contêineres têm uma vantagem óbvia - o contexto do aplicativo. Qualquer texto, imagem, arquivo de áudio etc., além do conteúdo óbvio, também traz a possibilidade de sua discussão natural e pode ser enviado não apenas da baía, mas no processo de um diálogo que não causa suspeitas.
Munidos dessas idéias, já podemos elaborar um protocolo simples de autenticação esteganográfica com base em uma chave comum:
- A -> B: uma mensagem bonita solicitando um contêiner esteganográfico de certos parâmetros;
- B: seleciona o contêiner C que corresponde ao contexto e aos parâmetros solicitados;
- B: similarmente cria uma mensagem M ;
- B -> A: C '= Incorporado (C, M, K) ;
- A: Verifica C ' quanto à conformidade com os parâmetros definidos;
- A -> B: M '= Extrato (C', K) ;
- B: Verifica se M e M ' correspondem.
Esse protocolo tem desvantagens óbvias - Alice e Bob devem ter uma chave comum e funções de incorporação e extração. Seu comprometimento pode levar à possibilidade de uma análise detalhada do método de autenticação do inimigo e colocar em risco outros usuários e a sede. Algo precisa ser consertado.
O artista não é um bug
Se o leitor foi para a escola após o advento das aulas de informática, ele deveria se lembrar de aprender o básico da algoritmo usando o artista de uma tartaruga, formiga e coisas do gênero. Sua idéia era demonstrar as possibilidades de otimizar um grande número de ações únicas manuais, criando programas simples. Para resolver nosso problema, precisamos seguir na direção oposta.

Como podemos simplificar a escrita do algoritmo final da sequência de etapas até a descrição processual de acordo com os parâmetros fornecidos, podemos realizar o processo inverso. Se você imaginar um contêiner como uma matriz de alguns de seus componentes, a incorporação de uma mensagem por chave pode ser escrita como uma sequência ordenada de operações nos elementos do contêiner em índices específicos com vários parâmetros constantes.
É aí que a não-matemática começa, então peço aos tímidos que simplesmente folheiem os parágrafos de aparência difícil para a seção de operações ou até um pouco mais. Nada terrível vai acontecer, eu prometo.
Para incorporar os dados, precisamos de uma sequência do formulário: (f1, S1, i, D1), (f2, S2, j, D2) ... , onde:
- Di - alguma parte dos dados incorporados;
- i, j são os índices dos elementos do contêiner;
- fi: (Estado, Elemento, D) -> (Estado, Elemento) - função incorporada;
- Si é um certo estado, o contexto da operação, (El ', S [i + 1]) = fi (Si, El, Di) .
Para extraí-lo, você não precisa armazenar partes dos dados (K.O.), portanto, existem triplos suficientes: (g1, S1, I1), (g2, S2, I2) ... com os mesmos valores, apenas gi: (State, Element) -> (Estado, D) .
Tudo isso pode ser representado pelo diagrama simétrico abaixo. Se, por algum motivo, não consegui alcançar clareza, não é assustador, basta ler.

Pode-se observar que a função de incorporação possui um maior número de graus de liberdade. Ao contrário de sua irmã, ela modifica o contêiner, enquanto faz isso com base em dois elementos independentes - dados incorporados e o elemento. Obrigado, ou, mais precisamente, por isso, são possíveis duas abordagens globais para a implementação do algoritmo de esteganografia por esse sistema:
- Escolha os índices mais apropriados dos elementos a serem alterados de acordo com a função de incorporação (menos perceptível ou que não exija nada) e transfira a sequência formada ligada a um contêiner específico. Com essa abordagem, eles precisam ser isolados um do outro antes que seja necessário incorporar usando métodos clássicos, como criptografia e outras mídias seguras;
- Encontre um método para dividir o contêiner em elementos e a função de incorporação que qualquer alteração esperada será igualmente invisível. Nesse caso, a sequência é independente do contêiner e pode ser criada mesmo por um gerador completamente aleatório. Menos flexibilidade e falta de controle do pior caso possível. Por outro lado, essa abordagem é mais simples e mais conveniente quando aplicada em campo, portanto, a seguir, a utilizarei.
Se o estado não for necessário para o algoritmo, todas as opções acima permanecerão válidas, simplesmente sem uma única letra e bloco no diagrama. Sem ele, é ainda mais fácil, na verdade.
E por que precisamos disso?
Agora, se você souber com antecedência quais contêineres com quais mensagens e chaves serão usadas, em vez de divulgar completamente as partes do algoritmo, poderá gerar e fornecer agentes para usar apenas sequências semelhantes e um intérprete para elas. Bem, ok, não apenas dando, é claro, mas mais sobre isso mais tarde.
Adicionar assimetria
Mesmo um artista de tartaruga pode desenhar um quadrado de centenas de maneiras diferentes, simplesmente mudando a ordem das operações e adicionando novas. Isso significa que ninguém nos incomoda e faz o mesmo com as seqüências descritas para dados de entrada fixos.
Ou seja, podemos pegar a sequência de incorporação, adicionar novas operações, misturar tudo e, para que o resultado permaneça o mesmo. A menos que, na presença de um estado, seja necessário rastreá-lo e adicionar separadamente as alterações necessárias à sequência. É por isso que sem é mais fácil, sim.
De um jeito ou de outro, depois de tão amassado e barulhento, até o próprio incorporador não será mais capaz de entender o que ele está realmente incorporando: qualquer sequência de N operações representará N! mensagens potencialmente incorporadas - uma para cada permutação das partes incorporadas. Ao mesmo tempo, o próprio N é uma grande questão. Portanto, pode-se abrir essas seqüências - elas não fornecem nenhuma informação sobre a mensagem incorporada, nem sobre o algoritmo e a chave usados.
Ao extrair informações, é muito importante para nós, tanto a ordem (restaurar a mesma mensagem correta quanto possível) e o número de partes a serem extraídas, para que as seqüências de extração permaneçam inalteradas desde o momento do nascimento. Como eles contêm implicitamente informações sobre a chave, gerador e algoritmo usado, eles, como os animais do livro vermelho, precisam ser armazenados e protegidos. E mantenha isso em segredo.
O que a assimetria tem a ver com isso? O fato é que agora cada sequência de extração está associada a um número infinito de incorporadores. E restaurar um do outro é, no caso geral, uma tarefa insolúvel.
Estamos operando
Esquecemos qualquer matemática próxima e retornamos à tarefa original - como podemos enviar Alice e Bob para o território inimigo, a fim de:
- eles não se conheciam
- não tinha algoritmos secretos na mão
- mas você pode verificar um ao outro enquanto se comunica em um canal aberto?
Bem, com o primeiro parágrafo, tudo fica claro, apenas não fornecemos informações explícitas uma sobre a outra, nem chaves compartilhadas. Para o segundo, você precisa se lembrar da descrição do protocolo acima. Agora podemos excluir diretamente os algoritmos Incorporar e Extrair que representam potenciais segredos de estado e tudo mais. E, levando isso em consideração, no terceiro é possível elaborar o seguinte protocolo de duas etapas.
Geração de informações de autenticação antes do início da missão com sede como parte confiável de Trent:
- T: seleciona o algoritmo secreto e a chave secreta K, cria com a ajuda deles:
- Sequência de extração Ex ;
- adequado para autenticação (abaixo) do contexto Ctx ;
- T -> A: Ctx, Ex ;
- T: usando Ex e o contexto criado, gera:
- mensagem M não usada anteriormente para os agentes selecionados dentre as partes | Ex | ;
- uma sequência única de Em , abrindo-a como descrito acima;
- T -> B: Em, h (M) , se desejado, cria conjuntos adicionais.
Assim, Alice tem apenas uma sequência para todas as ocasiões e o contexto de contato futuro, e Bob se torna o feliz proprietário de um conjunto de sequências únicas e hashes de mensagens que eles incorporam.
O protocolo de autenticação já durante a missão é assim:
- A -> B: uma mensagem IM inicial com base no contexto Ctx com uma descrição do contêiner;
- B: seleciona o C ~ IM apropriado;
- B -> A: C '= Em (C) ;
- A: verifica a conformidade com C '~ IM (como as alterações são invisíveis, elas devem ser salvas);
- A -> B: M '= Ex (C') , marca M 'como usado;
- B: verifica, h (M ') == h (M) , destrói Em, h (M) .
Um leitor atento perceberá que, antes do protocolo, Alice e Bob têm apenas um conjunto de informações, o que por si só não significa nada para eles, nem para um adversário em potencial, e apenas durante o "brincar com as cores".
Cada conjunto aberto de Bob é usado apenas uma vez, que é controlado pelo penúltimo passo de Alice. Ao encontrar um M usado anteriormente (e, portanto, Em invisível por ela) por outra pessoa, ela percebe que um de seus "associados" é falso.
O uso repetido pela mesma pessoa diz a ela que ela não está ciente dos meandros do protocolo e certamente não é a pessoa com quem ela teve que fazer contato. Bem, antes tarde do que nunca.

Ok, é assim que tudo parece muito complicado e incompreensível. Alguém chegou aqui?
Vamos demonstrá-lo melhor na prática, porque até os próprios espiões não precisam conhecer os detalhes do protocolo para uso, muito menos os leitores pobres. Apenas a princípio um pouco sobre como tudo foi implementado.
Alta tecnologia
Portanto, resta apenas escrever tudo o que é necessário para o protocolo. Bem, você não faz tudo com as mãos (embora possa). E hoje a vítima do meu código estará ... girando a roda da fortuna ... Java? Bem, tudo bem, ao mesmo tempo em que tudo estiver na STL, você não precisará procurar nada.
Vamos começar com a API necessária. Para funcionar, você só precisa determinar a classe da matriz de elementos de contêiner com a capacidade de receber e alterar elementos por índice:
class MyContainer implements StegoContainer<MyElement> { public MyElement get(int i) {
O uso adicional é reduzido à criação de um invólucro de um autômato esteganográfico sobre o contêiner necessário e ao fornecimento das funções de incorporação e extração de sua entrada:
StegoMachine<MyState, MyElement> myMachine = new StegoMachine( initialState, new MyContainer<MyElement>() ); final StegoEmbed myEmbed = (st, el, dp) -> {
Classes com o sufixo Stateless são usadas da mesma maneira, se a implementação do algoritmo não exigir a manutenção do estado interno.
Os geradores de sequência podem funcionar como você gosta e não possui uma API comum. No caso geral, qualquer coisa pode fazer parte dos dados em geral, de bits isolados a arte rupestre em uma codificação separada.
Exemplo de implementação
Sobre o método
Como exemplo de implementação, usando as interfaces criadas, implementei um algoritmo simples da família LSB para imagens de bitmap com compactação sem perdas. Seus elementos são pixels que não têm vizinhos no bit menos significativo de todos os componentes RGB. A função incorporar trabalha com bits únicos dos dados de origem e simplesmente altera o bit de ordem inferior do valor de um dos componentes (para o qual o índice apontará).
É bastante simples, mas é ótimo para implementar o protocolo, já que alterar qualquer elemento é igualmente imperceptível de acordo com a escolha deles, portanto os índices dos elementos a serem alterados são gerados usando um gerador aleatório. No caso de Java, usando SecureRandom , mas se desejado, ele muda facilmente para sua fonte de entropia.
No entanto, este é um método muito simples, não recomendo usá-lo para espiões de verdade.
Sobre hashes
Como o texto tende a ficar distorcido, dependendo da identidade simulada do agente (alguns não colocam letras maiúsculas, outros gostam de colocar emoticons etc., outros geralmente são analfabetos), sugiro usar sha256 para calcular o hash, mas apenas com palavras impressas em letras minúsculas:
h("Hello world?...") == h("hello, world!11")
Sobre a interface
O pacote de software consiste em duas partes - uma para gerar sequências e outros hashes para o Trent, a outra para incorporar e verificar as mensagens recebidas quanto à conformidade.
O trabalho com ambos ocorre na linha de comando, através de seus argumentos e fluxos de entrada e saída; nenhuma outra interface foi fornecida (medo e horror). Ainda assim, ser aquele funcionário da sede, que o espião - significa ter algum tipo de qualificação. Bem, se não, ainda mostrarei um exemplo.
O que todos eles fazem?
Para começar, Trent na sede precisa elaborar informações de autenticação. Em particular, pensar antecipadamente em uma situação na qual os agentes trabalharão.
Por exemplo, deixe Bob ser um freelancer de gráficos e Alice, sua cliente. A autenticação ocorrerá sob o disfarce de um pedido para criar gráficos / design / outra coisa.
Relatamos essas informações úteis para ambos e retornamos ao próprio protocolo. Prepararemos uma mensagem incorporada no M.txt com antecedência , minimizando o número de caracteres: "é adequado para mim onde transferir dinheiro". Gere Em e Ex usando o utilitário para Trent:
Trent@HQWorkstation:~$ java -jar HQUtil.jar -ex $(stat -c%s "M.txt") 4096 > Ex.txt Trent@HQWorkstation:~$ cat Ex.txt | java -jar HQUtil.jar -em "$(cat M.txt)" 0.25 4096 > Em.txt Trent@HQWorkstation:~$ cat M.txt | java -jar HQUtil.jar -h > hash.bin
Aqui $(stat -c%s "M.txt")
retorna o tamanho da mensagem em bytes e 4096 - a restrição no intervalo de índices gerados (para permitir o uso de contêineres menores). Da mesma forma, $(cat M.txt)
usado para passar a própria mensagem para o parâmetro da linha de comandos. Em princípio, você pode fazer sem o bash, usando seu próprio trabalho manual, mas para quem é mais conveniente.
Ex.txt é passado para Alice, Em.txt e hash.bin para Bob. Agora imagine que os agentes foram implantados com sucesso e desejam se comunicar - prosseguimos para a execução do protocolo. Bob coloca seu currículo ou oferta de emprego em alguma troca e Alice começa a se comunicar:
: , %_% : , . ? : ,
Bob procura a imagem de um guarda-chuva, talvez até desenhe ele mesmo se a alma é criativa, comprime / impõe um pouco uma marca d'água (ou o que os freelancers estão fazendo lá agora) e faz:
Bob@PC:~$ cat Em.txt | java -jar SpyUtil.jar -e umbrella.png
Depois de esperar um pouco, fingindo trabalhar, se na verdade ele não fez isso, ele envia a Alice o contêiner recebido, naturalmente, tendo em mente o contexto:
: , ,
Transmite um guarda-chuva com uma mensagem, 670kb Por sua vez, recupera a mensagem armazenada internamente:
Alice@PC:~$ cat Ex.txt | java -jar SpyUtil.jar -e umbrella.png
Transforma um conjunto de palavras em uma frase normal e envia para Bob:
: , , ?
Ele verifica a precisão da mensagem:
Bob@PC:~$ java -jar SpyUtil.jar -c hash.bin ", , ?" , , ? - Correct
E continua a comunicação fácil, se estiver tudo bem. Todo o diálogo por parte do observador é mais ou menos assim:
É claro que a contrainteligência não encontrará nada suspeito em tudo isso interceptado. De fato, mesmo os métodos de análise stego neste caso nem sempre serão aplicados - bem, alguém encomendou uma foto de um guarda-chuva por 5 dólares, encontrou algo para surpreender a Internet. Os recursos e pessoas de computação não são infinitos para verificar todas essas situações. A autenticação foi bem sucedida, a cortina.
-> github