Skip to content

Commit

Permalink
feat: BBS+ Signatures 2020 verifier
Browse files Browse the repository at this point in the history
Signed-off-by: Dmitriy Kinoshenko <dkinoshenko@gmail.com>

hyperledger-archives#1725
  • Loading branch information
kdimak committed Oct 5, 2020
1 parent 47a2ea3 commit f3aed05
Show file tree
Hide file tree
Showing 2 changed files with 376 additions and 0 deletions.
217 changes: 217 additions & 0 deletions pkg/doc/signature/suite/bbssignature2020/bbs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package bbssignature2020

import (
"encoding/base64"
"net/http"
"strings"
"testing"

"github.com/btcsuite/btcutil/base58"
"github.com/piprate/json-gold/ld"
"github.com/square/go-jose/v3/json"
"github.com/stretchr/testify/require"

"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
)

func TestBBS(t *testing.T) {
proofMap := map[string]interface{}{
"@context": "https://w3id.org/security/v2",
"type": "https://w3c-ccg.github.io/ldp-bbs2020/context/v1#BbsBlsSignature2020",
"created": "2020-10-05T15:37:27Z",
"verificationMethod": "did:example:489398593#test",
"proofPurpose": "assertionMethod",
}

processor := jsonld.NewProcessor("URDNA2015")

withDocumentLoader := jsonld.WithDocumentLoader(createLDPBBS2020DocumentLoader())

proofCanonical, err := processor.GetCanonicalDocument(proofMap, withDocumentLoader)
require.NoError(t, err)

printCanonical(t, proofCanonical)

var vcMap map[string]interface{}

vcJSON := `
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/citizenship/v1",
"https://w3c-ccg.github.io/ldp-bbs2020/context/v1"
],
"id": "https://issuer.oidp.uscis.gov/credentials/83627465",
"type": [
"VerifiableCredential",
"PermanentResidentCard"
],
"issuer": "did:example:489398593",
"identifier": "83627465",
"name": "Permanent Resident Card",
"description": "Government of Example Permanent Resident Card.",
"issuanceDate": "2019-12-03T12:19:52Z",
"expirationDate": "2029-12-03T12:19:52Z",
"credentialSubject": {
"id": "did:example:b34ca6cd37bbf23",
"type": [
"PermanentResident",
"Person"
],
"givenName": "JOHN",
"familyName": "SMITH",
"gender": "Male",
"image": "data:image/png;base64,iVBORw0KGgokJggg==",
"residentSince": "2015-01-01",
"lprCategory": "C09",
"lprNumber": "999-999-999",
"commuterClassification": "C1",
"birthCountry": "Bahamas",
"birthDate": "1958-07-17"
}
}
`

err = json.Unmarshal([]byte(vcJSON), &vcMap)
require.NoError(t, err)

vcMapCompacted, err := processor.Compact(vcMap, getSecurityContextMap(), withDocumentLoader)
require.NoError(t, err)

vcCanonical, err := processor.GetCanonicalDocument(vcMapCompacted)

require.NoError(t, err)

printCanonical(t, vcCanonical)

statements := append(getStatements(string(proofCanonical)), getStatements(string(vcCanonical))...)
t.Logf("statements: %v", statements)

verifyData := append(
statementsToBytes(getStatements(string(proofCanonical))),
statementsToBytes(getStatements(string(vcCanonical)))...,
)
t.Logf("verify data: %v", verifyData)
}

func TestProofCreation(t *testing.T) {
proofJSON := `
{
"@context": "https://w3c-ccg.github.io/ldp-bbs2020/context/v1",
"type": "BbsBlsSignature2020"
}
`

proofMap, err := toMap(proofJSON)
require.NoError(t, err)

processor := jsonld.NewProcessor("URDNA2015")
withDocumentLoader := jsonld.WithDocumentLoader(createLDPBBS2020DocumentLoader())

proofCompacted, err := processor.Compact(proofMap, map[string]interface{}{
"@context": "https://w3id.org/security/v2",
}, withDocumentLoader)
require.NoError(t, err)

proofStr, err := json.MarshalIndent(proofCompacted, "", "\t")
require.NoError(t, err)

t.Logf("proofCompacted: %s", proofStr)
}

func TestPublicKeyB58ToBase64(t *testing.T) {
pkB58 := "oqpWYKaZD9M1Kbe94BVXpr8WTdFBNZyKv48cziTiQUeuhm7sBhCABMyYG4kcMrseC68YTFFgyhiNeBKjzdKk9MiRWuLv5H4FFujQsQK2KTAtzU8qTBiZqBHMmnLF4PL7Ytu"

pkBytes := base58.Decode(pkB58)

t.Logf("pk: %s", string(pkBytes))

pkBase64 := base64.RawStdEncoding.EncodeToString(pkBytes)
t.Logf("pkBase64=%s", pkBase64)
}

func createLDPBBS2020DocumentLoader() ld.DocumentLoader {
loader := ld.NewCachingDocumentLoader(ld.NewRFC7324CachingDocumentLoader(&http.Client{}))

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

loader.AddDocument("https://w3c-ccg.github.io/ldp-bbs2020/context/v1", reader)

reader, err = ld.DocumentFromReader(strings.NewReader(securityJSONLD))
if err != nil {
panic(err)
}

loader.AddDocument("https://w3id.org/security/v2", reader)

return loader
}

