Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
doc: Add SD-JWT Docs
Browse files Browse the repository at this point in the history
Add SD-JST Docs

Signed-off-by: Sandra Vrtikapa <sandra.vrtikapa@securekey.com>
  • Loading branch information
sandrask committed Mar 2, 2023
1 parent a948231 commit 5869aa2
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 10 deletions.
1 change: 1 addition & 0 deletions pkg/doc/sdjwt/holder/holder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

// Package holder enables the Holder: an entity that receives SD-JWTs from the Issuer and has control over them.
package holder

import (
Expand Down
78 changes: 69 additions & 9 deletions pkg/doc/sdjwt/issuer/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@ Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

/*
Package issuer enables the Issuer: An entity that creates SD-JWTs.
An SD-JWT is a digitally signed document containing digests over the claims
(per claim: a random salt, the claim name and the claim value).
It MAY further contain clear-text claims that are always disclosed to the Verifier.
It MUST be digitally signed using the Issuer's private key.
SD-JWT-DOC = (METADATA, SD-CLAIMS, NON-SD-CLAIMS)
SD-JWT = SD-JWT-DOC | SIG(SD-JWT-DOC, ISSUER-PRIV-KEY)
SD-CLAIMS is an array of digest values that ensure the integrity of
and map to the respective Disclosures. Digest values are calculated
over the Disclosures, each of which contains the claim name (CLAIM-NAME),
the claim value (CLAIM-VALUE), and a random salt (SALT).
Digests are calculated using a hash function:
SD-CLAIMS = (
HASH(SALT, CLAIM-NAME, CLAIM-VALUE)
)*
SD-CLAIMS can also be nested deeper to capture more complex objects.
The Issuer further creates a set of Disclosures for all claims in the
SD-JWT. The Disclosures are sent to the Holder together with the SD-JWT:
DISCLOSURES = (
(SALT, CLAIM-NAME, CLAIM-VALUE)
)*
The SD-JWT and the Disclosures are sent to the Holder by the Issuer:
COMBINED-ISSUANCE = SD-JWT | DISCLOSURES
*/
package issuer

import (
Expand Down Expand Up @@ -76,63 +110,70 @@ func WithJSONMarshaller(jsonMarshal func(v interface{}) ([]byte, error)) NewOpt
}
}

// WithSaltFnc is option is for marshalling disclosure.
// WithSaltFnc is an option for generating salt. Mostly used for testing.
// A new salt MUST be chosen for each claim independently of other salts.
// The RECOMMENDED minimum length of the randomly-generated portion of the salt is 128 bits.
// It is RECOMMENDED to base64url-encode the salt value, producing a string.
func WithSaltFnc(fnc func() (string, error)) NewOpt {
return func(opts *newOpts) {
opts.getSalt = fnc
}
}

// WithIssuedAt is an option for SD-JWT payload.
// WithIssuedAt is an option for SD-JWT payload. This is a clear-text claim that is always disclosed.
func WithIssuedAt(issuedAt *jwt.NumericDate) NewOpt {
return func(opts *newOpts) {
opts.IssuedAt = issuedAt
}
}

// WithAudience is an option for SD-JWT payload.
// WithAudience is an option for SD-JWT payload. This is a clear-text claim that is always disclosed.
func WithAudience(audience string) NewOpt {
return func(opts *newOpts) {
opts.Audience = audience
}
}

// WithExpiry is an option for SD-JWT payload.
// WithExpiry is an option for SD-JWT payload. This is a clear-text claim that is always disclosed.
func WithExpiry(expiry *jwt.NumericDate) NewOpt {
return func(opts *newOpts) {
opts.Expiry = expiry
}
}

// WithNotBefore is an option for SD-JWT payload.
// WithNotBefore is an option for SD-JWT payload. This is a clear-text claim that is always disclosed.
func WithNotBefore(notBefore *jwt.NumericDate) NewOpt {
return func(opts *newOpts) {
opts.NotBefore = notBefore
}
}

// WithSubject is an option for SD-JWT payload.
// WithSubject is an option for SD-JWT payload. This is a clear-text claim that is always disclosed.
func WithSubject(subject string) NewOpt {
return func(opts *newOpts) {
opts.Subject = subject
}
}

// WithJTI is an option for SD-JWT payload.
// WithJTI is an option for SD-JWT payload. This is a clear-text claim that is always disclosed.
func WithJTI(jti string) NewOpt {
return func(opts *newOpts) {
opts.JTI = jti
}
}

// WithID is an option for SD-JWT payload.
// WithID is an option for SD-JWT payload. This is a clear-text claim that is always disclosed.
func WithID(id string) NewOpt {
return func(opts *newOpts) {
opts.ID = id
}
}

