summaryrefslogtreecommitdiffstats
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/api/api.go70
-rw-r--r--internal/api/errorcode_string.go29
-rw-r--r--internal/cryptoutil/cryptoutil.go139
-rw-r--r--internal/cryptoutil/cryptoutil_test.go159
-rw-r--r--internal/cryptoutil/oae2.go297
5 files changed, 0 insertions, 694 deletions
diff --git a/internal/api/api.go b/internal/api/api.go
deleted file mode 100644
index 6c9ab3a..0000000
--- a/internal/api/api.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// Package api contains type definitions for the gob API.
-package api
-
-import "encoding/gob"
-
-type ErrorCode int
-
-//go:generate go tool stringer -type=ErrorCode
-const (
- Ok ErrorCode = iota
- Unauthenticated // missing credentials
- PermissionDenied // invalid credentials, or login required
- BadRequest // incorrect request format (try gob)
- InvalidArgument // one or more arguments was invalid
- NotFound // requested resource was not found
- Internal // server encountered an unexpected error
-)
-
-func (c ErrorCode) Error() string {
- return c.String()
-}
-
-type Response struct {
- Status ErrorCode
- Err string
- Ok any
-}
-
-type LoginResponse struct {
- Token string
-}
-
-type ListNotesRequest struct{}
-
-type ListNotesResponse struct {
- Notes []string
-}
-
-type CreateNoteRequest struct {
- ContinuationToken []byte
- FileName string
- Chunk []byte
- More bool
-}
-
-type CreateNoteResponse struct {
- Name string
- ContinuationToken []byte
-}
-
-type ReadNoteRequest struct {
- Note string
-}
-
-type ReadNoteResponseStream struct {
- FileName string
- Chunk []byte
-}
-
-type DeleteNoteRequest struct{}
-
-type DeleteNoteResponse struct{}
-
-func init() {
- gob.Register(&LoginResponse{})
- gob.Register(&ListNotesResponse{})
- gob.Register(&CreateNoteResponse{})
- gob.Register(&ReadNoteResponseStream{})
- gob.Register(&DeleteNoteResponse{})
-}
diff --git a/internal/api/errorcode_string.go b/internal/api/errorcode_string.go
deleted file mode 100644
index 5ff2ff0..0000000
--- a/internal/api/errorcode_string.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Code generated by "stringer -type=ErrorCode"; DO NOT EDIT.
-
-package api
-
-import "strconv"
-
-func _() {
- // An "invalid array index" compiler error signifies that the constant values have changed.
- // Re-run the stringer command to generate them again.
- var x [1]struct{}
- _ = x[Ok-0]
- _ = x[Unauthenticated-1]
- _ = x[PermissionDenied-2]
- _ = x[BadRequest-3]
- _ = x[InvalidArgument-4]
- _ = x[NotFound-5]
- _ = x[Internal-6]
-}
-
-const _ErrorCode_name = "OkUnauthenticatedPermissionDeniedBadRequestInvalidArgumentNotFoundInternal"
-
-var _ErrorCode_index = [...]uint8{0, 2, 17, 33, 43, 58, 66, 74}
-
-func (i ErrorCode) String() string {
- if i < 0 || i >= ErrorCode(len(_ErrorCode_index)-1) {
- return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")"
- }
- return _ErrorCode_name[_ErrorCode_index[i]:_ErrorCode_index[i+1]]
-}
diff --git a/internal/cryptoutil/cryptoutil.go b/internal/cryptoutil/cryptoutil.go
deleted file mode 100644
index 0429b02..0000000
--- a/internal/cryptoutil/cryptoutil.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// Package cryptoutil contains friendly wrappers around the algorithms from the
-// standard library's crypto package.
-package cryptoutil
-
-import (
- "crypto/hkdf"
- "crypto/hmac"
- "crypto/pbkdf2"
- "crypto/rand"
- "crypto/sha512"
- "crypto/subtle"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "io"
-)
-
-const (
- defaultIterations = 4718580 // from cmd/finditers
-
- oneSaltSize = 64
- hashSize = 64
- certSize = sha512.Size
-)
-
-// SaltSize is the expected salt length for HashIter.
-const SaltSize = 2 * oneSaltSize
-
-// A PasswordHash must be PasswordHashSize bytes.
-const PasswordHashSize = SaltSize + hashSize
-
-// A PasswordHash is derived from the user's password and can be passed to
-// CheckPassword to verify if two passwords match. A PasswordHash must be
-// PasswordHashSize bytes.
-type PasswordHash []byte
-
-func (h PasswordHash) String() string { return hex.EncodeToString(h) }
-
-// A RawKey is generated from a password hash and can be used to derive further
-// key material.
-type RawKey struct {
- key []byte
-}
-
-// An HMACKey must be HMACKeySize bytes.
-const HMACKeySize = sha512.BlockSize
-
-// An HMAC key can be used to symmetrically sign and verify messages
-// using HMAC-SHA512. An HMACKey must be HMACKeyLen bytes.
-type HMACKey []byte
-
-// A SignedMessage is a message that has been cryptographically signed
-// with HMAC-SHA512.
-type SignedMessage []byte
-
-// An EncryptedMessage is encrypted with AES-256-CTR-HMAC-SHA512.
-type EncryptedMessage []byte
-
-// HashIter runs PBKDF2-SHA512 for iter iterations. Useful for benchmarking. The
-// salt must be SaltSize bytes.
-func HashIter(password string, salt []byte, iter int) ([]byte, error) {
- return pbkdf2.Key(sha512.New, password, salt, iter, sha512.Size)
-}
-
-func hashWithSalt(password string, salt []byte) (RawKey, []byte, error) {
- hash, err := HashIter(password, salt[:oneSaltSize], defaultIterations)
- if err != nil {
- return RawKey{}, nil, err
- }
- key, err := hkdf.Extract(sha512.New, hash, salt[oneSaltSize:])
- if err != nil {
- return RawKey{}, nil, err
- }
- pwHash, err := hkdf.Expand(sha512.New, key, "pwhash", hashSize)
- if err != nil {
- return RawKey{}, nil, err
- }
- return RawKey{key}, pwHash, nil
-}
-
-// Hash hashes a user password using PBKDF2-SHA512.
-func Hash(password string) (PasswordHash, error) {
- salt := make([]byte, SaltSize, SaltSize+hashSize)
- rand.Read(salt)
- _, pwHash, err := hashWithSalt(password, salt)
- if err != nil {
- return nil, err
- }
- return append(salt, pwHash...), nil
-}
-
-// CheckPassword verifies password against a PasswordHash and returns an
-// EncryptionKey derived from the password if successful.
-func (h PasswordHash) CheckPassword(password string) (RawKey, error) {
- salt, expectedHash := h[:SaltSize], h[SaltSize:]
- key, pwHash, err := hashWithSalt(password, salt)
- if err != nil {
- return RawKey{}, err
- }
- if subtle.ConstantTimeCompare(pwHash, expectedHash) == 0 {
- return RawKey{}, errors.New("incorrect password")
- }
- return key, nil
-}
-
-func (k HMACKey) mac(out, msg []byte, info string) []byte {
- mac := hmac.New(sha512.New, k)
- buf := make([]byte, 0, binary.MaxVarintLen64)
- mac.Write(binary.AppendUvarint(buf, uint64(len(info))))
- io.WriteString(mac, info)
- mac.Write(msg)
- return mac.Sum(out)
-}
-
-// Sign generates an HMAC-SHA512 signature and appends it to msg. The info and
-// additionalData are also authenticated, but are not included in the returned
-// signed message.
-func (k HMACKey) Sign(msg []byte, info string) SignedMessage {
- return k.mac(msg, msg, info)
-}
-
-// Verify checks whether the given message has a valid signature, and returns
-// the raw message if it does. The additionalData must match the data passed
-// to Sign.
-func (k HMACKey) Verify(msg SignedMessage, info string) ([]byte, bool) {
- if len(msg) < certSize {
- return nil, false
- }
- msg, sig := msg[:len(msg)-certSize], msg[len(msg)-certSize:]
- if !hmac.Equal(sig, k.mac(nil, msg, info)) {
- return nil, false
- }
- return msg, true
-}
-
-// EncryptionKey derives an EncryptionKey.
-func (k RawKey) EncryptionKey() (EncryptionKey, error) {
- return hkdf.Expand(sha512.New, k.key, "encrypt", sha512.Size)
-}
diff --git a/internal/cryptoutil/cryptoutil_test.go b/internal/cryptoutil/cryptoutil_test.go
deleted file mode 100644
index 059a9e1..0000000
--- a/internal/cryptoutil/cryptoutil_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package cryptoutil
-
-import (
- "bytes"
- "encoding/hex"
- "io"
- "testing"
-)
-
-func mustHex(t testing.TB, s string) []byte {
- t.Helper()
- bytes, err := hex.DecodeString(s)
- if err != nil {
- t.Fatalf("Bad hex %q: %s", s, err)
- }
- return bytes
-}
-
-func TestPassword(t *testing.T) {
- const password = "eclair scroll gliding sled shining removed ascend android cheer confetti"
- hash, err := Hash(password)
- if err != nil {
- t.Fatalf("Hash(%q) failed: %s", password, err)
- }
- _, err = hash.CheckPassword(password)
- if err != nil {
- t.Errorf("%x.CheckPassword(%q) failed: %s", hash, password, err)
- }
-}
-
-func TestSignature(t *testing.T) {
- key := HMACKey(mustHex(t, "669e06ec457778b9a8133edb0a87ea82c6b141ffbbc63c038da96258175eb35c"))
- msg := []byte("test message")
- signedMsg := key.Sign(msg, "info")
- got, ok := key.Verify(signedMsg, "info")
- if !ok {
- t.Fatalf("Verify(%x) rejected the message", signedMsg)
- }
- if !bytes.Equal(got, msg) {
- t.Errorf("Verify(%x) = %x, want %x", signedMsg, got, msg)
- }
-}
-
-func TestEncrypt(t *testing.T) {
- key := EncryptionKey(mustHex(t, "b6de26860e0a39aa134732e58055c06ba028675453f73a6912bc932e58d5743d0e423217f06487d3e96a59980301fcc97dfcc4c6b19765f2947de3c33ab7ef9e"))
- for _, tc := range []struct {
- desc string
- msg []byte
- }{{
- desc: "empty",
- msg: nil,
- }, {
- desc: "short",
- msg: []byte("test message"),
- }, {
- desc: "long",
- msg: func() []byte {
- msg := make([]byte, 1024*1024)
- for i := range msg {
- msg[i] = byte(i)
- }
- return msg
- }(),
- }} {
- t.Run(tc.desc, func(t *testing.T) {
- encryptedMsg := new(bytes.Buffer)
- w := key.NewWriter(encryptedMsg, WithAdditionalData([]byte("additional data")))
- if _, err := w.Write(tc.msg); err != nil {
- t.Fatalf("EncryptingWriter.Write(%q) failed: %s", tc.msg, err)
- }
- if err := w.Close(); err != nil {
- t.Fatalf("EncryptingWriter.Close() failed: %s", err)
- }
- got, err := io.ReadAll(key.NewReader(bytes.NewReader(encryptedMsg.Bytes()), WithAdditionalData([]byte("additional data"))))
- if err != nil {
- t.Fatalf("DecryptingReader.Read(%x) failed: %s", encryptedMsg, err)
- }
- if !bytes.Equal(got, tc.msg) {
- t.Errorf("DecryptingReader.Read(%x) = %x, want %x", encryptedMsg, got, tc.msg)
- }
- })
- }
-}
-
-func TestDeriveKey(t *testing.T) {
- const password = "automaker moisture botch tubular kelp rinse rule giggly donated stock"
- hash, err := Hash(password)
- if err != nil {
- t.Fatalf("Hash(%q) failed: %s", password, err)
- }
- rawKey, err := hash.CheckPassword(password)
- if err != nil {
- t.Fatalf("%x.CheckPassword(%q) failed: %s", hash, password, err)
- }
- key, err := rawKey.EncryptionKey()
- if err != nil {
- t.Fatalf("EncryptionKey() failed: %s", err)
- }
- msg := []byte("test message")
- encryptedMsg := new(bytes.Buffer)
- w := key.NewWriter(encryptedMsg, WithAdditionalData([]byte("additional data")))
- if _, err := w.Write(msg); err != nil {
- t.Fatalf("EncryptingWriter.Write(%q) failed: %s", msg, err)
- }
- if err := w.Close(); err != nil {
- t.Fatalf("EncryptingWriter.Close() failed: %s", err)
- }
- got, err := io.ReadAll(key.NewReader(encryptedMsg, WithAdditionalData([]byte("additional data"))))
- if err != nil {
- t.Fatalf("DecryptingReader.Read(%x) failed: %s", encryptedMsg, err)
- }
- if !bytes.Equal(got, msg) {
- t.Errorf("DecryptingReader.Read(%x) = %x, want %x", encryptedMsg, got, msg)
- }
-}
-
-func BenchmarkEncrypt(b *testing.B) {
- key := EncryptionKey(mustHex(b, "b6de26860e0a39aa134732e58055c06ba028675453f73a6912bc932e58d5743d0e423217f06487d3e96a59980301fcc97dfcc4c6b19765f2947de3c33ab7ef9e"))
- msg := make([]byte, 1024*1024)
- for i := range msg {
- msg[i] = byte(i)
- }
- encryptedMsg := bytes.NewBuffer(make([]byte, 0, 35+len(msg)+16*(len(msg)+defaultOpts.segmentSize-1)/defaultOpts.segmentSize /* ?? */))
- additionalData := []byte("additional data")
- for b.Loop() {
- w := key.NewWriter(encryptedMsg, WithAdditionalData(additionalData))
- if _, err := w.Write(msg); err != nil {
- b.Fatal(err)
- }
- if err := w.Close(); err != nil {
- b.Fatal(err)
- }
- encryptedMsg.Reset()
- }
-}
-
-func BenchmarkDecrypt(b *testing.B) {
- key := EncryptionKey(mustHex(b, "b6de26860e0a39aa134732e58055c06ba028675453f73a6912bc932e58d5743d0e423217f06487d3e96a59980301fcc97dfcc4c6b19765f2947de3c33ab7ef9e"))
- msg := make([]byte, 1024*1024)
- for i := range msg {
- msg[i] = byte(i)
- }
- encryptedMsg := new(bytes.Buffer)
- additionalData := []byte("additional data")
- w := key.NewWriter(encryptedMsg, WithAdditionalData(additionalData))
- if _, err := w.Write(msg); err != nil {
- b.Fatal(err)
- }
- if err := w.Close(); err != nil {
- b.Fatal(err)
- }
- decryptedMsg := make([]byte, len(msg))
- for b.Loop() {
- r := key.NewReader(bytes.NewReader(encryptedMsg.Bytes()), WithAdditionalData(additionalData))
- if _, err := io.ReadFull(r, decryptedMsg); err != nil {
- b.Fatal(err)
- }
- }
-}
diff --git a/internal/cryptoutil/oae2.go b/internal/cryptoutil/oae2.go
deleted file mode 100644
index bc8f53e..0000000
--- a/internal/cryptoutil/oae2.go
+++ /dev/null
@@ -1,297 +0,0 @@
-package cryptoutil
-
-import (
- "bufio"
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/hkdf"
- "crypto/rand"
- "crypto/sha512"
- "encoding/binary"
- "errors"
- "io"
-)
-
-// Online Authenticated Encryption from https://eprint.iacr.org/2015/189.pdf
-
-const (
- aeadOverhead = 16
- aesKeySize = 32
- noncePrefixSize = 3
- gcmNonceSize = 12
- headerSize = aesKeySize + noncePrefixSize
-)
-
-// An EncryptionKey is used for encrypting and decrypting data. A key can be any
-// length, but at least 64 bytes would be recommended.
-type EncryptionKey []byte
-
-type oae2 struct {
- key EncryptionKey
- additionalData []byte
-
- aead cipher.AEAD
- i int64
- noncePrefix [noncePrefixSize]byte
-}
-
-func (o *oae2) initialize(header []byte) error {
- o.i = 1
- copy(o.noncePrefix[:], header[aesKeySize:])
- derivedKey, err := hkdf.Key(sha512.New, o.key, header[:aesKeySize], "", aesKeySize)
- if err != nil {
- return err
- }
- block, err := aes.NewCipher(derivedKey)
- if err != nil {
- return err
- }
- o.aead, err = cipher.NewGCM(block)
- return err
-}
-
-func (o *oae2) nonce(nonce []byte, lastBlock bool) error {
- copy(nonce, o.noncePrefix[:])
- if o.i < 0 {
- return errors.New("counter overflowed (64 bits??)")
- }
- binary.BigEndian.PutUint64(nonce[noncePrefixSize:], uint64(o.i))
- if lastBlock {
- nonce[gcmNonceSize-1] = 1
- }
- return nil
-}
-
-func (o *oae2) encryptBlock(out, block []byte, lastBlock bool) ([]byte, error) {
- nonce := make([]byte, gcmNonceSize)
- if err := o.nonce(nonce, lastBlock); err != nil {
- return nil, err
- }
- encrypted := o.aead.Seal(out, nonce, block, o.additionalData)
- o.i++
- o.additionalData = nil
- return encrypted, nil
-}
-
-func (o *oae2) decryptBlock(out, block []byte, lastBlock bool) ([]byte, error) {
- nonce := make([]byte, gcmNonceSize)
- if err := o.nonce(nonce, lastBlock); err != nil {
- return nil, err
- }
- decrypted, err := o.aead.Open(out, nonce, block, o.additionalData)
- if err != nil {
- return nil, err
- }
- o.i++
- o.additionalData = nil
- return decrypted, nil
-}
-
-// An EncryptingWriter encrypts data in segments using the STREAM construction
-// described in https://eprint.iacr.org/2015/189.pdf. The writer buffers data up
-// to the segment size, so it's important to call Close to flush the
-// final segment.
-type EncryptingWriter struct {
- w io.Writer
- oae2 oae2
- segmentSize int
-
- initialized bool
- err error
- buf []byte
-}
-
-type opts struct {
- additionalData []byte
- segmentSize int
-}
-
-var defaultOpts = opts{segmentSize: 192*1024 - 1 - aeadOverhead}
-
-type Option func(*opts)
-
-func WithAdditionalData(ad []byte) Option {
- return func(opts *opts) {
- opts.additionalData = ad
- }
-}
-
-func WithSegmentSize(segmentSize int) Option {
- return func(opts *opts) {
- opts.segmentSize = segmentSize
- }
-}
-
-// NewWriter returns a new EncryptingWriter that writes to w. The additionalData
-// will be authenticated with the first segment, but is not written to w. The
-// same additional data must be provided when decrypting.
-func (k EncryptionKey) NewWriter(w io.Writer, options ...Option) *EncryptingWriter {
- opts := defaultOpts
- for _, o := range options {
- o(&opts)
- }
- return &EncryptingWriter{
- w: w,
- oae2: oae2{
- key: k,
- additionalData: opts.additionalData,
- },
- segmentSize: opts.segmentSize,
- }
-}
-
-func (w *EncryptingWriter) initialize() error {
- w.initialized = true
- w.buf = make([]byte, 0, w.segmentSize+aeadOverhead)
- header := make([]byte, headerSize)
- rand.Read(header)
- if w.err = w.oae2.initialize(header); w.err != nil {
- return w.err
- }
- _, w.err = w.w.Write(header)
- return w.err
-}
-
-func (w *EncryptingWriter) writeBuf() error {
- var encrypted []byte
- if encrypted, w.err = w.oae2.encryptBlock(w.buf[:0], w.buf, false); w.err != nil {
- return w.err
- }
- if _, w.err = w.w.Write(encrypted); w.err != nil {
- return w.err
- }
- w.buf = w.buf[:0]
- return nil
-}
-
-func (w *EncryptingWriter) Write(buf []byte) (int, error) {
- if !w.initialized {
- if err := w.initialize(); err != nil {
- return 0, err
- }
- }
- if w.err != nil {
- return 0, w.err
- }
- r := bytes.NewReader(buf)
- nn := 0
- for r.Len() > 0 {
- if len(w.buf) == w.segmentSize {
- if err := w.writeBuf(); err != nil {
- return nn, err
- }
- }
- n, _ := r.Read(w.buf[len(w.buf):w.segmentSize])
- w.buf = w.buf[:len(w.buf)+n]
- nn += n
- }
- return nn, nil
-}
-
-var errClosed = errors.New("closed")
-
-// Close encrypts writes the final segment. It does not close the
-// underlying writer.
-func (w *EncryptingWriter) Close() error {
- if !w.initialized {
- if err := w.initialize(); err != nil {
- return err
- }
- }
- if w.err == errClosed {
- return nil
- }
- if w.err != nil {
- return w.err
- }
- var encrypted []byte
- if encrypted, w.err = w.oae2.encryptBlock(w.buf[:0], w.buf, true); w.err != nil {
- return w.err
- }
- if _, w.err = w.w.Write(encrypted); w.err != nil {
- return w.err
- }
- w.err = errClosed
- return nil
-}
-
-// A DecryptingReader decrypts data using the STREAM construction.
-type DecryptingReader struct {
- r *bufio.Reader
- oae2 oae2
- segmentSize int
-
- initialized bool
- buf bytes.Buffer
-}
-
-// NewReader returns a new DecryptingWriter that decrypts data from r. The
-// additionaData must be the same that was provided when encrypting.
-func (k EncryptionKey) NewReader(r io.Reader, options ...Option) *DecryptingReader {
- opts := defaultOpts
- for _, o := range options {
- o(&opts)
- }
- return &DecryptingReader{
- r: bufio.NewReaderSize(r, headerSize),
- oae2: oae2{
- key: k,
- additionalData: opts.additionalData,
- },
- segmentSize: opts.segmentSize,
- }
-}
-
-func (r *DecryptingReader) initialize() error {
- header, err := r.r.Peek(headerSize)
- if err != nil {
- return err
- }
- if err := r.oae2.initialize(header); err != nil {
- return err
- }
- r.r.Discard(len(header))
- r.buf = *bytes.NewBuffer(make([]byte, 0, r.segmentSize+aeadOverhead))
- r.initialized = true
- return nil
-}
-
-func (r *DecryptingReader) fillBuf() error {
- r.buf.Reset()
- buf := r.buf.AvailableBuffer()
- buf = buf[:cap(buf)]
- n, err := io.ReadFull(r.r, buf)
- if n == 0 {
- if err == io.ErrUnexpectedEOF {
- return io.EOF
- }
- return err
- }
- buf = buf[:n]
- if n > 0 {
- // Peek one extra byte to check if this is the last segment
- _, readErr := r.r.Peek(1)
- result, err := r.oae2.decryptBlock(buf[:0], buf, readErr == io.EOF)
- if err != nil {
- return err
- }
- r.buf.Write(result)
- }
- return nil
-}
-
-func (r *DecryptingReader) Read(buf []byte) (int, error) {
- if !r.initialized {
- if err := r.initialize(); err != nil {
- return 0, err
- }
- }
- if r.buf.Len() == 0 {
- if err := r.fillBuf(); err != nil {
- return 0, err
- }
- }
- n, _ := r.buf.Read(buf)
- return n, nil
-}