github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/persistence/problems/reader_deer.go (about)

     1  package problems
     2  
     3  import (
     4  	"archive/zip"
     5  	"bufio"
     6  	"bytes"
     7  	"crypto/rsa"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"github.com/LanceLRQ/deer-common/constants"
    11  	"github.com/LanceLRQ/deer-common/persistence"
    12  	commonStructs "github.com/LanceLRQ/deer-common/structs"
    13  	"github.com/LanceLRQ/deer-common/utils"
    14  	"github.com/pkg/errors"
    15  	uuid "github.com/satori/go.uuid"
    16  	"golang.org/x/crypto/openpgp"
    17  	"io"
    18  	"os"
    19  	"path"
    20  	"reflect"
    21  )
    22  
    23  // 解析判题结果
    24  func parseProblemPackageBinary(reader io.Reader, unpackBody bool) (*ProblemPackage, error) {
    25  	// 校验魔数
    26  	magic := uint16(0)
    27  	if err := binary.Read(reader, binary.BigEndian, &magic); err != nil {
    28  		return nil, errors.Errorf("read file error: %s", err.Error())
    29  	}
    30  	if magic != constants.ProblemPackageMagicCode {
    31  		return nil, errors.Errorf("not deer-executor problem package file")
    32  	}
    33  	// 开始解析package
    34  	pack := ProblemPackage{}
    35  	if err := binary.Read(reader, binary.BigEndian, &pack.Version); err != nil {
    36  		return nil, errors.Errorf("read [version] error: %s", err.Error())
    37  	}
    38  	if err := binary.Read(reader, binary.BigEndian, &pack.CommitVersion); err != nil {
    39  		return nil, errors.Errorf("read [version] error: %s", err.Error())
    40  	}
    41  	if err := binary.Read(reader, binary.BigEndian, &pack.ConfigSize); err != nil {
    42  		return nil, errors.Errorf("read [config size] error: %s", err.Error())
    43  	}
    44  	if err := binary.Read(reader, binary.BigEndian, &pack.BodySize); err != nil {
    45  		return nil, errors.Errorf("read [body size] error: %s", err.Error())
    46  	}
    47  	if err := binary.Read(reader, binary.BigEndian, &pack.CertSize); err != nil {
    48  		return nil, errors.Errorf("read [cert size] error: %s", err.Error())
    49  	}
    50  	// 如果有证书
    51  	if pack.CertSize > 0 {
    52  		pack.Certificate = make([]byte, pack.CertSize)
    53  		if err := binary.Read(reader, binary.BigEndian, &pack.Certificate); err != nil {
    54  			return nil, errors.Errorf("read [cert public key] error: %s", err.Error())
    55  		}
    56  	}
    57  	// 读取签名
    58  	if err := binary.Read(reader, binary.BigEndian, &pack.SignSize); err != nil {
    59  		return nil, errors.Errorf("read [sign size] error: %s", err.Error())
    60  	}
    61  	pack.Signature = make([]byte, pack.SignSize)
    62  	if err := binary.Read(reader, binary.BigEndian, &pack.Signature); err != nil {
    63  		return nil, errors.Errorf("read [signature] error: %s", err.Error())
    64  	}
    65  	// 读取Config
    66  	pack.Configs = make([]byte, pack.ConfigSize)
    67  	if err := binary.Read(reader, binary.BigEndian, &pack.Configs); err != nil {
    68  		return nil, errors.Errorf("read [config] error: %s", err.Error())
    69  	}
    70  	if unpackBody {
    71  		// 理论上BodySize是多余的,剩下的都是body,这里就作为校验吧!
    72  		tmpBodyFileName := uuid.NewV1().String() + ".tmp.zip"
    73  		tmpBodyFilePath := path.Join("/tmp/", tmpBodyFileName)
    74  		pack.BodyPackageFile = tmpBodyFilePath
    75  		tmpBodyFile, err := os.Create(pack.BodyPackageFile)
    76  		if err != nil {
    77  			return nil, errors.Errorf("create body package temp file error: %s", err.Error())
    78  		}
    79  		defer tmpBodyFile.Close()
    80  		if _, err := io.Copy(tmpBodyFile, reader); err != nil {
    81  			return nil, errors.Errorf("write body package temp file error: %s", err.Error())
    82  		}
    83  	}
    84  
    85  	return &pack, nil
    86  }
    87  
    88  // 校验判题结果数据包
    89  func validateProblemPackage(pack *ProblemPackage) (bool, error) {
    90  	// 打开临时文件
    91  	tmpBodyFile, err := os.Open(pack.BodyPackageFile)
    92  	if err != nil {
    93  		return false, errors.Errorf("open body package temp file error: %s", err.Error())
    94  	}
    95  	defer tmpBodyFile.Close()
    96  
    97  	hash, err := persistence.SHA256Streams([]io.Reader{
    98  		bytes.NewReader(pack.Configs),
    99  		tmpBodyFile,
   100  	})
   101  	if err != nil {
   102  		return false, err
   103  	}
   104  	// 进行签名校验
   105  	if pack.CertSize > 0 {
   106  		// Read GPG Keys
   107  		elist, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pack.Certificate))
   108  		if err != nil {
   109  			return false, err
   110  		}
   111  		if len(elist) < 1 {
   112  			return false, errors.Errorf("GPG key error")
   113  		}
   114  		publicKey := elist[0].PrimaryKey.PublicKey.(*rsa.PublicKey)
   115  		err = persistence.RSA2048Verify(hash, pack.Signature, publicKey)
   116  		if err != nil {
   117  			return false, err
   118  		}
   119  	} else {
   120  		return reflect.DeepEqual(hash, pack.Signature), nil
   121  	}
   122  	return true, nil
   123  }
   124  
   125  func readProblemPackage(problemFile string, unpack bool) (*ProblemPackage, error) {
   126  	fp, err := os.Open(problemFile)
   127  	if err != nil {
   128  		return nil, errors.Errorf("open file (%s) error: %s", problemFile, err.Error())
   129  	}
   130  	defer fp.Close()
   131  
   132  	reader := bufio.NewReader(fp)
   133  	pack, err := parseProblemPackageBinary(reader, unpack)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	return pack, nil
   139  }
   140  
   141  func doProblemPackageValidation(pack *ProblemPackage, validate bool) error {
   142  	ok, err := validateProblemPackage(pack)
   143  	var errmsg error
   144  	if !ok || err != nil {
   145  		if err != nil {
   146  			errmsg = errors.Errorf("validate package hash error: %s", err.Error())
   147  		}
   148  		errmsg = errors.Errorf("validate package hash error")
   149  	}
   150  	// 如果出错并且现在必须要验证错误,则抛出
   151  	if errmsg != nil && validate {
   152  		return errmsg
   153  	} else {
   154  		fmt.Println("Warning! Package signature validation failed.")
   155  	}
   156  	return nil
   157  }
   158  
   159  // 读取题目信息
   160  func ReadProblemInfo(problemFile string, unpack, validate bool, workDir string) (*commonStructs.JudgeConfiguration, string, error) {
   161  	pack, err := readProblemPackage(problemFile, unpack)
   162  	if err != nil {
   163  		return nil, "", err
   164  	}
   165  	config := commonStructs.JudgeConfiguration{}
   166  	utils.JSONBytesObject(pack.Configs, &config)
   167  
   168  	err = doProblemPackageValidation(pack, validate)
   169  	if err != nil {
   170  		return nil, "", err
   171  	}
   172  
   173  	if unpack {
   174  		zipReader, err := zip.OpenReader(pack.BodyPackageFile)
   175  		if err != nil {
   176  			return nil, "", errors.Errorf("open body file (%s) error: %s", problemFile, err.Error())
   177  		}
   178  		defer zipReader.Close()
   179  
   180  		err = UnZip(zipReader, workDir)
   181  		if err != nil {
   182  			return nil, "", err
   183  		}
   184  		configFile := path.Join(workDir, "problem.json")
   185  		fp, err := os.Create(configFile)
   186  		if err != nil {
   187  			return nil, "", err
   188  		}
   189  		defer fp.Close()
   190  		_, err = fp.Write(pack.Configs)
   191  		if err != nil {
   192  			return nil, "", err
   193  		}
   194  		return &config, configFile, nil
   195  	}
   196  
   197  	return &config, "", nil
   198  }
   199  
   200  // 读取题目携带的GPG信息
   201  func ReadProblemGPGInfo(problemFile string) (string, error) {
   202  	pack, err := readProblemPackage(problemFile, false)
   203  	if err != nil {
   204  		return "", err
   205  	}
   206  
   207  	err = doProblemPackageValidation(pack, false)
   208  	if err != nil {
   209  		return "", err
   210  	}
   211  
   212  	if pack.CertSize == 0 {
   213  		return "no GPG public key", nil
   214  	} else {
   215  		elist, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pack.Certificate))
   216  		if err != nil {
   217  			return "", err
   218  		}
   219  		if len(elist) < 1 {
   220  			return "", errors.Errorf("GPG key error")
   221  		}
   222  		rel := ""
   223  		for _, identify := range elist[0].Identities {
   224  			rel += identify.Name + "\n"
   225  		}
   226  		return rel, nil
   227  	}
   228  }