encrypt.go (view raw)
1package main
2
3import (
4 "bytes"
5 "crypto/rand"
6 "encoding/base64"
7 "fmt"
8 "io"
9 "strings"
10 "time"
11
12 "golang.org/x/crypto/nacl/box"
13 "humungus.tedunangst.com/r/webs/gencache"
14)
15
16type boxSecKey struct {
17 key *[32]byte
18}
19type boxPubKey struct {
20 key *[32]byte
21}
22
23func encryptString(plain string, seckey boxSecKey, pubkey boxPubKey) (string, error) {
24 var nonce [24]byte
25 rand.Read(nonce[:])
26 out := box.Seal(nil, []byte(plain), &nonce, pubkey.key, seckey.key)
27
28 var sb strings.Builder
29 b64 := base64.NewEncoder(base64.StdEncoding, &sb)
30 b64.Write(nonce[:])
31 b64.Write(out)
32 b64.Close()
33 return sb.String(), nil
34}
35
36func decryptString(encmsg string, seckey boxSecKey, pubkey boxPubKey) (string, error) {
37 var buf bytes.Buffer
38 b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encmsg))
39 io.Copy(&buf, b64)
40 data := buf.Bytes()
41 if len(data) < 24 {
42 return "", fmt.Errorf("not enough data")
43 }
44 var nonce [24]byte
45 copy(nonce[:], data)
46 data = data[24:]
47 out, ok := box.Open(nil, data, &nonce, pubkey.key, seckey.key)
48 if !ok {
49 return "", fmt.Errorf("error decrypting chonk")
50 }
51 return string(out), nil
52}
53
54func b64tokey(s string) (*[32]byte, error) {
55 var buf bytes.Buffer
56 b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(s))
57 n, _ := io.Copy(&buf, b64)
58 if n != 32 {
59 return nil, fmt.Errorf("bad key size")
60 }
61 var key [32]byte
62 copy(key[:], buf.Bytes())
63 return &key, nil
64}
65
66func tob64(data []byte) string {
67 var sb strings.Builder
68 b64 := base64.NewEncoder(base64.StdEncoding, &sb)
69 b64.Write(data)
70 b64.Close()
71 return sb.String()
72}
73
74func newChatKeys() (boxPubKey, boxSecKey) {
75 pub, sec, _ := box.GenerateKey(rand.Reader)
76 return boxPubKey{pub}, boxSecKey{sec}
77}
78
79var chatkeys = gencache.New(gencache.Options[string, boxPubKey]{Fill: func(xonker string) (boxPubKey, bool) {
80 data := getxonker(xonker, chatKeyProp)
81 if data == "" {
82 dlog.Printf("hitting the webs for missing chatkey: %s", xonker)
83 j, err := GetJunk(readyLuserOne, xonker)
84 if err != nil {
85 ilog.Printf("error getting %s: %s", xonker, err)
86 when := time.Now().UTC().Format(dbtimeformat)
87 stmtSaveXonker.Exec(xonker, "failed", chatKeyProp, when)
88 return boxPubKey{}, true
89 }
90 allinjest(originate(xonker), j)
91 data = getxonker(xonker, chatKeyProp)
92 if data == "" {
93 ilog.Printf("key not found after ingesting")
94 when := time.Now().UTC().Format(dbtimeformat)
95 stmtSaveXonker.Exec(xonker, "failed", chatKeyProp, when)
96 return boxPubKey{}, true
97 }
98 }
99 if data == "failed" {
100 ilog.Printf("lookup previously failed chatkey %s", xonker)
101 return boxPubKey{}, true
102 }
103 var pubkey boxPubKey
104 var err error
105 pubkey.key, err = b64tokey(data)
106 if err != nil {
107 ilog.Printf("error decoding %s pubkey: %s", xonker, err)
108 }
109 return pubkey, true
110}, Limit: 512})
111
112func getchatkey(xonker string) (boxPubKey, bool) {
113 pubkey, _ := chatkeys.Get(xonker)
114 return pubkey, pubkey.key != nil
115}