2023-11-02 07:09:51 +00:00
|
|
|
package crypto
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/ecdh"
|
|
|
|
|
"crypto/rand"
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/crypto/blake2b"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Direction int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
KeySize uint = 32
|
|
|
|
|
|
|
|
|
|
SendDirection Direction = iota
|
|
|
|
|
ReceiveDirection
|
|
|
|
|
)
|
|
|
|
|
|
2023-11-08 07:33:19 +00:00
|
|
|
var (
|
|
|
|
|
privateKeyFileMode = os.FileMode(int(0600))
|
|
|
|
|
publicKeyFileMode = os.FileMode(int(0644))
|
|
|
|
|
)
|
|
|
|
|
|
2023-11-02 07:09:51 +00:00
|
|
|
func NewPrivateKey() *ecdh.PrivateKey {
|
|
|
|
|
c := ecdh.X25519()
|
|
|
|
|
k, err := c.GenerateKey(rand.Reader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(fmt.Sprintf("could not generate private key: %s", err.Error()))
|
|
|
|
|
}
|
|
|
|
|
return k
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewSharedKey(local *ecdh.PrivateKey, remote *ecdh.PublicKey, direction Direction) []byte {
|
|
|
|
|
// Calculating a shared secret.
|
|
|
|
|
secret, err := local.ECDH(remote)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Take into account public keys for key derivation.
|
|
|
|
|
// See raw_shared_secret @ https://monocypher.org/manual/x25519#DESCRIPTION
|
|
|
|
|
xof, err := blake2b.NewXOF(32, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
xof.Write(secret)
|
|
|
|
|
if direction == SendDirection {
|
|
|
|
|
xof.Write(local.PublicKey().Bytes())
|
|
|
|
|
xof.Write(remote.Bytes())
|
|
|
|
|
} else {
|
|
|
|
|
xof.Write(remote.Bytes())
|
|
|
|
|
xof.Write(local.PublicKey().Bytes())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := make([]byte, 32)
|
|
|
|
|
_, err = xof.Read(key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return key
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-12 07:39:45 +00:00
|
|
|
func PrivateKeyFromB64(encodedKey []byte) *ecdh.PrivateKey {
|
2023-11-12 08:03:53 +00:00
|
|
|
keyBytes := make([]byte, KeySize)
|
2023-11-12 07:39:45 +00:00
|
|
|
_, err := base64.StdEncoding.Decode(keyBytes, encodedKey)
|
2023-11-02 07:09:51 +00:00
|
|
|
if err != nil {
|
2023-11-12 07:39:45 +00:00
|
|
|
panic(fmt.Errorf("could not decode base64 private key: %w", err))
|
2023-11-02 07:09:51 +00:00
|
|
|
}
|
2023-11-12 07:39:45 +00:00
|
|
|
return PrivateKeyFromBytes(keyBytes)
|
2023-11-02 07:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-12 07:39:45 +00:00
|
|
|
func PublicKeyFromB64(encodedKey []byte) *ecdh.PublicKey {
|
2023-11-12 08:03:53 +00:00
|
|
|
keyBytes := make([]byte, KeySize)
|
2023-11-12 07:39:45 +00:00
|
|
|
_, err := base64.StdEncoding.Decode(keyBytes, encodedKey)
|
2023-11-02 07:09:51 +00:00
|
|
|
if err != nil {
|
2023-11-12 07:39:45 +00:00
|
|
|
panic(fmt.Errorf("could not decode base64 public key: %w", err))
|
2023-11-02 07:09:51 +00:00
|
|
|
}
|
2023-11-12 07:39:45 +00:00
|
|
|
return PublicKeyFromBytes(keyBytes)
|
2023-11-02 07:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-12 07:39:45 +00:00
|
|
|
func PrivateKeyFromBytes(keyBytes []byte) *ecdh.PrivateKey {
|
|
|
|
|
key, err := ecdh.X25519().NewPrivateKey(keyBytes)
|
2023-11-08 07:33:19 +00:00
|
|
|
if err != nil {
|
2023-11-12 07:39:45 +00:00
|
|
|
panic(err)
|
2023-11-08 07:33:19 +00:00
|
|
|
}
|
2023-11-12 07:39:45 +00:00
|
|
|
return key
|
2023-11-02 07:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-12 07:39:45 +00:00
|
|
|
func PublicKeyFromBytes(keyBytes []byte) *ecdh.PublicKey {
|
|
|
|
|
key, err := ecdh.X25519().NewPublicKey(keyBytes)
|
2023-11-02 07:09:51 +00:00
|
|
|
if err != nil {
|
2023-11-12 07:39:45 +00:00
|
|
|
panic(err)
|
2023-11-02 07:09:51 +00:00
|
|
|
}
|
2023-11-12 07:39:45 +00:00
|
|
|
return key
|
2023-11-02 07:09:51 +00:00
|
|
|
}
|