/* NNCP -- Node to Node copy, utilities for store-and-forward data exchange Copyright (C) 2016-2023 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package nncp import ( "bytes" "crypto/rand" "hash" xdr "github.com/davecgh/go-xdr/xdr2" "go.cypherpunks.ru/balloon" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/chacha20poly1305" ) const ( DefaultS = 1 << 20 / 32 DefaultT = 1 << 4 DefaultP = 2 ) type EBlob struct { Magic [8]byte SCost uint32 TCost uint32 PCost uint32 Salt *[32]byte Blob []byte } func blake256() hash.Hash { h, err := blake2b.New256(nil) if err != nil { panic(err) } return h } // Create an encrypted blob. sCost -- memory space requirements, number // of hash-output sized (32 bytes) blocks. tCost -- time requirements, // number of rounds. pCost -- number of parallel jobs. func NewEBlob(sCost, tCost, pCost int, password, data []byte) ([]byte, error) { salt := new([32]byte) var err error if _, err = rand.Read(salt[:]); err != nil { return nil, err } eblob := EBlob{ Magic: MagicNNCPBv3.B, SCost: uint32(sCost), TCost: uint32(tCost), PCost: uint32(pCost), Salt: salt, Blob: nil, } var eblobBuf bytes.Buffer if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil { return nil, err } key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost) aead, err := chacha20poly1305.New(key) if err != nil { return nil, err } buf := make([]byte, 0, len(data)+aead.Overhead()) buf = aead.Seal(buf, make([]byte, aead.NonceSize()), data, eblobBuf.Bytes()) eblob.Blob = buf eblobBuf.Reset() if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil { return nil, err } return eblobBuf.Bytes(), nil } func DeEBlob(eblobRaw, password []byte) ([]byte, error) { var eblob EBlob var err error if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil { return nil, err } switch eblob.Magic { case MagicNNCPBv1.B: err = MagicNNCPBv1.TooOld() case MagicNNCPBv2.B: err = MagicNNCPBv1.TooOld() case MagicNNCPBv3.B: default: err = BadMagic } if err != nil { return nil, err } key := balloon.H( blake256, password, eblob.Salt[:], int(eblob.SCost), int(eblob.TCost), int(eblob.PCost), ) aead, err := chacha20poly1305.New(key) if err != nil { return nil, err } ciphertext := eblob.Blob eblob.Blob = nil var eblobBuf bytes.Buffer if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil { return nil, err } data, err := aead.Open( ciphertext[:0], make([]byte, aead.NonceSize()), ciphertext, eblobBuf.Bytes(), ) if err != nil { return nil, err } return data, nil }