As primitivas Prehash e SignPrehash dividem o cálculo de uma assinatura digital em duas etapas:
- O prehash só precisa da chave pública. Ele transforma uma mensagem de comprimento arbitrário em um valor de pré-hash curto e de tamanho fixo.
- O SignPrehash precisa da chave privada. Ele transforma um valor de pré-hash em uma assinatura.
A assinatura resultante é uma assinatura comum da mensagem original. Você o verifica com a primitiva Assinatura digital PublicKeyVerify regular, e os verificadores não precisam saber nem se importar que a assinatura foi produzida em duas etapas.
Trata o valor de pré-hash como bytes opacos. Ele tem um tamanho fixo e carrega o ID da chave para a qual foi calculado, mas o layout faz parte do formato de fio do Tink, e você não deve analisar nem construir um por conta própria. Se você estiver migrando o Tink ou precisar dos detalhes no nível de byte, consulte Formato de fio do Tink.
Use esse par de primitivas quando:
- A chave de assinatura está em outro lugar, por exemplo, em um HSM, em um KMS ou atrás de um limite de RPC, e você não quer enviar a mensagem completa por esse limite.
- A mensagem é grande, e o assinante remoto impõe um limite de tamanho para a solicitação.
Se nenhuma delas se aplicar, use a primitiva Assinatura digital simples. Ela é mais fácil de usar e mais difícil de usar de forma indevida.
Conjunto de chaves
As duas primitivas selecionam chaves de maneira diferente, da mesma forma que a assinatura e a verificação fazem para a primitiva Assinatura digital:
Prehash.Computesempre usa a chave primária do conjunto de chaves públicas e registra o ID dessa chave no valor de pré-hash. É o lado que escolhe com qual chave a assinatura será feita.SignPrehash.Signlê o ID da chave do valor de pré-hash e assina com a chave ativada correspondente do conjunto de chaves privadas. É o lado que segue uma escolha já feita por outra pessoa. Se nenhuma chave ativada no conjunto de chaves tiver esse ID, a chamada vai falhar.
Cada chave em um conjunto de chaves SignPrehash precisa ter um requisito de ID. Caso contrário, a criação da primitiva falha.
Garantias mínimas de segurança
- A assinatura resultante tem as mesmas propriedades de uma assinatura produzida pela primitiva Assinatura digital com o mesmo tipo de chave.
- O Tink adiciona ao valor pré-hash um prefixo de 5 bytes que contém um valor especial reservado e o ID da chave para a qual ele foi calculado.
SignPrehashassina o valor apenas com essa chave. Se o valor está criptograficamente vinculado a essa chave depende do algoritmo. Para Mu ML-DSA externa, sim. Consulte Formato de fio do Tink. - As mensagens podem ter qualquer tamanho.
Itens a serem observados
- O assinante não pode inspecionar o que assina. Qualquer pessoa que possa ligar para
SignPrehashpode receber uma mensagem arbitrária assinada, e o signatário não tem como aplicar uma política ao conteúdo da mensagem. Proteja o acesso aSignPrehashda mesma forma que você protege o acesso aPublicKeySign. - Proteja o valor de pré-hash em trânsito. Ele não é secreto, mas um invasor que pode modificá-lo em voo controla o que é assinado.
Escolher um tipo de chave
O ML-DSA no modo External Mu, conforme descrito na RFC 9881, é o único algoritmo compatível com a Tink para Prehash e SignPrehash. Use uma chave ML-DSA padrão com essas primitivas.
Recomendamos ML_DSA_65 para a maioria dos casos de uso.
A chave precisa ter um requisito de ID, porque todo valor de pré-hash começa com um prefixo que contém o ID da chave para a qual ele foi calculado. As seguintes variantes são aceitas:
TINK: a assinatura resultante começa com o prefixo de saída de 5 bytes usual do Tink.NO_PREFIX_WITH_PREHASH_ID: a assinatura resultante não tem um prefixo de saída, mas a chave ainda tem o ID de que o valor de pré-hash precisa.
As chaves que usam a variante NO_PREFIX (bruta) não são compatíveis porque não têm um ID.
Exemplos
Os exemplos a seguir assinam uma mensagem em duas etapas e verificam a assinatura resultante com a primitiva Assinatura digital comum.
Eles são mostrados como um bloco para facilitar a leitura. Em uma implantação real, as duas etapas são executadas em lugares diferentes. Consulte Quero assinar dados com um assinante remoto para ver um tutorial detalhado.
C++
#include "tink/keyset_handle.h" #include "tink/public_key_verify.h" #include "tink/signature/config_2026.h" #include "tink/signature/prehash.h" #include "tink/signature/sign_prehash.h" using ::crypto::tink::ConfigSignature2026; using ::crypto::tink::Prehash; using ::crypto::tink::PublicKeyVerify; using ::crypto::tink::SignPrehash; // 1. Wherever the message is: compute the prehash value. This needs only // the public keyset. absl::StatusOr<std::unique_ptr<Prehash>> prehasher = public_handle.GetPrimitive<Prehash>(ConfigSignature2026()); if (!prehasher.ok()) return prehasher.status(); absl::StatusOr<std::string> prehash = (*prehasher)->Compute(message); if (!prehash.ok()) return prehash.status(); // 2. Wherever the private key is: turn the prehash value into a signature. absl::StatusOr<std::unique_ptr<SignPrehash>> signer = private_handle.GetPrimitive<SignPrehash>(ConfigSignature2026()); if (!signer.ok()) return signer.status(); absl::StatusOr<std::string> signature = (*signer)->Sign(*prehash); if (!signature.ok()) return signature.status(); // 3. Anywhere: verify the signature over the original message. absl::StatusOr<std::unique_ptr<PublicKeyVerify>> verifier = public_handle.GetPrimitive<PublicKeyVerify>(ConfigSignature2026()); if (!verifier.ok()) return verifier.status(); absl::Status verified = (*verifier)->Verify(*signature, message);
Go
import ( "github.com/tink-crypto/tink-go/v2/signature" "github.com/tink-crypto/tink-go/v2/signprehash" ) // 1. Wherever the message is: compute the prehash value. This needs only // the public keyset. prehasher, err := signprehash.NewPrehash(publicHandle) if err != nil { return err } prehash, err := prehasher.ComputePrehash(message) if err != nil { return err } // 2. Wherever the private key is: turn the prehash value into a signature. signer, err := signprehash.NewPrehashSigner(privateHandle) if err != nil { return err } sig, err := signer.SignPrehash(prehash) if err != nil { return err } // 3. Anywhere: verify the signature over the original message. verifier, err := signature.NewVerifier(publicHandle) if err != nil { return err } if err := verifier.Verify(sig, message); err != nil { return err }
Temas relacionados
- Quero assinar dados com um assinante remoto: um tutorial detalhado do fluxo de duas etapas.
- Assinatura digital: assine e verifique em uma única etapa, quando o signatário puder ver a mensagem.
- Tipos de chaves primitivas compatíveis por linguagem
- Formato de fio do Tink: o layout no nível de byte de um valor de pré-hash para portar o Tink para outra linguagem.