all repos — honk @ 0ee7c6e3644710a9c774df7eeeac3f305279f68d

my fork of honk

encrypt.go (view raw)

  1//
  2// Copyright (c) 2024 Ted Unangst <tedu@tedunangst.com>
  3//
  4// Permission to use, copy, modify, and distribute this software for any
  5// purpose with or without fee is hereby granted, provided that the above
  6// copyright notice and this permission notice appear in all copies.
  7//
  8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15
 16package main
 17
 18import (
 19	"bytes"
 20	"crypto/rand"
 21	"encoding/base64"
 22	"fmt"
 23	"io"
 24	"strings"
 25	"time"
 26
 27	"golang.org/x/crypto/nacl/box"
 28	"humungus.tedunangst.com/r/webs/gencache"
 29)
 30
 31type boxSecKey struct {
 32	key *[32]byte
 33}
 34type boxPubKey struct {
 35	key *[32]byte
 36}
 37
 38func encryptString(plain string, seckey boxSecKey, pubkey boxPubKey) (string, error) {
 39	var nonce [24]byte
 40	rand.Read(nonce[:])
 41	out := box.Seal(nil, []byte(plain), &nonce, pubkey.key, seckey.key)
 42
 43	var sb strings.Builder
 44	b64 := base64.NewEncoder(base64.StdEncoding, &sb)
 45	b64.Write(nonce[:])
 46	b64.Write(out)
 47	b64.Close()
 48	return sb.String(), nil
 49}
 50
 51func decryptString(encmsg string, seckey boxSecKey, pubkey boxPubKey) (string, error) {
 52	var buf bytes.Buffer
 53	b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encmsg))
 54	io.Copy(&buf, b64)
 55	data := buf.Bytes()
 56	if len(data) < 24 {
 57		return "", fmt.Errorf("not enough data")
 58	}
 59	var nonce [24]byte
 60	copy(nonce[:], data)
 61	data = data[24:]
 62	out, ok := box.Open(nil, data, &nonce, pubkey.key, seckey.key)
 63	if !ok {
 64		return "", fmt.Errorf("error decrypting chonk")
 65	}
 66	return string(out), nil
 67}
 68
 69func b64tokey(s string) (*[32]byte, error) {
 70	var buf bytes.Buffer
 71	b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(s))
 72	n, _ := io.Copy(&buf, b64)
 73	if n != 32 {
 74		return nil, fmt.Errorf("bad key size")
 75	}
 76	var key [32]byte
 77	copy(key[:], buf.Bytes())
 78	return &key, nil
 79}
 80
 81func tob64(data []byte) string {
 82	var sb strings.Builder
 83	b64 := base64.NewEncoder(base64.StdEncoding, &sb)
 84	b64.Write(data)
 85	b64.Close()
 86	return sb.String()
 87}
 88
 89func newChatKeys() (boxPubKey, boxSecKey) {
 90	pub, sec, _ := box.GenerateKey(rand.Reader)
 91	return boxPubKey{pub}, boxSecKey{sec}
 92}
 93
 94var chatkeys = gencache.New(gencache.Options[string, boxPubKey]{Fill: func(xonker string) (boxPubKey, bool) {
 95	data := getxonker(xonker, chatKeyProp)
 96	if data == "" {
 97		dlog.Printf("hitting the webs for missing chatkey: %s", xonker)
 98		j, err := GetJunk(readyLuserOne, xonker)
 99		if err != nil {
100			ilog.Printf("error getting %s: %s", xonker, err)
101			when := time.Now().UTC().Format(dbtimeformat)
102			stmtSaveXonker.Exec(xonker, "failed", chatKeyProp, when)
103			return boxPubKey{}, true
104		}
105		allinjest(originate(xonker), j)
106		data = getxonker(xonker, chatKeyProp)
107		if data == "" {
108			ilog.Printf("key not found after ingesting")
109			when := time.Now().UTC().Format(dbtimeformat)
110			stmtSaveXonker.Exec(xonker, "failed", chatKeyProp, when)
111			return boxPubKey{}, true
112		}
113	}
114	if data == "failed" {
115		ilog.Printf("lookup previously failed chatkey %s", xonker)
116		return boxPubKey{}, true
117	}
118	var pubkey boxPubKey
119	var err error
120	pubkey.key, err = b64tokey(data)
121	if err != nil {
122		ilog.Printf("error decoding %s pubkey: %s", xonker, err)
123	}
124	return pubkey, true
125}, Limit: 512})
126
127func getchatkey(xonker string) (boxPubKey, bool) {
128	pubkey, _ := chatkeys.Get(xonker)
129	return pubkey, pubkey.key != nil
130}