...

Source file src/github.com/sassoftware/relic/lib/pgptools/verify.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  	"crypto"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"io/ioutil"
    26  	"time"
    27  
    28  	"golang.org/x/crypto/openpgp"
    29  	"golang.org/x/crypto/openpgp/clearsign"
    30  	"golang.org/x/crypto/openpgp/packet"
    31  )
    32  
    33  type PgpSignature struct {
    34  	Key          *openpgp.Key
    35  	CreationTime time.Time
    36  	Hash         crypto.Hash
    37  }
    38  
    39  // Verify a detached PGP signature in "signature" over the document in
    40  // "signed", using keys from "keyring". Returns a value of ErrNoKey if the key
    41  // cannot be found.
    42  func VerifyDetached(signature, signed io.Reader, keyring openpgp.EntityList) (*PgpSignature, error) {
    43  	packetReader := packet.NewReader(signature)
    44  	genpkt, err := packetReader.Next()
    45  	if err == io.EOF {
    46  		return nil, errors.New("no PGP signature found")
    47  	} else if err != nil {
    48  		return nil, err
    49  	}
    50  	// parse
    51  	var hash crypto.Hash
    52  	var keyID uint64
    53  	var creationTime time.Time
    54  	switch pkt := genpkt.(type) {
    55  	case *packet.SignatureV3:
    56  		hash = pkt.Hash
    57  		keyID = pkt.IssuerKeyId
    58  		creationTime = pkt.CreationTime
    59  	case *packet.Signature:
    60  		if pkt.IssuerKeyId == nil {
    61  			return nil, errors.New("Missing keyId in signature")
    62  		}
    63  		hash = pkt.Hash
    64  		keyID = *pkt.IssuerKeyId
    65  		creationTime = pkt.CreationTime
    66  	default:
    67  		return nil, errors.New("not a PGP signature")
    68  	}
    69  	// find key
    70  	keys := keyring.KeysById(keyID)
    71  	if len(keys) == 0 {
    72  		return nil, ErrNoKey(keyID)
    73  	}
    74  	// calculate hash
    75  	if !hash.Available() {
    76  		return nil, errors.New("signature uses unknown digest")
    77  	}
    78  	d := hash.New()
    79  	if _, err := io.Copy(d, signed); err != nil {
    80  		return nil, err
    81  	}
    82  	// check signature
    83  	switch pkt := genpkt.(type) {
    84  	case *packet.SignatureV3:
    85  		err = keys[0].PublicKey.VerifySignatureV3(d, pkt)
    86  	case *packet.Signature:
    87  		err = keys[0].PublicKey.VerifySignature(d, pkt)
    88  	}
    89  	return &PgpSignature{&keys[0], creationTime, hash}, err
    90  }
    91  
    92  // Verify a cleartext PGP signature in "signature" using keys from "keyring".
    93  // Returns a value of ErrNoKey in the key cannot be found. If "cleartext" is
    94  // not nil, then write the embedded cleartext as it is verified.
    95  func VerifyClearSign(signature io.Reader, cleartext io.Writer, keyring openpgp.EntityList) (*PgpSignature, error) {
    96  	blob, err := ioutil.ReadAll(signature)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	csblock, rest := clearsign.Decode(blob)
   101  	if csblock == nil {
   102  		return nil, errors.New("malformed clearsign signature")
   103  	} else if bytes.Contains(rest, []byte("-----BEGIN")) {
   104  		return nil, errors.New("clearsign contains multiple documents")
   105  	}
   106  	if cleartext != nil {
   107  		if _, err := cleartext.Write(csblock.Bytes); err != nil {
   108  			return nil, err
   109  		}
   110  	}
   111  	sig, err := VerifyDetached(csblock.ArmoredSignature.Body, bytes.NewReader(csblock.Bytes), keyring)
   112  	return sig, err
   113  }
   114  
   115  // Verify an inline PGP signature in "signature" using keys from "keyring".
   116  // Returns a value of ErrNoKey if the key cannot be found. If "cleartext" is
   117  // not nil, then write the embedded cleartext as it is verified.
   118  func VerifyInline(signature io.Reader, cleartext io.Writer, keyring openpgp.EntityList) (*PgpSignature, error) {
   119  	md, err := openpgp.ReadMessage(signature, keyring, nil, nil)
   120  	if err == io.EOF {
   121  		return nil, ErrNoContent{}
   122  	} else if err != nil {
   123  		return nil, err
   124  	} else if md.SignedBy == nil {
   125  		return nil, ErrNoKey(md.SignedByKeyId)
   126  	}
   127  	if cleartext == nil {
   128  		cleartext = ioutil.Discard
   129  	}
   130  	if _, err := io.Copy(cleartext, md.UnverifiedBody); err != nil {
   131  		return nil, err
   132  	}
   133  	// reading UnverifiedBody in full triggers the signature validation
   134  	sig := &PgpSignature{Key: md.SignedBy}
   135  	if md.Signature != nil {
   136  		sig.CreationTime = md.Signature.CreationTime
   137  		sig.Hash = md.Signature.Hash
   138  	} else if md.SignatureV3 != nil {
   139  		sig.CreationTime = md.SignatureV3.CreationTime
   140  		sig.Hash = md.Signature.Hash
   141  	}
   142  	return sig, md.SignatureError
   143  }
   144  
   145  // Returned by Verify* functions when the key used for signing is not in the
   146  // keyring. The value is the KeyID of the missing key.
   147  type ErrNoKey uint64
   148  
   149  func (e ErrNoKey) Error() string {
   150  	return fmt.Sprintf("keyId %x not found", uint64(e))
   151  }
   152  
   153  // Returned by VerifyInline if the signature is actually a detached signature
   154  type ErrNoContent struct{}
   155  
   156  func (ErrNoContent) Error() string {
   157  	return "missing content for detached signature"
   158  }
   159  

View as plain text