...

Source file src/github.com/sassoftware/relic/lib/pgptools/clearsign.go

Documentation: github.com/sassoftware/relic/lib/pgptools

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package pgptools
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"crypto"
    23  	"errors"
    24  	"io"
    25  	"io/ioutil"
    26  
    27  	"golang.org/x/crypto/openpgp"
    28  	"golang.org/x/crypto/openpgp/armor"
    29  	"golang.org/x/crypto/openpgp/clearsign"
    30  	"golang.org/x/crypto/openpgp/packet"
    31  )
    32  
    33  var sigHeader = []byte("-----BEGIN PGP SIGNATURE-----")
    34  var crlf = []byte("\r\n")
    35  
    36  // Do a cleartext signature, signing the document in "message" and writing the result to "w"
    37  func ClearSign(w io.Writer, signer *openpgp.Entity, message io.Reader, config *packet.Config) error {
    38  	e, err := clearsign.Encode(w, signer.PrivateKey, config)
    39  	if err != nil {
    40  		return err
    41  	}
    42  	if _, err := io.Copy(e, message); err != nil {
    43  		return err
    44  	}
    45  	if err := e.Close(); err != nil {
    46  		return err
    47  	}
    48  	_, err = w.Write(crlf)
    49  	return err
    50  }
    51  
    52  // Do a cleartext signature but skip writing the embedded original document and
    53  // write just the signature block to "w"
    54  func DetachClearSign(w io.Writer, signer *openpgp.Entity, message io.Reader, config *packet.Config) error {
    55  	readPipe, writePipe := io.Pipe()
    56  	done := make(chan error)
    57  	go func() {
    58  		tail, err := tailClearSign(readPipe)
    59  		if err == nil {
    60  			w.Write(tail)
    61  		}
    62  		done <- err
    63  	}()
    64  	err := ClearSign(writePipe, signer, message, config)
    65  	writePipe.CloseWithError(err)
    66  	return <-done
    67  }
    68  
    69  // Consume bytes from a Reader, returning only the signature block at the end
    70  func tailClearSign(r io.Reader) ([]byte, error) {
    71  	s := bufio.NewScanner(r)
    72  	out := bytes.NewBuffer(make([]byte, 0, 1024))
    73  	copying := false
    74  	for s.Scan() {
    75  		line := s.Bytes()
    76  		if copying || bytes.Equal(line, sigHeader) {
    77  			copying = true
    78  			out.Write(line)
    79  			out.WriteString("\r\n")
    80  		}
    81  	}
    82  	return out.Bytes(), s.Err()
    83  }
    84  
    85  // Create a cleartext signature by merging an original document stream in
    86  // "message" with a detached signature in "sig" produced by DetachClearSign()
    87  func MergeClearSign(w io.Writer, sig []byte, message io.Reader) error {
    88  	config, err := configFromSig(sig)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	// Make a fake entity just to get ClearSign() to produce the right framework
    93  	signer := &openpgp.Entity{PrivateKey: &packet.PrivateKey{
    94  		PrivateKey: fakeSigner{},
    95  		PublicKey:  packet.PublicKey{PubKeyAlgo: packet.PubKeyAlgoRSA},
    96  	}}
    97  
    98  	out := bufio.NewWriter(w)
    99  	defer out.Flush()
   100  	readPipe, writePipe := io.Pipe()
   101  	done := make(chan error)
   102  	go func() {
   103  		done <- headClearSign(readPipe, out)
   104  	}()
   105  
   106  	err = ClearSign(writePipe, signer, message, config)
   107  	writePipe.CloseWithError(err)
   108  	if err := <-done; err != nil {
   109  		return err
   110  	}
   111  
   112  	_, err = out.Write(sig)
   113  	return err
   114  }
   115  
   116  // Copy bytes, stopping before the signature block at the end
   117  func headClearSign(r io.Reader, w io.Writer) error {
   118  	s := bufio.NewScanner(r)
   119  	for s.Scan() {
   120  		line := s.Bytes()
   121  		if bytes.Equal(line, sigHeader) {
   122  			// found the end of the document, drain the rest of the reader
   123  			_, err := io.Copy(ioutil.Discard, r)
   124  			return err
   125  		}
   126  		if _, err := w.Write(line); err != nil {
   127  			return err
   128  		}
   129  		if _, err := w.Write(crlf); err != nil {
   130  			return err
   131  		}
   132  	}
   133  	if s.Err() != nil {
   134  		return s.Err()
   135  	}
   136  	return errors.New("Signature block not found")
   137  }
   138  
   139  // Set up a pgp signature config based on the hash algorithm in an existing
   140  // signature
   141  func configFromSig(sigarmor []byte) (*packet.Config, error) {
   142  	block, err := armor.Decode(bytes.NewReader(sigarmor))
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	if block.Type != "PGP SIGNATURE" {
   147  		return nil, errors.New("Not a PGP signature")
   148  	}
   149  	parser := packet.NewReader(block.Body)
   150  	pkt, err := parser.Next()
   151  	if err != nil {
   152  		return nil, errors.New("Not a PGP signature")
   153  	}
   154  	var hashFunc crypto.Hash
   155  	switch sig := pkt.(type) {
   156  	case *packet.Signature:
   157  		hashFunc = sig.Hash
   158  	case *packet.SignatureV3:
   159  		hashFunc = sig.Hash
   160  	default:
   161  		return nil, errors.New("Not a PGP signature")
   162  	}
   163  	return &packet.Config{DefaultHash: hashFunc}, nil
   164  }
   165  
   166  type fakeSigner struct{}
   167  
   168  func (fakeSigner) Public() crypto.PublicKey {
   169  	return nil
   170  }
   171  
   172  func (fakeSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
   173  	return []byte("fake signature here"), nil
   174  }
   175  

View as plain text