Home Artigos Como usar a biblioteca Tink fornecida pelo Google, em Go

Como usar a biblioteca Tink fornecida pelo Google, em Go

0
1
114
Googles's Tink for Go

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.Writerio.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

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Veja Também

Um guia de boas práticas de segurança para desenvolvimento em Go

Confira este livro on-line gratuito sobre boas práticas de segurança de desenvolvimento pa…