github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/sign/sign.go (about)

     1  // Copyright (c) 2016 Readium Foundation
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification,
     4  // are permitted provided that the following conditions are met:
     5  //
     6  // 1. Redistributions of source code must retain the above copyright notice, this
     7  //    list of conditions and the following disclaimer.
     8  // 2. Redistributions in binary form must reproduce the above copyright notice,
     9  //    this list of conditions and the following disclaimer in the documentation and/or
    10  //    other materials provided with the distribution.
    11  // 3. Neither the name of the organization nor the names of its contributors may be
    12  //    used to endorse or promote products derived from this software without specific
    13  //    prior written permission
    14  //
    15  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    16  // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    17  // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    18  // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    19  // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    20  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    21  // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    22  // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    23  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    24  // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    25  
    26  package sign
    27  
    28  import (
    29  	"crypto"
    30  	"crypto/ecdsa"
    31  	"crypto/rand"
    32  	"crypto/rsa"
    33  	"crypto/sha256"
    34  	"crypto/tls"
    35  	"errors"
    36  	"math"
    37  )
    38  
    39  type Signer interface {
    40  	Sign(interface{}) (Signature, error)
    41  }
    42  
    43  type Signature struct {
    44  	Certificate []byte `json:"certificate"`
    45  	Value       []byte `json:"value"`
    46  	Algorithm   string `json:"algorithm"`
    47  }
    48  
    49  // ECDSA
    50  type ecdsaSigner struct {
    51  	key  *ecdsa.PrivateKey
    52  	cert *tls.Certificate
    53  }
    54  
    55  // Used to fill the resulting output according to the XMLDSIG spec
    56  func copyWithLeftPad(dest, src []byte) {
    57  	numPaddingBytes := len(dest) - len(src)
    58  	for i := 0; i < numPaddingBytes; i++ {
    59  		dest[i] = 0
    60  	}
    61  	copy(dest[numPaddingBytes:], src)
    62  }
    63  
    64  func (signer *ecdsaSigner) Sign(in interface{}) (sig Signature, err error) {
    65  	plain, err := Canon(in)
    66  	if err != nil {
    67  		return
    68  	}
    69  
    70  	hashed := sha256.Sum256(plain)
    71  	r, s, err := ecdsa.Sign(rand.Reader, signer.key, hashed[:])
    72  	if err != nil {
    73  		return
    74  	}
    75  
    76  	curveSizeInBytes := int(math.Ceil(float64(signer.key.Curve.Params().BitSize) / 8))
    77  
    78  	// The resulting signature is the concatenation of the big-endian octet strings
    79  	// of the r and s parameters, each padded to the byte size of the curve order.
    80  	sig.Value = make([]byte, 2*curveSizeInBytes)
    81  	copyWithLeftPad(sig.Value[0:curveSizeInBytes], r.Bytes())
    82  	copyWithLeftPad(sig.Value[curveSizeInBytes:], s.Bytes())
    83  
    84  	sig.Algorithm = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"
    85  	sig.Certificate = signer.cert.Certificate[0]
    86  	return
    87  }
    88  
    89  // RSA
    90  type rsaSigner struct {
    91  	key  *rsa.PrivateKey
    92  	cert *tls.Certificate
    93  }
    94  
    95  func (signer *rsaSigner) Sign(in interface{}) (sig Signature, err error) {
    96  	plain, err := Canon(in)
    97  	if err != nil {
    98  		return
    99  	}
   100  
   101  	hashed := sha256.Sum256(plain)
   102  	sig.Value, err = rsa.SignPKCS1v15(rand.Reader, signer.key, crypto.SHA256, hashed[:])
   103  	if err != nil {
   104  		return
   105  	}
   106  
   107  	sig.Algorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
   108  	sig.Certificate = signer.cert.Certificate[0]
   109  
   110  	return
   111  }
   112  
   113  // Creates a new signer given the certificate type. Currently supports
   114  // RSA (PKCS1v15) and ECDSA (SHA256 is used in both cases)
   115  func NewSigner(certificate *tls.Certificate) (Signer, error) {
   116  	switch k := certificate.PrivateKey.(type) {
   117  	case *ecdsa.PrivateKey:
   118  		return &ecdsaSigner{k, certificate}, nil
   119  	case *rsa.PrivateKey:
   120  		return &rsaSigner{k, certificate}, nil
   121  	}
   122  
   123  	return nil, errors.New("Unsupported certificate type")
   124  }