From 27078d2f3b249202b8f799d2cf9e0b3cd80d69ec Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Fri, 9 Nov 2018 23:45:23 -0500 Subject: Vendoring github.com/Shopify/ejson Signed-off-by: Dave Henderson --- .../Shopify/ejson/crypto/boxed_message.go | 104 ++++++++++++++ vendor/github.com/Shopify/ejson/crypto/crypto.go | 160 +++++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 vendor/github.com/Shopify/ejson/crypto/boxed_message.go create mode 100644 vendor/github.com/Shopify/ejson/crypto/crypto.go (limited to 'vendor/github.com/Shopify/ejson/crypto') diff --git a/vendor/github.com/Shopify/ejson/crypto/boxed_message.go b/vendor/github.com/Shopify/ejson/crypto/boxed_message.go new file mode 100644 index 00000000..5dc86980 --- /dev/null +++ b/vendor/github.com/Shopify/ejson/crypto/boxed_message.go @@ -0,0 +1,104 @@ +package crypto + +import ( + "encoding/base64" + "fmt" + "regexp" + "strconv" +) + +var messageParser = regexp.MustCompile("\\AEJ\\[(\\d):([A-Za-z0-9+=/]{44}):([A-Za-z0-9+=/]{32}):(.+)\\]\\z") + +// boxedMessage dumps and loads the wire format for encrypted messages. The +// schema is fairly simple: +// +// "EJ[" +// SchemaVersion ( "1" ) +// ":" +// EncrypterPublic :: base64-encoded 32-byte key +// ":" +// Nonce :: base64-encoded 24-byte nonce +// ":" +// Box :: base64-encoded encrypted message +// "]" +type boxedMessage struct { + SchemaVersion int + EncrypterPublic [32]byte + Nonce [24]byte + Box []byte +} + +// IsBoxedMessage tests whether a value is formatted using the boxedMessage +// format. This can be used to determine whether a string value requires +// encryption or is already encrypted. +func IsBoxedMessage(data []byte) bool { + return messageParser.Find(data) != nil +} + +// Dump dumps to the wire format +func (b *boxedMessage) Dump() []byte { + pub := base64.StdEncoding.EncodeToString(b.EncrypterPublic[:]) + nonce := base64.StdEncoding.EncodeToString(b.Nonce[:]) + box := base64.StdEncoding.EncodeToString(b.Box) + + str := fmt.Sprintf("EJ[%d:%s:%s:%s]", + b.SchemaVersion, pub, nonce, box) + return []byte(str) +} + +// Load restores from the wire format. +func (b *boxedMessage) Load(from []byte) error { + var ssver, spub, snonce, sbox string + var err error + + allMatches := messageParser.FindAllStringSubmatch(string(from), -1) // -> [][][]byte + if len(allMatches) != 1 { + return fmt.Errorf("invalid message format") + } + matches := allMatches[0] + if len(matches) != 5 { + return fmt.Errorf("invalid message format") + } + + ssver = matches[1] + spub = matches[2] + snonce = matches[3] + sbox = matches[4] + + b.SchemaVersion, err = strconv.Atoi(ssver) + if err != nil { + return err + } + + pub, err := base64.StdEncoding.DecodeString(spub) + if err != nil { + return err + } + pubBytes := []byte(pub) + if len(pubBytes) != 32 { + return fmt.Errorf("public key invalid") + } + var public [32]byte + copy(public[:], pubBytes[0:32]) + b.EncrypterPublic = public + + nnc, err := base64.StdEncoding.DecodeString(snonce) + if err != nil { + return err + } + nonceBytes := []byte(nnc) + if len(nonceBytes) != 24 { + return fmt.Errorf("nonce invalid") + } + var nonce [24]byte + copy(nonce[:], nonceBytes[0:24]) + b.Nonce = nonce + + box, err := base64.StdEncoding.DecodeString(sbox) + if err != nil { + return err + } + b.Box = []byte(box) + + return nil +} diff --git a/vendor/github.com/Shopify/ejson/crypto/crypto.go b/vendor/github.com/Shopify/ejson/crypto/crypto.go new file mode 100644 index 00000000..63de1c6a --- /dev/null +++ b/vendor/github.com/Shopify/ejson/crypto/crypto.go @@ -0,0 +1,160 @@ +// Package crypto implements a simple convenience wrapper around +// golang.org/x/crypto/nacl/box. It ultimately models a situation where you +// don't care about authenticating the encryptor, so the nonce and encryption +// public key are prepended to the encrypted message. +// +// Shared key precomputation is used when encrypting but not when decrypting. +// This is not an inherent limitation, but it would complicate the +// implementation a little bit to do precomputation during decryption also. +// If performance becomes an issue (highly unlikely), it's completely feasible +// to add. +package crypto + +import ( + "crypto/rand" + "errors" + "fmt" + + "golang.org/x/crypto/nacl/box" +) + +// Keypair models a Curve25519 keypair. To generate a new Keypair, declare an +// empty one and call Generate() on it. +type Keypair struct { + Public [32]byte + Private [32]byte +} + +// Encrypter is generated from a keypair (typically a newly-generated ephemeral +// keypair, used only for this session) with the public key of an authorized +// decrypter. It is then capable of encrypting messages to that decrypter's +// private key. An instance should normally be obtained only by calling +// Encrypter() on a Keypair instance. +type Encrypter struct { + Keypair *Keypair + PeerPublic [32]byte + SharedKey [32]byte +} + +// Decrypter is generated from a keypair (a fixed keypair, generally, whose +// private key is stored in configuration management or otherwise), and used to +// decrypt messages. It should normally be obtained by calling Decrypter() on a +// Keypair instance. +type Decrypter struct { + Keypair *Keypair +} + +// ErrDecryptionFailed means the decryption didn't work. This normally +// indicates that the message was corrupted or the wrong keypair was used. +var ErrDecryptionFailed = errors.New("couldn't decrypt message") + +// Generate generates a new Curve25519 keypair into a (presumably) empty Keypair +// structure. +func (k *Keypair) Generate() (err error) { + var pub, priv *[32]byte + pub, priv, err = box.GenerateKey(rand.Reader) + if err != nil { + return + } + k.Public = *pub + k.Private = *priv + return +} + +// PublicString returns the public key in the canonical hex-encoded printable form. +func (k *Keypair) PublicString() string { + return fmt.Sprintf("%x", k.Public) +} + +// PrivateString returns the private key in the canonical hex-encoded printable form. +func (k *Keypair) PrivateString() string { + return fmt.Sprintf("%x", k.Private) +} + +// Encrypter returns an Encrypter instance, given a public key, to encrypt +// messages to the paired, unknown, private key. +func (k *Keypair) Encrypter(peerPublic [32]byte) *Encrypter { + return NewEncrypter(k, peerPublic) +} + +// Decrypter returns a Decrypter instance, used to decrypt properly formatted +// messages from arbitrary encrypters. +func (k *Keypair) Decrypter() *Decrypter { + return &Decrypter{Keypair: k} +} + +// NewEncrypter instantiates an Encrypter after pre-computing the shared key for +// the owned keypair and the given decrypter public key. +func NewEncrypter(kp *Keypair, peerPublic [32]byte) *Encrypter { + var shared [32]byte + box.Precompute(&shared, &peerPublic, &kp.Private) + return &Encrypter{ + Keypair: kp, + PeerPublic: peerPublic, + SharedKey: shared, + } +} + +func (e *Encrypter) encrypt(message []byte) (*boxedMessage, error) { + nonce, err := genNonce() + if err != nil { + return nil, err + } + + out := box.SealAfterPrecomputation(nil, []byte(message), &nonce, &e.SharedKey) + + return &boxedMessage{ + SchemaVersion: 1, + EncrypterPublic: e.Keypair.Public, + Nonce: nonce, + Box: out, + }, nil +} + +// Encrypt takes a plaintext message and returns an encrypted message. Unlike +// raw nacl/box encryption, this message is decryptable without passing the +// nonce or public key out-of-band, as it includes both. This is not less +// secure, it just doesn't allow for authorizing the encryptor. That's fine, +// since authorization isn't a desired property of this particular cryptosystem. +func (e *Encrypter) Encrypt(message []byte) ([]byte, error) { + if IsBoxedMessage(message) { + return message, nil + } + boxedMessage, err := e.encrypt(message) + if err != nil { + return nil, err + } + return boxedMessage.Dump(), nil +} + +// Decrypt is passed an encrypted message or a particular format (the format +// generated by (*Encrypter)Encrypt(), which includes the nonce and public key +// used to create the ciphertext. It returns the decrypted string. Note that, +// unlike with encryption, Shared-key-precomputation is not used for decryption. +func (d *Decrypter) Decrypt(message []byte) ([]byte, error) { + var bm boxedMessage + if err := bm.Load(message); err != nil { + return nil, err + } + return d.decrypt(&bm) +} + +func (d *Decrypter) decrypt(bm *boxedMessage) ([]byte, error) { + plaintext, ok := box.Open(nil, bm.Box, &bm.Nonce, &bm.EncrypterPublic, &d.Keypair.Private) + if !ok { + return nil, ErrDecryptionFailed + } + return plaintext, nil +} + +func genNonce() (nonce [24]byte, err error) { + var n int + n, err = rand.Read(nonce[0:24]) + if err != nil { + return + } + if n != 24 { + err = fmt.Errorf("not enough bytes returned from rand.Reader") + } + return +} -- cgit v1.2.3