signify.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // Copyright 2020 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. // signFile reads the contents of an input file and signs it (in armored format)
  17. // with the key provided, placing the signature into the output file.
  18. package signify
  19. import (
  20. "encoding/base64"
  21. "errors"
  22. "fmt"
  23. "io/ioutil"
  24. "os"
  25. "strings"
  26. "time"
  27. "crypto/ed25519"
  28. )
  29. var (
  30. errInvalidKeyHeader = errors.New("Incorrect key header")
  31. errInvalidKeyLength = errors.New("invalid, key length != 104")
  32. )
  33. func parsePrivateKey(key string) (ed25519.PrivateKey, []byte, []byte, error) {
  34. keydata, err := base64.StdEncoding.DecodeString(key)
  35. if err != nil {
  36. return nil, nil, nil, err
  37. }
  38. if len(keydata) != 104 {
  39. return nil, nil, nil, errInvalidKeyLength
  40. }
  41. if string(keydata[:2]) != "Ed" {
  42. return nil, nil, nil, errInvalidKeyHeader
  43. }
  44. return ed25519.PrivateKey(keydata[40:]), keydata[:2], keydata[32:40], nil
  45. }
  46. func commentHasManyLines(comment string) bool {
  47. firstLFIndex := strings.IndexByte(comment, 10)
  48. return (firstLFIndex >= 0 && firstLFIndex < len(comment)-1)
  49. }
  50. // SignifySignFile creates a signature of the input file.
  51. func SignifySignFile(input string, output string, key string, unTrustedComment string, trustedComment string) error {
  52. in, err := os.Open(input)
  53. if err != nil {
  54. return err
  55. }
  56. defer in.Close()
  57. out, err := os.Create(output)
  58. if err != nil {
  59. return err
  60. }
  61. defer out.Close()
  62. skey, header, keyNum, err := parsePrivateKey(key)
  63. if err != nil {
  64. return err
  65. }
  66. filedata, err := ioutil.ReadAll(in)
  67. if err != nil {
  68. return err
  69. }
  70. rawSig := ed25519.Sign(skey, filedata)
  71. var sigdata []byte
  72. sigdata = append(sigdata, header...)
  73. sigdata = append(sigdata, keyNum...)
  74. sigdata = append(sigdata, rawSig...)
  75. // Check that the trusted comment fits in one line
  76. if commentHasManyLines(unTrustedComment) {
  77. return errors.New("untrusted comment must fit on a single line")
  78. }
  79. if unTrustedComment == "" {
  80. unTrustedComment = "verify with " + input + ".pub"
  81. }
  82. out.WriteString(fmt.Sprintf("untrusted comment: %s\n%s\n", unTrustedComment, base64.StdEncoding.EncodeToString(sigdata)))
  83. // Add the trusted comment if unavailable
  84. if trustedComment == "" {
  85. trustedComment = fmt.Sprintf("timestamp:%d", time.Now().Unix())
  86. }
  87. // Check that the trusted comment fits in one line
  88. if commentHasManyLines(trustedComment) {
  89. return errors.New("trusted comment must fit on a single line")
  90. }
  91. var sigAndComment []byte
  92. sigAndComment = append(sigAndComment, rawSig...)
  93. sigAndComment = append(sigAndComment, []byte(trustedComment)...)
  94. out.WriteString(fmt.Sprintf("trusted comment: %s\n%s\n", trustedComment, base64.StdEncoding.EncodeToString(ed25519.Sign(skey, sigAndComment))))
  95. return nil
  96. }