-
Notifications
You must be signed in to change notification settings - Fork 55
Schema Signing & Verification #123
Changes from 7 commits
93d95f3
138f56a
4768cab
d88effa
b03dee6
b5b384e
2d49fd2
c107669
5a85622
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package keyaccess | ||
|
||
import ( | ||
"crypto" | ||
"fmt" | ||
|
||
"github.com/TBD54566975/ssi-sdk/cryptosuite" | ||
didsdk "github.com/TBD54566975/ssi-sdk/did" | ||
"github.com/goccy/go-json" | ||
"github.com/lestrrat-go/jwx/jwk" | ||
"github.com/mr-tron/base58" | ||
"github.com/multiformats/go-multibase" | ||
"github.com/multiformats/go-varint" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
// GetVerificationInformation resolves a DID and provides a kid and public key needed for data verification | ||
// it is possible that a DID has multiple verification methods, in which case a kid must be provided, otherwise | ||
// resolution will fail. | ||
func GetVerificationInformation(did didsdk.DIDDocument, maybeKID string) (kid string, pubKey crypto.PublicKey, err error) { | ||
if did.IsEmpty() { | ||
return "", nil, errors.Errorf("did doc: %+v is empty", did) | ||
} | ||
verificationMethods := did.VerificationMethod | ||
if len(verificationMethods) == 0 { | ||
return "", nil, errors.Errorf("did doc: %s has no verification methods", did.ID) | ||
} | ||
|
||
// handle the case where a kid is provided && there are multiple verification methods | ||
if len(verificationMethods) > 1 { | ||
if kid == "" { | ||
return "", nil, errors.Errorf("kid is required for did: %s, which has multiple verification methods", did.ID) | ||
} | ||
for _, method := range verificationMethods { | ||
if method.ID == kid { | ||
kid = did.ID | ||
pubKey, err = extractKeyFromVerificationMethod(verificationMethods[0]) | ||
return | ||
} | ||
} | ||
} | ||
// TODO(gabe): some DIDs, like did:key have KIDs that aren't used, so we need to know when to use a kid vs the DID | ||
kid = did.ID | ||
pubKey, err = extractKeyFromVerificationMethod(verificationMethods[0]) | ||
return | ||
} | ||
|
||
func extractKeyFromVerificationMethod(method didsdk.VerificationMethod) (pubKey crypto.PublicKey, err error) { | ||
if method.PublicKeyMultibase != "" { | ||
pubKeyBytes, multiBaseErr := multibaseToPubKeyBytes(method.PublicKeyMultibase) | ||
if multiBaseErr != nil { | ||
err = multiBaseErr | ||
return | ||
} | ||
pubKey, err = cryptosuite.PubKeyBytesToTypedKey(pubKeyBytes, method.Type) | ||
return | ||
} else if method.PublicKeyBase58 != "" { | ||
pubKeyDecoded, b58Err := base58.Decode(method.PublicKeyBase58) | ||
if b58Err != nil { | ||
err = b58Err | ||
return | ||
} | ||
pubKey, err = cryptosuite.PubKeyBytesToTypedKey(pubKeyDecoded, method.Type) | ||
return | ||
} else if method.PublicKeyJWK != nil { | ||
jwkBytes, jwkErr := json.Marshal(method.PublicKeyJWK) | ||
if err != nil { | ||
err = jwkErr | ||
return | ||
} | ||
pubKey, err = jwk.ParseKey(jwkBytes) | ||
return | ||
} | ||
err = errors.New("no public key found in verification method") | ||
return | ||
} | ||
|
||
// multibaseToPubKey converts a multibase encoded public key to public key bytes for known multibase encodings | ||
func multibaseToPubKeyBytes(mb string) ([]byte, error) { | ||
if mb == "" { | ||
err := fmt.Errorf("could not decode value: %s", mb) | ||
logrus.WithError(err).Error() | ||
return nil, err | ||
} | ||
|
||
encoding, decoded, err := multibase.Decode(mb) | ||
if err != nil { | ||
logrus.WithError(err).Error("could not decode did:key") | ||
return nil, err | ||
} | ||
if encoding != didsdk.Base58BTCMultiBase { | ||
err := fmt.Errorf("expected %d encoding but found %d", didsdk.Base58BTCMultiBase, encoding) | ||
logrus.WithError(err).Error() | ||
return nil, err | ||
} | ||
|
||
// n = # bytes for the int, which we expect to be two from our multicodec | ||
_, n, err := varint.FromUvarint(decoded) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if n != 2 { | ||
return nil, errors.New("error parsing did:key varint") | ||
} | ||
pubKeyBytes := decoded[n:] | ||
return pubKeyBytes, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -87,8 +87,15 @@ func (dr DIDRouter) CreateDIDByMethod(ctx context.Context, w http.ResponseWriter | |
} | ||
|
||
var request CreateDIDByMethodRequest | ||
invalidCreateDIDRequest := "invalid create DID request" | ||
if err := framework.Decode(r, &request); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alternativly you could do if framework.Decode(r, &request) != nil || framework.ValidateRequest(request) != nil { since its the same output (but you need to get err variable in a golang way somehow lol maybe not possible) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, unfortunately this wouldn't work because both return unique errors and we couldn't capture both. conditional error assignment would be a nice feature 🤔 |
||
errMsg := "invalid create DID request" | ||
errMsg := invalidCreateDIDRequest | ||
logrus.WithError(err).Error(errMsg) | ||
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) | ||
} | ||
|
||
if err := framework.ValidateRequest(request); err != nil { | ||
errMsg := invalidCreateDIDRequest | ||
logrus.WithError(err).Error(errMsg) | ||
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved this to its own package, since it has use outside of just cred verification