// WithHolderPublicKey is an option for SD-JWT payload.
// The Holder can prove legitimate possession of an SD-JWT by proving control over the same private key during
// the issuance and presentation. An SD-JWT with Holder Binding contains a public key or a reference to a public key
// that matches to the private key controlled by the Holder.
// The "cnf" claim value MUST represent only a single proof-of-possession key. This implementation is using CNF "jwk".
func WithHolderPublicKey(jwk *jwk.JWK) NewOpt {
return func(opts *newOpts) {
opts.HolderPublicKey = jwk
Expand Down Expand Up @@ -181,6 +222,15 @@ func WithNonSelectivelyDisclosableClaims(nonSDClaims []string) NewOpt {
}

// New creates new signed Selective Disclosure JWT based on input claims.
// The Issuer MUST create a Disclosure for each selectively disclosable claim as follows:
// Create an array of three elements in this order:
//
// A salt value. Generated by the system, the salt value MUST be unique for each claim that is to be selectively disclosed.
// The claim name, or key, as it would be used in a regular JWT body. This MUST be a string.
// The claim's value, as it would be used in a regular JWT body. The value MAY be of any type that is allowed in JSON, including numbers, strings, booleans, arrays, and objects.
//
// Then JSON-encode the array such that an UTF-8 string is produced.
// Then base64url-encode the byte representation of the UTF-8 string to create the Disclosure.
func New(issuer string, claims interface{}, headers jose.Headers,
signer jose.Signer, opts ...NewOpt) (*SelectiveDisclosureJWT, error) {
nOpts := &newOpts{
Expand Down Expand Up @@ -223,7 +273,17 @@ func New(issuer string, claims interface{}, headers jose.Headers,
return &SelectiveDisclosureJWT{Disclosures: disclosures, SignedJWT: signedJWT}, nil
}

// NewFromVC creates new signed Selective Disclosure JWT based on vc.
/*
NewFromVC creates new signed Selective Disclosure JWT based on Verifiable Credential.
Algorithm:
- extract credential subject map from verifiable credential
- create un-signed SD-JWT plus Disclosures with credential subject map
- decode claims from SD-JWT to get credential subject map with selective disclosures
- replace VC credential subject with newly created credential subject with selective disclosures
- create signed SD-JWT based on VC
- return signed SD-JWT plus Disclosures
*/
func NewFromVC(vc map[string]interface{}, headers jose.Headers,
signer jose.Signer, opts ...NewOpt) (*SelectiveDisclosureJWT, error) {
csObj, ok := common.GetKeyFromVC(credentialSubjectKey, vc)
Expand Down
36 changes: 36 additions & 0 deletions pkg/doc/sdjwt/sdjwt-doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

// Package sdjwt implements creating JSON Web Token (JWT) documents that support selective disclosure of JWT claims.
//
// In an SD-JWT, claims can be hidden, but cryptographically protected against undetected modification.
//
// When issuing the SD-JWT to the Holder, the Issuer also sends the cleartext counterparts of all hidden claims,
// the so-called Disclosures, separate from the SD-JWT itself.
//
// The Holder decides which claims to disclose to a Verifier and forwards the respective Disclosures
// together with the SD-JWT to the Verifier.
//
// The Verifier has to verify that all disclosed claim values were part of the original, Issuer-signed SD-JWT.
// The Verifier will not, however, learn any claim values not disclosed in the Disclosures.
//
// This implementation supports:
//
// - selectively disclosable claims in flat data structures as well as more complex, nested data structures
//
// - combining selectively disclosable claims with clear-text claims that are always disclosed
//
// - options for specifying registered claim names that will be included in plaintext (e.g. iss, exp, or nbf)
//
// - option for configuring clear-text claims
//
// For selectively disclosable claims, claim names are always blinded.
//
// This implementation also supports an optional mechanism for Holder Binding,
// the concept of binding an SD-JWT to key material controlled by the Holder.
// The strength of the Holder Binding is conditional upon the trust in the protection
// of the private key of the key pair an SD-JWT is bound to.
package sdjwt
8 changes: 7 additions & 1 deletion pkg/doc/sdjwt/verifier/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

/*
Package verifier enables the Verifier: An entity that requests, checks and
extracts the claims from an SD-JWT and respective Disclosures.
*/
package verifier

import (
Expand Down Expand Up @@ -45,7 +49,7 @@ func WithJWTDetachedPayload(payload []byte) ParseOpt {
}
}

// WithSignatureVerifier option is for definition of JWT detached payload.
// WithSignatureVerifier option is for definition of signature verifier.
func WithSignatureVerifier(signatureVerifier jose.SignatureVerifier) ParseOpt {
return func(opts *parseOpts) {
opts.sigVerifier = signatureVerifier
Expand Down Expand Up @@ -95,6 +99,8 @@ func WithLeewayForClaimsValidation(duration time.Duration) ParseOpt {
}

// Parse parses combined format for presentation and returns verified claims.
// The Verifier has to verify that all disclosed claim values were part of the original, Issuer-signed SD-JWT.
// The Verifier will not, however, learn any claim values not disclosed in the Disclosures.
func Parse(combinedFormatForPresentation string, opts ...ParseOpt) (map[string]interface{}, error) {
defaultSigningAlgorithms := []string{"EdDSA", "RS256"}
pOpts := &parseOpts{
Expand Down

0 comments on commit 5869aa2

Please sign in to comment.