Skip to content

Commit

Permalink
refactor(#29): readme updates, key usages param changes
Browse files Browse the repository at this point in the history
  • Loading branch information
stevencedro committed Apr 13, 2024
1 parent 3934d4b commit b436eb7
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 110 deletions.
165 changes: 117 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,27 @@ This library is still in active development and all algorithms are not yet suppo

## Algorithms

When passing algorithms params into subtle functions, we use the `webcrypto.Algorithm` struct. It has the following properties:
When passing algorithm params into subtle functions, we use the `webcrypto.Algorithm` struct. It has the following properties:

| Field | Type | Description |
| :---- | :--- | :---------- |
| Name | `string` | The algorithm name. |
| Params | `any` | The algorithm parameters as defined by the parameters described by that algorithm in the WebCrypto specification. |

For each algorithm and function described below, listed are the appropriate algorithm params that need to be passed in.
See specific algorithms for the parameter types to be passed in.

### ECDSA

The **ECDSA** algorithm is the implementation of operations described in [§23](https://www.w3.org/TR/WebCryptoAPI/#ecdsa) of the W3C specification.
The **ECDSA** algorithm is the implementation of operations described in [§23](https://www.w3.org/TR/WebCryptoAPI/#ecdsa) of the W3C specification. You can import it into your program with `import "github.com/armortal/webcrypto-go/algorithms/ecdsa"`.

#### Parameter Definitions

Below are the parameters that supported ECDSA operations will take according to
[§23.2](https://www.w3.org/TR/WebCryptoAPI/#ecdsa-registration).

##### Params

As specified in [§23.1](https://www.w3.org/TR/WebCryptoAPI/#EcdsaParams-dictionary)
As specified in [§23.3](https://www.w3.org/TR/WebCryptoAPI/#EcdsaParams-dictionary)

| Field | Type | Description |
| :---- | :--- | :---------- |
Expand Down Expand Up @@ -102,15 +105,15 @@ import (
func main() {
// generate a new P-256 ECDSA key
key, err := webcrypto.Subtle().GenerateKey(
&webcrypto.Algorithm{
Name: "ECDSA",
Params: &ecdsa.KeyGenParams{
NamedCurve: "P-256",
},
}, true, []webcrypto.KeyUsage{
webcrypto.Sign,
webcrypto.Verify,
})
&webcrypto.Algorithm{
Name: "ECDSA",
Params: &ecdsa.KeyGenParams{
NamedCurve: "P-256",
},
}, true, []webcrypto.KeyUsage{
webcrypto.Sign,
webcrypto.Verify,
})
if err != nil {
panic(err)
}
Expand All @@ -134,7 +137,7 @@ func main() {
Name: "ECDSA",
Params: &ecdsa.Params{
Hash: "SHA-256",
}
},
}, ckp.PublicKey(), sig, []byte("test"))
if err != nil {
panic(err)
Expand All @@ -145,7 +148,7 @@ func main() {
}

// export the public/private key as webcrypto.JsonWebKey
out, err := webcrypto.Subtle().ExportKey(webcrypto.JWK, ckp.PrivateKey())
out, err := webcrypto.Subtle().ExportKey(webcrypto.Jwk, ckp.PrivateKey())
if err != nil {
panic(err)
}
Expand All @@ -154,26 +157,51 @@ func main() {

// do something with jwk

// import a public/private key and return webcrypto.CryptoKey
ck, err := webcrypto.Subtle().ImportKey(webcrypto.JWK, jwk, &webcrypto.Algorithm{
// import a public/private key
ck, err := webcrypto.Subtle().ImportKey(webcrypto.Jwk, jwk, &webcrypto.Algorithm{
Name: "ECDSA",
Params: &ecdsa.KeyImportParams{
NamedCurve: "P-256",
},
}, true, []webcrypto.KeyUsages{
}, true, []webcrypto.KeyUsage{
webcrypto.Sign,
})
if err != nil {
panic(err)
}

// do something with the imported key
// do something with the imported webcrypto.CryptoKey
}
```

### HMAC

The **HMAC** algorithm is the implementation of operations described in [§29](https://www.w3.org/TR/WebCryptoAPI/#hmac) of the W3C specification.
The **HMAC** algorithm is the implementation of operations described in [§29](https://www.w3.org/TR/WebCryptoAPI/#hmac) of the W3C specification. You can import it into your program with `import "github.com/armortal/webcrypto-go/algorithms/hmac"`.

#### Parameter Definitions

Below are the parameters that supported HMAC operations will take according to
[§29.2](https://www.w3.org/TR/WebCryptoAPI/#hmac-registration).

##### KeyGenParams

As specified in [§29.5](https://www.w3.org/TR/WebCryptoAPI/#hmac-keygen-params)

| Field | Type | Description |
| :---- | :--- | :---------- |
| Hash | `string` | The inner hash function to use. See the supported [hash algorithms](#hash-algorithms). |
| Length | `uint64` | The length (in bits) of the key to generate. If unspecified, the recommended length will be used, which is the size of the associated hash function's block size. |

###### ImportParams

As specified in [§29.3](https://www.w3.org/TR/WebCryptoAPI/#hmac-importparams)

| Field | Type | Description |
| :---- | :--- | :---------- |
| Hash | `string` | The inner hash function to use. See the supported [hash algorithms](#hash-algorithms). |
| Length | `uint64` | The length (in bits) of the key. |

#### Examples

```go
package main
Expand All @@ -184,61 +212,102 @@ import (
)

func main() {
// Generate a new key. A *hmac.CryptoKey is returned which implements webcrypto.CryptoKey
// generate a new key
key, err := webcrypto.Subtle().GenerateKey(
&Algorithm{
Name: "ECDSA",
Params: ecdsa.KeyGenParams{

}
}
&hmac.Algorithm{
KeyGenParams: &hmac.KeyGenParams{
&webcrypto.Algorithm{
Name: "HMAC",
Params: &hmac.KeyGenParams{
Hash: "SHA-256",
},
}, true, webcrypto.Sign, webcrypto.Verify)
}, true, []webcrypto.KeyUsage{
webcrypto.Sign,
webcrypto.Verify,
})

if err != nil {
panic(err)
}

// the generated key returns a webcrypto.CryptoKey
cryptokey := key.(webcrypto.CryptoKey)

// Sign some data. Note that this library uses io.Reader to pass bytes of data.
sig, err := webcrypto.Subtle().Sign(
&hmac.Algorithm{}, cryptokey, bytes.NewReader([]byte("helloworld")))
// sign some data - no params required.
sig, err := webcrypto.Subtle().Sign(&webcrypto.Algorithm{
Name: "HMAC",
}, cryptokey, []byte("test"))

// Verify the signature
ok, err := webcrypto.Subtle().Verify(
&hmac.Algorithm{}, cryptokey, sig, bytes.NewReader([]byte("helloworld")))
if err != nil {
panic(err)
}

// Export the key as *webcrypto.JsonWebKey
// verify the signature
ok, err := webcrypto.Subtle().Verify(&webcrypto.Algorithm{
Name: "HMAC",
}, cryptokey, sig, []byte("test"))

if err != nil {
panic(err)
}

// export the key as *webcrypto.JsonWebKey
out, err := webcrypto.Subtle().ExportKey(webcrypto.Jwk, cryptoKey)
if err != nil {
panic(err)
}

jwk := out.(*webcrypto.JsonWebKey)
// do something with jwk

// export the key as raw bytes
out, err = webcrypto.Subtle().ExportKey(webcrypto.Raw, cryptoKey)
if err != nil {
panic(err)
}

// Export the key as raw bytes
out, err := webcrypto.Subtle().ExportKey(webcrypto.Raw, cryptoKey)
raw := out.([]byte)
// do something with raw bytes

// Import a JsonWebKey
// import a key from a jwk
in, err := webcrypto.Subtle().ImportKey(
webcrypto.Jwk,
jwk,
&hmac.Algorithm{
ImportParams: &hmac.ImportParams{
&webcrypto.Algorithm{
Name: "HMAC",
Params: &hmac.ImportParams{
Hash: "SHA-256",
},
},
true,
webcrypto.Sign, webcrypto.Verify)
[]webcrypto.KeyUsage{
webcrypto.Sign,
webcrypto.Verify,
})

if err != nil {
panic(err)
}

// Import a key from raw bytes
in, err := webcrypto.Subtle().ImportKey(
// import a key from raw bytes
in, err = webcrypto.Subtle().ImportKey(
webcrypto.Raw,
raw,
&hmac.Algorithm{
ImportParams: &hmac.ImportParams{
&webcrypto.Algorithm{
Name: "HMAC",
Params: &hmac.ImportParams{
Hash: "SHA-256",
},
},
true,
webcrypto.Sign, webcrypto.Verify)
[]webcrypto.KeyUsage{
webcrypto.Sign,
webcrypto.Verify,
})

if err != nil {
panic(err)
}

// do something with your imported keys
}
```

Expand Down
16 changes: 8 additions & 8 deletions algorithms/ecdsa/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (s *subtleCrypto) DeriveBits(algorithm *webcrypto.Algorithm, baseKey webcry
}

// DeriveKey is not supported.
func (s *subtleCrypto) DeriveKey(algorithm *webcrypto.Algorithm, baseKey webcrypto.CryptoKey, derivedKeyType *webcrypto.Algorithm, extractable bool, keyUsages ...webcrypto.KeyUsage) (webcrypto.CryptoKey, error) {
func (s *subtleCrypto) DeriveKey(algorithm *webcrypto.Algorithm, baseKey webcrypto.CryptoKey, derivedKeyType *webcrypto.Algorithm, extractable bool, keyUsages []webcrypto.KeyUsage) (webcrypto.CryptoKey, error) {
return nil, webcrypto.ErrMethodNotSupported()
}

Expand Down Expand Up @@ -177,7 +177,7 @@ func exportKeyJwk(key *CryptoKey) (*webcrypto.JsonWebKey, error) {

// GenerateKey generates a new CryptoKeyPair as per 'Generate Key' operation at
// §23.7 (https://www.w3.org/TR/WebCryptoAPI/#ecdsa-operations).
func (s *subtleCrypto) GenerateKey(algorithm *webcrypto.Algorithm, extractable bool, keyUsages ...webcrypto.KeyUsage) (any, error) {
func (s *subtleCrypto) GenerateKey(algorithm *webcrypto.Algorithm, extractable bool, keyUsages []webcrypto.KeyUsage) (any, error) {
nameAndParamsOrPanic[*KeyGenParams](algorithm)
params := algorithm.Params.(*KeyGenParams)

Expand Down Expand Up @@ -239,7 +239,7 @@ func (s *subtleCrypto) GenerateKey(algorithm *webcrypto.Algorithm, extractable b
}

// ImportKey is not supported.
func (s *subtleCrypto) ImportKey(format webcrypto.KeyFormat, keyData any, algorithm *webcrypto.Algorithm, extractable bool, keyUsages ...webcrypto.KeyUsage) (webcrypto.CryptoKey, error) {
func (s *subtleCrypto) ImportKey(format webcrypto.KeyFormat, keyData any, algorithm *webcrypto.Algorithm, extractable bool, keyUsages []webcrypto.KeyUsage) (webcrypto.CryptoKey, error) {
nameAndParamsOrPanic[*KeyImportParams](algorithm)
params := algorithm.Params.(*KeyImportParams)

Expand All @@ -249,13 +249,13 @@ func (s *subtleCrypto) ImportKey(format webcrypto.KeyFormat, keyData any, algori
if !ok {
return nil, webcrypto.NewError(webcrypto.ErrDataError, "keyData must be *webcrypto.JsonWebKey")
}
return importKeyJwk(jwk, params, extractable, keyUsages...)
return importKeyJwk(jwk, params, extractable, keyUsages)
case webcrypto.PKCS8:
b, ok := keyData.([]byte)
if !ok {
return nil, webcrypto.NewError(webcrypto.ErrDataError, "keyData must be []byte")
}
return importKeyPKCS8(b, params, extractable, keyUsages...)
return importKeyPKCS8(b, params, extractable, keyUsages)
default:
return nil, webcrypto.NewError(webcrypto.ErrNotSupportedError, "key format not supported")
}
Expand All @@ -267,7 +267,7 @@ func (s *subtleCrypto) ImportKey(format webcrypto.KeyFormat, keyData any, algori
// Although the specification states that we should first analyse the private key info as we construct our
// crypto key, the standard go library doesn't support access to the underlying pkcs8 struct so
// the implementation in this library will take these values from the algorithm provided in the params.
func importKeyPKCS8(keyData []byte, params *KeyImportParams, extractable bool, keyUsages ...webcrypto.KeyUsage) (*CryptoKey, error) {
func importKeyPKCS8(keyData []byte, params *KeyImportParams, extractable bool, keyUsages []webcrypto.KeyUsage) (*CryptoKey, error) {
if err := util.AreUsagesValid(
[]webcrypto.KeyUsage{webcrypto.Decrypt, webcrypto.UnwrapKey}, keyUsages); err != nil {
return nil, err
Expand Down Expand Up @@ -313,7 +313,7 @@ func importKeyPKCS8(keyData []byte, params *KeyImportParams, extractable bool, k

// importKeyJwk will import a JWK. The method of importing JWK is specified at
// §22.4 importKey (https://www.w3.org/TR/WebCryptoAPI/#rsa-oaep-operations).
func importKeyJwk(keyData *webcrypto.JsonWebKey, params *KeyImportParams, extractable bool, keyUsages ...webcrypto.KeyUsage) (*CryptoKey, error) {
func importKeyJwk(keyData *webcrypto.JsonWebKey, params *KeyImportParams, extractable bool, keyUsages []webcrypto.KeyUsage) (*CryptoKey, error) {
// If the "kty" field of jwk is not a case-sensitive string match
// to "EC", then throw a DataError.
if keyData.Kty != "EC" {
Expand Down Expand Up @@ -452,7 +452,7 @@ func (s *subtleCrypto) UnwrapKey(format webcrypto.KeyFormat,
unwrapAlgorithm *webcrypto.Algorithm,
unwrappedKeyAlgorithm *webcrypto.Algorithm,
extractable bool,
keyUsages ...webcrypto.KeyUsage) (webcrypto.CryptoKey, error) {
keyUsages []webcrypto.KeyUsage) (webcrypto.CryptoKey, error) {
return nil, webcrypto.ErrMethodNotSupported()
}

Expand Down
10 changes: 5 additions & 5 deletions algorithms/ecdsa/ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func Test_GenerateKey(t *testing.T) {
Params: &KeyGenParams{
NamedCurve: curve,
},
}, extractable, usages...)
}, extractable, usages)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -128,7 +128,7 @@ func Test_SignAndVerify(t *testing.T) {
Params: &KeyGenParams{
NamedCurve: P256,
},
}, false, webcrypto.Sign)
}, false, []webcrypto.KeyUsage{webcrypto.Sign})
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -210,7 +210,7 @@ func Test_testData(t *testing.T) {
t.Error(err)
}

k, err := subtle.ImportKey(webcrypto.Jwk, &jwk, &webcrypto.Algorithm{Name: "ECDSA", Params: &KeyImportParams{NamedCurve: P256}}, true, webcrypto.Verify)
k, err := subtle.ImportKey(webcrypto.Jwk, &jwk, &webcrypto.Algorithm{Name: "ECDSA", Params: &KeyImportParams{NamedCurve: P256}}, true, []webcrypto.KeyUsage{webcrypto.Verify})
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -240,7 +240,7 @@ func Test_ExportAndImportJsonWebKey(t *testing.T) {
Params: &KeyGenParams{
NamedCurve: P256,
},
}, true, webcrypto.Sign)
}, true, []webcrypto.KeyUsage{webcrypto.Sign})
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -304,7 +304,7 @@ func Test_ExportAndImportJsonWebKey(t *testing.T) {
}

// import the key
imp, err := subtle.ImportKey(webcrypto.Jwk, jwk, &webcrypto.Algorithm{Name: "ECDSA", Params: &KeyImportParams{NamedCurve: P256}}, true, webcrypto.Verify)
imp, err := subtle.ImportKey(webcrypto.Jwk, jwk, &webcrypto.Algorithm{Name: "ECDSA", Params: &KeyImportParams{NamedCurve: P256}}, true, []webcrypto.KeyUsage{webcrypto.Verify})
if err != nil {
t.Error(err)
}
Expand Down
Loading

0 comments on commit b436eb7

Please sign in to comment.