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 }