
Tink é uma biblioteca multi-linguagem e multiplataforma desenvolvida dentro do Go que fornece API’s de criptografia cuja maneira de usar, é mais fácil do que as alternativas já populares. Damian Gryski diz que o suporte da Tink para o Go “agora está completo”, por isso, vale a pena dar uma olhada.
As subseções a seguir apresentam instruções e / ou snippets de código Go para algumas tarefas criptográficas comuns no Tink.
Instalando Tink
Para instalar. execute localmente:
go get github.com/google/tink/go/...
para rodar todos os testes localmente:
cd $GOPATH/go/src/github.com/google/tink/go go test ./...
A API Golang Tink também suporta builds de Bazel. Para rodar os testes usando Bazel:
cd $GOPATH/go/src/github.com/google/tink/go bazel build ... && bazel test ...
GoDoc
GoDocs da a API do Tink pode ser encontrado aqui.
Obtendo e usando primitivas
Primitivas representam operações criptográficas oferecidas pelo Tink, portanto formam o núcleo da API do Tink. Uma primitiva é apenas uma interface que especifica quais operações são oferecidas pelo primitivo. Uma primitiva pode ter várias implementações e o usuário escolhe uma implementação desejada usando uma chave do tipo correspondente.
Uma lista de primitivos e suas implementações atualmente suportadas pelo Tink em Golang podem ser encontradas aqui.
AEAD
Criptografia AEAD garante a confidencialidade e autencidade dos dados. Essa primitiva é segura contra CPA.
package main import ( "fmt" "log" "github.com/google/tink/go/aead" "github.com/google/tink/go/keyset" ) func main() { kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) if err != nil { log.Fatal(err) } a, err := aead.New(kh) if err != nil { log.Fatal(err) } ct, err := a.Encrypt([]byte("this data needs to be encrypted"), []byte("associated data")) if err != nil { log.Fatal(err) } pt, err := a.Decrypt(ct, []byte("associated data")) if err != nil { log.Fatal(err) } fmt.Printf("Cipher text: %s\nPlain text: %s\n", ct, pt) }
MAC
O MAC calcula uma tag para uma determinada mensagem que pode ser usada para autenticar uma mensagem. O MAC protege a integridade dos dados, além de fornecer a autenticidade da mensagem.
package main import ( "fmt" "log" "github.com/google/tink/go/keyset" "github.com/google/tink/go/mac" ) func main() { kh, err := keyset.NewHandle(mac.HMACSHA256Tag256KeyTemplate()) if err != nil { log.Fatal(err) } m, err := mac.New(kh) if err != nil { log.Fatal(err) } mac, err := m.ComputeMAC([]byte("this data needs to be MACed")) if err != nil { log.Fatal(err) } if m.VerifyMAC(mac, []byte("this data needs to be MACed")); err != nil { log.fatal("MAC verification failed") } fmt.Println("MAC verification succeeded.") }
AEAD Determinista
Ao contrário de AEAD, implementações dessa interface não são semanticamente seguras, porque a criptografia do mesmo texto sem formatação sempre produz o mesmo ciphertext.
package main import ( "bytes" "fmt" "log" "github.com/google/tink/go/daead" "github.com/google/tink/go/keyset" ) func main() { kh, err := keyset.NewHandle(daead.AESSIVKeyTemplate()) if err != nil { log.Fatal(err) } d, err := daead.New(kh) if err != nil { log.Fatal(err) } ct1, err := d.EncryptDeterministically([]byte("this data needs to be encrypted"), []byte("additional data")) if err != nil { log.Fatal(err) } ct2, err := d.EncryptDeterministically([]byte("this data needs to be encrypted"), []byte("additional data")) if err != nil { log.Fatal(err) } if !bytes.Equal(ct1, ct2) { log.Fatal("cipher texts are not equal") } fmt.Print("Cipher texts are equal.\n") pt, err := d.DecryptDeterministically(ct1, []byte("additional data")) if err != nil { log.Fatal(err) } fmt.Printf("Plain text: %s\n", pt) }
Assinatura Digital
Para assinar dados usando Tink você pode usar os modelos de chaves ECDSA ou ED25519.
package main import ( "fmt" "log" "github.com/google/tink/go/keyset" "github.com/google/tink/go/signature" ) func main() { khPriv, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate()) if err != nil { log.Fatal(err) } s, err := signature.NewSigner(khPriv) if err != nil { log.Fatal(err) } a, err := s.Sign([]byte("this data needs to be signed")) if err != nil { log.Fatal(err) } khPub, err := khPriv.Public() if err != nil { log.Fatal(err) } v, err := signature.NewVerifier(khPub) if err := v.Verify(a, []byte("this data needs to be signed")); err != nil { log.Fatal("signature verification failed") } fmt.Println("Signature verification succeeded.") }
Encriptação e Decriptação Híbridas
A funcionalidade de Criptografia Híbrida é representada como um par de primitivos (interfaces):
- HybridEncrypt para encriptação de dados;
- HybridDecrypt para decriptação
As implementações dessas interfaces são seguras contra adaptive chosen ciphertext attacks.
Além do texto simples, a encriptação usa um parâmetro extra, contextInfo. Geralmente, são dados públicos implícitos do contexto. Ele está ligado ao ciphertext resultante, que permite verificar a integridade de contextInfo (mas não há garantias em relação ao sigilo ou autenticidade de contextInfo).
package main import ( "fmt" "log" "github.com/google/tink/go/hybrid" "github.com/google/tink/go/keyset" ) func main() { khPriv, err := keyset.NewHandle(hybrid.ECIESHKDFAES128CTRHMACSHA256KeyTemplate()) if err != nil { log.Fatal(err) } he, err := hybrid.NewHybridEncrypt(khPriv) if err != nil { log.Fatal(err) } ct, err := h.Encrypt([]byte("secret message"), []byte("context info")) if err != nil { log.Fatal(err) } khPub, err := khPriv.PublicKey() if err != nil { log.Fatal(err) } hd, err := hybrid.NewHybridDecrypt(khPub) pt, err := hd.Decrypt(ct, []byte("context info")) if err != nil { log.Fatal(err) } fmt.Printf("Cipher text: %s\nPlain text: %s\n", ct, pt) }
Encriptação Envelope
As APIs do Tink funcionam com o GCP e o AWS KMS (consulte mais informações sobre os principais Key Management Systems e Credentials).
package main import ( "fmt" "github.com/google/tink/go/aead" "github.com/google/tink/go/core/registry" "github.com/google/tink/go/integration/gcpkms" "github.com/google/tink/go/keyset" ) const ( keyURI = "gcp-kms://......" // customize for your key credentialsPath = "/mysecurestorage/credentials.json" ) func main() { gcpclient, err := gcpkms.NewGCPClient(keyURI) if err != nil { log.Fatal(err) } _, err = gcpclient.LoadCredentials(credentialsPath) if err != nil { log.Fatal(err) } registry.RegisterKMSClient(gcpclient) dek := aead.AES128CTRHMACSHA256KeyTemplate() kh, err := keyset.NewHandle(aead.KMSEnvelopeAEADKeyTemplate(keyURI, dek)) if err != nil { log.Fatal(err) } a, err := aead.New(kh) if err != nil { log.Fatal(err) } ct, err := a.Encrypt([]byte("secret message"), []byte("associated data")) if err != nil { log.Fatal(err) } pt, err := a.Decrypt(ct, []byte("associated data")) if err != nil { log.Fatal(err) } fmt.Printf("Cipher text: %s\nPlain text: %s\n", ct, pt) }
Gerenciamento de Chaves
Gerando novo conjunto(s) de chaves
Para tirar proveito de rotação de chaves e outros recursos de gerenciamento de chaves, um usuário do Tink trabalha normalmente não com chaves únicas, mas com conjuntos de chaves. Os conjuntos de chaves são apenas conjuntos de chaves com alguns parâmetros e metadados adicionais.
Internamente, o Tink armazena conjuntos de chaves como Protocol Buffers, mas você pode trabalhar com conjuntos de chaves por meio de um wrapper chamado identificador de conjunto de chaves. Você pode gerar um novo conjunto de chaves e obter seu identificador usando um KeyTemplate. Os objetos KeysetHandle impõem certas restrições que impedem o vazamento acidental do material sensível da chave.
package main import ( "fmt" "log" "github.com/google/tink/go/aead" "github.com/google/tink/go/keyset" ) func main() { // Other key templates can also be used. kh, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) if err != nil { log.Fatal(err) } fmt.Println(kh.String()) }
Key Templates estão disponíveis para MAC, Assinaturas Digitais, Criptografia AEAD, Criptografia DAEAD e Criptografia Híbrida.
Key Template Type | Key Template |
---|---|
AEAD | aead.AES128CTRHMACSHA256KeyTemplate() |
AEAD | aead.AES128GCMKeyTemplate() |
AEAD | aead.AES256CTRHMACSHA256KeyTemplate() |
AEAD | aead.AES256GCMKeyTemplate() |
AEAD | aead.ChaCha20Poly1305KeyTemplate() |
AEAD | aead.XChaCha20Poly1305KeyTemplate() |
DAEAD | daead.AESSIVKeyTemplate() |
MAC | mac.HMACSHA256Tag128KeyTemplate() |
MAC | mac.HMACSHA256Tag256KeyTemplate() |
MAC | mac.HMACSHA512Tag256KeyTemplate() |
MAC | mac.HMACSHA512Tag512KeyTemplate() |
Signature | signature.ECDSAP256KeyTemplate() |
Signature | signature.ECDSAP384KeyTemplate() |
Signature | signature.ECDSAP521KeyTemplate() |
Hybrid | hybrid.ECIESHKDFAES128GCMKeyTemplate() |
Hybrid | hybrid.ECIESHKDFAES128CTRHMACSHA256KeyTemplate() |
Para evitar o vazamento acidental de material sensível da chave, deve-se evitar misturar a geração e o uso de chaves no código. Para suportar a separação dessas atividades, o Tink fornece uma ferramenta de linha de comando, a Tinkey, que pode ser usada para tarefas comuns de gerenciamento de chaves.
Armazenar e carregar conjuntos de chaves existentes
Depois de gerar a chave, você talvez queira persistir essa chave em um sistema de armazenamento. O Tink suporta a persistência das chaves após a encriptação em qualquer implementação de io.Writer
e io.Reader
.
package main import ( "fmt" "log" "github.com/golang/protobuf/proto" "github.com/google/tink/go/aead" "github.com/google/tink/go/core/registry" "github.com/google/tink/go/integration/gcpkms" "github.com/google/tink/go/keyset" ) const ( keyURI = "gcp-kms://..." credentialsPath = "/mysecurestorage/..." ) func main() { // Generate a new key. kh1, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) if err != nil { log.Fatal(err) } // Fetch the master key from a KMS. gcpClient := gcpkms.NewGCPClient(keyURI) _, err := gpcClient.LoadCredentials(credentialsPath) if err != nil { log.Fatal(err) } registry.RegisterKMSClient(gcpClient) backend, err := gcpClient.GetAEAD(keyURI) if err != nil { log.Fatal(err) } masterKey, err = aead.NewKMSEnvelopeAead(*aead.AES256GCMKeyTemplate(), backend) if err != nil { log.Fatal(err) } // An io.Reader and io.Writer implementation which simply writes to memory. memKeyset := &keyset.MemReaderWriter{} // Write encrypts the keyset handle with the master key and writes to the // io.Writer implementation (memKeyset). We recommend you encrypt the keyset // handle before persisting it. if err := kh1.Write(memKeyset, masterKey); err != nil { log.Fatal(err) } // Read reads the encrypted keyset handle back from the io.Reader implementation // and decrypts it using the master key. kh2, err := keyset.Read(memKeyset, masterKey) if err != nil { log.Fatal(err) } if !proto.Equal(kh1.Keyset(), kh2.Keyset()) { log.Fatal("key handlers are not equal") } fmt.Println("Key handlers are equal.") }
Fonte: Tink for Go HOW-TO