mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	* update github.com/PuerkitoBio/goquery * update github.com/alecthomas/chroma * update github.com/blevesearch/bleve/v2 * update github.com/caddyserver/certmagic * update github.com/go-enry/go-enry/v2 * update github.com/go-git/go-billy/v5 * update github.com/go-git/go-git/v5 * update github.com/go-redis/redis/v8 * update github.com/go-testfixtures/testfixtures/v3 * update github.com/jaytaylor/html2text * update github.com/json-iterator/go * update github.com/klauspost/compress * update github.com/markbates/goth * update github.com/mattn/go-isatty * update github.com/mholt/archiver/v3 * update github.com/microcosm-cc/bluemonday * update github.com/minio/minio-go/v7 * update github.com/prometheus/client_golang * update github.com/unrolled/render * update github.com/xanzy/go-gitlab * update github.com/yuin/goldmark * update github.com/yuin/goldmark-highlighting Co-authored-by: techknowlogick <techknowlogick@gitea.io>
		
			
				
	
	
		
			365 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			365 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| // Copyright (C) 2019 ProtonTech AG
 | |
| 
 | |
| package packet
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"crypto/cipher"
 | |
| 	"crypto/rand"
 | |
| 	"encoding/binary"
 | |
| 	"io"
 | |
| 
 | |
| 	"github.com/ProtonMail/go-crypto/openpgp/errors"
 | |
| 	"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
 | |
| )
 | |
| 
 | |
| // AEADEncrypted represents an AEAD Encrypted Packet (tag 20, RFC4880bis-5.16).
 | |
| type AEADEncrypted struct {
 | |
| 	cipher        CipherFunction
 | |
| 	mode          AEADMode
 | |
| 	chunkSizeByte byte
 | |
| 	Contents      io.Reader // Encrypted chunks and tags
 | |
| 	initialNonce  []byte    // Referred to as IV in RFC4880-bis
 | |
| }
 | |
| 
 | |
| // Only currently defined version
 | |
| const aeadEncryptedVersion = 1
 | |
| 
 | |
| // An AEAD opener/sealer, its configuration, and data for en/decryption.
 | |
| type aeadCrypter struct {
 | |
| 	aead           cipher.AEAD
 | |
| 	chunkSize      int
 | |
| 	initialNonce   []byte
 | |
| 	associatedData []byte       // Chunk-independent associated data
 | |
| 	chunkIndex     []byte       // Chunk counter
 | |
| 	bytesProcessed int          // Amount of plaintext bytes encrypted/decrypted
 | |
| 	buffer         bytes.Buffer // Buffered bytes accross chunks
 | |
| }
 | |
| 
 | |
| // aeadEncrypter encrypts and writes bytes. It encrypts when necessary according
 | |
| // to the AEAD block size, and buffers the extra encrypted bytes for next write.
 | |
| type aeadEncrypter struct {
 | |
| 	aeadCrypter                // Embedded plaintext sealer
 | |
| 	writer      io.WriteCloser // 'writer' is a partialLengthWriter
 | |
| }
 | |
| 
 | |
| // aeadDecrypter reads and decrypts bytes. It buffers extra decrypted bytes when
 | |
| // necessary, similar to aeadEncrypter.
 | |
| type aeadDecrypter struct {
 | |
| 	aeadCrypter           // Embedded ciphertext opener
 | |
| 	reader      io.Reader // 'reader' is a partialLengthReader
 | |
| 	peekedBytes []byte    // Used to detect last chunk
 | |
| 	eof         bool
 | |
| }
 | |
| 
 | |
