-
Notifications
You must be signed in to change notification settings - Fork 89
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
feat: add Certificates store and validation #2072
Merged
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
9c123bc
chore: start moving certificates to its own store
siepra 51e4b9c
chore: postpone validation work
siepra 5d6eb4f
chore: move certificates to its own store
siepra 10bae11
chore: Merge branch 'chore/validate-csrs' into chore/1899
siepra c7a1fa1
fix: checking username availability
siepra 00180bf
test: fix
siepra 877a038
fix: lint
siepra fe15b53
test: temporarily remove empty spec files
siepra 68e9754
fix: resolving promises on certificates loading
siepra 14793ff
fix: updating peer list
siepra ccb11a8
chore: remove leftover
siepra a4ad686
test: add certificates store unit tests
siepra 714235b
feat: validate certificates against the authority and format #1899
siepra 73c4f8d
chore: Merge chore/validate-scrs into chore/1899
siepra f7ff976
chore: Revert "chore: Merge chore/validate-scrs into chore/1899"
siepra 869c389
chore: Merge branch 'develop' into chore/1899
siepra 9b222fe
chore: update CHANGELOG.md
siepra a725fa4
chore: Merge branch 'chore/validate-csrs' into chore/1899
siepra 45b5547
test: verify certificate against the authority (negative case)
siepra b704261
fix: lint
siepra fae74a7
bug: updatePeersList returning wrong data type
vinkabuki 64d64c0
fix: update localdb peers list
vinkabuki 561d8d5
chore: Revert "fix: update localdb peers list"
siepra c67793e
chore: Merge branch 'develop' into chore/1899
siepra a3f28a0
test: unskip write event test
siepra e28124a
test: remove deprecated and broken cases
siepra 3134e20
fix: disable broken test
siepra File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
138 changes: 138 additions & 0 deletions
138
packages/backend/src/nest/storage/certificates/certificates.store.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { getCrypto } from 'pkijs' | ||
|
||
import { EventEmitter } from 'events' | ||
import { StorageEvents } from '../storage.types' | ||
|
||
import EventStore from 'orbit-db-eventstore' | ||
import OrbitDB from 'orbit-db' | ||
|
||
import { loadCertificate, keyFromCertificate } from '@quiet/identity' | ||
|
||
import { ConnectionProcessInfo, NoCryptoEngineError, SocketActionTypes } from '@quiet/types' | ||
|
||
import { IsNotEmpty, IsBase64, validate } from 'class-validator' | ||
import { ValidationError } from '@nestjs/common' | ||
|
||
import createLogger from '../../common/logger' | ||
|
||
const logger = createLogger('CertificatesStore') | ||
|
||
class UserCertificateData { | ||
@IsNotEmpty() | ||
@IsBase64() | ||
certificate: string | ||
} | ||
|
||
export class CertificatesStore { | ||
public orbitDb: OrbitDB | ||
public store: EventStore<string> | ||
|
||
constructor(orbitDb: OrbitDB) { | ||
this.orbitDb = orbitDb | ||
} | ||
|
||
public async init(emitter: EventEmitter) { | ||
logger('Initializing certificates log store') | ||
|
||
this.store = await this.orbitDb.log<string>('certificates', { | ||
replicate: false, | ||
accessController: { | ||
write: ['*'], | ||
}, | ||
}) | ||
|
||
this.store.events.on('write', async (_address, entry) => { | ||
logger('Saved certificate locally') | ||
|
||
emitter.emit(StorageEvents.LOAD_CERTIFICATES, { | ||
certificates: await this.getCertificates(), | ||
}) | ||
|
||
// await this.updatePeersList() | ||
}) | ||
|
||
this.store.events.on('ready', async () => { | ||
siepra marked this conversation as resolved.
Show resolved
Hide resolved
|
||
logger('Loaded certificates to memory') | ||
|
||
emitter.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.LOADED_CERTIFICATES) | ||
|
||
emitter.emit(StorageEvents.LOAD_CERTIFICATES, { | ||
certificates: await this.getCertificates(), | ||
}) | ||
}) | ||
|
||
this.store.events.on('replicated', async () => { | ||
logger('REPLICATED: Certificates') | ||
|
||
emitter.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.CERTIFICATES_REPLICATED) | ||
|
||
emitter.emit(StorageEvents.LOAD_CERTIFICATES, { | ||
certificates: await this.getCertificates(), | ||
}) | ||
|
||
// await this.updatePeersList() | ||
}) | ||
|
||
} | ||
|
||
private async validateCertificate(certificate: string): Promise<boolean> { | ||
logger('Validating certificate') | ||
try { | ||
const crypto = getCrypto() | ||
|
||
if (!crypto) { | ||
throw new NoCryptoEngineError() | ||
} | ||
|
||
const parsedCertificate = loadCertificate(certificate) | ||
await parsedCertificate.verify() | ||
|
||
await this.validateCertificateFormat(certificate) | ||
|
||
// Validate | ||
|
||
} catch (err) { | ||
logger.error('Failed to validate user certificate:', certificate, err?.message) | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
private async validateCertificateFormat(certificate: string): Promise<ValidationError[]> { | ||
const data = new UserCertificateData() | ||
data.certificate = certificate | ||
|
||
const validationErrors = await validate(data) | ||
|
||
return validationErrors | ||
} | ||
|
||
protected async getCertificates() { | ||
const filteredCertificatesMap: Map<string, string> = new Map() | ||
|
||
const allCertificates = this.store | ||
.iterator({ limit: -1 }) | ||
.collect() | ||
.map(e => e.payload.value) | ||
|
||
await Promise.all( | ||
allCertificates | ||
.filter(async certificate => { | ||
const validation = await this.validateCertificate(certificate) | ||
return Boolean(validation) | ||
}).map(async certificate => { | ||
const parsedCertificate = loadCertificate(certificate) | ||
const pubKey = keyFromCertificate(parsedCertificate) | ||
|
||
if (filteredCertificatesMap.has(pubKey)) { | ||
filteredCertificatesMap.delete(pubKey) | ||
} | ||
|
||
filteredCertificatesMap.set(pubKey, certificate) | ||
}) | ||
) | ||
return [...filteredCertificatesMap.values()] | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Do I get it right, that we want to introduce the same method for certificate? Also, what are we particularly interested in validating inside
CertificateContainsFields
?