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

Go Mobile support for DID Key #457

Merged
merged 11 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20.7
go-version: 1.21.0

- name: Install Mage
run: go install github.com/magefile/mage
Expand All @@ -38,7 +38,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20.7
go-version: 1.21.0

- name: Install Mage
run: go install github.com/magefile/mage
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.20.7
go-version: 1.21.0
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@
.DS_Store

# IDE
.idea/
.idea/

# Mobile
*.jar
*.aar
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ When you're ready you may:

| Requirement | Tested Version | Installation Instructions |
|-------------|----------------|--------------------------------------------------------|
| Go | 1.20.7 | [go.dev](https://go.dev/doc/tutorial/compile-install) |
| Go | 1.21.0 | [go.dev](https://go.dev/doc/tutorial/compile-install) |
| Mage | 1.13.0-6 | [magefile.org](https://magefile.org/) |

### Go
Expand All @@ -23,7 +23,7 @@ You may verify your `go` installation via the terminal:

```
$> go version
go version go1.20.7 darwin/amd64
go version go1.21.0 darwin/amd64
```

If you do not have go, we recommend installing it by:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![godoc ssi-sdk](https://img.shields.io/badge/godoc-ssi--sdk-blue)](https://pkg.go.dev/github.com/TBD54566975/ssi-sdk)
[![go version 1.20.7](https://img.shields.io/badge/go_version-1.20.7-brightgreen)](https://golang.org/)
[![go version 1.21.0](https://img.shields.io/badge/go_version-1.21.0-brightgreen)](https://golang.org/)
[![Go Report Card A+](https://goreportcard.com/badge/github.com/TBD54566975/ssi-sdk)](https://goreportcard.com/report/github.com/TBD54566975/ssi-sdk)
[![license Apache 2](https://img.shields.io/badge/license-Apache%202-black)](https://github.com/TBD54566975/ssi-sdk/blob/main/LICENSE)
[![issues](https://img.shields.io/github/issues/TBD54566975/ssi-sdk)](https://github.com/TBD54566975/ssi-sdk/issues)
Expand Down
99 changes: 46 additions & 53 deletions crypto/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"crypto/rsa"
"crypto/x509"
"fmt"
"math/big"
"reflect"

"github.com/btcsuite/btcd/btcec/v2"
Expand Down Expand Up @@ -67,7 +66,8 @@ type Option struct {
}

var (
ECDSAMarshalCompressed = Option{Name: "ecdsa-compressed", Value: true}
ECDSAMarshalCompressed = Option{Name: "ecdsa-compressed", Value: true}
ECDSAUnmarshalCompressed = Option{Name: "ecdsa-compressed", Value: true}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value of both options is the same. Is that intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, one used for marshaling one for unmarshaling

I could condense to a ECDSACompressed option

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally discourage using an Option type which has a field named Name. It's bypasses the type system, and makes it easy to shoot yourself in the foot.

I would suggest using an enum es below:

const (
  ECDSAMarshalCompressed int = iota
  ECDSAUnmarshalCompressed
)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

)

// PubKeyToBytes constructs a byte representation of a public key, for a set number of supported key types
Expand All @@ -85,11 +85,25 @@ func PubKeyToBytes(key crypto.PublicKey, opts ...Option) ([]byte, error) {
case secp.PublicKey:
return k.SerializeCompressed(), nil
case ecdsa.PublicKey:
curve := k.Curve
if len(opts) == 1 && opts[0].Name == "ecdsa-compressed" && opts[0].Value.(bool) {
if k.Curve == btcec.S256() {
x := new(btcec.FieldVal)
x.SetByteSlice(k.X.Bytes())
y := new(btcec.FieldVal)
y.SetByteSlice(k.Y.Bytes())
return btcec.NewPublicKey(x, y).SerializeCompressed(), nil
}

// check if we should marshal the key in compressed form
if len(opts) == 1 && opts[0] == ECDSAMarshalCompressed {
return elliptic.MarshalCompressed(k.Curve, k.X, k.Y), nil
}
return elliptic.Marshal(curve, k.X, k.Y), nil

// go from ecdsa public key to bytes
pk, err := x509.MarshalPKIXPublicKey(&k)
if err != nil {
return nil, err
}
return pk, nil
case rsa.PublicKey:
return x509.MarshalPKCS1PublicKey(&k), nil
case dilithium.PublicKey:
Expand All @@ -107,7 +121,7 @@ func PubKeyToBytes(key crypto.PublicKey, opts ...Option) ([]byte, error) {

// BytesToPubKey reconstructs a public key given some bytes and a target key type
// It is assumed the key was turned into byte form using the sibling method `PubKeyToBytes`
func BytesToPubKey(keyBytes []byte, kt KeyType) (crypto.PublicKey, error) {
func BytesToPubKey(keyBytes []byte, kt KeyType, opts ...Option) (crypto.PublicKey, error) {
switch kt {
case Ed25519:
return ed25519.PublicKey(keyBytes), nil
Expand All @@ -120,56 +134,35 @@ func BytesToPubKey(keyBytes []byte, kt KeyType) (crypto.PublicKey, error) {
}
return *pubKey, nil
case SECP256k1ECDSA:
x, y := elliptic.Unmarshal(btcec.S256(), keyBytes)
return ecdsa.PublicKey{
Curve: btcec.S256(),
X: x,
Y: y,
}, nil
case P224:
var x, y *big.Int
x, y = elliptic.Unmarshal(elliptic.P224(), keyBytes)
if x == nil || y == nil {
x, y = elliptic.UnmarshalCompressed(elliptic.P224(), keyBytes)
}
return ecdsa.PublicKey{
Curve: elliptic.P224(),
X: x,
Y: y,
}, nil
case P256:
var x, y *big.Int
x, y = elliptic.Unmarshal(elliptic.P256(), keyBytes)
if x == nil || y == nil {
x, y = elliptic.UnmarshalCompressed(elliptic.P256(), keyBytes)
pk, err := secp.ParsePubKey(keyBytes)
if err != nil {
return nil, err
}
return ecdsa.PublicKey{
Curve: elliptic.P256(),
X: x,
Y: y,
}, nil
case P384:
var x, y *big.Int
x, y = elliptic.Unmarshal(elliptic.P384(), keyBytes)
if x == nil || y == nil {
x, y = elliptic.UnmarshalCompressed(elliptic.P384(), keyBytes)
return *pk.ToECDSA(), nil
case P224, P256, P384, P521:
// check if we should unmarshal the key in compressed form
if len(opts) == 1 && opts[0] == ECDSAUnmarshalCompressed {
switch kt {
case P224:
x, y := elliptic.UnmarshalCompressed(elliptic.P224(), keyBytes)
return ecdsa.PublicKey{Curve: elliptic.P224(), X: x, Y: y}, nil
case P256:
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), keyBytes)
return ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}, nil
case P384:
x, y := elliptic.UnmarshalCompressed(elliptic.P384(), keyBytes)
return ecdsa.PublicKey{Curve: elliptic.P384(), X: x, Y: y}, nil
case P521:
x, y := elliptic.UnmarshalCompressed(elliptic.P521(), keyBytes)
return ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y}, nil
}
}
return ecdsa.PublicKey{
Curve: elliptic.P384(),
X: x,
Y: y,
}, nil
case P521:
var x, y *big.Int
x, y = elliptic.Unmarshal(elliptic.P521(), keyBytes)
if x == nil || y == nil {
x, y = elliptic.UnmarshalCompressed(elliptic.P521(), keyBytes)

key, err := x509.ParsePKIXPublicKey(keyBytes)
if err != nil {
return nil, err
}
return ecdsa.PublicKey{
Curve: elliptic.P521(),
X: x,
Y: y,
}, nil
return *key.(*ecdsa.PublicKey), nil
case RSA:
pubKey, err := x509.ParsePKCS1PublicKey(keyBytes)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion did/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func GenerateDIDKey(kt crypto.KeyType) (gocrypto.PrivateKey, *DIDKey, error) {
return nil, nil, errors.Wrap(err, "could not generate key for did:key")
}

pubKeyBytes, err := crypto.PubKeyToBytes(pubKey)
pubKeyBytes, err := crypto.PubKeyToBytes(pubKey, crypto.ECDSAMarshalCompressed)
if err != nil {
return nil, nil, errors.Wrap(err, "could not convert public key to byte")
}
Expand Down
3 changes: 2 additions & 1 deletion did/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ func DecodeMultibasePublicKeyWithType(data []byte) ([]byte, cryptosuite.LDKeyTyp

// ConstructJWKVerificationMethod builds a DID verification method with a known LD key type as a JWK
func ConstructJWKVerificationMethod(id, controller string, pubKeyBytes []byte, cryptoKeyType crypto.KeyType) (*VerificationMethod, error) {
pubKey, err := crypto.BytesToPubKey(pubKeyBytes, cryptoKeyType)
// TODO(gabe): consider exposing compression as an option instead of a default
pubKey, err := crypto.BytesToPubKey(pubKeyBytes, cryptoKeyType, crypto.ECDSAUnmarshalCompressed)
if err != nil {
return nil, errors.Wrap(err, "converting bytes to public key")
}
Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/TBD54566975/ssi-sdk

go 1.19
go 1.21

require (
github.com/bits-and-blooms/bitset v1.8.0
Expand Down Expand Up @@ -59,8 +59,11 @@ require (
github.com/segmentio/asm v1.2.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/mobile v0.0.0-20230818142238-7088062f872d // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.1.6 // indirect
)
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,20 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/mobile v0.0.0-20230818142238-7088062f872d h1:Ouem7YgI783/xoG5NZUHbg/ggHFOutUUoq1ZRlCCTbM=
golang.org/x/mobile v0.0.0-20230818142238-7088062f872d/go.mod h1:kQNMt2gXlYXNazoSeytBi7knmDN7YS/JzMKFYxgoNxc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -165,6 +170,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60 h1:o4bs4seAAlSiZQAZbO6/RP5XBCZCooQS3Pgc0AUjWts=
golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
Expand Down
9 changes: 6 additions & 3 deletions go.work
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
go 1.20
go 1.21

toolchain go1.21.0

use (
./sd-jwt
.
)
./sd-jwt
./mobile
)
7 changes: 7 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -238,16 +238,23 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
34 changes: 33 additions & 1 deletion magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import (
)

const (
Go = "go"
Go = "go"
gomobile = "gomobile"
)

// Build builds the library.
Expand Down Expand Up @@ -305,3 +306,34 @@ func Vuln() error {
func installGoVulnIfNotPresent() error {
return installIfNotPresent("govulncheck", "golang.org/x/vuln/cmd/govulncheck@latest")
}

func installGoMobileIfNotPresent() error {
return installIfNotPresent(gomobile, "golang.org/x/mobile/cmd/gomobile@latest")
}

// IOS Generates the iOS packages
// Note: this command also installs "gomobile" if not present
func IOS() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this target, as well as the Android target, be documented in a README?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes good call

if err := installGoMobileIfNotPresent(); err != nil {
logrus.WithError(err).Fatal("Error installing gomobile")
return err
}

println("Building iOS...")
bindIOS := sh.RunCmd(gomobile, "bind", "-target", "ios", "-tags", "jwx_es256k")
return bindIOS("./mobile")
}

// Android Generates the Android packages
// Note: this command also installs "gomobile" if not present
func Android() error {
if err := installGoMobileIfNotPresent(); err != nil {
logrus.WithError(err).Fatal("Error installing gomobile")
return err
}

apiLevel := "33"
println("Building Android - API Level: " + apiLevel + "...")
bindAndroid := sh.RunCmd("gomobile", "bind", "-target", "android", "-androidapi", "33", "-tags", "jwx_es256k")
return bindAndroid("./mobile")
}
Loading