| func (ae *AEADEncrypted) parse(buf io.Reader) error {
 | |
| 	headerData := make([]byte, 4)
 | |
| 	if n, err := io.ReadFull(buf, headerData); n < 4 {
 | |
| 		return errors.AEADError("could not read aead header:" + err.Error())
 | |
| 	}
 | |
| 	// Read initial nonce
 | |
| 	mode := AEADMode(headerData[2])
 | |
| 	nonceLen := mode.NonceLength()
 | |
| 	if nonceLen == 0 {
 | |
| 		return errors.AEADError("unknown mode")
 | |
| 	}
 | |
| 	initialNonce := make([]byte, nonceLen)
 | |
| 	if n, err := io.ReadFull(buf, initialNonce); n < nonceLen {
 | |
| 		return errors.AEADError("could not read aead nonce:" + err.Error())
 | |
| 	}
 | |
| 	ae.Contents = buf
 | |
| 	ae.initialNonce = initialNonce
 | |
| 	c := headerData[1]
 | |
| 	if _, ok := algorithm.CipherById[c]; !ok {
 | |
| 		return errors.UnsupportedError("unknown cipher: " + string(c))
 | |
| 	}
 | |
| 	ae.cipher = CipherFunction(c)
 | |
| 	ae.mode = mode
 | |
| 	ae.chunkSizeByte = byte(headerData[3])
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Decrypt returns a io.ReadCloser from which decrypted bytes can be read, or
 | |
| // an error.
 | |
| func (ae *AEADEncrypted) Decrypt(ciph CipherFunction, key []byte) (io.ReadCloser, error) {
 | |
| 	return ae.decrypt(key)
 | |
| }
 | |
| 
 | |
| // decrypt prepares an aeadCrypter and returns a ReadCloser from which
 | |
| // decrypted bytes can be read (see aeadDecrypter.Read()).
 | |
| func (ae *AEADEncrypted) decrypt(key []byte) (io.ReadCloser, error) {
 | |
| 	blockCipher := ae.cipher.new(key)
 | |
| 	aead := ae.mode.new(blockCipher)
 | |
| 	// Carry the first tagLen bytes
 | |
| 	tagLen := ae.mode.TagLength()
 | |
| 	peekedBytes := make([]byte, tagLen)
 | |
| 	n, err := io.ReadFull(ae.Contents, peekedBytes)
 | |
| 	if n < tagLen || (err != nil && err != io.EOF) {
 | |
| 		return nil, errors.AEADError("Not enough data to decrypt:" + err.Error())
 | |
| 	}
 | |
| 	chunkSize := decodeAEADChunkSize(ae.chunkSizeByte)
 | |
| 	return &aeadDecrypter{
 | |
| 		aeadCrypter: aeadCrypter{
 | |
| 			aead:           aead,
 | |
| 			chunkSize:      chunkSize,
 | |
| 			initialNonce:   ae.initialNonce,
 | |
| 			associatedData: ae.associatedData(),
 | |
| 			chunkIndex:     make([]byte, 8),
 | |
| 		},
 | |
| 		reader:      ae.Contents,
 | |
| 		peekedBytes: peekedBytes}, nil
 | |
| }
 | |
| 
 | |
| // Read decrypts bytes and reads them into dst. It decrypts when necessary and
 | |
| // buffers extra decrypted bytes. It returns the number of bytes copied into dst
 | |
| // and an error.
 | |
| func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
 | |
| 	// Return buffered plaintext bytes from previous calls
 | |
| 	if ar.buffer.Len() > 0 {
 | |
| 		return ar.buffer.Read(dst)
 | |
| 	}
 | |
| 
 | |
| 	// Return EOF if we've previously validated the final tag
 | |
| 	if ar.eof {
 | |
| 		return 0, io.EOF
 | |
| 	}
 | |
| 
 | |
| 	// Read a chunk
 | |
| 	tagLen := ar.aead.Overhead()
 | |
| 	cipherChunkBuf := new(bytes.Buffer)
 | |
| 	_, errRead := io.CopyN(cipherChunkBuf, ar.reader, int64(ar.chunkSize + tagLen))
 | |
| 	cipherChunk := cipherChunkBuf.Bytes()
 | |
| 	if errRead != nil && errRead != io.EOF {
 | |
| 		return 0, errRead
 | |
| 	}
 | |
| 	decrypted, errChunk := ar.openChunk(cipherChunk)
 | |
| 	if errChunk != nil {
 | |
| 		return 0, errChunk
 | |
| 	}
 | |
| 
 | |
| 	// Return decrypted bytes, buffering if necessary
 | |
| 	if len(dst) < len(decrypted) {
 | |
| 		n = copy(dst, decrypted[:len(dst)])
 | |
| 		ar.buffer.Write(decrypted[len(dst):])
 | |
| 	} else {
 | |
| 		n = copy(dst, decrypted)
 | |
| 	}
 | |
| 
 | |
| 	// Check final authentication tag
 | |
| 	if errRead == io.EOF {
 | |
| 		errChunk := ar.validateFinalTag(ar.peekedBytes)
 | |
| 		if errChunk != nil {
 | |
| 			return n, errChunk
 | |
| 		}
 | |
| 		ar.eof = true // Mark EOF for when we've returned all buffered data
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Close is noOp. The final authentication tag of the stream was already
 | |
| // checked in the last Read call. In the future, this function could be used to
 | |
| // wipe the reader and peeked, decrypted bytes, if necessary.
 | |
| func (ar *aeadDecrypter) Close() (err error) {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // SerializeAEADEncrypted initializes the aeadCrypter and returns a writer.
 | |
| // This writer encrypts and writes bytes (see aeadEncrypter.Write()).
 | |
| func SerializeAEADEncrypted(w io.Writer, key []byte, cipher CipherFunction, mode AEADMode, config *Config) (io.WriteCloser, error) {
 | |
| 	writeCloser := noOpCloser{w}
 | |
| 	writer, err := serializeStreamHeader(writeCloser, packetTypeAEADEncrypted)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Data for en/decryption: tag, version, cipher, aead mode, chunk size
 | |
| 	aeadConf := config.AEAD()
 | |
| 	prefix := []byte{
 | |
| 		0xD4,
 | |
| 		aeadEncryptedVersion,
 | |
| 		byte(config.Cipher()),
 | |
| 		byte(aeadConf.Mode()),
 | |
| 		aeadConf.ChunkSizeByte(),
 | |
| 	}
 | |
| 	n, err := writer.Write(prefix[1:])
 | |
| 	if err != nil || n < 4 {
 | |
| 		return nil, errors.AEADError("could not write AEAD headers")
 | |
| 	}
 | |
| 	// Sample nonce
 | |
| 	nonceLen := aeadConf.Mode().NonceLength()
 | |
| 	nonce := make([]byte, nonceLen)
 | |
| 	n, err = rand.Read(nonce)
 | |
| 	if err != nil {
 | |
| 		panic("Could not sample random nonce")
 | |
| 	}
 | |
| 	_, err = writer.Write(nonce)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	blockCipher := CipherFunction(config.Cipher()).new(key)
 | |
| 	alg := AEADMode(aeadConf.Mode()).new(blockCipher)
 | |
| 
 | |
| 	chunkSize := decodeAEADChunkSize(aeadConf.ChunkSizeByte())
 | |
| 	return &aeadEncrypter{
 | |
| 		aeadCrypter: aeadCrypter{
 | |
| 			aead:           alg,
 | |
| 			chunkSize:      chunkSize,
 | |
| 			associatedData: prefix,
 | |
| 			chunkIndex:     make([]byte, 8),
 | |
| 			initialNonce:   nonce,
 | |
| 		},
 | |
| 		writer: writer}, nil
 | |
| }
 | |
| 
 | |
| // Write encrypts and writes bytes. It encrypts when necessary and buffers extra
 | |
| // plaintext bytes for next call. When the stream is finished, Close() MUST be
 | |
| // called to append the final tag.
 | |
| func (aw *aeadEncrypter) Write(plaintextBytes []byte) (n int, err error) {
 | |
| 	// Append plaintextBytes to existing buffered bytes
 | |
| 	n, err = aw.buffer.Write(plaintextBytes)
 | |
| 	if err != nil {
 | |
| 		return n, err
 | |
| 	}
 | |
| 	// Encrypt and write chunks
 | |
| 	for aw.buffer.Len() >= aw.chunkSize {
 | |
| 		plainChunk := aw.buffer.Next(aw.chunkSize)
 | |
| 		encryptedChunk, err := aw.sealChunk(plainChunk)
 | |
| 		if err != nil {
 | |
| 			return n, err
 | |
| 		}
 | |
| 		_, err = aw.writer.Write(encryptedChunk)
 | |
| 		if err != nil {
 | |
| 			return n, err
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Close encrypts and writes the remaining buffered plaintext if any, appends
 | |
| // the final authentication tag, and closes the embedded writer. This function
 | |
| // MUST be called at the end of a stream.
 | |
| func (aw *aeadEncrypter) Close() (err error) {
 | |
| 	// Encrypt and write a chunk if there's buffered data left, or if we haven't
 | |
| 	// written any chunks yet.
 | |
| 	if aw.buffer.Len() > 0 || aw.bytesProcessed == 0 {
 | |
| 		plainChunk := aw.buffer.Bytes()
 | |
| 		lastEncryptedChunk, err := aw.sealChunk(plainChunk)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		_, err = aw.writer.Write(lastEncryptedChunk)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	// Compute final tag (associated data: packet tag, version, cipher, aead,
 | |
| 	// chunk size, index, total number of encrypted octets).
 | |
| 	adata := append(aw.associatedData[:], aw.chunkIndex[:]...)
 | |
| 	adata = append(adata, make([]byte, 8)...)
 | |
| 	binary.BigEndian.PutUint64(adata[13:], uint64(aw.bytesProcessed))
 | |
| 	nonce := aw.computeNextNonce()
 | |
| 	finalTag := aw.aead.Seal(nil, nonce, nil, adata)
 | |
| 	_, err = aw.writer.Write(finalTag)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return aw.writer.Close()
 | |
| }
 | |
| 
 | |
| // sealChunk Encrypts and authenticates the given chunk.
 | |
| func (aw *aeadEncrypter) sealChunk(data []byte) ([]byte, error) {
 | |
| 	if len(data) > aw.chunkSize {
 | |
| 		return nil, errors.AEADError("chunk exceeds maximum length")
 | |
| 	}
 | |
| 	if aw.associatedData == nil {
 | |
| 		return nil, errors.AEADError("can't seal without headers")
 | |
| 	}
 | |
| 	adata := append(aw.associatedData, aw.chunkIndex...)
 | |
| 	nonce := aw.computeNextNonce()
 | |
| 	encrypted := aw.aead.Seal(nil, nonce, data, adata)
 | |
| 	aw.bytesProcessed += len(data)
 | |
| 	if err := aw.aeadCrypter.incrementIndex(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return encrypted, nil
 | |
| }
 | |
| 
 | |
| // openChunk decrypts and checks integrity of an encrypted chunk, returning
 | |
| // the underlying plaintext and an error. It access peeked bytes from next
 | |
| // chunk, to identify the last chunk and decrypt/validate accordingly.
 | |
| func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) {
 | |
| 	tagLen := ar.aead.Overhead()
 | |
| 	// Restore carried bytes from last call
 | |
| 	chunkExtra := append(ar.peekedBytes, data...)
 | |
| 	// 'chunk' contains encrypted bytes, followed by an authentication tag.
 | |
| 	chunk := chunkExtra[:len(chunkExtra)-tagLen]
 | |
| 	ar.peekedBytes = chunkExtra[len(chunkExtra)-tagLen:]
 | |
| 	adata := append(ar.associatedData, ar.chunkIndex...)
 | |
| 	nonce := ar.computeNextNonce()
 | |
| 	plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	ar.bytesProcessed += len(plainChunk)
 | |
| 	if err = ar.aeadCrypter.incrementIndex(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return plainChunk, nil
 | |
| }
 | |
| 
 | |
| // Checks the summary tag. It takes into account the total decrypted bytes into
 | |
| // the associated data. It returns an error, or nil if the tag is valid.
 | |
| func (ar *aeadDecrypter) validateFinalTag(tag []byte) error {
 | |
| 	// Associated: tag, version, cipher, aead, chunk size, index, and octets
 | |
| 	amountBytes := make([]byte, 8)
 | |
| 	binary.BigEndian.PutUint64(amountBytes, uint64(ar.bytesProcessed))
 | |
| 	adata := append(ar.associatedData, ar.chunkIndex...)
 | |
| 	adata = append(adata, amountBytes...)
 | |
| 	nonce := ar.computeNextNonce()
 | |
| 	_, err := ar.aead.Open(nil, nonce, tag, adata)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Associated data for chunks: tag, version, cipher, mode, chunk size byte
 | |
| func (ae *AEADEncrypted) associatedData() []byte {
 | |
| 	return []byte{
 | |
| 		0xD4,
 | |
| 		aeadEncryptedVersion,
 | |
| 		byte(ae.cipher),
 | |
| 		byte(ae.mode),
 | |
| 		ae.chunkSizeByte}
 | |
| }
 | |
| 
 | |
| // computeNonce takes the incremental index and computes an eXclusive OR with
 | |
| // the least significant 8 bytes of the receivers' initial nonce (see sec.
 | |
| // 5.16.1 and 5.16.2). It returns the resulting nonce.
 | |
| func (wo *aeadCrypter) computeNextNonce() (nonce []byte) {
 | |
| 	nonce = make([]byte, len(wo.initialNonce))
 | |
| 	copy(nonce, wo.initialNonce)
 | |
| 	offset := len(wo.initialNonce) - 8
 | |
| 	for i := 0; i < 8; i++ {
 | |
| 		nonce[i+offset] ^= wo.chunkIndex[i]
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // incrementIndex perfoms an integer increment by 1 of the integer represented by the
 | |
| // slice, modifying it accordingly.
 | |
| func (wo *aeadCrypter) incrementIndex() error {
 | |
| 	index := wo.chunkIndex
 | |
| 	if len(index) == 0 {
 | |
| 		return errors.AEADError("Index has length 0")
 | |
| 	}
 | |
| 	for i := len(index) - 1; i >= 0; i-- {
 | |
| 		if index[i] < 255 {
 | |
| 			index[i]++
 | |
| 			return nil
 | |
| 		}
 | |
| 		index[i] = 0
 | |
| 	}
 | |
| 	return errors.AEADError("cannot further increment index")
 | |
| }
 |