blob: 81cf86a535e30e6932aae07651e51f621b825141 (
plain) (
blame)
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
|
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.security;
import java.nio.ByteBuffer;
import java.util.Base64;
/**
* A SealedSharedKey represents the public part of a secure one-way ephemeral key exchange.
*
* It is "sealed" in the sense that it is expected to be computationally infeasible
* for anyone to derive the correct shared key from the sealed key without holding
* the correct private key.
*
* A SealedSharedKey can be converted to--and from--an opaque string token representation.
* This token representation is expected to be used as a convenient serialization
* form when communicating shared keys.
*/
public record SealedSharedKey(int keyId, byte[] eciesPayload) {
/** Current encoding version of opaque sealed key tokens. Must be less than 256. */
public static final int CURRENT_TOKEN_VERSION = 1;
/**
* Creates an opaque URL-safe string token that contains enough information to losslessly
* reconstruct the SealedSharedKey instance when passed verbatim to fromTokenString().
*/
public String toTokenString() {
if (keyId >= (1 << 24)) {
throw new IllegalArgumentException("Key id is too large to be encoded");
}
ByteBuffer encoded = ByteBuffer.allocate(4 + eciesPayload.length);
encoded.putInt((CURRENT_TOKEN_VERSION << 24) | keyId);
encoded.put(eciesPayload);
encoded.flip();
byte[] encBytes = new byte[encoded.remaining()];
encoded.get(encBytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(encBytes);
}
/**
* Attempts to unwrap a SealedSharedKey opaque token representation that was previously
* created by a call to toTokenString().
*/
public static SealedSharedKey fromTokenString(String tokenString) {
byte[] rawTokenBytes = Base64.getUrlDecoder().decode(tokenString);
if (rawTokenBytes.length < 4) {
throw new IllegalArgumentException("Decoded token too small to contain a header");
}
ByteBuffer decoded = ByteBuffer.wrap(rawTokenBytes);
int versionAndKeyId = decoded.getInt();
int version = versionAndKeyId >>> 24;
if (version != CURRENT_TOKEN_VERSION) {
throw new IllegalArgumentException("Token had unexpected version. Expected %d, was %d"
.formatted(CURRENT_TOKEN_VERSION, version));
}
byte[] eciesPayload = new byte[decoded.remaining()];
decoded.get(eciesPayload);
int keyId = versionAndKeyId & 0xffffff;
return new SealedSharedKey(keyId, eciesPayload);
}
}
|