github.com/core-coin/go-core/v2@v2.1.9/crypto/signify/signify.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // signFile reads the contents of an input file and signs it (in armored format)
    18  // with the key provided, placing the signature into the output file.
    19  
    20  package signify
    21  
    22  import (
    23  	"bytes"
    24  	"crypto/ed25519"
    25  	"encoding/base64"
    26  	"errors"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"strings"
    30  	"time"
    31  )
    32  
    33  var (
    34  	errInvalidKeyHeader = errors.New("incorrect key header")
    35  	errInvalidKeyLength = errors.New("invalid, key length != 104")
    36  )
    37  
    38  func parsePrivateKey(key string) (k ed25519.PrivateKey, header []byte, keyNum []byte, err error) {
    39  	keydata, err := base64.StdEncoding.DecodeString(key)
    40  	if err != nil {
    41  		return nil, nil, nil, err
    42  	}
    43  	if len(keydata) != 104 {
    44  		return nil, nil, nil, errInvalidKeyLength
    45  	}
    46  	if string(keydata[:2]) != "Ed" {
    47  		return nil, nil, nil, errInvalidKeyHeader
    48  	}
    49  	return ed25519.PrivateKey(keydata[40:]), keydata[:2], keydata[32:40], nil
    50  }
    51  
    52  // SignFile creates a signature of the input file.
    53  //
    54  // This accepts base64 keys in the format created by the 'signify' tool.
    55  // The signature is written to the 'output' file.
    56  func SignFile(input string, output string, key string, untrustedComment string, trustedComment string) error {
    57  	// Pre-check comments and ensure they're set to something.
    58  	if strings.IndexByte(untrustedComment, '\n') >= 0 {
    59  		return errors.New("untrusted comment must not contain newline")
    60  	}
    61  	if strings.IndexByte(trustedComment, '\n') >= 0 {
    62  		return errors.New("trusted comment must not contain newline")
    63  	}
    64  	if untrustedComment == "" {
    65  		untrustedComment = "verify with " + input + ".pub"
    66  	}
    67  	if trustedComment == "" {
    68  		trustedComment = fmt.Sprintf("timestamp:%d", time.Now().Unix())
    69  	}
    70  
    71  	filedata, err := ioutil.ReadFile(input)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	skey, header, keyNum, err := parsePrivateKey(key)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	// Create the main data signature.
    81  	rawSig := ed25519.Sign(skey, filedata)
    82  	var dataSig []byte
    83  	dataSig = append(dataSig, header...)
    84  	dataSig = append(dataSig, keyNum...)
    85  	dataSig = append(dataSig, rawSig...)
    86  
    87  	// Create the comment signature.
    88  	var commentSigInput []byte
    89  	commentSigInput = append(commentSigInput, rawSig...)
    90  	commentSigInput = append(commentSigInput, []byte(trustedComment)...)
    91  	commentSig := ed25519.Sign(skey, commentSigInput)
    92  
    93  	// Create the output file.
    94  	var out = new(bytes.Buffer)
    95  	fmt.Fprintln(out, "untrusted comment:", untrustedComment)
    96  	fmt.Fprintln(out, base64.StdEncoding.EncodeToString(dataSig))
    97  	fmt.Fprintln(out, "trusted comment:", trustedComment)
    98  	fmt.Fprintln(out, base64.StdEncoding.EncodeToString(commentSig))
    99  	return ioutil.WriteFile(output, out.Bytes(), 0644)
   100  }