mirror of
https://github.com/smallstep/certificates.git
synced 2024-11-17 15:29:21 +00:00
7a5c4a1112
In Go, len returns signed ints, not unsigned ints; consequently, this code comparison overflows on 32-bit systems, like ARM.
116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
package authority
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// DefaultProvisionersLimit is the default limit for listing provisioners.
|
|
const DefaultProvisionersLimit = 20
|
|
|
|
// DefaultProvisionersMax is the maximum limit for listing provisioners.
|
|
const DefaultProvisionersMax = 100
|
|
|
|
// GetEncryptedKey returns the JWE key corresponding to the given kid argument.
|
|
func (a *Authority) GetEncryptedKey(kid string) (string, error) {
|
|
val, ok := a.encryptedKeyIndex.Load(kid)
|
|
if !ok {
|
|
return "", &apiError{errors.Errorf("encrypted key with kid %s was not found", kid),
|
|
http.StatusNotFound, context{}}
|
|
}
|
|
|
|
key, ok := val.(string)
|
|
if !ok {
|
|
return "", &apiError{errors.Errorf("stored value is not a string"),
|
|
http.StatusInternalServerError, context{}}
|
|
}
|
|
return key, nil
|
|
}
|
|
|
|
// GetProvisioners returns a map listing each provisioner and the JWK Key Set
|
|
// with their public keys.
|
|
func (a *Authority) GetProvisioners(cursor string, limit int) ([]*Provisioner, string, error) {
|
|
provisioners, nextCursor := a.sortedProvisioners.Find(cursor, limit)
|
|
return provisioners, nextCursor, nil
|
|
}
|
|
|
|
type uidProvisioner struct {
|
|
provisioner *Provisioner
|
|
uid string
|
|
}
|
|
|
|
func newSortedProvisioners(provisioners []*Provisioner) (provisionerSlice, error) {
|
|
if len(provisioners) > math.MaxInt32 {
|
|
return nil, errors.New("too many provisioners")
|
|
}
|
|
|
|
var slice provisionerSlice
|
|
bi := make([]byte, 4)
|
|
for i, p := range provisioners {
|
|
sum, err := provisionerSum(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Use the first 4 bytes (32bit) of the sum to insert the order
|
|
// Using big endian format to get the strings sorted:
|
|
// 0x00000000, 0x00000001, 0x00000002, ...
|
|
binary.BigEndian.PutUint32(bi, uint32(i))
|
|
sum[0], sum[1], sum[2], sum[3] = bi[0], bi[1], bi[2], bi[3]
|
|
bi[0], bi[1], bi[2], bi[3] = 0, 0, 0, 0
|
|
slice = append(slice, uidProvisioner{
|
|
provisioner: p,
|
|
uid: hex.EncodeToString(sum),
|
|
})
|
|
}
|
|
sort.Sort(slice)
|
|
return slice, nil
|
|
}
|
|
|
|
type provisionerSlice []uidProvisioner
|
|
|
|
func (p provisionerSlice) Len() int { return len(p) }
|
|
func (p provisionerSlice) Less(i, j int) bool { return p[i].uid < p[j].uid }
|
|
func (p provisionerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
|
|
|
func (p provisionerSlice) Find(cursor string, limit int) ([]*Provisioner, string) {
|
|
switch {
|
|
case limit <= 0:
|
|
limit = DefaultProvisionersLimit
|
|
case limit > DefaultProvisionersMax:
|
|
limit = DefaultProvisionersMax
|
|
}
|
|
|
|
n := len(p)
|
|
cursor = fmt.Sprintf("%040s", cursor)
|
|
i := sort.Search(n, func(i int) bool { return p[i].uid >= cursor })
|
|
|
|
var slice []*Provisioner
|
|
for ; i < n && len(slice) < limit; i++ {
|
|
slice = append(slice, p[i].provisioner)
|
|
}
|
|
if i < n {
|
|
return slice, strings.TrimLeft(p[i].uid, "0")
|
|
}
|
|
return slice, ""
|
|
}
|
|
|
|
// provisionerSum returns the SHA1 of the json representation of the
|
|
// provisioner. From this we will create the unique and sorted id.
|
|
func provisionerSum(p *Provisioner) ([]byte, error) {
|
|
b, err := json.Marshal(p.Key)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error marshalling provisioner")
|
|
}
|
|
sum := sha1.Sum(b)
|
|
return sum[:], nil
|
|
}
|