...

Source file src/github.com/sassoftware/relic/lib/pgptools/inline.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  	"bytes"
    21  	"errors"
    22  	"io"
    23  
    24  	"golang.org/x/crypto/openpgp/armor"
    25  	"golang.org/x/crypto/openpgp/packet"
    26  )
    27  
    28  const maxLiteralSize = (1 << 31) - 512 // int32_max minus some room for the literal data header
    29  
    30  // MergeSignature combines a detached signature with a cleartext message and writes it as an inline signed message with optional ASCII armor
    31  func MergeSignature(w io.Writer, sig []byte, message io.Reader, withArmor bool, filename string) (err error) {
    32  	var armorer io.WriteCloser = nopCloseWriter{w}
    33  	if withArmor {
    34  		armorer, err = armor.Encode(w, "PGP MESSAGE", nil)
    35  		if err != nil {
    36  			return err
    37  		}
    38  	}
    39  	// write one-pass signature header
    40  	if err := writeOnePass(armorer, sig); err != nil {
    41  		return err
    42  	}
    43  	// write literal data
    44  	if size := getSize(message); size >= 0 {
    45  		if err := serializeLiteral(armorer, message, size, filename); err != nil {
    46  			return err
    47  		}
    48  	} else {
    49  		litWriter, err := packet.SerializeLiteral(nopCloseWriter{armorer}, true, filename, 0)
    50  		if err != nil {
    51  			return err
    52  		}
    53  		if _, err := io.Copy(litWriter, message); err != nil {
    54  			return err
    55  		}
    56  		if err := litWriter.Close(); err != nil {
    57  			return err
    58  		}
    59  	}
    60  	// write signature
    61  	if _, err := armorer.Write(sig); err != nil {
    62  		return err
    63  	}
    64  	return armorer.Close()
    65  }
    66  
    67  // write a one-pass signature header with the fields copied from the detached signature in sig
    68  func writeOnePass(w io.Writer, sig []byte) error {
    69  	genpkt, err := packet.Read(bytes.NewReader(sig))
    70  	if err != nil {
    71  		return err
    72  	}
    73  	// parse
    74  	op := packet.OnePassSignature{IsLast: true}
    75  	switch pkt := genpkt.(type) {
    76  	case *packet.SignatureV3:
    77  		op.SigType = pkt.SigType
    78  		op.Hash = pkt.Hash
    79  		op.PubKeyAlgo = pkt.PubKeyAlgo
    80  		op.KeyId = pkt.IssuerKeyId
    81  	case *packet.Signature:
    82  		op.SigType = pkt.SigType
    83  		op.Hash = pkt.Hash
    84  		op.PubKeyAlgo = pkt.PubKeyAlgo
    85  		if pkt.IssuerKeyId != nil {
    86  			op.KeyId = *pkt.IssuerKeyId
    87  		}
    88  	default:
    89  		return errors.New("not a PGP signature")
    90  	}
    91  	return op.Serialize(w)
    92  }
    93  
    94  // write size bytes from r into w as a literal data packet
    95  func serializeLiteral(w io.Writer, r io.Reader, size int32, filename string) error {
    96  	if len(filename) > 255 {
    97  		filename = filename[:255]
    98  	}
    99  	var buf bytes.Buffer
   100  	buf.WriteByte('b')                 // binary mode
   101  	buf.WriteByte(byte(len(filename))) // filename
   102  	buf.WriteString(filename)          // filename
   103  	buf.Write([]byte{0, 0, 0, 0})      // timestamp
   104  	packetType := 11                   // literal data
   105  	psize := int64(size) + int64(buf.Len())
   106  	if psize > (2<<31)-1 {
   107  		return errors.New("literal too big")
   108  	}
   109  	if err := serializeHeader(w, packetType, int(psize)); err != nil {
   110  		return err
   111  	}
   112  	if _, err := w.Write(buf.Bytes()); err != nil {
   113  		return err
   114  	}
   115  	_, err := io.CopyN(w, r, int64(size))
   116  	return err
   117  }
   118  
   119  // get the size from a reader if it's seekable and not too big to fit in a single literal data packet, otherwise returns -1
   120  func getSize(r io.Reader) int32 {
   121  	seek, ok := r.(io.Seeker)
   122  	if !ok {
   123  		return -1
   124  	}
   125  	start, err := seek.Seek(0, io.SeekCurrent)
   126  	if err != nil {
   127  		return -1
   128  	}
   129  	end, err := seek.Seek(0, io.SeekEnd)
   130  	if err != nil {
   131  		return -1
   132  	}
   133  	seek.Seek(start, io.SeekStart)
   134  	size := end - start
   135  	if size > maxLiteralSize {
   136  		return -1
   137  	}
   138  	return int32(size)
   139  }
   140  
   141  // serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section
   142  // 4.2.
   143  func serializeHeader(w io.Writer, ptype int, length int) (err error) {
   144  	var buf [6]byte
   145  	var n int
   146  
   147  	buf[0] = 0x80 | 0x40 | byte(ptype)
   148  	if length < 192 {
   149  		buf[1] = byte(length)
   150  		n = 2
   151  	} else if length < 8384 {
   152  		length -= 192
   153  		buf[1] = 192 + byte(length>>8)
   154  		buf[2] = byte(length)
   155  		n = 3
   156  	} else {
   157  		buf[1] = 255
   158  		buf[2] = byte(length >> 24)
   159  		buf[3] = byte(length >> 16)
   160  		buf[4] = byte(length >> 8)
   161  		buf[5] = byte(length)
   162  		n = 6
   163  	}
   164  
   165  	_, err = w.Write(buf[:n])
   166  	return
   167  }
   168  
   169  type nopCloseWriter struct {
   170  	io.Writer
   171  }
   172  
   173  func (nopCloseWriter) Close() error {
   174  	return nil
   175  }
   176  

View as plain text