package nkeys
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"encoding/binary"
"io"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/nacl/box"
)
const (
curveKeyLen = 32
curveDecodeLen = 35
curveNonceLen = 24
)
type ckp struct {
seed [curveKeyLen ]byte
}
func CreateCurveKeys () (KeyPair , error ) {
return CreateCurveKeysWithRand (nil )
}
func CreateCurveKeysWithRand (rr io .Reader ) (KeyPair , error ) {
var kp ckp
_ , priv , err := ed25519 .GenerateKey (rr )
if err != nil {
return nil , err
}
kp .seed = [curveKeyLen ]byte (priv .Seed ())
return &kp , nil
}
func FromCurveSeed (seed []byte ) (KeyPair , error ) {
pb , raw , err := DecodeSeed (seed )
if err != nil {
return nil , err
}
if pb != PrefixByteCurve || len (raw ) != curveKeyLen {
return nil , ErrInvalidCurveSeed
}
var kp ckp
copy (kp .seed [:], raw )
return &kp , nil
}
func (pair *ckp ) Seed () ([]byte , error ) {
return EncodeSeed (PrefixByteCurve , pair .seed [:])
}
func (pair *ckp ) PublicKey () (string , error ) {
var pub [curveKeyLen ]byte
curve25519 .ScalarBaseMult (&pub , &pair .seed )
key , err := Encode (PrefixByteCurve , pub [:])
return string (key ), err
}
func (pair *ckp ) PrivateKey () ([]byte , error ) {
return Encode (PrefixBytePrivate , pair .seed [:])
}
func decodePubCurveKey(src string , dest []byte ) error {
var raw [curveDecodeLen ]byte
n , err := b32Enc .Decode (raw [:], []byte (src ))
if err != nil {
return err
}
if n != curveDecodeLen {
return ErrInvalidCurveKey
}
if prefix := PrefixByte (raw [0 ]); prefix != PrefixByteCurve {
return ErrInvalidPublicKey
}
var crc uint16
end := n - 2
sum := raw [end :n ]
checksum := bytes .NewReader (sum )
if err := binary .Read (checksum , binary .LittleEndian , &crc ); err != nil {
return err
}
if err := validate (raw [:end ], crc ); err != nil {
return err
}
copy (dest , raw [1 :end ])
return nil
}
const XKeyVersionV1 = "xkv1"
const vlen = len (XKeyVersionV1 )
func (pair *ckp ) Seal (input []byte , recipient string ) ([]byte , error ) {
return pair .SealWithRand (input , recipient , rand .Reader )
}
func (pair *ckp ) SealWithRand (input []byte , recipient string , rr io .Reader ) ([]byte , error ) {
var (
rpub [curveKeyLen ]byte
nonce [curveNonceLen ]byte
out [vlen + curveNonceLen ]byte
err error
)
if err = decodePubCurveKey (recipient , rpub [:]); err != nil {
return nil , ErrInvalidRecipient
}
if _ , err := io .ReadFull (rr , nonce [:]); err != nil {
return nil , err
}
copy (out [:vlen ], []byte (XKeyVersionV1 ))
copy (out [vlen :], nonce [:])
return box .Seal (out [:], input , &nonce , &rpub , &pair .seed ), nil
}
func (pair *ckp ) Open (input []byte , sender string ) ([]byte , error ) {
if len (input ) <= vlen +curveNonceLen {
return nil , ErrInvalidEncrypted
}
var (
spub [curveKeyLen ]byte
nonce [curveNonceLen ]byte
err error
)
if !bytes .Equal (input [:vlen ], []byte (XKeyVersionV1 )) {
return nil , ErrInvalidEncVersion
}
copy (nonce [:], input [vlen :vlen +curveNonceLen ])
if err = decodePubCurveKey (sender , spub [:]); err != nil {
return nil , ErrInvalidSender
}
decrypted , ok := box .Open (nil , input [vlen +curveNonceLen :], &nonce , &spub , &pair .seed )
if !ok {
return nil , ErrCouldNotDecrypt
}
return decrypted , nil
}
func (pair *ckp ) Wipe () {
io .ReadFull (rand .Reader , pair .seed [:])
}
func (pair *ckp ) Sign (_ []byte ) ([]byte , error ) {
return nil , ErrInvalidCurveKeyOperation
}
func (pair *ckp ) Verify (_ []byte , _ []byte ) error {
return ErrInvalidCurveKeyOperation
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .