File: parameters.go

package info (click to toggle)
golang-github-tink-crypto-tink-go 2.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 14,996 kB
  • sloc: sh: 876; makefile: 6
file content (188 lines) | stat: -rw-r--r-- 6,081 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jwtrsassapkcs1

import (
	"fmt"

	"github.com/tink-crypto/tink-go/v2/key"
)

// KIDStrategy is the strategy for handling the "kid" header.
// See https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.4
// The available options are:
//   - Base64EncodedKeyID: The `kid` is the URL safe (RFC 4648 Section 5) base64-encoded big-endian `key_id` in the keyset.
//   - Ignored: The `kid` header is ignored.
//   - Custom: The `kid` is fixed. It can be obtained by calling `key.GetKid()`.
type KIDStrategy int

const (
	// UnknownKIDStrategy indicates that the key ID strategy is unknown.
	UnknownKIDStrategy KIDStrategy = iota
	// Base64EncodedKeyIDAsKID indicates that the a Base64 encoded key ID is
	// used as the kid.
	//
	// Using this strategy means that:
	//  - [jwt.Signer]'s SignAndEncode always adds the `kid`.
	//  - [jwt.Verifyer]'s VerifyAndDecode checks that the `kid` is
	//    present and equal to the key ID.
	//
	// NOTE: This strategy is recommended by Tink.
	Base64EncodedKeyIDAsKID
	// IgnoredKID indicates that the kid is ignored.
	//
	// Using this strategy means that:
	//  - [jwt.Signer]'s SignAndEncode does not write the kid header
	//  - [jwt.Verifyer]'s VerifyAndDecode ignores the kid header
	IgnoredKID
	// CustomKID indicates that the kid has a custom fixed value.
	//
	// Using this strategy means that:
	//  - [jwt.Signer]'s SignAndEncode writes the KID header to the value
	//    given by key.KID()
	//  - [jwt.Verifyer]'s VerifyAndDecode if the kid is present then it
	//    must match key.KID(); if the kid is not present, it will be
	//    accepted.
	//
	// NOTE: Tink doesn't allow creation of random JWT RSA-SSA-PKCS1 keys from
	// parameters when parameters.KIDStrategy() == CustomKID.
	CustomKID
)

func (k KIDStrategy) String() string {
	switch k {
	case Base64EncodedKeyIDAsKID:
		return "Base64EncodedKeyIDAsKID"
	case IgnoredKID:
		return "IgnoredKID"
	case CustomKID:
		return "CustomKID"
	default:
		return "UnknownKIDStrategy"
	}
}

// Algorithm is the signature algorithm.
// See https://datatracker.ietf.org/doc/html/rfc7518#section-3.3
// The available options are:
//   - RS256: RSASSA-PKCS1 using SHA-256
//   - RS384: RSASSA-PKCS1 using SHA-384
//   - RS512: RSASSA-PKCS1 using SHA-512
type Algorithm int

const (
	// UnknownAlgorithm indicates that the algorithm is unknown.
	UnknownAlgorithm Algorithm = iota
	// RS256 is RSASSA-PKCS1 using SHA-256.
	RS256 Algorithm = iota + 1
	// RS384 is RSASSA-PKCS1 using SHA-384.
	RS384
	// RS512 is RSASSA-PKCS1 using SHA-512.
	RS512
)

func (a Algorithm) String() string {
	switch a {
	case RS256:
		return "RS256"
	case RS384:
		return "RS384"
	case RS512:
		return "RS512"
	default:
		return "UnknownAlgorithm"
	}
}

// Parameters represents the parameters of a JWT RSA SSA PKCS1 key.
type Parameters struct {
	kidStrategy       KIDStrategy
	algorithm         Algorithm
	modulusSizeInBits int
	publicExponent    int
}

var _ key.Parameters = (*Parameters)(nil)

const (
	// f4 is the public exponent 65537.
	f4 = 65537
	// Max exponent for RSA keys used in
	// https://cs.opensource.google/go/go/+/master:src/crypto/internal/fips140/rsa/rsa.go;l=370;drc=a76cc5a4ecb004616404cac5bb756da293818469
	maxExponent = 1<<31 - 1
)

// ParametersOpts represents the options for creating a [Parameters] instance.
type ParametersOpts struct {
	ModulusSizeInBits int
	PublicExponent    int
	Algorithm         Algorithm
	KidStrategy       KIDStrategy
}

// NewParameters creates a new Parameters instance.
func NewParameters(opts ParametersOpts) (*Parameters, error) {
	if opts.ModulusSizeInBits < 2048 {
		return nil, fmt.Errorf("invalid modulus size: %v, want >= 2048", opts.ModulusSizeInBits)
	}
	if opts.Algorithm == UnknownAlgorithm {
		return nil, fmt.Errorf("unsupported algorithm: %v", opts.Algorithm)
	}
	if opts.KidStrategy == UnknownKIDStrategy {
		return nil, fmt.Errorf("unsupported kid strategy: %v", opts.KidStrategy)
	}
	if opts.PublicExponent < f4 || opts.PublicExponent > maxExponent {
		return nil, fmt.Errorf("invalid public exponent: %v, want >= %v and <= %v", opts.PublicExponent, f4, maxExponent)
	}
	// To be consistent with tink-java and tink-cc.
	if opts.PublicExponent%2 != 1 {
		return nil, fmt.Errorf("invalid public exponent: %v, want odd", opts.PublicExponent)
	}
	return &Parameters{
		kidStrategy:       opts.KidStrategy,
		algorithm:         opts.Algorithm,
		modulusSizeInBits: opts.ModulusSizeInBits,
		publicExponent:    opts.PublicExponent,
	}, nil
}

// KIDStrategy returns the KIDStrategy.
func (p *Parameters) KIDStrategy() KIDStrategy { return p.kidStrategy }

// Algorithm returns the Algorithm.
func (p *Parameters) Algorithm() Algorithm { return p.algorithm }

// ModulusSizeInBits returns the ModulusSizeInBits.
func (p *Parameters) ModulusSizeInBits() int { return p.modulusSizeInBits }

// PublicExponent returns the PublicExponent.
func (p *Parameters) PublicExponent() int { return p.publicExponent }

// HasIDRequirement returns true if the key has an ID requirement.
func (p *Parameters) HasIDRequirement() bool {
	return p.kidStrategy == Base64EncodedKeyIDAsKID
}

// Equal returns true if the two Parameters are equal.
func (p *Parameters) Equal(other key.Parameters) bool {
	otherParams, ok := other.(*Parameters)
	if !ok {
		return false
	}
	return p.kidStrategy == otherParams.kidStrategy &&
		p.algorithm == otherParams.algorithm &&
		p.modulusSizeInBits == otherParams.modulusSizeInBits &&
		p.publicExponent == otherParams.publicExponent
}