a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky
atproto bluesky typescript npm
101
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(crypto): allow exporting keys imported via PrivateKeyExportable

Mary 4574af87 387b2919

+117
+5
.changeset/fifty-ties-reply.md
··· 1 + --- 2 + '@atcute/crypto': minor 3 + --- 4 + 5 + allow exporting keys imported via PrivateKeyExportable classes
+72
packages/utilities/crypto/lib/keypairs/p256.ts
··· 250 250 return new P256PrivateKeyExportable(keypair.privateKey, keypair.publicKey); 251 251 } 252 252 253 + static override async importRaw( 254 + privateKeyBytes: Uint8Array, 255 + publicKeyBytes?: Uint8Array, 256 + ): Promise<P256PrivateKeyExportable> { 257 + const pkcs8 = concat([PKCS8_PRIVATE_KEY_PREFIX, privateKeyBytes]); 258 + 259 + // always import as extractable for exportable keys 260 + const privateKey = await crypto.subtle.importKey('pkcs8', pkcs8, ECDSA_ALG, true, ['sign']); 261 + 262 + let publicKey: CryptoKey; 263 + 264 + if (publicKeyBytes) { 265 + if (!SUPPORTS_COMPRESSED_EC_KEYS) { 266 + publicKey = await crypto.subtle.importKey( 267 + 'raw', 268 + uncompressP256Point(publicKeyBytes), 269 + ECDSA_ALG, 270 + true, 271 + ['verify'], 272 + ); 273 + } else { 274 + publicKey = await crypto.subtle.importKey( 275 + 'spki', 276 + concat([SPKI_PREFIX, publicKeyBytes]), 277 + ECDSA_ALG, 278 + true, 279 + ['verify'], 280 + ); 281 + } 282 + } else { 283 + publicKey = await deriveEcPublicKeyFromPrivateKey(privateKey, ['verify']); 284 + } 285 + 286 + const keypair = new P256PrivateKeyExportable(privateKey, publicKey); 287 + 288 + if (publicKeyBytes) { 289 + await checkKeypairRelationship(keypair); 290 + } 291 + 292 + return keypair; 293 + } 294 + 295 + static override async importCryptoKey( 296 + privateKey: CryptoKey, 297 + publicKey?: CryptoKey, 298 + ): Promise<P256PrivateKeyExportable> { 299 + assertType((privateKey.algorithm as any).namedCurve === 'P-256', '1st key is not an ECDSA P-256 key'); 300 + assertType(privateKey.type === 'private', '1st key is not a private key'); 301 + assertType(privateKey.extractable, 'private key must be extractable'); 302 + 303 + if (publicKey) { 304 + assertType((publicKey.algorithm as any).namedCurve === 'P-256', '2nd key is not an ECDSA P-256 key'); 305 + assertType(publicKey.type === 'public', '2nd key is not a public key'); 306 + assertType(publicKey.extractable, 'public key must be extractable'); 307 + } 308 + 309 + const keypair = new P256PrivateKeyExportable( 310 + privateKey, 311 + publicKey ?? (await deriveEcPublicKeyFromPrivateKey(privateKey, ['verify'])), 312 + ); 313 + 314 + if (publicKey) { 315 + await checkKeypairRelationship(keypair); 316 + } 317 + 318 + return keypair; 319 + } 320 + 321 + static override async importCryptoKeyPair(keypair: CryptoKeyPair): Promise<P256PrivateKeyExportable> { 322 + return await this.importCryptoKey(keypair.privateKey, keypair.publicKey); 323 + } 324 + 253 325 exportPrivateKey(format: 'jwk'): Promise<JsonWebKey>; 254 326 exportPrivateKey(format: 'multikey'): Promise<string>; 255 327 exportPrivateKey(format: 'raw'): Promise<Uint8Array<ArrayBuffer>>;
+27
packages/utilities/crypto/lib/keypairs/secp256k1-node.ts
··· 173 173 return new NodeSecp256k1PrivateKeyExportable(keypair.privateKey, keypair.publicKey); 174 174 } 175 175 176 + static override async importRaw( 177 + privateKeyBytes: Uint8Array, 178 + publicKeyBytes?: Uint8Array, 179 + ): Promise<NodeSecp256k1PrivateKeyExportable> { 180 + const privateKey = createPrivateKey({ 181 + key: Buffer.concat([PKCS8_PRIVATE_KEY_PREFIX, privateKeyBytes]), 182 + format: 'der', 183 + type: 'pkcs8', 184 + }); 185 + 186 + const publicKey = publicKeyBytes 187 + ? createPublicKey({ 188 + key: Buffer.concat([SPKI_PREFIX, publicKeyBytes]), 189 + format: 'der', 190 + type: 'spki', 191 + }) 192 + : createPublicKey(privateKey); 193 + 194 + const keypair = new NodeSecp256k1PrivateKeyExportable(privateKey, publicKey); 195 + 196 + if (publicKeyBytes) { 197 + await checkKeypairRelationship(keypair); 198 + } 199 + 200 + return keypair; 201 + } 202 + 176 203 exportPrivateKey(format: 'jwk'): Promise<JsonWebKey>; 177 204 exportPrivateKey(format: 'multikey'): Promise<string>; 178 205 exportPrivateKey(format: 'raw'): Promise<Uint8Array<ArrayBuffer>>;
+13
packages/utilities/crypto/lib/keypairs/secp256k1-web.ts
··· 139 139 return new Secp256k1PrivateKeyExportable(privateKeyBytes, publicKeyBytes); 140 140 } 141 141 142 + static override async importRaw( 143 + privateKeyBytes: Uint8Array, 144 + publicKeyBytes?: Uint8Array, 145 + ): Promise<Secp256k1PrivateKeyExportable> { 146 + const keypair = new Secp256k1PrivateKeyExportable(privateKeyBytes, publicKeyBytes ?? getPublicKey(privateKeyBytes)); 147 + 148 + if (publicKeyBytes) { 149 + await checkKeypairRelationship(keypair); 150 + } 151 + 152 + return keypair; 153 + } 154 + 142 155 exportPrivateKey(format: 'jwk'): Promise<JsonWebKey>; 143 156 exportPrivateKey(format: 'multikey'): Promise<string>; 144 157 exportPrivateKey(format: 'raw'): Promise<Uint8Array<ArrayBuffer>>;