-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
padding fixes #321
padding fixes #321
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
@@ -17,7 +17,7 @@ export function base64urlToBuffer( | |||
for (let i = 0; i < str.length; i++) { | |||
byteView[i] = str.charCodeAt(i); | |||
} | |||
return buffer; | |||
return byteView; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unrelated to this specific change, but up until now this function was essentially just returning an empty buffer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM assuming tests pass!
uint8ArrayToHexString, | ||
} from "@turnkey/encoding"; | ||
|
||
const DEFAULT_JWK_MEMBER_BYTE_LENGTH = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super tiny nit, if we plan on setting this in multiple files maybe we can export it from @turnkey/encoding
}); | ||
|
||
test("correctly converts turnkey API key to JWK", function () { | ||
for (let i = 0; i < 100; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jest doesn't like indefinite (while: true) loops, so just picked an arbitrary fairly large value here
uint8ArrayToHexString(new Uint8Array(decodedX)), | ||
DEFAULT_JWK_MEMBER_BYTE_LENGTH | ||
); | ||
// // Manipulate x and y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
temporarily commenting this logic out in order to be able to reproduce the issue when converting turnkey API key to jwk
2e4fa27
to
0e269bc
Compare
// If a length is specified, ensure we sufficiently pad | ||
let paddedBuffer = new Uint8Array(length); | ||
paddedBuffer.set(buffer, length - buffer.length); | ||
return paddedBuffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's important to handle cases where length
is less than buffer.length
. In this case, maybe an error?
jwkCopy.d = paddedD; | ||
jwkCopy.x = paddedX; | ||
jwkCopy.y = paddedY; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What led to padding (x, y, d) as opposed to just d
? Given the jwk is already created I think we're good with the existing (x, y) components within it?
@@ -9,12 +15,39 @@ export function convertTurnkeyApiKeyToJwk(input: { | |||
|
|||
const jwk = pointDecode(hexStringToUint8Array(compressedPublicKeyHex)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If x and y aren't padded for whatever reason, let's fix this inside of pointDecode
so we don't have to extract, pad, and then put things "back" in the JWK:
sdk/packages/api-key-stamper/src/tink/elliptic_curves.ts
Lines 154 to 155 in 08e2b8c
x: Bytes.toBase64(integerToByteArray(x), /* websafe */ true), | |
y: Bytes.toBase64(integerToByteArray(y), /* websafe */ true), |
376ac71
to
2425f82
Compare
2425f82
to
c6d7a1e
Compare
96042d0
to
2d7e5a9
Compare
import { | ||
DEFAULT_JWK_MEMBER_BYTE_LENGTH, | ||
uint8ArrayFromHexString, | ||
} from "@turnkey/encoding"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm, this seems a bit off because we have a vendor'd library import our encoding lib :/
@@ -34,12 +38,20 @@ function byteArrayToInteger(bytes: Uint8Array): bigint { | |||
return BigInt("0x" + Bytes.toHex(bytes)); | |||
} | |||
|
|||
/** Converts bigint to byte array. */ | |||
function integerToByteArray(i: bigint): Uint8Array { | |||
/** Converts bigint to byte array. This implementation has been modified to optionally augment the resulting byte array to a certain length. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's insert this comment at the top of the file so it's obvious (in case someone is trying to update this file down the road)
if (!length) { | ||
return Bytes.fromHex(input); | ||
} | ||
if (input.length / 2 > length) { | ||
throw new Error( | ||
"hex value cannot fit in a buffer of " + length + " byte(s)" | ||
); | ||
} | ||
return uint8ArrayFromHexString(input, length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is necessary. Let's require the length
argument (integerToByteArray
isn't called anywhere with an undefined length), and pad with 0
without relying on our library function. We can also remove the special case of an odd-length input
and just rely on generic padding:
function integerToByteArray(i: bigint, length: number): Uint8Array {
let input = i.toString(16);
let numHexChars = length * 2
if numHexChars < input.length {
throw new Error(`cannot pack integer with ${input.length} hex chars into$ {length} bytes`);
} else {
let padding = "0".repeat(numHexChars - input.length);
}
return Bytes.fromHex(padding + input);
}
packages/encoding/src/index.ts
Outdated
export function stringToBase64urlString(input: string): string { | ||
// string to base64 -- we do not rely on the browser's btoa since it's not present in React Native environments | ||
const base64String = btoa(input); | ||
return base64StringToBase64UrlEncodedString(base64String); | ||
} | ||
|
||
export function base64urlToBuffer(baseurl64String: string): ArrayBuffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from tests this isn't used anywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same as #321 (comment))
packages/encoding/src/index.ts
Outdated
baseurl64String.replace(/-/g, "+").replace(/_/g, "/") + padding; | ||
|
||
// Base64 to binary string | ||
const str = atob(base64String); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally we should avoid relying on things that are "magically" available. This may work in Node but might not work in React Native, for example (I'd like our core "encoding" functions to be pure JS so we don't have to deal with polyfills or worry about different implementations across JS runtimes)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense! This also was cut/pasted from packages/http/src/webauthn-json/base64url.ts
/ packages/webauthn-stamper/src/webauthn-json/base64url.ts
. But if this is "over-consolidation", happy to take this out. That and the fact that this isn't pure JS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I lean towards deleting these two functions because they come from a vendored lib https://github.com/tkhq/sdk/blob/main/packages/http/src/webauthn-json/README.md
This will make this diff smaller too! :)
packages/encoding/src/index.ts
Outdated
return byteView; | ||
} | ||
|
||
export function bufferToBase64url(buffer: ArrayBuffer): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. This doesn't seem to be used anywhere outside of the new tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was planning on consolidating various utils in packages/encoding
instead of having the same util functions in http
(specifically packages/http/src/webauthn-json/base64url.ts
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ended up removing for now! maybe we can move it back, consolidate utilities in encoding
down the line, along with a pureJS implementation of atob
(similar to the btoa
impl we have vendored in)
@@ -1,5 +1,6 @@ | |||
/** | |||
* Code modified from https://github.com/google/tink/blob/6f74b99a2bfe6677e3670799116a57268fd067fa/javascript/subtle/elliptic_curves.ts | |||
* - The implementation of integerToByteArray has been modified to optionally augment the resulting byte array to a certain length. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not optionally anymore :)
packages/encoding/src/index.ts
Outdated
baseurl64String.replace(/-/g, "+").replace(/_/g, "/") + padding; | ||
|
||
// Base64 to binary string | ||
const str = atob(base64String); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I lean towards deleting these two functions because they come from a vendored lib https://github.com/tkhq/sdk/blob/main/packages/http/src/webauthn-json/README.md
This will make this diff smaller too! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Summary & Motivation
$title
There are cases where we want to manually pad buffers to get to a certain length, primarily when we're dealing with jwks.
The nature of this change is similar in spirit to another change we recently rolled out for our iframes: tkhq/frames#44
How I Tested These Changes
Unit, smoke via #322
Did you add a changeset?
If updating one of our packages, you'll likely need to add a changeset to your PR. To do so, run
pnpm changeset
.pnpm changeset
will generate a file where you should write a human friendly message about the changes. Note how this (example) includes the package name (should be auto added by the command) along with the type of semver change (major.minor.patch) (which you should set).These changes will be used at release time to determine what packages to publish and how to bump their version. For more context see this comment.