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
|
||||
|
||||
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 (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // 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/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
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/term"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func GenerateKey(password string) ([]byte, error) {
|
||||
salt := make([]byte, 16)
|
||||
_, err := rand.Read(salt[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type CipherMethodType byte
|
||||
|
||||
key := argon2.IDKey([]byte(password), salt, 1, 16*1024, 4, 32)
|
||||
const (
|
||||
segmentSizeBytesLen int = 4
|
||||
|
||||
return key, nil
|
||||
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
|
||||
}
|
||||
|
||||
func ReadPasswordFromTerminal() (string, error) {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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())
|
||||
if !term.IsTerminal(inputFd) {
|
||||
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