change workstation
This commit is contained in:
parent
3dba047793
commit
bd976b1533
8
go.mod
8
go.mod
@ -2,12 +2,14 @@ module gitea.suyono.dev/suyono/simple-privacy-tool
|
|||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/spf13/cobra v1.7.0
|
require (
|
||||||
|
github.com/spf13/cobra v1.7.0
|
||||||
|
golang.org/x/crypto v0.11.0
|
||||||
|
golang.org/x/term v0.10.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
golang.org/x/crypto v0.11.0 // indirect
|
|
||||||
golang.org/x/sys v0.10.0 // indirect
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
golang.org/x/term v0.10.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
69
privacy/chacha.go
Normal file
69
privacy/chacha.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package privacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type chacha20 struct {
|
||||||
|
aead cipher.AEAD
|
||||||
|
privacy *Privacy
|
||||||
|
block []byte
|
||||||
|
blank bool
|
||||||
|
nonce []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChaCha20(key []byte, p *Privacy) (c *chacha20, err error) {
|
||||||
|
c = &chacha20{
|
||||||
|
privacy: p,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.aead, err = chacha20poly1305.NewX(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.nonce = make([]byte, c.aead.NonceSize())
|
||||||
|
c.block = make([]byte, 0, c.aead.NonceSize()+int(p.GetSegmentSize())+c.aead.Overhead())
|
||||||
|
c.blank = true
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20) write(wc io.WriteCloser, inwr []byte) error {
|
||||||
|
var err error
|
||||||
|
if c.blank {
|
||||||
|
_, err = rand.Read(c.nonce)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(inwr) > 0 {
|
||||||
|
c.blank = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.block)+len(inwr) < int(c.privacy.GetSegmentSize()) {
|
||||||
|
start := len(c.block)
|
||||||
|
c.block = c.block[:start+len(inwr)]
|
||||||
|
copy(c.block[start:], inwr)
|
||||||
|
return nil
|
||||||
|
} else if len(c.block)+len(inwr) == int(c.privacy.GetSegmentSize()) {
|
||||||
|
start := len(c.block)
|
||||||
|
c.block = c.block[:start+len(inwr)]
|
||||||
|
copy(c.block[start:], inwr)
|
||||||
|
|
||||||
|
lenSlot := make([]byte, 4)
|
||||||
|
blockLen := uint32(len(c.block) + c.aead.Overhead())
|
||||||
|
binary.BigEndian.PutUint32(lenSlot, blockLen)
|
||||||
|
|
||||||
|
//TODO: fix and resume from here, it was a stopping point
|
||||||
|
//result := c.aead.Seal(c.block[:0], c.nonce, c.block, lenSlot)
|
||||||
|
c.aead.Seal(c.block[:0], c.nonce, c.block, lenSlot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -1,26 +1,223 @@
|
|||||||
package privacy
|
package privacy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"golang.org/x/crypto/argon2"
|
"golang.org/x/crypto/argon2"
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKey(password string) ([]byte, error) {
|
type CipherMethodType byte
|
||||||
salt := make([]byte, 16)
|
|
||||||
_, err := rand.Read(salt[1:])
|
const (
|
||||||
|
segmentSizeBytesLen int = 4
|
||||||
|
|
||||||
|
Uninitialised CipherMethodType = 0
|
||||||
|
XChaCha20Simple CipherMethodType = 1
|
||||||
|
|
||||||
|
DefaultCipherMethod CipherMethodType = XChaCha20Simple
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidSaltLen = errors.New("invalid salt length")
|
||||||
|
ErrUninitialisedMethod = errors.New("cipher method type uninitialised")
|
||||||
|
ErrInvalidCipherMethod = errors.New("invalid cipher method type")
|
||||||
|
ErrCannotReadMagicBytes = errors.New("cannot read magic bytes")
|
||||||
|
ErrInvalidReadFlow = errors.New("func ReadMagic should be called before calling Read")
|
||||||
|
ErrInvalidKeyState = errors.New("func GenerateKey should be called first")
|
||||||
|
ErrInvalidSegmentLength = errors.New("segment length is too long")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reader struct {
|
||||||
|
*Privacy
|
||||||
|
reader io.Reader
|
||||||
|
buf []byte
|
||||||
|
bufSlice []byte
|
||||||
|
spillOver []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type WriteCloser struct {
|
||||||
|
*Privacy
|
||||||
|
writeCloser io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
type Privacy struct {
|
||||||
|
salt []byte
|
||||||
|
segmentSize uint32
|
||||||
|
cmType CipherMethodType
|
||||||
|
aead cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPrivacy() *Privacy {
|
||||||
|
return &Privacy{
|
||||||
|
segmentSize: 64 * 1024 * 1024,
|
||||||
|
cmType: Uninitialised,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrivacyReader(reader io.Reader) *Reader {
|
||||||
|
return &Reader{
|
||||||
|
Privacy: newPrivacy(),
|
||||||
|
reader: reader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrivacyWriteCloser(wc io.WriteCloser, cmType CipherMethodType) *WriteCloser {
|
||||||
|
privacy := newPrivacy()
|
||||||
|
privacy.cmType = cmType
|
||||||
|
return &WriteCloser{
|
||||||
|
Privacy: privacy,
|
||||||
|
writeCloser: wc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Privacy) SetSalt(salt []byte) error {
|
||||||
|
if len(salt) != 16 {
|
||||||
|
return ErrInvalidSaltLen
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.salt) != 16 {
|
||||||
|
p.salt = make([]byte, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p.salt, salt)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Privacy) GetSegmentSize() uint32 {
|
||||||
|
return p.segmentSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Privacy) SetSegmentSize(size uint32) {
|
||||||
|
p.segmentSize = size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Privacy) NewSalt() error {
|
||||||
|
if len(p.salt) != 16 {
|
||||||
|
p.salt = make([]byte, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cmType == Uninitialised {
|
||||||
|
return ErrUninitialisedMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
p.salt[0] = byte(p.cmType)
|
||||||
|
_, err := rand.Read(p.salt[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
key := argon2.IDKey([]byte(password), salt, 1, 16*1024, 4, 32)
|
return nil
|
||||||
|
|
||||||
return key, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadPasswordFromTerminal() (string, error) {
|
func (p *Privacy) GenerateKey(passphrase string) error {
|
||||||
|
var (
|
||||||
|
key []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if p.cmType == Uninitialised {
|
||||||
|
return ErrUninitialisedMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
key = argon2.IDKey([]byte(passphrase), p.salt, 1, 16*1024, 4, 32)
|
||||||
|
switch p.cmType {
|
||||||
|
case XChaCha20Simple:
|
||||||
|
p.aead, err = chacha20poly1305.NewX(key)
|
||||||
|
default:
|
||||||
|
return ErrInvalidCipherMethod
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadMagic() (err error) {
|
||||||
|
if r.cmType == Uninitialised {
|
||||||
|
magic := make([]byte, 16)
|
||||||
|
_, err = r.reader.Read(magic[:1])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch CipherMethodType(magic[0]) {
|
||||||
|
case XChaCha20Simple:
|
||||||
|
r.cmType = XChaCha20Simple
|
||||||
|
_, err = r.reader.Read(magic[1:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.SetSalt(magic)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ErrInvalidCipherMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Read(b []byte) (n int, err error) {
|
||||||
|
var (
|
||||||
|
segmentLen uint32
|
||||||
|
segmentLenBytes []byte
|
||||||
|
//nonce []byte
|
||||||
|
//ciphertext []byte
|
||||||
|
//plaintext []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
if r.cmType == Uninitialised {
|
||||||
|
return 0, ErrInvalidReadFlow
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.aead == nil {
|
||||||
|
return 0, ErrInvalidKeyState
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(r.buf) != int(r.segmentSize)+r.aead.Overhead()+r.aead.NonceSize() {
|
||||||
|
r.buf = make([]byte, int(r.segmentSize)+r.aead.Overhead()+r.aead.NonceSize())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(r.spillOver) != int(r.segmentSize) {
|
||||||
|
r.spillOver = make([]byte, int(r.segmentSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.bufSlice) == 0 {
|
||||||
|
//TODO: nothing in the buffer, fill it up
|
||||||
|
segmentLenBytes = make([]byte, segmentSizeBytesLen)
|
||||||
|
n, err = r.reader.Read(segmentLenBytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentLen = binary.LittleEndian.Uint32(segmentLenBytes)
|
||||||
|
if segmentLen > r.segmentSize {
|
||||||
|
return 0, ErrInvalidSegmentLength
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = r.reader.Read(r.buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: fix me, placeholder!
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadPassphraseFromTerminal() (string, error) {
|
||||||
var inputFd int = int(os.Stdin.Fd())
|
var inputFd int = int(os.Stdin.Fd())
|
||||||
if !term.IsTerminal(inputFd) {
|
if !term.IsTerminal(inputFd) {
|
||||||
return "", errors.New("not a terminal")
|
return "", errors.New("not a terminal")
|
||||||
|
|||||||
24
privacy/privacy_test.go
Normal file
24
privacy/privacy_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package privacy
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTrial(t *testing.T) {
|
||||||
|
x := make([]byte, 20)
|
||||||
|
for i := range x {
|
||||||
|
x[i] = byte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("len x:", len(x))
|
||||||
|
t.Log("cap x:", cap(x))
|
||||||
|
t.Log("x:", x)
|
||||||
|
|
||||||
|
y := x[5:13]
|
||||||
|
t.Log("len y:", len(y))
|
||||||
|
t.Log("cap y:", cap(y))
|
||||||
|
t.Log("y:", y)
|
||||||
|
|
||||||
|
z := y[10:15]
|
||||||
|
t.Log("len z:", len(z))
|
||||||
|
t.Log("cap z:", cap(z))
|
||||||
|
t.Log("z:", z)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user