Skip to content

Commit 39748e7

Browse files
committed
add ecdsa support
1 parent 085cb61 commit 39748e7

9 files changed

Lines changed: 255 additions & 125 deletions

File tree

client.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type ClientOptions struct {
5959
APIVersion string
6060
APIPrefix string
6161
APIURL string
62+
Scheme string
6263
}
6364

6465
// Client represents the internal HTTP client and config used for API requests.
@@ -83,6 +84,7 @@ func NewClient() *Client {
8384
APIPrefix: APIPrefix,
8485
APIVersion: APIVersion,
8586
APIURL: APIURL,
87+
Scheme: Scheme,
8688
},
8789
mutex,
8890
}
@@ -104,6 +106,7 @@ func NewClientWithOptions(options *ClientOptions) *Client {
104106
APIPrefix: options.APIPrefix,
105107
APIVersion: options.APIVersion,
106108
APIURL: options.APIURL,
109+
Scheme: options.Scheme,
107110
},
108111
mutex,
109112
}
@@ -252,6 +255,10 @@ func (c *Client) new(ctx context.Context, method string, path string, params int
252255
req.Header.Add("Keygen-Environment", c.Environment)
253256
}
254257

258+
if c.Scheme != "" {
259+
req.Header.Add("Keygen-Accept-Signature", fmt.Sprintf("algorithm=%q", c.Scheme))
260+
}
261+
255262
req.Header.Add("Keygen-Version", c.APIVersion)
256263

