mirror of
https://github.com/taigrr/arc
synced 2025-01-18 04:33:13 -08:00
171 lines
3.4 KiB
Go
171 lines
3.4 KiB
Go
// Copyright (C) 2016 - Will Glozer. All rights reserved.
|
|
|
|
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/subtle"
|
|
"errors"
|
|
"io"
|
|
|
|
"github.com/dchest/blake2b"
|
|
"github.com/magical/argon2"
|
|
"github.com/wg/arc/binary"
|
|
"github.com/wg/ecies"
|
|
"github.com/wg/ecies/xchacha20poly1305"
|
|
)
|
|
|
|
const (
|
|
Public = 0x01
|
|
Private = 0x02
|
|
NonSize = xchacha20poly1305.NonceSize
|
|
TagSize = xchacha20poly1305.TagSize
|
|
)
|
|
|
|
type (
|
|
PublicKey [56]byte
|
|
PrivateKey [56]byte
|
|
)
|
|
|
|
type KeyContainer struct {
|
|
Version byte
|
|
Type byte
|
|
Iterations uint32
|
|
Memory uint32
|
|
Salt [32]byte
|
|
Tag [TagSize]byte
|
|
Nonce [NonSize]byte
|
|
Key [56]byte
|
|
Password []byte
|
|
File io.ReadWriteCloser
|
|
}
|
|
|
|
var (
|
|
ErrInvalidPublicKey = errors.New("invalid public key")
|
|
ErrInvalidPrivateKey = errors.New("invalid private key")
|
|
)
|
|
|
|
func GenerateKeypair() (*PublicKey, *PrivateKey, error) {
|
|
var public, private [56]byte
|
|
err := ecies.GenerateCurve448Key(rand.Reader, &public, &private)
|
|
return (*PublicKey)(&public), (*PrivateKey)(&private), err
|
|
}
|
|
|
|
func ComputeSharedKey(public *PublicKey, private *PrivateKey, size uint8) ([]byte, error) {
|
|
hash, err := blake2b.New(&blake2b.Config{Size: size})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var secret [56]byte
|
|
err = ecies.X448(&secret, (*[56]byte)(public), (*[56]byte)(private))
|
|
hash.Write(secret[:])
|
|
|
|
for i := range secret {
|
|
secret[i] = 0
|
|
}
|
|
|
|
return hash.Sum(nil), err
|
|
}
|
|
|
|
func (private *PrivateKey) Zero() {
|
|
for i := range private {
|
|
private[i] = 0
|
|
}
|
|
}
|
|
|
|
func NewKeyContainer(file io.ReadWriteCloser, password []byte, iterations, memory uint32) *KeyContainer {
|
|
return &KeyContainer{
|
|
Version: 1,
|
|
Iterations: iterations,
|
|
Memory: memory,
|
|
Password: password,
|
|
File: file,
|
|
}
|
|
}
|
|
|
|
func (c *KeyContainer) ReadPublicKey(key *PublicKey) error {
|
|
return c.read(Public, (*[56]byte)(key))
|
|
}
|
|
|
|
func (c *KeyContainer) WritePublicKey(key *PublicKey) error {
|
|
return c.write(Public, (*[56]byte)(key))
|
|
}
|
|
|
|
func (c *KeyContainer) ReadPrivateKey(key *PrivateKey) error {
|
|
return c.read(Private, (*[56]byte)(key))
|
|
}
|
|
|
|
func (c *KeyContainer) WritePrivateKey(key *PrivateKey) error {
|
|
return c.write(Private, (*[56]byte)(key))
|
|
}
|
|
|
|
func (c *KeyContainer) Close() error {
|
|
return c.File.Close()
|
|
}
|
|
|
|
func (c *KeyContainer) read(t byte, key *[56]byte) error {
|
|
switch err := binary.Read(c.File, binary.LE, c); {
|
|
case err != nil:
|
|
return err
|
|
case c.Type != t && t == Public:
|
|
return ErrInvalidPublicKey
|
|
case c.Type != t && t == Private:
|
|
return ErrInvalidPrivateKey
|
|
}
|
|
|
|
var tag [TagSize]byte
|
|
x, err := c.cipher()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
x.Decrypt(key[:], c.Key[:])
|
|
x.Tag(tag[:0])
|
|
|
|
if subtle.ConstantTimeCompare(c.Tag[:], tag[:]) != 1 {
|
|
return ErrInvalidPrivateKey
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *KeyContainer) write(t byte, key *[56]byte) error {
|
|
c.Type = t
|
|
|
|
if _, err := rand.Read(c.Salt[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := rand.Read(c.Nonce[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
x, err := c.cipher()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
x.Encrypt(c.Key[:], key[:])
|
|
x.Tag(c.Tag[:0])
|
|
|
|
return binary.Write(c.File, binary.LE, c)
|
|
}
|
|
|
|
func (c *KeyContainer) cipher() (*xchacha20poly1305.XChaCha20Poly1305, error) {
|
|
var (
|
|
salt = c.Salt[:]
|
|
iterations = int(c.Iterations)
|
|
memory = int64(c.Memory)
|
|
keySize = xchacha20poly1305.KeySize
|
|
)
|
|
|
|
key, err := argon2.Key(c.Password, salt, iterations, 1, memory, keySize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
x := &xchacha20poly1305.XChaCha20Poly1305{}
|
|
return x, x.Init(key[:], c.Nonce[:])
|
|
}
|