Skip to content

Commit

Permalink
chore: simpler ED25519/Nitro signing/verification
Browse files Browse the repository at this point in the history
Remove redundant httpsihgnature.NitroVerifier.
Instead consistently use nitro.NewVerifier.

Fix LookupVerifier signature. The method should return the verifier
interface, not a pointer to it.

Remove unused or constant arguments from various helper methods for
signing and verification.

For payment commands read operator key file and PCR2 from environment
variables for convenience.
  • Loading branch information
ibukanov committed Jul 24, 2024
1 parent 4865ff3 commit c11ba3f
Show file tree
Hide file tree
Showing 39 changed files with 505 additions and 456 deletions.
16 changes: 16 additions & 0 deletions libs/httpsignature/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,19 @@ func GenerateEd25519Key(rand io.Reader) (Ed25519PubKey, ed25519.PrivateKey, erro
publicKey, privateKey, err := ed25519.GenerateKey(nil)
return Ed25519PubKey(publicKey), privateKey, err
}

func GetEd25519KeyId(key ed25519.PrivateKey) string {
pubkey := key.Public().(ed25519.PublicKey)
return hex.EncodeToString(pubkey)
}

func GetEd25519RequestSignator(key ed25519.PrivateKey) ParameterizedSignator {
return ParameterizedSignator{
SignatureParams: SignatureParams{
Algorithm: ED25519,
KeyID: GetEd25519KeyId(key),
Headers: RequestSigningHeaders,
},
Signator: key,
}
}
4 changes: 2 additions & 2 deletions libs/httpsignature/encapsulate.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ func (sr *HTTPSignedRequest) Extract(r *http.Request) (*SignatureParams, error)
if k == RequestTargetHeader {
method, uri, found := strings.Cut(v, " ")
if !found {
return nil, errors.New("invalid encapsulated (request-target) pseudo-header value")
return nil, fmt.Errorf("invalid encapsulated %s pseudo-header value", RequestTargetHeader)
}
r.Method = strings.ToUpper(method)
pURI, err := url.ParseRequestURI(uri)
if err != nil {
return nil, fmt.Errorf("invalid encapsulated (request-target) pseudo-header value: %e", err)
return nil, fmt.Errorf("invalid encapsulated %s pseudo-header value: %e", RequestTargetHeader, err)
}
r.URL = pURI
} else {
Expand Down
9 changes: 4 additions & 5 deletions libs/httpsignature/encapsulate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package httpsignature
import (
"bytes"
"context"
"crypto"
"encoding/hex"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -50,7 +49,7 @@ func TestEncapsulateRequest(t *testing.T) {
sp.KeyID = "primary"
sp.Headers = []string{"digest", "foo"}

valid, err := sp.Verify(pubKey, crypto.Hash(0), &r2)
valid, err := sp.VerifyRequest(pubKey, &r2)
assert.NoError(t, err)
assert.Equal(t, true, valid, "The siganture should be valid after an encapsulation roundtrip")

Expand All @@ -60,7 +59,7 @@ func TestEncapsulateRequest(t *testing.T) {
_, err = er.Extract(&r3)
assert.NoError(t, err)

valid, err = sp.Verify(pubKey, crypto.Hash(0), &r3)
valid, err = sp.VerifyRequest(pubKey, &r3)
assert.NoError(t, err)
assert.Equal(t, false, valid, "The siganture should be invalid since the body is different")
}
Expand Down Expand Up @@ -103,7 +102,7 @@ func TestEncapsulateResponse(t *testing.T) {
sp.KeyID = "primary"
sp.Headers = []string{"digest", "foo"}

valid, err := sp.VerifyResponse(pubKey, crypto.Hash(0), &r2)
valid, err := sp.VerifyResponse(pubKey, &r2)
assert.NoError(t, err)
assert.Equal(t, true, valid, "The siganture should be valid after an encapsulation roundtrip")

Expand All @@ -113,7 +112,7 @@ func TestEncapsulateResponse(t *testing.T) {
_, err = er.Extract(&r3)
assert.NoError(t, err)

valid, err = sp.VerifyResponse(pubKey, crypto.Hash(0), &r3)
valid, err = sp.VerifyResponse(pubKey, &r3)
assert.NoError(t, err)
assert.Equal(t, false, valid, "The siganture should be invalid since the body is different")
}
12 changes: 8 additions & 4 deletions libs/httpsignature/hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@ import (
// HMACKey is a symmetric key that can be used for HMAC-SHA512 request signing and verification
type HMACKey string

// Sign the message using the hmac key
func (key HMACKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
func hmacSign(key HMACKey, message []byte) ([]byte, error) {
hhash := hmac.New(sha512.New, []byte(key))
// writing the message (HTTP signing string) to it
_, err = hhash.Write(message)
_, err := hhash.Write(message)
if err != nil {
return nil, err
}
// Get the hash sum, do not base64 encode it since sig was decoded already
return hhash.Sum(nil), nil
}

// Sign the message using the hmac key
func (key HMACKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
return hmacSign(key, message)
}

// Verify the signature sig for message using the hmac key
func (key HMACKey) Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) {
hashSum, err := key.Sign(nil, message, nil)
hashSum, err := hmacSign(key, message)
if err != nil {
return false, err
}
Expand Down
96 changes: 58 additions & 38 deletions libs/httpsignature/httpsignature.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ import (

// SignatureParams contains parameters needed to create and verify signatures
type SignatureParams struct {
Algorithm Algorithm
KeyID string
DigestAlgorithm *crypto.Hash // optional
Headers []string // optional
Algorithm Algorithm
KeyID string
Headers []string // optional
}

// signature is an internal represention of an http signature and it's parameters
Expand All @@ -50,7 +49,6 @@ type Verifier interface {
type ParameterizedSignator struct {
SignatureParams
Signator Signator
Opts crypto.SignerOpts
}

// ParameterizedSignatorResponseWriter wraps a response writer to sign the response automatically
Expand All @@ -63,7 +61,8 @@ type ParameterizedSignatorResponseWriter struct {
// Keystore provides a way to lookup a public key based on the keyID a request was signed with
type Keystore interface {
// LookupVerifier based on the keyID
LookupVerifier(ctx context.Context, keyID string) (context.Context, *Verifier, error)
// TODO: fix the return type, it should be just Verifier, not *Verifier.
LookupVerifier(ctx context.Context, keyID string) (context.Context, Verifier, error)
}

// StaticKeystore is a keystore that always returns a static verifier independent of keyID
Expand All @@ -75,14 +74,16 @@ type StaticKeystore struct {
type ParameterizedKeystoreVerifier struct {
SignatureParams
Keystore Keystore
Opts crypto.SignerOpts
}

const (
// HostHeader is the host header
HostHeader = "host"
ContentLengthHeader = "content-length"
ContentTypeHeader = "content-type"
DateHeader = "date"
// DigestHeader is the header where a digest of the body will be stored
DigestHeader = "digest"
// HostHeader is the host header
HostHeader = "host"
// RequestTargetHeader is a pseudo header consisting of the HTTP method and request uri
RequestTargetHeader = "(request-target)"
)
Expand All @@ -91,9 +92,18 @@ var (
signatureRegex = regexp.MustCompile(`(\w+)="([^"]*)"`)
)

var RequestSigningHeaders = []string{
RequestTargetHeader,
HostHeader,
DateHeader,
DigestHeader,
ContentLengthHeader,
ContentTypeHeader,
}

// LookupVerifier by returning a static verifier
func (sk *StaticKeystore) LookupVerifier(ctx context.Context, keyID string) (context.Context, *Verifier, error) {
return ctx, &sk.Verifier, nil
func (sk *StaticKeystore) LookupVerifier(ctx context.Context, keyID string) (context.Context, Verifier, error) {
return ctx, sk.Verifier, nil
}

// TODO Add New function
Expand Down Expand Up @@ -151,7 +161,9 @@ func (sp *SignatureParams) buildSigningString(body []byte, headers http.Header,

signedHeaders := sp.Headers
if len(signedHeaders) == 0 {
signedHeaders = []string{"date"}
signedHeaders = []string{
DateHeader,
}
}

for i, header := range signedHeaders {
Expand All @@ -168,17 +180,14 @@ func (sp *SignatureParams) buildSigningString(body []byte, headers http.Header,
// Just like before default to SHA256
var d digest.Instance
d.Hash = crypto.SHA256

// If something else is set though use that hash instead
if sp.DigestAlgorithm != nil {
d.Hash = *sp.DigestAlgorithm
}

if body != nil {
d.Update(body)
}
headers.Add("Digest", d.String())
out = append(out, []byte(fmt.Sprintf("%s: %s", "digest", d.String()))...)
out = append(
out,
[]byte(fmt.Sprintf("%s: %s", DigestHeader, d.String()))...,
)
} else if header == HostHeader {
if req == nil {
return nil, fmt.Errorf("request must be present to use the Host header")
Expand All @@ -188,6 +197,9 @@ func (sp *SignatureParams) buildSigningString(body []byte, headers http.Header,
host := headers.Get(requestutils.HostHeaderKey)
if host == "" {
host = req.Host
if host == "" && req.URL != nil {
host = req.URL.Host
}
} else {
host = strings.Join(headers[http.CanonicalHeaderKey(header)], ", ")
}
Expand All @@ -209,25 +221,25 @@ func (sp *SignatureParams) buildSigningString(body []byte, headers http.Header,
}

// Sign the included HTTP request req using signator and options opts
func (sp *SignatureParams) Sign(signator Signator, opts crypto.SignerOpts, req *http.Request) error {
func (sp *SignatureParams) SignRequest(signator Signator, req *http.Request) error {
ss, err := sp.BuildSigningString(req)
if err != nil {
return err
}
return sp.sign(signator, opts, ss, req.Header)
return sp.sign(signator, ss, req.Header)
}

// SignResponse using signator and options opts
func (sp *SignatureParams) SignResponse(signator Signator, opts crypto.SignerOpts, resp *http.Response) error {
func (sp *SignatureParams) SignResponse(signator Signator, resp *http.Response) error {
ss, err := sp.BuildSigningStringForResponse(resp)
if err != nil {
return err
}
return sp.sign(signator, opts, ss, resp.Header)
return sp.sign(signator, ss, resp.Header)
}

func (sp *SignatureParams) sign(signator Signator, opts crypto.SignerOpts, ss []byte, headers http.Header) error {
sig, err := signator.Sign(rand.Reader, ss, opts)
func (sp *SignatureParams) sign(signator Signator, ss []byte, headers http.Header) error {
sig, err := signator.Sign(rand.Reader, ss, crypto.Hash(0))
if err != nil {
return err
}
Expand All @@ -246,12 +258,12 @@ func (sp *SignatureParams) sign(signator Signator, opts crypto.SignerOpts, ss []

// SignRequest using signator and options opts in the parameterized signator
func (p *ParameterizedSignator) SignRequest(req *http.Request) error {
return p.SignatureParams.Sign(p.Signator, p.Opts, req)
return p.SignatureParams.SignRequest(p.Signator, req)
}

// SignResponse using signator and options opts in the parameterized signator
func (p *ParameterizedSignator) SignResponse(resp *http.Response) error {
return p.SignatureParams.SignResponse(p.Signator, p.Opts, resp)
return p.SignatureParams.SignResponse(p.Signator, resp)
}

// NewParameterizedSignatorResponseWriter wraps the provided response writer and signs the response
Expand Down Expand Up @@ -283,7 +295,7 @@ func (psrw *ParameterizedSignatorResponseWriter) Write(body []byte) (int, error)
if err != nil {
return -1, err
}
err = psrw.SignatureParams.sign(psrw.Signator, psrw.Opts, ss, psrw.Header())
err = psrw.SignatureParams.sign(psrw.Signator, ss, psrw.Header())
if err != nil {
return -1, err
}
Expand All @@ -294,25 +306,33 @@ func (psrw *ParameterizedSignatorResponseWriter) Write(body []byte) (int, error)
return psrw.w.Write(body)
}

// Verify the HTTP signature s over HTTP request req using verifier with options opts
func (sp *SignatureParams) Verify(verifier Verifier, opts crypto.SignerOpts, req *http.Request) (bool, error) {
// Verify the HTTP signature s over HTTP request req using verifier
func (sp *SignatureParams) VerifyRequest(verifier Verifier, req *http.Request) (bool, error) {
signingStr, err := sp.BuildSigningString(req)
if err != nil {
return false, err
}
return sp.verify(verifier, opts, signingStr, req.Header)
valid, err := sp.verify(verifier, signingStr, req.Header)
if err != nil {
err = fmt.Errorf("failed to verify HTTP request - %w", err)
}
return valid, err
}

// VerifyResponse by verifying the HTTP signature over HTTP response resp using verifier with options opts
func (sp *SignatureParams) VerifyResponse(verifier Verifier, opts crypto.SignerOpts, resp *http.Response) (bool, error) {
func (sp *SignatureParams) VerifyResponse(verifier Verifier, resp *http.Response) (bool, error) {
signingStr, err := sp.BuildSigningStringForResponse(resp)
if err != nil {
return false, err
}
return sp.verify(verifier, opts, signingStr, resp.Header)
valid, err := sp.verify(verifier, signingStr, resp.Header)
if err != nil {
err = fmt.Errorf("failed to verify HTTP response - %w", err)
}
return valid, err
}

func (sp *SignatureParams) verify(verifier Verifier, opts crypto.SignerOpts, ss []byte, headers http.Header) (bool, error) {
func (sp *SignatureParams) verify(verifier Verifier, ss []byte, headers http.Header) (bool, error) {
var tmp signature
err := tmp.UnmarshalText([]byte(headers.Get("Signature")))
if err != nil {
Expand All @@ -323,7 +343,7 @@ func (sp *SignatureParams) verify(verifier Verifier, opts crypto.SignerOpts, ss
if err != nil {
return false, err
}
return verifier.Verify(ss, sig, opts)
return verifier.Verify(ss, sig, crypto.Hash(0))
}

// VerifyRequest using keystore to lookup verifier with options opts
Expand All @@ -347,7 +367,7 @@ func (pkv *ParameterizedKeystoreVerifier) VerifyRequest(req *http.Request) (cont
sp.Algorithm = pkv.SignatureParams.Algorithm
sp.Headers = pkv.SignatureParams.Headers

valid, err := sp.Verify(*verifier, pkv.Opts, req)
valid, err := sp.VerifyRequest(verifier, req)
if err != nil {
return nil, "", err
}
Expand Down Expand Up @@ -425,7 +445,7 @@ func SignatureParamsFromRequest(req *http.Request) (*SignatureParams, error) {
var s signature
err := s.UnmarshalText([]byte(req.Header.Get("Signature")))
if err != nil {
return nil, err
return nil, fmt.Errorf("bad HTTP request - %w", err)
}
return &s.SignatureParams, nil
}
Expand All @@ -435,7 +455,7 @@ func SignatureParamsFromResponse(resp *http.Response) (*SignatureParams, error)
var s signature
err := s.UnmarshalText([]byte(resp.Header.Get("Signature")))
if err != nil {
return nil, err
return nil, fmt.Errorf("bad HTTP response - %w", err)
}
return &s.SignatureParams, nil
}
Loading

0 comments on commit c11ba3f

Please sign in to comment.