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 }