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 }