remove sign additionalPublicKeys signature requirements
This commit is contained in:
parent
437e69cfc4
commit
9705ec4a47
|
@ -10,8 +10,6 @@ export class APMultipleKeys1708980134301 {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_171e64971c780ebd23fae140bb"`);
|
await queryRunner.query(`DROP INDEX "public"."IDX_171e64971c780ebd23fae140bb"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PublicKey" character varying(128)`);
|
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PublicKey" character varying(128)`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PrivateKey" character varying(128)`);
|
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PrivateKey" character varying(128)`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PublicKeySignature" character varying(720)`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519SignatureAlgorithm" character varying(32)`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_10c146e4b39b443ede016f6736d"`);
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_10c146e4b39b443ede016f6736d"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
|
||||||
|
@ -34,8 +32,6 @@ export class APMultipleKeys1708980134301 {
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" DROP DEFAULT`);
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" DROP DEFAULT`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" TYPE "public"."user_profile_followersVisibility_enum_old" USING "followersVisibility"::"text"::"public"."user_profile_followersVisibility_enum_old"`);
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" TYPE "public"."user_profile_followersVisibility_enum_old" USING "followersVisibility"::"text"::"public"."user_profile_followersVisibility_enum_old"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" SET DEFAULT 'public'`);
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" SET DEFAULT 'public'`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519SignatureAlgorithm"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PublicKeySignature"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PrivateKey"`);
|
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PrivateKey"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PublicKey"`);
|
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PublicKey"`);
|
||||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `);
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { sign } from 'node:crypto';
|
|
||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiUser } from '@/models/User.js';
|
||||||
|
@ -12,7 +11,7 @@ import { RedisKVCache } from '@/misc/cache.js';
|
||||||
import type { MiUserKeypair } from '@/models/UserKeypair.js';
|
import type { MiUserKeypair } from '@/models/UserKeypair.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { ED25519_PUBLIC_KEY_SIGNATURE_ALGORITHM, genEd25519KeyPair } from '@/misc/gen-key-pair.js';
|
import { genEd25519KeyPair } from '@/misc/gen-key-pair.js';
|
||||||
import { GlobalEventService, GlobalEvents } from '@/core/GlobalEventService.js';
|
import { GlobalEventService, GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -56,12 +55,9 @@ export class UserKeypairService implements OnApplicationShutdown {
|
||||||
const keypair = await this.cache.fetch(userId);
|
const keypair = await this.cache.fetch(userId);
|
||||||
if (keypair.ed25519PublicKey != null) return;
|
if (keypair.ed25519PublicKey != null) return;
|
||||||
const ed25519 = await genEd25519KeyPair();
|
const ed25519 = await genEd25519KeyPair();
|
||||||
const ed25519PublicKeySignature = sign(ED25519_PUBLIC_KEY_SIGNATURE_ALGORITHM, Buffer.from(ed25519.publicKey), keypair.privateKey).toString('base64');
|
|
||||||
await this.userKeypairsRepository.update({ userId }, {
|
await this.userKeypairsRepository.update({ userId }, {
|
||||||
ed25519PublicKey: ed25519.publicKey,
|
ed25519PublicKey: ed25519.publicKey,
|
||||||
ed25519PrivateKey: ed25519.privateKey,
|
ed25519PrivateKey: ed25519.privateKey,
|
||||||
ed25519PublicKeySignature,
|
|
||||||
ed25519SignatureAlgorithm: `rsa-${ED25519_PUBLIC_KEY_SIGNATURE_ALGORITHM}`,
|
|
||||||
});
|
});
|
||||||
this.globalEventService.publishInternalEvent('userKeypairUpdated', { userId });
|
this.globalEventService.publishInternalEvent('userKeypairUpdated', { userId });
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ export class ApRendererService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderKey(user: MiLocalUser, publicKey: string, postfix?: string, signature?: IKey['signature']): IKey {
|
public renderKey(user: MiLocalUser, publicKey: string, postfix?: string): IKey {
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
|
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
|
||||||
type: 'Key',
|
type: 'Key',
|
||||||
|
@ -259,7 +259,6 @@ export class ApRendererService {
|
||||||
type: 'spki',
|
type: 'spki',
|
||||||
format: 'pem',
|
format: 'pem',
|
||||||
}) as string,
|
}) as string,
|
||||||
signature,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +500,7 @@ export class ApRendererService {
|
||||||
discoverable: user.isExplorable,
|
discoverable: user.isExplorable,
|
||||||
publicKey: this.renderKey(user, keypair.publicKey, '#main-key'),
|
publicKey: this.renderKey(user, keypair.publicKey, '#main-key'),
|
||||||
additionalPublicKeys: [
|
additionalPublicKeys: [
|
||||||
...(keypair.ed25519PublicKey ? [this.renderKey(user, keypair.ed25519PublicKey, '#ed25519-key', { signatureAlgorithm: keypair.ed25519SignatureAlgorithm!, signatureValue: keypair.ed25519PublicKeySignature! })] : []),
|
...(keypair.ed25519PublicKey ? [this.renderKey(user, keypair.ed25519PublicKey, '#ed25519-key')] : []),
|
||||||
],
|
],
|
||||||
isCat: user.isCat,
|
isCat: user.isCat,
|
||||||
attachment: attachment.length ? attachment : undefined,
|
attachment: attachment.length ? attachment : undefined,
|
||||||
|
|
|
@ -212,16 +212,6 @@ export class ApPersonService implements OnModuleInit {
|
||||||
if (keyIdHost !== expectHost) {
|
if (keyIdHost !== expectHost) {
|
||||||
throw new Error('invalid Actor: additionalPublicKeys.id has different host');
|
throw new Error('invalid Actor: additionalPublicKeys.id has different host');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key.signature) {
|
|
||||||
throw new Error('invalid Actor: additionalPublicKeys.signature is not set');
|
|
||||||
}
|
|
||||||
if (typeof key.signature.signatureAlgorithm !== 'string') {
|
|
||||||
throw new Error('invalid Actor: additionalPublicKeys.signature.signatureAlgorithm is not a string');
|
|
||||||
}
|
|
||||||
if (typeof key.signature.signatureValue !== 'string') {
|
|
||||||
throw new Error('invalid Actor: additionalPublicKeys.signature.signatureValue is not a string');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,16 +386,11 @@ export class ApPersonService implements OnModuleInit {
|
||||||
|
|
||||||
if (person.additionalPublicKeys) {
|
if (person.additionalPublicKeys) {
|
||||||
for (const key of person.additionalPublicKeys) {
|
for (const key of person.additionalPublicKeys) {
|
||||||
if (
|
await transactionalEntityManager.save(new MiUserPublickey({
|
||||||
key.signature && key.signature.signatureAlgorithm && key.signature.signatureValue &&
|
keyId: key.id,
|
||||||
verify(key.signature.signatureAlgorithm, Buffer.from(key.publicKeyPem), person.publicKey.publicKeyPem, Buffer.from(key.signature.signatureValue, 'base64'))
|
userId: user.id,
|
||||||
) {
|
keyPem: key.publicKeyPem,
|
||||||
await transactionalEntityManager.save(new MiUserPublickey({
|
}));
|
||||||
keyId: key.id,
|
|
||||||
userId: user.id,
|
|
||||||
keyPem: key.publicKeyPem,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,16 +548,11 @@ export class ApPersonService implements OnModuleInit {
|
||||||
|
|
||||||
if (person.additionalPublicKeys) {
|
if (person.additionalPublicKeys) {
|
||||||
for (const key of person.additionalPublicKeys) {
|
for (const key of person.additionalPublicKeys) {
|
||||||
if (
|
await this.userPublickeysRepository.update({ keyId: key.id }, {
|
||||||
key.signature && key.signature.signatureAlgorithm && key.signature.signatureValue &&
|
userId: exist.id,
|
||||||
verify(key.signature.signatureAlgorithm, Buffer.from(key.publicKeyPem), person.publicKey.publicKeyPem, Buffer.from(key.signature.signatureValue, 'base64'))
|
keyPem: key.publicKeyPem,
|
||||||
) {
|
});
|
||||||
await this.userPublickeysRepository.update({ keyId: key.id }, {
|
availablePublicKeys.add(key.id);
|
||||||
userId: exist.id,
|
|
||||||
keyPem: key.publicKeyPem,
|
|
||||||
});
|
|
||||||
availablePublicKeys.add(key.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,14 +236,6 @@ export interface IKey extends IObject {
|
||||||
id: string;
|
id: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
publicKeyPem: string;
|
publicKeyPem: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature of publicKeyPem, signed by root privateKey (for additionalPublicKey)
|
|
||||||
*/
|
|
||||||
signature?: {
|
|
||||||
signatureAlgorithm: string;
|
|
||||||
signatureValue: string
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IApDocument extends IObject {
|
export interface IApDocument extends IObject {
|
||||||
|
|
|
@ -8,8 +8,6 @@ import * as util from 'node:util';
|
||||||
|
|
||||||
const generateKeyPair = util.promisify(crypto.generateKeyPair);
|
const generateKeyPair = util.promisify(crypto.generateKeyPair);
|
||||||
|
|
||||||
export const ED25519_PUBLIC_KEY_SIGNATURE_ALGORITHM = 'sha256';
|
|
||||||
|
|
||||||
export async function genRsaKeyPair(modulusLength = 4096) {
|
export async function genRsaKeyPair(modulusLength = 4096) {
|
||||||
return await generateKeyPair('rsa', {
|
return await generateKeyPair('rsa', {
|
||||||
modulusLength,
|
modulusLength,
|
||||||
|
@ -44,13 +42,10 @@ export async function genEd25519KeyPair() {
|
||||||
export async function genRSAAndEd25519KeyPair(rsaModulusLength = 4096) {
|
export async function genRSAAndEd25519KeyPair(rsaModulusLength = 4096) {
|
||||||
const rsa = await genRsaKeyPair(rsaModulusLength);
|
const rsa = await genRsaKeyPair(rsaModulusLength);
|
||||||
const ed25519 = await genEd25519KeyPair();
|
const ed25519 = await genEd25519KeyPair();
|
||||||
const ed25519PublicKeySignature = crypto.sign(ED25519_PUBLIC_KEY_SIGNATURE_ALGORITHM, Buffer.from(ed25519.publicKey), rsa.privateKey).toString('base64');
|
|
||||||
return {
|
return {
|
||||||
publicKey: rsa.publicKey,
|
publicKey: rsa.publicKey,
|
||||||
privateKey: rsa.privateKey,
|
privateKey: rsa.privateKey,
|
||||||
ed25519PublicKey: ed25519.publicKey,
|
ed25519PublicKey: ed25519.publicKey,
|
||||||
ed25519PrivateKey: ed25519.privateKey,
|
ed25519PrivateKey: ed25519.privateKey,
|
||||||
ed25519PublicKeySignature,
|
|
||||||
ed25519SignatureAlgorithm: `rsa-${ED25519_PUBLIC_KEY_SIGNATURE_ALGORITHM}`,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,26 +48,6 @@ export class MiUserKeypair {
|
||||||
})
|
})
|
||||||
public ed25519PrivateKey: string | null;
|
public ed25519PrivateKey: string | null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature of ed25519PublicKey, signed by privateKey. (base64)
|
|
||||||
*/
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 720,
|
|
||||||
nullable: true,
|
|
||||||
default: null,
|
|
||||||
})
|
|
||||||
public ed25519PublicKeySignature: string | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature algorithm of ed25519PublicKeySignature.
|
|
||||||
*/
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 32,
|
|
||||||
nullable: true,
|
|
||||||
default: null,
|
|
||||||
})
|
|
||||||
public ed25519SignatureAlgorithm: string | null;
|
|
||||||
|
|
||||||
constructor(data: Partial<MiUserKeypair>) {
|
constructor(data: Partial<MiUserKeypair>) {
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,6 @@ describe(genRSAAndEd25519KeyPair, () => {
|
||||||
expect(keyPair.ed25519PrivateKey).toMatch(/^-----BEGIN PRIVATE KEY-----/);
|
expect(keyPair.ed25519PrivateKey).toMatch(/^-----BEGIN PRIVATE KEY-----/);
|
||||||
expect(keyPair.ed25519PrivateKey).toMatch(/-----END PRIVATE KEY-----\n$/);
|
expect(keyPair.ed25519PrivateKey).toMatch(/-----END PRIVATE KEY-----\n$/);
|
||||||
expect(keyPair.ed25519PrivateKey).not.toBe(keyPair2.ed25519PrivateKey);
|
expect(keyPair.ed25519PrivateKey).not.toBe(keyPair2.ed25519PrivateKey);
|
||||||
expect(keyPair.ed25519PublicKeySignature).toBe(
|
|
||||||
crypto.sign(keyPair.ed25519SignatureAlgorithm.split('-').pop(), Buffer.from(keyPair.ed25519PublicKey), keyPair.privateKey).toString('base64'),
|
|
||||||
);
|
|
||||||
expect(crypto.verify(keyPair.ed25519SignatureAlgorithm, Buffer.from(keyPair.ed25519PublicKey), keyPair.publicKey, Buffer.from(keyPair.ed25519PublicKeySignature, 'base64'))).toBe(true);
|
|
||||||
expect(keyPair.ed25519PublicKeySignature).not.toBe(keyPair2.ed25519PublicKeySignature);
|
|
||||||
|
|
||||||
//const imported = await webCrypto.subtle.importKey('spki', Buffer.from(keyPair.publicKey).buffer, { name: 'rsa-pss', hash: 'sha-256' }, false, ['verify']);
|
//const imported = await webCrypto.subtle.importKey('spki', Buffer.from(keyPair.publicKey).buffer, { name: 'rsa-pss', hash: 'sha-256' }, false, ['verify']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue