gnunet-go

GNUnet Bindings for Go
Log | Files | Refs | README | LICENSE

gns_pkey.go (8452B)


      1 // This file is part of gnunet-go, a GNUnet-implementation in Golang.
      2 // Copyright (C) 2019-2022 Bernd Fix  >Y<
      3 //
      4 // gnunet-go is free software: you can redistribute it and/or modify it
      5 // under the terms of the GNU Affero General Public License as published
      6 // by the Free Software Foundation, either version 3 of the License,
      7 // or (at your option) any later version.
      8 //
      9 // gnunet-go is distributed in the hope that it will be useful, but
     10 // WITHOUT ANY WARRANTY; without even the implied warranty of
     11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12 // Affero General Public License for more details.
     13 //
     14 // You should have received a copy of the GNU Affero General Public License
     15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16 //
     17 // SPDX-License-Identifier: AGPL3.0-or-later
     18 
     19 package crypto
     20 
     21 import (
     22 	"crypto/aes"
     23 	"crypto/cipher"
     24 	"crypto/sha256"
     25 	"crypto/sha512"
     26 	"gnunet/enums"
     27 	"gnunet/util"
     28 
     29 	"github.com/bfix/gospel/crypto/ed25519"
     30 	"github.com/bfix/gospel/data"
     31 	"github.com/bfix/gospel/logger"
     32 	"github.com/bfix/gospel/math"
     33 	"golang.org/x/crypto/hkdf"
     34 )
     35 
     36 //======================================================================
     37 // PKEY implementation for GNS zone crypto:
     38 // ----------------------------------------
     39 // Based on the Ed25519 curve. Private keys are defined by a scalar
     40 // and signatures are based on a deterministic variant of ECDSA.
     41 //======================================================================
     42 
     43 // register our implementation
     44 func init() {
     45 	zoneImpl[ZONE_PKEY] = &ZoneImplementation{
     46 		NewPrivate:    func() ZonePrivateImpl { return &PKEYPrivateImpl{} },
     47 		PrivateSize:   32,
     48 		NewPublic:     func() ZoneKeyImpl { return &PKEYPublicImpl{} },
     49 		PublicSize:    32,
     50 		NewSignature:  func() ZoneSigImpl { return &PKEYSigImpl{} },
     51 		SignatureSize: 64,
     52 	}
     53 }
     54 
     55 //----------------------------------------------------------------------
     56 // Public key
     57 //----------------------------------------------------------------------
     58 
     59 // PKEYPublicImpl implements the public key scheme.
     60 type PKEYPublicImpl struct {
     61 	ztype enums.GNSType
     62 	pub   *ed25519.PublicKey
     63 }
     64 
     65 // Init instance from binary data. The data represents a big integer
     66 // (in big-endian notation) for the private scalar d.
     67 func (pk *PKEYPublicImpl) Init(data []byte) error {
     68 	pk.ztype = ZONE_PKEY
     69 	pk.pub = ed25519.NewPublicKeyFromBytes(data)
     70 	return nil
     71 }
     72 
     73 // Bytes returns a binary representation of the instance suitable for
     74 // consumption in 'Init()'.
     75 func (pk *PKEYPublicImpl) Bytes() []byte {
     76 	return pk.pub.Bytes()
     77 }
     78 
     79 // Derive a public key from this key based on a big integer
     80 // (key blinding). Returns the derived key and the blinding value.
     81 func (pk *PKEYPublicImpl) Derive(h *math.Int) (dPk ZoneKeyImpl, hOut *math.Int, err error) {
     82 	// limit to allowed value range
     83 	hOut = h.Mod(ed25519.GetCurve().N)
     84 	derived := pk.pub.Mult(hOut)
     85 	dPk = &PKEYPublicImpl{
     86 		pk.ztype,
     87 		derived,
     88 	}
     89 	return
     90 }
     91 
     92 // Encrypt binary data (of any size). Output can be larger than input
     93 func (pk *PKEYPublicImpl) Encrypt(data []byte, label string, expires util.AbsoluteTime) ([]byte, error) {
     94 	return pk.cipher(true, data, label, expires)
     95 }
     96 
     97 // Decrypt binary data (of any size). Output can be smaller than input
     98 func (pk *PKEYPublicImpl) Decrypt(data []byte, label string, expires util.AbsoluteTime) ([]byte, error) {
     99 	return pk.cipher(false, data, label, expires)
    100 }
    101 
    102 // Verify a signature for binary data
    103 func (pk *PKEYPublicImpl) Verify(data []byte, zs *ZoneSignature) (ok bool, err error) {
    104 	var sig *ed25519.EcSignature
    105 	if sig, err = ed25519.NewEcSignatureFromBytes(zs.Signature); err != nil {
    106 		return
    107 	}
    108 	return pk.pub.EcVerify(data, sig)
    109 }
    110 
    111 // BlockKey return the symmetric key (and initialization vector) based on
    112 // label and expiration time.
    113 func (pk *PKEYPublicImpl) BlockKey(label string, expires util.AbsoluteTime) (skey []byte, nLen int) {
    114 	// generate symmetric key
    115 	skey = make([]byte, 48)
    116 	kd := pk.pub.Bytes()
    117 	prk := hkdf.Extract(sha512.New, kd, []byte("gns-aes-ctx-key"))
    118 	rdr := hkdf.Expand(sha256.New, prk, []byte(label))
    119 	if _, err := rdr.Read(skey[:32]); err != nil {
    120 		logger.Printf(logger.ERROR, "[PKEYPublicImpl.BlockKey] failed: %s", err.Error())
    121 	}
    122 
    123 	// assemble initialization vector
    124 	iv := &struct {
    125 		Nonce   []byte            `size:"4"`    // 32 bit Nonce
    126 		Expire  util.AbsoluteTime ``            // Expiration time of block
    127 		Counter uint32            `order:"big"` // Block counter
    128 	}{
    129 		Nonce:   make([]byte, 4),
    130 		Expire:  expires,
    131 		Counter: 1,
    132 	}
    133 	prk = hkdf.Extract(sha512.New, kd, []byte("gns-aes-ctx-iv"))
    134 	rdr = hkdf.Expand(sha256.New, prk, []byte(label))
    135 	if _, err := rdr.Read(iv.Nonce); err != nil {
    136 		logger.Printf(logger.ERROR, "[PKEYPublicImpl.BlockKey] failed: %s", err.Error())
    137 	}
    138 	buf, _ := data.Marshal(iv)
    139 	copy(skey[32:], buf)
    140 	nLen = 4
    141 	return
    142 }
    143 
    144 // cipher implements symmetric en/-decryption (for block data).
    145 func (pk *PKEYPublicImpl) cipher(encrypt bool, data []byte, label string, expires util.AbsoluteTime) (out []byte, err error) {
    146 	// derive key material for decryption
    147 	skey, _ := pk.BlockKey(label, expires)
    148 
    149 	// En-/decrypt with AES CTR stream cipher
    150 	var blk cipher.Block
    151 	if blk, err = aes.NewCipher(skey[:32]); err != nil {
    152 		return
    153 	}
    154 	stream := cipher.NewCTR(blk, skey[32:])
    155 	out = make([]byte, len(data))
    156 	stream.XORKeyStream(out, data)
    157 	return
    158 }
    159 
    160 // ID returns the GNUnet identifier for a public zone key
    161 func (pk *PKEYPublicImpl) ID() string {
    162 	return util.EncodeBinaryToString(asBytes(enums.GNS_TYPE_PKEY, pk.pub.Bytes()))
    163 }
    164 
    165 //----------------------------------------------------------------------
    166 // Private key
    167 //----------------------------------------------------------------------
    168 
    169 // PKEYPrivateImpl implements the private key scheme.
    170 type PKEYPrivateImpl struct {
    171 	PKEYPublicImpl
    172 
    173 	prv *ed25519.PrivateKey
    174 }
    175 
    176 // Init instance from binary data. The data represents a big integer
    177 // (in big-endian notation) for the private scalar d.
    178 func (pk *PKEYPrivateImpl) Init(data []byte) error {
    179 	// generate key material
    180 	d := math.NewIntFromBytes(data)
    181 	pk.prv = ed25519.NewPrivateKeyFromD(d)
    182 	pk.ztype = enums.GNS_TYPE_PKEY
    183 	pk.pub = pk.prv.Public()
    184 	return nil
    185 }
    186 
    187 // Prepare a random byte array to be used as a random private PKEY
    188 func (pk *PKEYPrivateImpl) Prepare(rnd []byte) []byte {
    189 	md := sha256.Sum256(rnd)
    190 	d := math.NewIntFromBytes(md[:]).Mod(ed25519.GetCurve().N)
    191 	return util.Reverse(d.Bytes())
    192 }
    193 
    194 // Bytes returns a binary representation of the instance suitable for
    195 // consumption in 'Init()'.
    196 func (pk *PKEYPrivateImpl) Bytes() []byte {
    197 	return pk.prv.Bytes()
    198 }
    199 
    200 // Public returns the associate public key implementation.
    201 func (pk *PKEYPrivateImpl) Public() ZoneKeyImpl {
    202 	return &pk.PKEYPublicImpl
    203 }
    204 
    205 // Derive a public key from this key based on a big integer
    206 // (key blinding). Returns the derived key and the blinding value.
    207 func (pk *PKEYPrivateImpl) Derive(h *math.Int) (dPk ZonePrivateImpl, hOut *math.Int, err error) {
    208 	// limit to allowed value range
    209 	hOut = h.Mod(ed25519.GetCurve().N)
    210 	derived := pk.prv.Mult(hOut)
    211 	dPk = &PKEYPrivateImpl{
    212 		PKEYPublicImpl{
    213 			pk.ztype,
    214 			derived.Public(),
    215 		},
    216 		derived,
    217 	}
    218 	return
    219 }
    220 
    221 // Verify a signature for binary data
    222 func (pk *PKEYPrivateImpl) Sign(data []byte) (sig *ZoneSignature, err error) {
    223 	var s *ed25519.EcSignature
    224 	if s, err = pk.prv.EcSign(data); err != nil {
    225 		return
    226 	}
    227 	sd := s.Bytes()
    228 	sigImpl := new(PKEYSigImpl)
    229 	if err = sigImpl.Init(sd); err != nil {
    230 		return
    231 	}
    232 	sig = &ZoneSignature{
    233 		ZoneKey{
    234 			Type:    pk.ztype,
    235 			KeyData: pk.pub.Bytes(),
    236 		},
    237 		sd,
    238 		sigImpl,
    239 	}
    240 	return
    241 }
    242 
    243 // ID returns the GNUnet identifier for a private zone key
    244 // (little-endian big integer)
    245 func (pk *PKEYPrivateImpl) ID() string {
    246 	return util.EncodeBinaryToString(asBytes(
    247 		enums.GNS_TYPE_PKEY,
    248 		util.Reverse(pk.prv.D.Bytes())))
    249 }
    250 
    251 //----------------------------------------------------------------------
    252 // Signature
    253 //----------------------------------------------------------------------
    254 
    255 // ZoneSigImpl defines the methods for a signature object.
    256 type PKEYSigImpl struct {
    257 	sig *ed25519.EcSignature
    258 }
    259 
    260 // Init instance from binary data. The data represents big integers
    261 // R and S of the signature.
    262 func (s *PKEYSigImpl) Init(data []byte) (err error) {
    263 	s.sig, err = ed25519.NewEcSignatureFromBytes(data)
    264 	return
    265 }
    266 
    267 // Bytes returns a binary representation of the instance suitable for
    268 // consumption in 'Init()'.
    269 func (s *PKEYSigImpl) Bytes() []byte {
    270 	return s.sig.Bytes()
    271 }