mirror of https://github.com/golang/go.git
crypto,crypto/x509: implement MessageSigner
And use it in crypto/x509. This allows people to implement single-shot signers which do the hashing themselves. Fixes #63405 Change-Id: I038c2e10f77b050b6136c4c0a5b031cb416f59aa Reviewed-on: https://go-review.googlesource.com/c/go/+/654375 Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
d000963d04
commit
509c11f3a3
|
|
@ -0,0 +1,5 @@
|
|||
pkg crypto, func SignMessage(Signer, io.Reader, []uint8, SignerOpts) ([]uint8, error) #63405
|
||||
pkg crypto, type MessageSigner interface { Public, Sign, SignMessage } #63405
|
||||
pkg crypto, type MessageSigner interface, Public() PublicKey #63405
|
||||
pkg crypto, type MessageSigner interface, Sign(io.Reader, []uint8, SignerOpts) ([]uint8, error) #63405
|
||||
pkg crypto, type MessageSigner interface, SignMessage(io.Reader, []uint8, SignerOpts) ([]uint8, error) #63405
|
||||
|
|
@ -0,0 +1 @@
|
|||
[MessageSigner] is a new signing interface that can be implemented by signers that wish to hash the message to be signed themselves. A new function is also introduced, [SignMessage] which attempts to update a [Signer] interface to [MessageSigner], using the [MessageSigner.SignMessage] method if successful, and [Signer.Sign] if not. This can be used when code wishes to support both [Signer] and [MessageSigner].
|
||||
|
|
@ -0,0 +1 @@
|
|||
[CreateCertificate], [CreateCertificateRequest], and [CreateRevocationList] can now accept a [crypto.MessageSigner] signing interface as well as [crypto.Signer]. This allows these functions to use signers which implement "one-shot" signing interfaces, where hashing is done as part of the signing operation, instead of by the caller.
|
||||
|
|
@ -198,6 +198,23 @@ type Signer interface {
|
|||
Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)
|
||||
}
|
||||
|
||||
// MessageSigner is an interface for an opaque private key that can be used for
|
||||
// signing operations where the message is not pre-hashed by the caller.
|
||||
// It is a superset of the Signer interface so that it can be passed to APIs
|
||||
// which accept Signer, which may try to do an interface upgrade.
|
||||
//
|
||||
// MessageSigner.SignMessage and MessageSigner.Sign should produce the same
|
||||
// result given the same opts. In particular, MessageSigner.SignMessage should
|
||||
// only accept a zero opts.HashFunc if the Signer would also accept messages
|
||||
// which are not pre-hashed.
|
||||
//
|
||||
// Implementations which do not provide the pre-hashed Sign API should implement
|
||||
// Signer.Sign by always returning an error.
|
||||
type MessageSigner interface {
|
||||
Signer
|
||||
SignMessage(rand io.Reader, msg []byte, opts SignerOpts) (signature []byte, err error)
|
||||
}
|
||||
|
||||
// SignerOpts contains options for signing with a [Signer].
|
||||
type SignerOpts interface {
|
||||
// HashFunc returns an identifier for the hash function used to produce
|
||||
|
|
@ -221,3 +238,18 @@ type Decrypter interface {
|
|||
}
|
||||
|
||||
type DecrypterOpts any
|
||||
|
||||
// SignMessage signs msg with signer. If signer implements [MessageSigner],
|
||||
// [MessageSigner.SignMessage] is called directly. Otherwise, msg is hashed
|
||||
// with opts.HashFunc() and signed with [Signer.Sign].
|
||||
func SignMessage(signer Signer, rand io.Reader, msg []byte, opts SignerOpts) (signature []byte, err error) {
|
||||
if ms, ok := signer.(MessageSigner); ok {
|
||||
return ms.SignMessage(rand, msg, opts)
|
||||
}
|
||||
if opts.HashFunc() != 0 {
|
||||
h := opts.HashFunc().New()
|
||||
h.Write(msg)
|
||||
msg = h.Sum(nil)
|
||||
}
|
||||
return signer.Sign(rand, msg, opts)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package crypto_test
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type messageSignerOnly struct {
|
||||
k *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func (s *messageSignerOnly) Public() crypto.PublicKey {
|
||||
return s.k.Public()
|
||||
}
|
||||
|
||||
func (s *messageSignerOnly) Sign(_ io.Reader, _ []byte, _ crypto.SignerOpts) ([]byte, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (s *messageSignerOnly) SignMessage(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
h := opts.HashFunc().New()
|
||||
h.Write(msg)
|
||||
digest := h.Sum(nil)
|
||||
return s.k.Sign(rand, digest, opts)
|
||||
}
|
||||
|
||||
func TestSignMessage(t *testing.T) {
|
||||
block, _ := pem.Decode([]byte(strings.ReplaceAll(
|
||||
`-----BEGIN RSA TESTING KEY-----
|
||||
MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso
|
||||
tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE
|
||||
89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU
|
||||
l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s
|
||||
B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59
|
||||
3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+
|
||||
dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI
|
||||
FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J
|
||||
aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2
|
||||
BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx
|
||||
IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/
|
||||
fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u
|
||||
pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT
|
||||
Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl
|
||||
u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD
|
||||
fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X
|
||||
Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE
|
||||
k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo
|
||||
qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS
|
||||
CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ
|
||||
XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw
|
||||
AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r
|
||||
UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0
|
||||
2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5
|
||||
7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3
|
||||
-----END RSA TESTING KEY-----`, "TESTING KEY", "PRIVATE KEY")))
|
||||
k, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
|
||||
msg := []byte("hello :)")
|
||||
|
||||
h := crypto.SHA256.New()
|
||||
h.Write(msg)
|
||||
digest := h.Sum(nil)
|
||||
|
||||
sig, err := crypto.SignMessage(k, rand.Reader, msg, &rsa.PSSOptions{Hash: crypto.SHA256})
|
||||
if err != nil {
|
||||
t.Fatalf("SignMessage failed with Signer: %s", err)
|
||||
}
|
||||
if err := rsa.VerifyPSS(&k.PublicKey, crypto.SHA256, digest, sig, nil); err != nil {
|
||||
t.Errorf("VerifyPSS failed for Signer signature: %s", err)
|
||||
}
|
||||
|
||||
sig, err = crypto.SignMessage(&messageSignerOnly{k}, rand.Reader, msg, &rsa.PSSOptions{Hash: crypto.SHA256})
|
||||
if err != nil {
|
||||
t.Fatalf("SignMessage failed with MessageSigner: %s", err)
|
||||
}
|
||||
if err := rsa.VerifyPSS(&k.PublicKey, crypto.SHA256, digest, sig, nil); err != nil {
|
||||
t.Errorf("VerifyPSS failed for MessageSigner signature: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1568,13 +1568,7 @@ func signingParamsForKey(key crypto.Signer, sigAlgo SignatureAlgorithm) (Signatu
|
|||
}
|
||||
|
||||
func signTBS(tbs []byte, key crypto.Signer, sigAlg SignatureAlgorithm, rand io.Reader) ([]byte, error) {
|
||||
signed := tbs
|
||||
hashFunc := sigAlg.hashFunc()
|
||||
if hashFunc != 0 {
|
||||
h := hashFunc.New()
|
||||
h.Write(signed)
|
||||
signed = h.Sum(nil)
|
||||
}
|
||||
|
||||
var signerOpts crypto.SignerOpts = hashFunc
|
||||
if sigAlg.isRSAPSS() {
|
||||
|
|
@ -1584,7 +1578,7 @@ func signTBS(tbs []byte, key crypto.Signer, sigAlg SignatureAlgorithm, rand io.R
|
|||
}
|
||||
}
|
||||
|
||||
signature, err := key.Sign(rand, signed, signerOpts)
|
||||
signature, err := crypto.SignMessage(key, rand, tbs, signerOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1646,7 +1640,7 @@ var emptyASN1Subject = []byte{0x30, 0}
|
|||
//
|
||||
// The currently supported key types are *rsa.PublicKey, *ecdsa.PublicKey and
|
||||
// ed25519.PublicKey. pub must be a supported key type, and priv must be a
|
||||
// crypto.Signer with a supported public key.
|
||||
// crypto.Signer or crypto.MessageSigner with a supported public key.
|
||||
//
|
||||
// The AuthorityKeyId will be taken from the SubjectKeyId of parent, if any,
|
||||
// unless the resulting certificate is self-signed. Otherwise the value from
|
||||
|
|
@ -2031,10 +2025,10 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
|
|||
// - Attributes (deprecated)
|
||||
//
|
||||
// priv is the private key to sign the CSR with, and the corresponding public
|
||||
// key will be included in the CSR. It must implement crypto.Signer and its
|
||||
// Public() method must return a *rsa.PublicKey or a *ecdsa.PublicKey or a
|
||||
// ed25519.PublicKey. (A *rsa.PrivateKey, *ecdsa.PrivateKey or
|
||||
// ed25519.PrivateKey satisfies this.)
|
||||
// key will be included in the CSR. It must implement crypto.Signer or
|
||||
// crypto.MessageSigner and its Public() method must return a *rsa.PublicKey or
|
||||
// a *ecdsa.PublicKey or a ed25519.PublicKey. (A *rsa.PrivateKey,
|
||||
// *ecdsa.PrivateKey or ed25519.PrivateKey satisfies this.)
|
||||
//
|
||||
// The returned slice is the certificate request in DER encoding.
|
||||
func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv any) (csr []byte, err error) {
|
||||
|
|
@ -2376,8 +2370,9 @@ type tbsCertificateList struct {
|
|||
// CreateRevocationList creates a new X.509 v2 [Certificate] Revocation List,
|
||||
// according to RFC 5280, based on template.
|
||||
//
|
||||
// The CRL is signed by priv which should be the private key associated with
|
||||
// the public key in the issuer certificate.
|
||||
// The CRL is signed by priv which should be a crypto.Signer or
|
||||
// crypto.MessageSigner associated with the public key in the issuer
|
||||
// certificate.
|
||||
//
|
||||
// The issuer may not be nil, and the crlSign bit must be set in [KeyUsage] in
|
||||
// order to use it as a CRL issuer.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
|
|
@ -4196,3 +4197,44 @@ func TestRejectCriticalSKI(t *testing.T) {
|
|||
t.Fatalf("ParseCertificate() unexpected error: %v, want: %s", err, expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
type messageSigner struct{}
|
||||
|
||||
func (ms *messageSigner) Public() crypto.PublicKey { return rsaPrivateKey.Public() }
|
||||
|
||||
func (ms *messageSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (ms *messageSigner) SignMessage(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
if _, ok := opts.(*rsa.PSSOptions); ok {
|
||||
return nil, errors.New("PSSOptions passed instead of hash")
|
||||
}
|
||||
h := opts.HashFunc().New()
|
||||
h.Write(msg)
|
||||
tbs := h.Sum(nil)
|
||||
return rsa.SignPKCS1v15(rand, rsaPrivateKey, opts.HashFunc(), tbs)
|
||||
}
|
||||
|
||||
func TestMessageSigner(t *testing.T) {
|
||||
template := Certificate{
|
||||
SignatureAlgorithm: SHA256WithRSA,
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: "Cert"},
|
||||
NotBefore: time.Unix(1000, 0),
|
||||
NotAfter: time.Unix(100000, 0),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), &messageSigner{})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateCertificate failed: %s", err)
|
||||
}
|
||||
cert, err := ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseCertificate failed: %s", err)
|
||||
}
|
||||
if err := cert.CheckSignatureFrom(cert); err != nil {
|
||||
t.Fatalf("CheckSignatureFrom failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue