2016-09-21 14:29:42 +00:00
|
|
|
package transit
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
|
|
|
"crypto/sha512"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"hash"
|
|
|
|
|
|
|
|
"github.com/hashicorp/vault/helper/errutil"
|
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
"github.com/hashicorp/vault/logical/framework"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (b *backend) pathSign() *framework.Path {
|
|
|
|
return &framework.Path{
|
|
|
|
Pattern: "sign/" + framework.GenericNameRegex("name") + framework.OptionalParamRegex("urlalgorithm"),
|
|
|
|
Fields: map[string]*framework.FieldSchema{
|
|
|
|
"name": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: "The key to use",
|
|
|
|
},
|
|
|
|
|
|
|
|
"input": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: "The base64-encoded input data",
|
|
|
|
},
|
|
|
|
|
2017-06-05 19:00:39 +00:00
|
|
|
"context": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: `Base64 encoded context for key derivation. Required if key
|
|
|
|
derivation is enabled; currently only available with ed25519 keys.`,
|
|
|
|
},
|
|
|
|
|
2016-09-21 14:29:42 +00:00
|
|
|
"algorithm": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Default: "sha2-256",
|
|
|
|
Description: `Hash algorithm to use (POST body parameter). Valid values are:
|
|
|
|
|
|
|
|
* sha2-224
|
|
|
|
* sha2-256
|
|
|
|
* sha2-384
|
|
|
|
* sha2-512
|
|
|
|
|
2017-07-28 13:30:27 +00:00
|
|
|
Defaults to "sha2-256". Not valid for all key types,
|
|
|
|
including ed25519.`,
|
2016-09-21 14:29:42 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"urlalgorithm": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: `Hash algorithm to use (POST URL parameter)`,
|
|
|
|
},
|
2017-06-06 20:02:54 +00:00
|
|
|
|
|
|
|
"key_version": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeInt,
|
|
|
|
Description: `The version of the key to use for signing.
|
|
|
|
Must be 0 (for latest) or a value greater than or equal
|
|
|
|
to the min_encryption_version configured on the key.`,
|
|
|
|
},
|
2016-09-21 14:29:42 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
|
|
logical.UpdateOperation: b.pathSignWrite,
|
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathSignHelpSyn,
|
|
|
|
HelpDescription: pathSignHelpDesc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *backend) pathVerify() *framework.Path {
|
|
|
|
return &framework.Path{
|
|
|
|
Pattern: "verify/" + framework.GenericNameRegex("name") + framework.OptionalParamRegex("urlalgorithm"),
|
|
|
|
Fields: map[string]*framework.FieldSchema{
|
|
|
|
"name": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: "The key to use",
|
|
|
|
},
|
|
|
|
|
2017-06-05 19:00:39 +00:00
|
|
|
"context": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: `Base64 encoded context for key derivation. Required if key
|
|
|
|
derivation is enabled; currently only available with ed25519 keys.`,
|
|
|
|
},
|
|
|
|
|
2016-09-21 14:29:42 +00:00
|
|
|
"signature": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: "The signature, including vault header/key version",
|
|
|
|
},
|
|
|
|
|
|
|
|
"hmac": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: "The HMAC, including vault header/key version",
|
|
|
|
},
|
|
|
|
|
|
|
|
"input": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: "The base64-encoded input data to verify",
|
|
|
|
},
|
|
|
|
|
|
|
|
"urlalgorithm": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: `Hash algorithm to use (POST URL parameter)`,
|
|
|
|
},
|
|
|
|
|
|
|
|
"algorithm": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Default: "sha2-256",
|
|
|
|
Description: `Hash algorithm to use (POST body parameter). Valid values are:
|
|
|
|
|
|
|
|
* sha2-224
|
|
|
|
* sha2-256
|
|
|
|
* sha2-384
|
|
|
|
* sha2-512
|
|
|
|
|
2017-06-05 19:00:39 +00:00
|
|
|
Defaults to "sha2-256". Not valid for all key types.`,
|
2016-09-21 14:29:42 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
|
|
logical.UpdateOperation: b.pathVerifyWrite,
|
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathVerifyHelpSyn,
|
|
|
|
HelpDescription: pathVerifyHelpDesc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *backend) pathSignWrite(
|
|
|
|
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
|
|
name := d.Get("name").(string)
|
2017-06-06 20:02:54 +00:00
|
|
|
ver := d.Get("key_version").(int)
|
2016-09-21 14:29:42 +00:00
|
|
|
inputB64 := d.Get("input").(string)
|
|
|
|
algorithm := d.Get("urlalgorithm").(string)
|
|
|
|
if algorithm == "" {
|
|
|
|
algorithm = d.Get("algorithm").(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
input, err := base64.StdEncoding.DecodeString(inputB64)
|
|
|
|
if err != nil {
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf("unable to decode input as base64: %s", err)), logical.ErrInvalidRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the policy
|
|
|
|
p, lock, err := b.lm.GetPolicyShared(req.Storage, name)
|
|
|
|
if lock != nil {
|
|
|
|
defer lock.RUnlock()
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if p == nil {
|
2017-05-12 18:14:00 +00:00
|
|
|
return logical.ErrorResponse("encryption key not found"), logical.ErrInvalidRequest
|
2016-09-21 14:29:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !p.Type.SigningSupported() {
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf("key type %v does not support signing", p.Type)), logical.ErrInvalidRequest
|
|
|
|
}
|
|
|
|
|
2017-06-05 19:00:39 +00:00
|
|
|
contextRaw := d.Get("context").(string)
|
|
|
|
var context []byte
|
|
|
|
if len(contextRaw) != 0 {
|
|
|
|
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
|
|
|
if err != nil {
|
|
|
|
return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.Type.HashSignatureInput() {
|
|
|
|
var hf hash.Hash
|
|
|
|
switch algorithm {
|
|
|
|
case "sha2-224":
|
|
|
|
hf = sha256.New224()
|
|
|
|
case "sha2-256":
|
|
|
|
hf = sha256.New()
|
|
|
|
case "sha2-384":
|
|
|
|
hf = sha512.New384()
|
|
|
|
case "sha2-512":
|
|
|
|
hf = sha512.New()
|
|
|
|
default:
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf("unsupported algorithm %s", algorithm)), nil
|
|
|
|
}
|
|
|
|
hf.Write(input)
|
|
|
|
input = hf.Sum(nil)
|
|
|
|
}
|
|
|
|
|
2017-06-06 20:02:54 +00:00
|
|
|
sig, err := p.Sign(ver, context, input)
|
2016-09-21 14:29:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-06-05 19:00:39 +00:00
|
|
|
if sig == nil {
|
2016-09-21 14:29:42 +00:00
|
|
|
return nil, fmt.Errorf("signature could not be computed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the response
|
|
|
|
resp := &logical.Response{
|
|
|
|
Data: map[string]interface{}{
|
2017-06-05 19:00:39 +00:00
|
|
|
"signature": sig.Signature,
|
2016-09-21 14:29:42 +00:00
|
|
|
},
|
|
|
|
}
|
2017-06-05 19:00:39 +00:00
|
|
|
|
|
|
|
if len(sig.PublicKey) > 0 {
|
|
|
|
resp.Data["public_key"] = sig.PublicKey
|
|
|
|
}
|
|
|
|
|
2016-09-21 14:29:42 +00:00
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *backend) pathVerifyWrite(
|
|
|
|
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
|
|
|
|
|
|
sig := d.Get("signature").(string)
|
|
|
|
hmac := d.Get("hmac").(string)
|
|
|
|
switch {
|
|
|
|
case sig != "" && hmac != "":
|
|
|
|
return logical.ErrorResponse("provide one of 'signature' or 'hmac'"), logical.ErrInvalidRequest
|
|
|
|
|
|
|
|
case sig == "" && hmac == "":
|
|
|
|
return logical.ErrorResponse("neither a 'signature' nor an 'hmac' were given to verify"), logical.ErrInvalidRequest
|
|
|
|
|
|
|
|
case hmac != "":
|
|
|
|
return b.pathHMACVerify(req, d, hmac)
|
|
|
|
}
|
|
|
|
|
|
|
|
name := d.Get("name").(string)
|
|
|
|
inputB64 := d.Get("input").(string)
|
|
|
|
algorithm := d.Get("urlalgorithm").(string)
|
|
|
|
if algorithm == "" {
|
|
|
|
algorithm = d.Get("algorithm").(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
input, err := base64.StdEncoding.DecodeString(inputB64)
|
|
|
|
if err != nil {
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf("unable to decode input as base64: %s", err)), logical.ErrInvalidRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the policy
|
|
|
|
p, lock, err := b.lm.GetPolicyShared(req.Storage, name)
|
|
|
|
if lock != nil {
|
|
|
|
defer lock.RUnlock()
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if p == nil {
|
2017-05-12 18:14:00 +00:00
|
|
|
return logical.ErrorResponse("encryption key not found"), logical.ErrInvalidRequest
|
2016-09-21 14:29:42 +00:00
|
|
|
}
|
|
|
|
|
2017-06-05 19:00:39 +00:00
|
|
|
if !p.Type.SigningSupported() {
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf("key type %v does not support verification", p.Type)), logical.ErrInvalidRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
contextRaw := d.Get("context").(string)
|
|
|
|
var context []byte
|
|
|
|
if len(contextRaw) != 0 {
|
|
|
|
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
|
|
|
if err != nil {
|
|
|
|
return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.Type.HashSignatureInput() {
|
|
|
|
var hf hash.Hash
|
|
|
|
switch algorithm {
|
|
|
|
case "sha2-224":
|
|
|
|
hf = sha256.New224()
|
|
|
|
case "sha2-256":
|
|
|
|
hf = sha256.New()
|
|
|
|
case "sha2-384":
|
|
|
|
hf = sha512.New384()
|
|
|
|
case "sha2-512":
|
|
|
|
hf = sha512.New()
|
|
|
|
default:
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf("unsupported algorithm %s", algorithm)), nil
|
|
|
|
}
|
|
|
|
hf.Write(input)
|
|
|
|
input = hf.Sum(nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
valid, err := p.VerifySignature(context, input, sig)
|
2016-09-21 14:29:42 +00:00
|
|
|
if err != nil {
|
|
|
|
switch err.(type) {
|
|
|
|
case errutil.UserError:
|
|
|
|
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
|
|
|
case errutil.InternalError:
|
|
|
|
return nil, err
|
|
|
|
default:
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the response
|
|
|
|
resp := &logical.Response{
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"valid": valid,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const pathSignHelpSyn = `Generate a signature for input data using the named key`
|
|
|
|
|
|
|
|
const pathSignHelpDesc = `
|
|
|
|
Generates a signature of the input data using the named key and the given hash algorithm.
|
|
|
|
`
|
|
|
|
const pathVerifyHelpSyn = `Verify a signature or HMAC for input data created using the named key`
|
|
|
|
|
|
|
|
const pathVerifyHelpDesc = `
|
|
|
|
Verifies a signature or HMAC of the input data using the named key and the given hash algorithm.
|
|
|
|
`
|