func printCanonical(t *testing.T, proofCanonical []byte) {
views := strings.Split(string(proofCanonical), "\n")
for _, v := range views {
if len(v) > 0 {
t.Logf("v=%s", v)
}
}
}

func statementsToBytes(statements []string) [][]byte {
sBytes := make([][]byte, len(statements))

for i := range statements {
sBytes[i] = []byte(statements[i])
}

return sBytes
}

func getStatements(canonical string) []string {
statements := make([]string, 0)

views := strings.Split(canonical, "\n")
for _, v := range views {
if len(v) > 0 {
statements = append(statements, v)
}
}

return statements
}

func getSecurityContextMap() map[string]interface{} {
return map[string]interface{}{
"@context": "https://w3id.org/security/v2",
}
}

func toMap(v interface{}) (map[string]interface{}, error) {
var (
b []byte
err error
)

switch cv := v.(type) {
case []byte:
b = cv
case string:
b = []byte(cv)
default:
b, err = json.Marshal(v)
if err != nil {
return nil, err
}
}

var m map[string]interface{}

err = json.Unmarshal(b, &m)
if err != nil {
return nil, err
}

return m, nil
}
159 changes: 159 additions & 0 deletions pkg/doc/signature/suite/bbssignature2020/definitions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package bbssignature2020

const ldpBBS2020JSONLD = `
{
"@context": {
"@version": 1.1,
"id": "@id",
"type": "@type",
"ldssk": "https://w3c-ccg.github.io/ldp-bbs2020/context/v1#",
"BbsBlsSignature2020": {
"@id": "https://w3c-ccg.github.io/ldp-bbs2020/context/v1#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://w3c-ccg.github.io/ldp-bbs2020/context/v1#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"
}
}
`

const securityJSONLD = `
{
"@context": [{
"@version": 1.1
}, "https://w3id.org/security/v1", {
"AesKeyWrappingKey2019": "sec:AesKeyWrappingKey2019",
"DeleteKeyOperation": "sec:DeleteKeyOperation",
"DeriveSecretOperation": "sec:DeriveSecretOperation",
"Ed25519Signature2018": "sec:Ed25519Signature2018",
"Ed25519VerificationKey2018": "sec:Ed25519VerificationKey2018",
"EquihashProof2018": "sec:EquihashProof2018",
"ExportKeyOperation": "sec:ExportKeyOperation",
"GenerateKeyOperation": "sec:GenerateKeyOperation",
"KmsOperation": "sec:KmsOperation",
"RevokeKeyOperation": "sec:RevokeKeyOperation",
"RsaSignature2018": "sec:RsaSignature2018",
"RsaVerificationKey2018": "sec:RsaVerificationKey2018",
"Sha256HmacKey2019": "sec:Sha256HmacKey2019",
"SignOperation": "sec:SignOperation",
"UnwrapKeyOperation": "sec:UnwrapKeyOperation",
"VerifyOperation": "sec:VerifyOperation",
"WrapKeyOperation": "sec:WrapKeyOperation",
"X25519KeyAgreementKey2019": "sec:X25519KeyAgreementKey2019",
"allowedAction": "sec:allowedAction",
"assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"},
"authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"},
"capability": {"@id": "sec:capability", "@type": "@id"},
"capabilityAction": "sec:capabilityAction",
"capabilityChain": {"@id": "sec:capabilityChain", "@type": "@id", "@container": "@list"},
"capabilityDelegation": {"@id": "sec:capabilityDelegationMethod", "@type": "@id", "@container": "@set"},
"capabilityInvocation": {"@id": "sec:capabilityInvocationMethod", "@type": "@id", "@container": "@set"},
"caveat": {"@id": "sec:caveat", "@type": "@id", "@container": "@set"},
"challenge": "sec:challenge",
"ciphertext": "sec:ciphertext",
"controller": {"@id": "sec:controller", "@type": "@id"},
"delegator": {"@id": "sec:delegator", "@type": "@id"},
"equihashParameterK": {"@id": "sec:equihashParameterK", "@type": "xsd:integer"},
"equihashParameterN": {"@id": "sec:equihashParameterN", "@type": "xsd:integer"},
"invocationTarget": {"@id": "sec:invocationTarget", "@type": "@id"},
"invoker": {"@id": "sec:invoker", "@type": "@id"},
"jws": "sec:jws",
"keyAgreement": {"@id": "sec:keyAgreementMethod", "@type": "@id", "@container": "@set"},
"kmsModule": {"@id": "sec:kmsModule"},
"parentCapability": {"@id": "sec:parentCapability", "@type": "@id"},
"plaintext": "sec:plaintext",
"proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"},
"proofPurpose": {"@id": "sec:proofPurpose", "@type": "@vocab"},
"proofValue": "sec:proofValue",
"referenceId": "sec:referenceId",
"unwrappedKey": "sec:unwrappedKey",
"verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"},
"verifyData": "sec:verifyData",
"wrappedKey": "sec:wrappedKey"
}]
}
`

0 comments on commit f3aed05

Please sign in to comment.