-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Proposal Details
crypto/rsa.SignPSS() and similar packages take a crypto.Hash, which is hard-coded to only support known-to-Go hash algorithms.
This is a total show-stopper for me, because I need to be able to support new, not-yet-existing hash algorithms.
I propose a new implementation for src/crypto/crypto.go:
// ...
type customImplementation struct {
name string
// int is used rather than uint8 of digestSizes in order to support future
// hash functions with large digests.
digestSize int
f func() hash.Hash
}
var customImplementations = make(map[Hash]customImplementation)
func (h Hash) String() string {
switch h {
// ...
default:
if implementation, exists := customImplementations[h]; exists {
return implementation.name
}
return "unknown hash value " + strconv.Itoa(int(h))
}
}
// ...
// Defines the limit of 1st-party hash functions.
const boundaryFirstParty Hash = 101
// ...
// Size returns the length, in bytes, of a digest resulting from the given hash
// function. It doesn't require that the hash function in question be linked
// into the program for hash functions known to Go authors. It does require
// that the hash function in question be linked into the program for 3rd-party
// hash functions.
func (h Hash) Size() int {
if h < 1 || h == maxHash {
panic("crypto: Size of unknown hash function")
}
if h < maxHash {
return int(digestSizes[h])
}
if implementation, exists := customImplementations[h]; exists {
return implementation.digestSize
}
panic("crypto: Size of unknown hash function")
}
// ...
// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
if h < 1 || h == maxHash {
panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
}
if h < maxHash {
f := hashes[h]
if f != nil {
return f()
}
}
if implementation, exists := customImplementations[h]; exists {
return implementation.f()
}
panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
}
// Available reports whether the given hash function is linked into the binary.
func (h Hash) Available() bool {
if h < maxHash && hashes[h] != nil {
return true
}
if _, exists := customImplementations[h]; exists {
return true
}
return false
}
// ...
// RegisterCustomHash registers a function that returns a new instance of the given
// 3rd-party hash function. This is intended to be called from the init function in
// packages that implement hash functions. Registering hash functions outside of an
// init function can cause thread-unsafe behaviors.
func RegisterCustomHash(h Hash, name string, digestSize int, f func() hash.Hash) error {
if h <= boundaryFirstParty {
return errors.New("crypto: RegisterCustomHash within 1st-party region")
}
if len(name) == 0 {
return errors.New("crypto: RegisterCustomHash of hash function without a name")
}
if digestSize < 1 {
return errors.New("crypto: RegisterCustomHash of hash function with empty digests")
}
if f == nil {
return errors.New("crypto: RegisterCustomHash of hash function without an allocator")
}
if _, exists := customImplementations[h]; exists {
return errors.New("crypto: RegisterCustomHash of an existing hash function")
}
customImplementations[h] = customImplementation{
name: name,
digestSize: digestSize,
f: f,
}
return nil
}
// ...(note that I would submit a pull request, but I am currently on a very shoddy hotspot internet connection that prevents me from performing the necessary tasks to do so.)
There is of course an issue with this proposal: 3rd-party hash algorithms might use the same value for crypto.Hash, which would lead to errors.
Ideally we would change the type definition of crypto.Hash to something more sensible than a uint, but this would break packages that use the uint to identify hash algorithms.
Perhaps a checksum of the hash algorithm's name to produce a uint would be better?
Metadata
Metadata
Assignees
Labels
Type
Projects
Status