257264
if in.Len() > 0 {
@@ -345,7 +352,7 @@ func (c *Client) send(req *http.Request, model interface{}) (*Response, error) {
345352
}
346353

347354
if c.PublicKey != "" {
348-
verifier := &verifier{c.PublicKey}
355+
verifier := &verifier{PublicKey: c.PublicKey, Scheme: c.Scheme}
349356

350357
if err := verifier.VerifyResponse(response); err != nil {
351358
Logger.Errorf("Error verifying response signature: id=%s status=%d size=%d body=%s err=%v", response.ID, response.Status, response.Size, response.tldr(), err)

errors.go

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -103,63 +103,65 @@ func (e *RateLimitError) Unwrap() error { return e.Err }
103103

104104
// General errors
105105
var (
106-
ErrReleaseLocationMissing = errors.New("release has no download URL")
107-
ErrUpgradeNotAvailable = errors.New("no upgrades available (already up-to-date)")
108-
ErrResponseSignatureMissing = errors.New("response signature is missing")
109-
ErrResponseSignatureInvalid = errors.New("response signature is invalid")
110-
ErrResponseDigestMissing = errors.New("response digest is missing")
111-
ErrResponseDigestInvalid = errors.New("response digest is invalid")
112-
ErrResponseDateMissing = errors.New("response date is missing")
113-
ErrResponseDateInvalid = errors.New("response date is invalid")
114-
ErrResponseDateTooOld = errors.New("response date is too old")
115-
ErrRequestSignatureMissing = errors.New("request signature is missing")
116-
ErrRequestSignatureInvalid = errors.New("request signature is invalid")
117-
ErrRequestDigestMissing = errors.New("request digest is missing")
118-
ErrRequestDigestInvalid = errors.New("request digest is invalid")
119-
ErrRequestDateMissing = errors.New("request date is missing")
120-
ErrRequestDateInvalid = errors.New("request date is invalid")
121-
ErrRequestDateTooOld = errors.New("request date is too old")
122-
ErrPublicKeyMissing = errors.New("public key is missing")
123-
ErrPublicKeyInvalid = errors.New("public key is invalid")
124-
ErrValidationFingerprintMissing = errors.New("validation fingerprint scope is missing")
125-
ErrValidationComponentsMissing = errors.New("validation components scope is missing")
126-
ErrValidationProductMissing = errors.New("validation product scope is missing")
127-
ErrHeartbeatPingFailed = errors.New("heartbeat ping failed")
128-
ErrHeartbeatRequired = errors.New("heartbeat is required")
129-
ErrHeartbeatDead = errors.New("heartbeat is dead")
130-
ErrMachineAlreadyActivated = errors.New("machine is already activated")
131-
ErrMachineLimitExceeded = errors.New("machine limit has been exceeded")
132-
ErrMachineNotFound = errors.New("machine no longer exists")
133-
ErrProcessNotFound = errors.New("process no longer exists")
134-
ErrMachineFileNotSupported = errors.New("machine file is not supported")
135-
ErrMachineFileNotEncrypted = errors.New("machine file is not encrypted")
136-
ErrMachineFileNotGenuine = errors.New("machine file is not genuine")
137-
ErrMachineFileExpired = errors.New("machine file is expired")
138-
ErrComponentNotActivated = errors.New("component is not activated")
139-
ErrComponentAlreadyActivated = errors.New("component is already activated")
140-
ErrComponentConflict = errors.New("component is duplicated")
141-
ErrProcessLimitExceeded = errors.New("process limit has been exceeded")
142-
ErrLicenseSchemeNotSupported = errors.New("license scheme is not supported")
143-
ErrLicenseSchemeMissing = errors.New("license scheme is missing")
144-
ErrLicenseKeyMissing = errors.New("license key is missing")
145-
ErrLicenseKeyNotGenuine = errors.New("license key is not genuine")
146-
ErrLicenseNotActivated = errors.New("license is not activated")
147-
ErrLicenseNotAllowed = errors.New("license authentication is not allowed by policy")
148-
ErrLicenseExpired = errors.New("license is expired")
149-
ErrLicenseSuspended = errors.New("license is suspended")
150-
ErrLicenseTooManyMachines = errors.New("license has too many machines")
151-
ErrLicenseTooManyCores = errors.New("license has too many cores")
152-
ErrLicenseTooManyProcesses = errors.New("license has too many processes")
153-
ErrLicenseNotSigned = errors.New("license is not signed")
154-
ErrLicenseInvalid = errors.New("license is invalid")
155-
ErrLicenseFileNotSupported = errors.New("license file is not supported")
156-
ErrLicenseFileNotEncrypted = errors.New("license file is not encrypted")
157-
ErrLicenseFileNotGenuine = errors.New("license file is not genuine")
158-
ErrLicenseFileExpired = errors.New("license file is expired")
159-
ErrLicenseFileSecretMissing = errors.New("license file secret is missing")
160-
ErrTokenNotAllowed = errors.New("token authentication is not allowed by policy")
161-
ErrTokenFormatInvalid = errors.New("token format is invalid")
162-
ErrTokenInvalid = errors.New("token is invalid")
163-
ErrTokenExpired = errors.New("token is expired")
164-
ErrSystemClockUnsynced = errors.New("system clock is out of sync")
106+
ErrReleaseLocationMissing = errors.New("release has no download URL")
107+
ErrUpgradeNotAvailable = errors.New("no upgrades available (already up-to-date)")
108+
ErrResponseSignatureMissing = errors.New("response signature is missing")
109+
ErrResponseSignatureInvalid = errors.New("response signature is invalid")
110+
ErrResponseSignatureNotSupported = errors.New("response signature is not supported")
111+
ErrResponseDigestMissing = errors.New("response digest is missing")
112+
ErrResponseDigestInvalid = errors.New("response digest is invalid")
113+
ErrResponseDateMissing = errors.New("response date is missing")
114+
ErrResponseDateInvalid = errors.New("response date is invalid")
115+
ErrResponseDateTooOld = errors.New("response date is too old")
116+
ErrRequestSignatureMissing = errors.New("request signature is missing")
117+
ErrRequestSignatureInvalid = errors.New("request signature is invalid")
118+
ErrRequestDigestMissing = errors.New("request digest is missing")
119+
ErrRequestDigestInvalid = errors.New("request digest is invalid")
120+
ErrRequestDateMissing = errors.New("request date is missing")
121+
ErrRequestDateInvalid = errors.New("request date is invalid")
122+
ErrRequestDateTooOld = errors.New("request date is too old")
123+
ErrPublicKeyMissing = errors.New("public key is missing")
124+
ErrPublicKeyInvalid = errors.New("public key is invalid")
125+
ErrSchemeNotSupported = errors.New("cryptographic scheme is not supported")
126+
ErrValidationFingerprintMissing = errors.New("validation fingerprint scope is missing")
127+
ErrValidationComponentsMissing = errors.New("validation components scope is missing")
128+
ErrValidationProductMissing = errors.New("validation product scope is missing")
129+
ErrHeartbeatPingFailed = errors.New("heartbeat ping failed")
130+
ErrHeartbeatRequired = errors.New("heartbeat is required")
131+
ErrHeartbeatDead = errors.New("heartbeat is dead")
132+
ErrMachineAlreadyActivated = errors.New("machine is already activated")
133+
ErrMachineLimitExceeded = errors.New("machine limit has been exceeded")
134+
ErrMachineNotFound = errors.New("machine no longer exists")
135+
ErrProcessNotFound = errors.New("process no longer exists")
136+
ErrMachineFileNotSupported = errors.New("machine file is not supported")
137+
ErrMachineFileNotEncrypted = errors.New("machine file is not encrypted")
138+
ErrMachineFileNotGenuine = errors.New("machine file is not genuine")
139+
ErrMachineFileExpired = errors.New("machine file is expired")
140+
ErrComponentNotActivated = errors.New("component is not activated")
141+
ErrComponentAlreadyActivated = errors.New("component is already activated")
142+
ErrComponentConflict = errors.New("component is duplicated")
143+
ErrProcessLimitExceeded = errors.New("process limit has been exceeded")
144+
ErrLicenseSchemeNotSupported = errors.New("license scheme is not supported")
145+
ErrLicenseSchemeMissing = errors.New("license scheme is missing")
146+
ErrLicenseKeyMissing = errors.New("license key is missing")
147+
ErrLicenseKeyNotGenuine = errors.New("license key is not genuine")
148+
ErrLicenseNotActivated = errors.New("license is not activated")
149+
ErrLicenseNotAllowed = errors.New("license authentication is not allowed by policy")
150+
ErrLicenseExpired = errors.New("license is expired")
151+
ErrLicenseSuspended = errors.New("license is suspended")
152+
ErrLicenseTooManyMachines = errors.New("license has too many machines")
153+
ErrLicenseTooManyCores = errors.New("license has too many cores")
154+
ErrLicenseTooManyProcesses = errors.New("license has too many processes")
155+
ErrLicenseNotSigned = errors.New("license is not signed")
156+
ErrLicenseInvalid = errors.New("license is invalid")
157+
ErrLicenseFileNotSupported = errors.New("license file is not supported")
158+
ErrLicenseFileNotEncrypted = errors.New("license file is not encrypted")
159+
ErrLicenseFileNotGenuine = errors.New("license file is not genuine")
160+
ErrLicenseFileExpired = errors.New("license file is expired")
161+
ErrLicenseFileSecretMissing = errors.New("license file secret is missing")
162+
ErrTokenNotAllowed = errors.New("token authentication is not allowed by policy")
163+
ErrTokenFormatInvalid = errors.New("token format is invalid")
164+
ErrTokenInvalid = errors.New("token is invalid")
165+
ErrTokenExpired = errors.New("token is expired")
166+
ErrSystemClockUnsynced = errors.New("system clock is out of sync")
165167
)

keygen.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var (
1818
APIURL = "https://api.keygen.sh"
1919

2020
// APIVersion is the currently supported API version.
21-
APIVersion = "1.7"
21+
APIVersion = "1.8"
2222

2323
// APIPrefix is the major version prefix included in all API URLs.
2424
APIPrefix = "v1"
@@ -67,4 +67,9 @@ var (
6767
// requests. Set this to a custom HTTP client, to implement e.g.
6868
// automatic retries, rate limiting checks, or for tests.
6969
HTTPClient = cleanhttp.DefaultPooledClient()
70+
71+
// Scheme is the acceptable cryptographic scheme to be used for
72+
// verifying signatures for license keys, license/machine files,
73+
// and responses. Supported schemes: ed25519, ecdsa-secp256r1.
74+
Scheme = "ed25519"
7075
)

keygen_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ func init() {
1818
log := Logger.(*logger)
1919
log.Level = LogLevelDebug
2020

21-
if url := os.Getenv("KEYGEN_CUSTOM_DOMAIN"); url != "" {
22-
APIURL = url
21+
if scheme := os.Getenv("KEYGEN_SIGNATURE_SCHEME"); scheme != "" {
22+
Scheme = scheme
23+
}
24+
25+
if host := os.Getenv("KEYGEN_HOST"); host != "" {
26+
APIURL = host
2327
}
2428

2529
PublicKey = os.Getenv("KEYGEN_PUBLIC_KEY")
@@ -38,6 +42,8 @@ func TestValidate(t *testing.T) {
3842
t.Fatalf("Should fingerprint the current machine: err=%v", err)
3943
}
4044

45+
Logger.Debugf("key=%q", LicenseKey)
46+
4147
if _, err := Validate(ctx); err != ErrValidationFingerprintMissing {
4248
t.Fatalf("Should have a required scope: err=%v", err)
4349
}

license.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212
type SchemeCode string
1313

1414
const (
15-
SchemeCodeEd25519 SchemeCode = "ED25519_SIGN"
15+
SchemeCodeEd25519 SchemeCode = "ED25519_SIGN"
16+
SchemeCodeECDSASecp256r1 SchemeCode = "ECDSA_SECP256R1_SIGN"
1617
)
1718

1819
// License represents a Keygen license object.
@@ -139,7 +140,7 @@ func (l *License) Verify() ([]byte, error) {
139140
return nil, ErrLicenseNotSigned
140141
}
141142

142-
verifier := &verifier{PublicKey: PublicKey}
143+
verifier := &verifier{PublicKey: PublicKey, Scheme: Scheme}
143144

144145
return verifier.VerifyLicense(l)
145146
}

license_file.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (lic *LicenseFile) SetRelationships(relationships map[string]interface{}) e
4949
// Decrypt verifies the license file's signature. It returns any errors
5050
// that occurred during verification, e.g. ErrLicenseFileInvalid.
5151
func (lic *LicenseFile) Verify() error {
52-
verifier := &verifier{PublicKey: PublicKey}
52+
verifier := &verifier{PublicKey: PublicKey, Scheme: Scheme}
5353

5454
if err := verifier.VerifyLicenseFile(lic); err != nil {
5555
return &LicenseFileError{err}
@@ -69,7 +69,7 @@ func (lic *LicenseFile) Decrypt(key string) (*LicenseFileDataset, error) {
6969
switch {
7070
case cert.Alg == "aes-256-gcm+rsa-pss-sha256" || cert.Alg == "aes-256-gcm+rsa-sha256":
7171
return nil, ErrLicenseFileNotSupported
72-
case cert.Alg != "aes-256-gcm+ed25519":
72+
case cert.Alg != "aes-256-gcm+ed25519" && cert.Alg != "aes-256-gcm+ecdsa-secp256r1":
7373
return nil, ErrLicenseFileNotEncrypted
7474
}
7575

machine_file.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func (lic *MachineFile) SetRelationships(relationships map[string]interface{}) e
5454
// Decrypt verifies the machine file's signature. It returns any errors
5555
// that occurred during verification, e.g. ErrMachineFileInvalid.
5656
func (lic *MachineFile) Verify() error {
57-
verifier := &verifier{PublicKey: PublicKey}
57+
verifier := &verifier{PublicKey: PublicKey, Scheme: Scheme}
5858

5959
if err := verifier.VerifyMachineFile(lic); err != nil {
6060
return &MachineFileError{err}
@@ -74,7 +74,7 @@ func (lic *MachineFile) Decrypt(key string) (*MachineFileDataset, error) {
7474
switch {
7575
case cert.Alg == "aes-256-gcm+rsa-pss-sha256" || cert.Alg == "aes-256-gcm+rsa-sha256":
7676
return nil, ErrMachineFileNotSupported
77-
case cert.Alg != "aes-256-gcm+ed25519":
77+
case cert.Alg != "aes-256-gcm+ed25519" && cert.Alg != "aes-256-gcm+ecdsa-secp256r1":
7878
return nil, ErrMachineFileNotEncrypted
7979
}
8080

0 commit comments

Comments
 (0)