Skip to content

Commit

Permalink
feat: Sign credential - support BBS+ (API) (hyperledger-archives#2601)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrii Soluk <[email protected]>
  • Loading branch information
soluchok authored and sudeshrshetty committed Jan 22, 2022
1 parent a23bd73 commit 2c59cca
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 42 deletions.
157 changes: 155 additions & 2 deletions pkg/controller/command/verifiable/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import (
"io"
"strings"

"github.com/piprate/json-gold/ld"

"github.com/hyperledger/aries-framework-go/pkg/common/log"
"github.com/hyperledger/aries-framework-go/pkg/controller/command"
"github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil"
ariescrypto "github.com/hyperledger/aries-framework-go/pkg/crypto"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
verifiablesigner "github.com/hyperledger/aries-framework-go/pkg/doc/signature/signer"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/bbsblssignature2020"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/jsonwebsignature2020"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
Expand Down Expand Up @@ -123,6 +127,9 @@ const (
// JSONWebSignature2020 json web signature suite.
JSONWebSignature2020 = "JsonWebSignature2020"

// BbsBlsSignature2020 BBS signature suite.
BbsBlsSignature2020 = "BbsBlsSignature2020"

// Ed25519KeyType ed25519 key type.
Ed25519KeyType = "Ed25519"

Expand All @@ -133,6 +140,8 @@ const (
Ed25519VerificationKey = "Ed25519VerificationKey"
)

const bbsContext = "https://w3id.org/security/bbs/v1"

type provable interface {
AddLinkedDataProof(context *verifiable.LinkedDataProofContext, jsonldOpts ...jsonld.ProcessorOpts) error
}
Expand All @@ -144,6 +153,7 @@ type keyResolver interface {
type kmsSigner struct {
keyHandle interface{}
crypto ariescrypto.Crypto
bbs bool
}

func newKMSSigner(keyManager kms.KeyManager, c ariescrypto.Crypto, creator string) (*kmsSigner, error) {
Expand All @@ -161,7 +171,24 @@ func newKMSSigner(keyManager kms.KeyManager, c ariescrypto.Crypto, creator strin
return &kmsSigner{keyHandle: keyHandler, crypto: c}, nil
}

func (s *kmsSigner) textToLines(txt string) [][]byte {
lines := strings.Split(txt, "\n")
linesBytes := make([][]byte, 0, len(lines))

for i := range lines {
if strings.TrimSpace(lines[i]) != "" {
linesBytes = append(linesBytes, []byte(lines[i]))
}
}

return linesBytes
}

func (s *kmsSigner) Sign(data []byte) ([]byte, error) {
if s.bbs {
return s.crypto.SignMulti(s.textToLines(string(data)), s.keyHandle)
}

v, err := s.crypto.Sign(data, s.keyHandle)
if err != nil {
return nil, err
Expand Down Expand Up @@ -832,13 +859,22 @@ func (o *Command) addLinkedDataProof(p provable, opts *ProofOptions) error {
signatureSuite = ed25519signature2018.New(suite.WithSigner(s))
case JSONWebSignature2020:
signatureSuite = jsonwebsignature2020.New(suite.WithSigner(s))
case BbsBlsSignature2020:
s.bbs = true
signatureSuite = bbsblssignature2020.New(suite.WithSigner(s))
default:
return fmt.Errorf("signature type unsupported %s", opts.SignatureType)
}

signatureRepresentation := verifiable.SignatureJWS

if opts.SignatureRepresentation == nil {
opts.SignatureRepresentation = &signatureRepresentation
}

signingCtx := &verifiable.LinkedDataProofContext{
VerificationMethod: opts.VerificationMethod,
SignatureRepresentation: verifiable.SignatureJWS,
SignatureRepresentation: *opts.SignatureRepresentation,
SignatureType: opts.SignatureType,
Suite: signatureSuite,
Created: opts.Created,
Expand All @@ -847,7 +883,12 @@ func (o *Command) addLinkedDataProof(p provable, opts *ProofOptions) error {
Purpose: opts.proofPurpose,
}

err = p.AddLinkedDataProof(signingCtx)
bbsLoader, err := bbsJSONLDDocumentLoader()
if err != nil {
return err
}

err = p.AddLinkedDataProof(signingCtx, jsonld.WithDocumentLoader(bbsLoader))
if err != nil {
return fmt.Errorf("failed to add linked data proof: %w", err)
}
Expand Down Expand Up @@ -1038,3 +1079,115 @@ func getProofPurpose(method did.VerificationRelationship) (string, error) {

return "assertionMethod", nil
}

// TODO: context should not be loaded here, the loader should be defined once for the whole system.
func bbsJSONLDDocumentLoader() (*ld.CachingDocumentLoader, error) {
loader := presexch.CachingJSONLDLoader()

reader, err := ld.DocumentFromReader(strings.NewReader(contextBBSContent))
if err != nil {
return nil, err
}

loader.AddDocument(bbsContext, reader)

return loader, nil
}

const contextBBSContent = `{
"@context": {
"@version": 1.1,
"id": "@id",
"type": "@type",
"ldssk": "https://w3id.org/security#",
"BbsBlsSignature2020": {
"@id": "https://w3id.org/security#BbsBlsSignature2020",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"proofValue": "sec:proofValue",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"BbsBlsSignatureProof2020": {
"@id": "https://w3id.org/security#BbsBlsSignatureProof2020",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"Bls12381G2Key2020": "ldssk:Bls12381G2Key2020"
}
}`
74 changes: 71 additions & 3 deletions pkg/controller/command/verifiable/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,35 @@ const vc = `
}
`

const bbsVc = `{
"@context":[
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://w3id.org/security/bbs/v1"
],
"credentialSubject":{
"degree":{
"degree":"MIT",
"degreeSchool":"MIT school",
"type":"BachelorDegree"
},
"id":"did:example:b34ca6cd37bbf23",
"name":"Jayden Doe",
"spouse":"did:example:c276e12ec21ebfeb1f712ebc6f1"
},
"description":"Government of Example Permanent Resident Card.",
"expirationDate":"2022-03-04T11:53:29.728412319+02:00",
"id":"https://issuer.oidp.uscis.gov/credentials/83627465",
"identifier":"83627465",
"issuanceDate":"2021-03-04T11:53:29.728412269+02:00",
"issuer":"did:example:489398593",
"name":"Permanent Resident Card",
"type":[
"VerifiableCredential",
"UniversityDegreeCredential"
]
}`

//nolint:lll
const vcWithDIDNotAvailble = `{
"@context":[
Expand Down Expand Up @@ -2073,6 +2102,47 @@ func TestCommand_SignCredential(t *testing.T) {
require.Contains(t, vc.Proofs[0]["type"], "JsonWebSignature2020")
})

t.Run("test sign credential with proof options - success (BbsBlsSignature2020)", func(t *testing.T) {
createdTime := time.Now().AddDate(-1, 0, 0)
signatureRepresentation := verifiable.SignatureProofValue

req := SignCredentialRequest{
Credential: []byte(bbsVc),
ProofOptions: &ProofOptions{
Domain: "issuer.example.com",
Challenge: "sample-random-test-value",
SignatureRepresentation: &signatureRepresentation,
Created: &createdTime,
SignatureType: BbsBlsSignature2020,
},
}

reqBytes, err := json.Marshal(req)
require.NoError(t, err)

var b bytes.Buffer
err = cmd.SignCredential(&b, bytes.NewBuffer(reqBytes))
require.NoError(t, err)

// verify response
var response SignCredentialResponse
err = json.NewDecoder(&b).Decode(&response)
require.NoError(t, err)
require.NotEmpty(t, response)

vc, err := verifiable.ParseCredential(response.VerifiableCredential, verifiable.WithDisabledProofCheck())

require.NoError(t, err)
require.NotNil(t, vc)
require.NotEmpty(t, vc.Proofs)
require.Len(t, vc.Proofs, 1)
require.Equal(t, vc.Proofs[0]["challenge"], req.Challenge)
require.Equal(t, vc.Proofs[0]["domain"], req.Domain)
require.Equal(t, vc.Proofs[0]["proofPurpose"], "assertionMethod")
require.Contains(t, vc.Proofs[0]["created"], strconv.Itoa(req.Created.Year()))
require.Contains(t, vc.Proofs[0]["type"], "BbsBlsSignature2020")
})

t.Run("test sign credential with proof options - success (ed25519 jsonwebsignature)", func(t *testing.T) {
createdTime := time.Now().AddDate(-1, 0, 0)
req := SignCredentialRequest{
Expand Down Expand Up @@ -2556,9 +2626,7 @@ func signVCWithBBS(r *require.Assertions, vc *verifiable.Credential) string {
pubKeyBytes, err := pubKey.Marshal()
r.NoError(err)

methodID := fingerprint.KeyFingerprint(0xeb, pubKeyBytes)
didKey := fmt.Sprintf("did:key:%s", methodID)
keyID := fmt.Sprintf("%s#%s", didKey, methodID)
didKey, keyID := fingerprint.CreateDIDKeyByCode(fingerprint.BLS12381g2PubKeyMultiCodec, pubKeyBytes)

bbsSigner, err := newBBSSigner(privKey)
r.NoError(err)
Expand Down
4 changes: 3 additions & 1 deletion pkg/controller/command/verifiable/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"encoding/json"
"time"

docverifiable "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"github.com/hyperledger/aries-framework-go/pkg/store/verifiable"
)

Expand Down Expand Up @@ -40,7 +41,8 @@ type IDArg struct {
// ProofOptions is model to allow the dynamic proofing options by the user.
type ProofOptions struct {
// VerificationMethod is the URI of the verificationMethod used for the proof.
VerificationMethod string `json:"verificationMethod,omitempty"`
VerificationMethod string `json:"verificationMethod,omitempty"`
SignatureRepresentation *docverifiable.SignatureRepresentation `json:"signatureRepresentation,omitempty"`
// Created date of the proof. If omitted current system time will be used.
Created *time.Time `json:"created,omitempty"`
// Domain is operational domain of a digital proof.
Expand Down
4 changes: 1 addition & 3 deletions pkg/controller/rest/verifiable/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1400,9 +1400,7 @@ func signVCWithBBS(r *require.Assertions, vc *verifiableapi.Credential) string {
pubKeyBytes, err := pubKey.Marshal()
r.NoError(err)

methodID := fingerprint.KeyFingerprint(0xeb, pubKeyBytes)
didKey := fmt.Sprintf("did:key:%s", methodID)
keyID := fmt.Sprintf("%s#%s", didKey, methodID)
didKey, keyID := fingerprint.CreateDIDKeyByCode(fingerprint.BLS12381g2PubKeyMultiCodec, pubKeyBytes)

bbsSigner, err := newBBSSigner(privKey)
r.NoError(err)
Expand Down
12 changes: 3 additions & 9 deletions pkg/didcomm/protocol/middleware/presentproof/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ func defaultPdOptions() *pdOptions {

// AddBBSProofFn add BBS+ proof to the Presentation.
func AddBBSProofFn(p Provider) func(presentation *verifiable.Presentation) error {
const bls12381g2pub = 0xeb

km, cr := p.KMS(), p.Crypto()

return func(presentation *verifiable.Presentation) error {
Expand All @@ -153,19 +151,15 @@ func AddBBSProofFn(p Provider) func(presentation *verifiable.Presentation) error
return err
}

methodID := fingerprint.KeyFingerprint(bls12381g2pub, pubKey)
didKey := fmt.Sprintf("%s#%s", fmt.Sprintf("did:key:%s", methodID), methodID)
_, didKey := fingerprint.CreateDIDKeyByCode(fingerprint.BLS12381g2PubKeyMultiCodec, pubKey)

presentation.Context = append(presentation.Context, bbsContext)

return presentation.AddLinkedDataProof(&verifiable.LinkedDataProofContext{
SignatureType: "BbsBlsSignature2020",
SignatureRepresentation: verifiable.SignatureProofValue,
Suite: bbsblssignature2020.New(
suite.WithSigner(newBBSSigner(km, cr, kid)),
suite.WithVerifier(bbsblssignature2020.NewG2PublicKeyVerifier()),
),
VerificationMethod: didKey,
Suite: bbsblssignature2020.New(suite.WithSigner(newBBSSigner(km, cr, kid))),
VerificationMethod: didKey,
}, jsonld.WithDocumentLoader(bbsLoader))
}
}
Expand Down
Loading

0 comments on commit 2c59cca

Please sign in to comment.