Skip to content

Commit ce7f78b

Browse files
committed
Update docs.
1 parent cba59f6 commit ce7f78b

26 files changed

Lines changed: 1712 additions & 885 deletions

README.md

Lines changed: 714 additions & 170 deletions
Large diffs are not rendered by default.

paseto/src/main/kotlin/net/aholbrook/paseto/Footer.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import kotlin.contracts.ExperimentalContracts
55
import kotlin.contracts.InvocationKind
66
import kotlin.contracts.contract
77

8-
sealed interface PasetoFooter
8+
sealed interface Footer
99

1010
@JvmInline
11-
value class StringFooter internal constructor(val value: String) : PasetoFooter
11+
value class StringFooter internal constructor(val value: String) : Footer
1212

1313
@ConsistentCopyVisibility
1414
data class ClaimFooter internal constructor(
1515
val keyId: String?, // kid
1616
val wrappedKey: String?, // wpk
1717
val claims: ClaimObject,
18-
) : PasetoFooter
18+
) : Footer
1919

2020
@PasetoDslMarker
2121
class ClaimFooterBuilder @PublishedApi internal constructor() {
@@ -54,30 +54,30 @@ inline fun footer(init: ClaimFooterBuilder.() -> Unit): ClaimFooter {
5454

5555
/**
5656
* A Paseto footer extracted from a token which has not been cryptographically verified. It is therefore possible that
57-
* a [TaintedPasetoFooter] has been tampered with.
57+
* a [TaintedFooter] has been tampered with.
5858
*
5959
* This is useful when the footer is used to carry information required to verify the key, like a key id (kid) claim.
6060
*/
61-
sealed interface TaintedPasetoFooter
61+
sealed interface TaintedFooter
6262

6363
@JvmInline
64-
value class TaintedStringFooter(val value: String) : TaintedPasetoFooter
64+
value class TaintedStringFooter(val value: String) : TaintedFooter
6565

6666
@ConsistentCopyVisibility
6767
data class TaintedClaimFooter internal constructor(
6868
val keyId: String?, // kid
6969
val wrappedKey: String?, // wpk
7070
val claims: ClaimObject,
71-
) : TaintedPasetoFooter
71+
) : TaintedFooter
7272

7373
/**
74-
* Converts a [PasetoFooter] to it's [TaintedPasetoFooter] variant.
74+
* Converts a [Footer] to it's [TaintedFooter] variant.
7575
*
7676
* This can be used to compare an [TaintedClaimFooter] against a [ClaimFooter] built using the [footer] builder.
77-
* @receiver A [PasetoFooter] instance to turn taint.
78-
* @return A [TaintedPasetoFooter] representation of the given [PasetoFooter].
77+
* @receiver A [Footer] instance to turn taint.
78+
* @return A [TaintedFooter] representation of the given [Footer].
7979
*/
80-
fun PasetoFooter.taint(): TaintedPasetoFooter? = when (this) {
80+
fun Footer.taint(): TaintedFooter = when (this) {
8181
is ClaimFooter -> TaintedClaimFooter(keyId, wrappedKey, claims)
8282
is StringFooter -> TaintedStringFooter(value)
8383
}

paseto/src/main/kotlin/net/aholbrook/paseto/FooterSerde.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class FooterOptionsBuilder @PublishedApi internal constructor() {
6969
)
7070
}
7171

72-
internal fun Json.encodeFooter(footerOptions: FooterOptions, footer: PasetoFooter): String {
72+
internal fun Json.encodeFooter(footerOptions: FooterOptions, footer: Footer): String {
7373
val encoded = when (footer) {
7474
is ClaimFooter -> encodeToString(ClaimFooterSerializer, footer)
7575
is StringFooter -> footer.value
@@ -79,7 +79,7 @@ internal fun Json.encodeFooter(footerOptions: FooterOptions, footer: PasetoFoote
7979
return encoded
8080
}
8181

82-
internal fun Json.decodeFooter(footerOptions: FooterOptions, footer: String): PasetoFooter {
82+
internal fun Json.decodeFooter(footerOptions: FooterOptions, footer: String): Footer {
8383
footerOptions.validateFooter(footer)
8484

8585
return when (footerOptions.parseMode) {

paseto/src/main/kotlin/net/aholbrook/paseto/Token.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ data class Token internal constructor(
1818
val issuedAt: Instant?, // iat
1919
val tokenId: String?, // jti
2020
val claims: ClaimObject,
21-
val footer: PasetoFooter,
21+
val footer: Footer,
2222
)
2323

2424
@PasetoDslMarker
@@ -31,7 +31,7 @@ class TokenBuilder @PublishedApi internal constructor(clock: Clock) {
3131
var issuedAt: Instant? = clock.instant() // iat
3232
var tokenId: String? = null // jti
3333
private var claims: ClaimObject = ClaimObject()
34-
private var footer: PasetoFooter = StringFooter("")
34+
private var footer: Footer = StringFooter("")
3535

3636
@OptIn(ExperimentalContracts::class)
3737
fun claims(init: ClaimObjectBuilder.() -> Unit) {
@@ -46,7 +46,7 @@ class TokenBuilder @PublishedApi internal constructor(clock: Clock) {
4646
this.claims = claims
4747
}
4848

49-
fun footer(footer: PasetoFooter) {
49+
fun footer(footer: Footer) {
5050
this.footer = footer
5151
}
5252

paseto/src/main/kotlin/net/aholbrook/paseto/TokenService.kt

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import kotlin.contracts.InvocationKind
1919
import kotlin.contracts.contract
2020

2121
sealed interface Purpose {
22-
class Public(val keyProvider: () -> KeyPair) : Purpose
23-
class Local(val keyProvider: () -> SymmetricKey) : Purpose
22+
class Public(val keyProvider: (footer: TaintedFooter) -> KeyPair) : Purpose
23+
class Local(val keyProvider: (footer: TaintedFooter) -> SymmetricKey) : Purpose
2424
}
2525

2626
@PasetoDslMarker
@@ -81,23 +81,23 @@ inline fun tokenService(version: Version, purpose: Purpose, init: TokenServiceBu
8181

8282
sealed interface TokenService {
8383
fun encode(token: Token, implicitAssertion: String = ""): String
84-
fun decode(token: String, footer: PasetoFooter? = null, implicitAssertion: String = ""): Token
84+
fun decode(token: String, footer: Footer? = null, implicitAssertion: String = ""): Token
8585

8686
/**
8787
* Decode the token's footer without verifying the token.
8888
*
89-
* This is useful if the token footer contains data required to decode the token. Returns an [TaintedPasetoFooter]
89+
* This is useful if the token footer contains data required to decode the token. Returns an [TaintedFooter]
9090
* to prevent misuse as this footer should **never** be used when checking the footer during decoding.
9191
*
9292
* @param [token] paseto token to decode the footer of.
93-
* @return [TaintedPasetoFooter] with the decoded footer contents.
93+
* @return [TaintedFooter] with the decoded footer contents.
9494
*/
95-
fun insecureGetFooter(token: String): TaintedPasetoFooter?
95+
fun insecureGetFooter(token: String): TaintedFooter
9696
}
9797

9898
internal class LocalTokenService internal constructor(
9999
private val paseto: Paseto,
100-
private val keyProvider: () -> SymmetricKey,
100+
private val keyProvider: (footer: TaintedFooter) -> SymmetricKey,
101101
private val rules: Rules,
102102
private val footerOptions: FooterOptions,
103103
private val json: Json = Json { explicitNulls = false },
@@ -112,20 +112,20 @@ internal class LocalTokenService internal constructor(
112112
val encodedFooter = json.encodeFooter(footerOptions, token.footer)
113113
return paseto.encrypt(
114114
m = encoded.toByteArray(Charsets.UTF_8),
115-
key = keyProvider(),
116-
footer = encodedFooter ?: "",
115+
key = keyProvider(token.footer.taint()),
116+
footer = encodedFooter,
117117
implicitAssertion = implicitAssertion,
118118
)
119119
}
120120

121-
override fun decode(token: String, footer: PasetoFooter?, implicitAssertion: String): Token {
121+
override fun decode(token: String, footer: Footer?, implicitAssertion: String): Token {
122122
if (implicitAssertion.isNotEmpty() && !paseto.supportsImplicitAssertion) {
123123
throw ImplicitAssertionsNotSupportedException(paseto.version)
124124
}
125125

126126
val (encoded, footer) = paseto.decrypt(
127127
token = token,
128-
key = keyProvider(),
128+
key = keyProvider(insecureGetFooter(token)),
129129
footer = footer?.let { json.encodeFooter(footerOptions, it) },
130130
implicitAssertion = implicitAssertion,
131131
)
@@ -136,13 +136,13 @@ internal class LocalTokenService internal constructor(
136136
return decoded
137137
}
138138

139-
override fun insecureGetFooter(token: String): TaintedPasetoFooter? =
139+
override fun insecureGetFooter(token: String): TaintedFooter =
140140
json.decodeFooter(footerOptions, extractFooter(token)).taint()
141141
}
142142

143143
internal class PublicTokenService internal constructor(
144144
private val paseto: Paseto,
145-
private val keyProvider: () -> KeyPair,
145+
private val keyProvider: (footer: TaintedFooter) -> KeyPair,
146146
private val rules: Rules,
147147
private val footerOptions: FooterOptions,
148148
private val json: Json = Json { explicitNulls = false },
@@ -156,27 +156,27 @@ internal class PublicTokenService internal constructor(
156156
rules.verifyAll(token, Rule.Mode.ENCODE)
157157
val encoded = json.encodeToString(PasetoTokenSerializer, token)
158158
val encodedFooter = json.encodeFooter(footerOptions, token.footer)
159-
val keyPair = keyProvider()
159+
val keyPair = keyProvider(token.footer.taint())
160160
if (keyPair.secretKey == null) {
161161
throw CannotSignWithoutSecretKey()
162162
}
163163
return paseto.sign(
164164
m = encoded.toByteArray(Charsets.UTF_8),
165165
secretKey = keyPair.secretKey,
166-
footer = encodedFooter ?: "",
166+
footer = encodedFooter,
167167
implicitAssertion = implicitAssertion,
168168
)
169169
}
170170

171-
override fun decode(token: String, footer: PasetoFooter?, implicitAssertion: String): Token {
171+
override fun decode(token: String, footer: Footer?, implicitAssertion: String): Token {
172172
// TODO expand service-test-vectors for v4 with implicit assertions
173173
if (implicitAssertion.isNotEmpty() && !paseto.supportsImplicitAssertion) {
174174
throw ImplicitAssertionsNotSupportedException(paseto.version)
175175
}
176176

177177
val (encoded, footer) = paseto.verify(
178178
token = token,
179-
publicKey = keyProvider().publicKey,
179+
publicKey = keyProvider(insecureGetFooter(token)).publicKey,
180180
footer = footer?.let { json.encodeFooter(footerOptions, it) },
181181
implicitAssertion = implicitAssertion,
182182
)
@@ -187,6 +187,6 @@ internal class PublicTokenService internal constructor(
187187
return decoded
188188
}
189189

190-
override fun insecureGetFooter(token: String): TaintedPasetoFooter? =
190+
override fun insecureGetFooter(token: String): TaintedFooter =
191191
json.decodeFooter(footerOptions, extractFooter(token)).taint()
192192
}

paseto/src/main/kotlin/net/aholbrook/paseto/crypto/ConstantTimeEquals.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import java.nio.charset.Charset
1515
* @param expected The expected byte array.
1616
* @return true if the arrays are equal, false if not.
1717
*/
18-
internal fun ByteArray.constantTimeEquals(expected: ByteArray): Boolean {
18+
fun ByteArray.constantTimeEquals(expected: ByteArray): Boolean {
1919
if (size != expected.size) {
2020
var result = 0
2121
for (i in indices) {
@@ -44,5 +44,5 @@ internal fun ByteArray.constantTimeEquals(expected: ByteArray): Boolean {
4444
* @param expected The expected byte array.
4545
* @return true if the arrays are equal, false if not.
4646
*/
47-
internal fun String.constantTimeEquals(expected: String, charset: Charset = Charsets.UTF_8): Boolean =
47+
fun String.constantTimeEquals(expected: String, charset: Charset = Charsets.UTF_8): Boolean =
4848
toByteArray(charset).constantTimeEquals(expected.toByteArray(charset))

paseto/src/main/kotlin/net/aholbrook/paseto/exception/Exceptions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class DecryptionException @InternalApi constructor(token: String) :
3333
open class InvalidFooterException @InternalApi constructor(msg: String, cause: Throwable? = null) :
3434
PasetoException(msg, cause)
3535

36-
class GenericInvalidFooterException @InternalApi constructor(val given: String?, val expected: String) :
36+
class IncorrectFooterException @InternalApi constructor(val given: String?, val expected: String) :
3737
PasetoException("Invalid footer in token: \"$given\" expected: \"$expected\".")
3838

3939
class FooterExceedsMaxLengthException @InternalApi constructor(val length: Int, val max: Int) :

paseto/src/main/kotlin/net/aholbrook/paseto/exception/KeyExceptions.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class KeyPurposeException @InternalApi constructor(val expected: String, val act
1212
class KeyVersionException @InternalApi constructor(val expected: Version, val actual: Version) :
1313
PasetoException("Got wrong Key version: $actual given, expected: $expected.")
1414

15-
class KeyReuseException @InternalApi constructor() :
16-
PasetoException("Key has already been used and cleared, please reload for each operation.")
15+
class KeyClearedException @InternalApi constructor() :
16+
PasetoException("Key instance has already been consumed and cleared. Lpad a new key for each operation.")
1717

1818
class KeyPemUnsupportedTypeException @InternalApi constructor(val type: String) :
1919
PasetoException("Unsupported PEM type: $type")

paseto/src/main/kotlin/net/aholbrook/paseto/protocol/Paseto.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import net.aholbrook.paseto.crypto.ED25519_PUBLICKEYBYTES
77
import net.aholbrook.paseto.crypto.ED25519_SECRETKEYBYTES
88
import net.aholbrook.paseto.crypto.constantTimeEquals
99
import net.aholbrook.paseto.decodeOrNull
10-
import net.aholbrook.paseto.exception.GenericInvalidFooterException
10+
import net.aholbrook.paseto.exception.IncorrectFooterException
1111
import net.aholbrook.paseto.exception.PasetoParseException
1212
import net.aholbrook.paseto.protocol.key.AsymmetricPublicKey
1313
import net.aholbrook.paseto.protocol.key.AsymmetricSecretKey
@@ -117,7 +117,7 @@ internal fun decodeFooter(token: String, sections: PasetoSections, expectedFoote
117117
?: throw PasetoParseException(PasetoParseException.Reason.INVALID_BASE64, token)
118118

119119
if (expectedFooter != null && !decodedFooter.constantTimeEquals(expectedFooter)) {
120-
throw GenericInvalidFooterException(decodedFooter, expectedFooter)
120+
throw IncorrectFooterException(decodedFooter, expectedFooter)
121121
}
122122

123123
return decodedFooter

paseto/src/main/kotlin/net/aholbrook/paseto/protocol/PasetoV1.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ internal object PasetoV1 : Paseto {
6464
".${Base64.UrlSafeNoPadding.encode(f)}"
6565
}
6666
} finally {
67-
key.clear()
67+
key.internalClear()
6868
cleanup.forEach { it.run() }
6969
}
7070
}
@@ -121,7 +121,7 @@ internal object PasetoV1 : Paseto {
121121

122122
return Pair(m.toString(Charsets.UTF_8), f)
123123
} finally {
124-
key.clear()
124+
key.internalClear()
125125
cleanup.forEach { it.run() }
126126
}
127127
}
@@ -147,7 +147,7 @@ internal object PasetoV1 : Paseto {
147147
".${Base64.UrlSafeNoPadding.encode(f)}"
148148
}
149149
} finally {
150-
secretKey.clear()
150+
secretKey.internalClear()
151151
}
152152
}
153153

0 commit comments

Comments
 (0)