Practical Cryptography with Go

Written by schmuio | Published 2023/01/18
Tech Story Tags: cryptography | programming | guide | technology | tech | tutorial | python | python-programming

TLDRIf you have ever tried to implement basic cryptographic operations and yet you are a developer rather than a cryptography professional, things can become overwhelming very quickly. What algorithm(s) to use, what is safe enough, what is not safe, which implementation, padding, what type of key, encoding, and how many bits should the key size be? Rings a bell?via the TL;DR App

🔑 Batteries-Included High-Level API for Application Programmers

If you have ever tried to implement basic cryptographic operations and yet you are a developer rather than a cryptography professional, things can become overwhelming very quickly. What algorithm(s) to use, what is safe enough, what is not safe, which implementation, padding, what type of key, encoding, and how many bits should the key size be? Rings a bell?

Having encountered these questions repeatedly, we decided to distill a high-level library with the most common cryptographic operations used ubiquitously today. We pursued creating a developer-friendly implementation like what pyca/cryptography offers in the python ecosystem, following best practices and recommendations from a wide array of literature. Enter schmuio/cryptography:

Key points:

  • Cryptography is exceedingly complex so its mathematical implementation should be left to the very limited number of deep subject matter experts in algorithms’ low-level details and properties

  • Respectful of the above, we have selected implementations from the go/crypto package and the Google Cloud Platform key management and cryptography APIs to underpin our higher-level functions

  • Yet, an overwhelming majority of cryptography libraries like go/crypto and Google’s Tink whilst obviously being industry-standard can be still pretty confusing to get going from a perspective of an application developer (nothing wrong with these libraries, but seemingly they are just targeted in a bit different audience)

  • This complexity either brings risks for one to get things wrong or acts as a deterrent to applying cryptographic operations altogether

  • To bridge this gap, we offer a very easy-to-handle selection of the recommended algorithms for symmetric encryption, asymmetric encryption, digital signatures, and time-based one-time passwords

Symmetric Encryption:

  • Arguably, this is the most intuitive case where a single key is used to both encrypt a plaintext and then decrypt the ciphertext to its original plaintext state

  • schmuio/cryptography offers two algorithms for symmetric encryption which considered the present industry standard — AES GCM and ChaCha20-Poly1305

  • Example:

    package yourpackage
    
    import (
        "github.com/schmuio/cryptography"
    )
    
    yourKey, err := cryptography.Key256b()
    // handle potential error ...
    
    ciphertext, err := cryptography.EncryptAesGcm("some-important-plaintext", yourKey)
    // handle potential error ...
    
    plaintext, err := cryptography.DecryptAesGcm(ciphertext, yourKey)
    // handle potential error ...
    
  • Keys, plaintext, and ciphertext are all in string format to prevent explicit dealing with type conversions, encodings, etc.

  • Key generation functions guarantee keys have the right bit sizes and are generated in a cryptographically sound way

Asymmetric Encryption:

  • Asymmetric algorithms use two separate keys — a public one (you can share it freely) for encryption and a private one (keep it a secret!) for decryption
  • This pattern lends itself well to cases where multiple parties can have a public key (e.g. all your users can have your public key) and one party (say your server) has access to the private key to decrypt the ciphertext
  • Example:
package yourpackage

import (
    "github.com/schmuio/cryptography"
)

privateKey, publicKey, err := cryptography.RsaKeyPairPem()
// handle potential error ...

ciphertext, err := cryptography.EncryptRsa("some-important-plaintext", publicKey)
// handle potential error ...

plaintext, err := cryptography.DecryptRsa(ciphertext, privateKey)
// handle potential error ...
  • Note: asymmetric encryption can be applied only to short plaintexts (e.g. up to a few hundred bits); if you want to encrypt larger data you might use hybrid encryption — a scheme where one uses symmetric encryption for the data and asymmetric encryption only to encrypt the symmetric key that encrypts the data

Digital Signatures:

  • Signatures prove the origin (i.e. who sent it) and authenticity (i.e. that it has not been changed in transit) of a message
  • They are constructs from asymmetric cryptography that works in a (broadly) reverse way asymmetric encryption does — the private key is used for signing and the public key is used for verification
  • A typical use case is multiple users verifying a server (Note: exchanging the keys safely to prevent man-in-the-middle attacks is a broad subject beyond the scope of this article)
  • Multiple algorithms are supported — RSA PKCS1v15, RSA PSS, ECDSA P256
  • Example:
package yourpackage

import (
    "github.com/schmuio/cryptography"
)
privateKey, publicKey, err := cryptography.RsaKeyPairPem()
// handle potential error ...

signature, err := cryptography.SignRsaPss("some-very-important-message", privateKeyPem)
// handle potential error ...

err = cryptography.VerifyEcdsa("some-very-important-message", signature, publicKey)
// handle potential error ...
  • Note: messages of arbitrary size can be signed because the message text is hashed to a fixed-size string before signing

Thank you for reading. If you have found this useful, please give us a star on GitHub, request a feature, or share a recommendation.


Written by schmuio | Having been working for enterprises for too long ...
Published by HackerNoon on 2023/01/18