Compare commits

...
Author SHA1 Message Date
StandardNotes CI 8de6023848 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-10-07 09:13:30 +00:00
Karol Sójko 01ba715eba feat(api): add workspaces api (#1765)
* feat(api): add workspaces api

* fix(api): lint issues
2022-10-07 10:36:30 +02:00
StandardNotes CI 3733707bf1 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-10-06 21:43:54 +00:00
Mo cbbe913cd6 feat: experimental 005 operator (#1753) 2022-10-06 11:03:43 -05:00
Mo c13dd883a4 fix(mobile): reenable bouncing in lists 2022-10-06 09:56:56 -05:00
Aman Harwara 1377846f3f fix: dark mode toggling 2022-10-06 18:53:27 +05:30
StandardNotes CI f9ee197f04 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-10-06 12:35:47 +00:00
Aman Harwara c944eb9365 fix: increase default editor font size on mobile 2022-10-06 17:35:53 +05:30
StandardNotes CI 4609053bd0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
2022-10-06 11:25:46 +00:00
Aman Harwara 1c10214104 chore: remove unused dev build gh release (#1750) 2022-10-06 16:23:59 +05:30
Aman Harwara 1602d8157f fix: add android platform check for status bar color sync 2022-10-06 16:22:25 +05:30
StandardNotes CI ed1d583476 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-10-06 08:53:37 +00:00
Aman Harwara 05aff2776b fix: add fallback for initial window size (#1749) 2022-10-06 13:49:06 +05:30
StandardNotes CI 4b6662d62d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2022-10-05 20:52:08 +00:00
StandardNotes CI ac0ad49af0 chore(release): components 2022-10-05 20:51:54 +00:00
Aman Harwara 09b994f8f9 feat: add free dark mode (#1748) 2022-10-06 01:49:35 +05:30
Mo 6c26b96cdc fix(mobile): passcode timing options (#1744) 2022-10-05 10:08:54 -05:00
113 changed files with 1222 additions and 256 deletions
-10
View File
@@ -48,16 +48,6 @@ jobs:
uses: martinbeentjes/npm-get-version-action@main
with:
path: packages/mobile
- name: Release
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.CI_PAT_TOKEN }}
tag_name: "@standardnotes/mobile@${{ steps.package-version.outputs.current-version}}"
prerelease: true
draft: true
name: "Mobile Dev ${{ steps.package-version.outputs.current-version }}"
files: |
packages/mobile/android/app/build/outputs/apk/dev/release/app-dev-release.apk
ios:
defaults:
run:
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Features
* **api:** add workspaces api ([#1765](https://github.com/standardnotes/app/issues/1765)) ([01ba715](https://github.com/standardnotes/app/commit/01ba715eba987a7da1ee062fec0b3593a7a453ed))
# [1.10.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
## [1.9.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/api
## [1.9.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/api
+4 -4
View File
@@ -10,10 +10,10 @@ module.exports = {
},
coverageThreshold: {
global: {
branches: 20,
functions: 66,
lines: 63,
statements: 63
branches: 22,
functions: 69,
lines: 67,
statements: 67
}
}
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.9.1",
"version": "1.11.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,5 +1,4 @@
import { RootKeyParamsInterface } from '@standardnotes/models'
import { ErrorMessage } from '../../Error/ErrorMessage'
import { ApiCallError } from '../../Error/ApiCallError'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
@@ -0,0 +1,3 @@
export enum WorkspaceApiOperations {
Creating,
}
@@ -0,0 +1,76 @@
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
import { WorkspaceApiService } from './WorkspaceApiService'
describe('WorkspaceApiService', () => {
let workspaceServer: WorkspaceServerInterface
const createService = () => new WorkspaceApiService(workspaceServer)
beforeEach(() => {
workspaceServer = {} as jest.Mocked<WorkspaceServerInterface>
workspaceServer.createWorkspace = jest.fn().mockReturnValue({
data: { uuid: '1-2-3' },
} as jest.Mocked<WorkspaceCreationResponse>)
})
it('should create a workspace', async () => {
const response = await createService().createWorkspace({
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
})
expect(response).toEqual({
data: {
uuid: '1-2-3',
},
})
expect(workspaceServer.createWorkspace).toHaveBeenCalledWith({
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
})
})
it('should not create a workspace if it is already creating', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[WorkspaceApiOperations.Creating, true]]),
})
let error = null
try {
await service.createWorkspace({
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not create a workspace if the server fails', async () => {
workspaceServer.createWorkspace = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().createWorkspace({
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
})
@@ -0,0 +1,43 @@
import { ErrorMessage } from '../../Error/ErrorMessage'
import { ApiCallError } from '../../Error/ApiCallError'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
import { WorkspaceApiServiceInterface } from './WorkspaceApiServiceInterface'
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
export class WorkspaceApiService implements WorkspaceApiServiceInterface {
private operationsInProgress: Map<WorkspaceApiOperations, boolean>
constructor(private workspaceServer: WorkspaceServerInterface) {
this.operationsInProgress = new Map()
}
async createWorkspace(dto: {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceName?: string
}): Promise<WorkspaceCreationResponse> {
if (this.operationsInProgress.get(WorkspaceApiOperations.Creating)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(WorkspaceApiOperations.Creating, true)
try {
const response = await this.workspaceServer.createWorkspace({
encryptedPrivateKey: dto.encryptedPrivateKey,
encryptedWorkspaceKey: dto.encryptedWorkspaceKey,
publicKey: dto.publicKey,
workspaceName: dto.workspaceName,
})
this.operationsInProgress.set(WorkspaceApiOperations.Creating, false)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}
}
@@ -0,0 +1,10 @@
import { WorkspaceCreationResponse } from '../../Response'
export interface WorkspaceApiServiceInterface {
createWorkspace(dto: {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceName?: string
}): Promise<WorkspaceCreationResponse>
}
+2
View File
@@ -5,3 +5,5 @@ export * from './User/UserApiService'
export * from './User/UserApiServiceInterface'
export * from './WebSocket/WebSocketApiService'
export * from './WebSocket/WebSocketApiServiceInterface'
export * from './Workspace/WorkspaceApiService'
export * from './Workspace/WorkspaceApiServiceInterface'
@@ -0,0 +1,7 @@
export type WorkspaceCreationRequestParams = {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceName?: string
[additionalParam: string]: unknown
}
+1
View File
@@ -6,3 +6,4 @@ export * from './Subscription/SubscriptionInviteListRequestParams'
export * from './Subscription/SubscriptionInviteRequestParams'
export * from './User/UserRegistrationRequestParams'
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
export * from './Workspace/WorkspaceCreationRequestParams'
@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { WorkspaceCreationResponseBody } from './WorkspaceCreationResponseBody'
export interface WorkspaceCreationResponse extends HttpResponse {
data: Either<WorkspaceCreationResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export type WorkspaceCreationResponseBody = {
uuid: string
}
@@ -12,3 +12,5 @@ export * from './User/UserRegistrationResponse'
export * from './User/UserRegistrationResponseBody'
export * from './WebSocket/WebSocketConnectionTokenResponse'
export * from './WebSocket/WebSocketConnectionTokenResponseBody'
export * from './Workspace/WorkspaceCreationResponse'
export * from './Workspace/WorkspaceCreationResponseBody'
@@ -0,0 +1,9 @@
const WorkspacePaths = {
createWorkspace: '/v1/workspaces',
}
export const Paths = {
v1: {
...WorkspacePaths,
},
}
@@ -0,0 +1,31 @@
import { HttpServiceInterface } from '../../Http'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceServer } from './WorkspaceServer'
describe('WorkspaceServer', () => {
let httpService: HttpServiceInterface
const createServer = () => new WorkspaceServer(httpService)
beforeEach(() => {
httpService = {} as jest.Mocked<HttpServiceInterface>
httpService.post = jest.fn().mockReturnValue({
data: { uuid: '1-2-3' },
} as jest.Mocked<WorkspaceCreationResponse>)
})
it('should create a workspace', async () => {
const response = await createServer().createWorkspace({
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
})
expect(response).toEqual({
data: {
uuid: '1-2-3',
},
})
})
})
@@ -0,0 +1,16 @@
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { Paths } from './Paths'
import { WorkspaceServerInterface } from './WorkspaceServerInterface'
export class WorkspaceServer implements WorkspaceServerInterface {
constructor(private httpService: HttpServiceInterface) {}
async createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse> {
const response = await this.httpService.post(Paths.v1.createWorkspace, params)
return response as WorkspaceCreationResponse
}
}
@@ -0,0 +1,6 @@
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
export interface WorkspaceServerInterface {
createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse>
}
+2
View File
@@ -4,3 +4,5 @@ export * from './User/UserServer'
export * from './User/UserServerInterface'
export * from './WebSocket/WebSocketServer'
export * from './WebSocket/WebSocketServerInterface'
export * from './Workspace/WorkspaceServer'
export * from './Workspace/WorkspaceServerInterface'
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.7.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/components-meta
## [2.7.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/components-meta
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -70,9 +70,9 @@
"binary": "b621bfd81829a205f2f02e85d446e277ad20e367b92676ec20b3ee59b1bfab3f"
},
"org.standardnotes.markdown-visual-editor": {
"version": "1.1.2",
"base64": "2645d31c51fa6a198c14a32737ee4045dcbbecb8bdaa1432ca4686cab78834d2",
"binary": "c4479f8768f5f6cda41850212a8e03927990adfa33ebd36e29ee6de2335e8e46"
"version": "1.1.3",
"base64": "8bb35d66b0ef2bee2243dbdf514c0bf50ef706c034395cb9d50119f017fef9aa",
"binary": "1738193ab2a57cf280c29c9ca1e4caf49179009c7382048577d2b34eaf96d5e9"
},
"org.standardnotes.simple-task-editor": {
"version": "1.4.4",
@@ -80,9 +80,9 @@
"binary": "9df983a144b9686f4f58b03bf78bf1f84f53aea60251621a75586722bf576475"
},
"org.standardnotes.token-vault": {
"version": "2.2.4",
"base64": "ac07bd989bfb1100aeaacf6687b492b4aac754645da5a132ecd879dc64786522",
"binary": "8f74bacd0dd3a0a6e04bcce9c54694bb2f429b470a3023e96e0a750a271184ae"
"version": "2.2.5",
"base64": "ca103f730737d3a3046001666b626139692c08ab4dd5748b0290fdcb1b1eb04a",
"binary": "52f419cd60da00a2cad13fd357b50993c7694160fcca1c1bdf1859e3bdca3a2b"
},
"org.standardnotes.standard-sheets": {
"version": "1.5.1",
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/components-meta",
"version": "2.7.15",
"version": "2.7.16",
"private": true,
"author": "Standard Notes.",
"main": "dist",
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/markdown-visual
## [1.1.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
### Bug Fixes
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/markdown-visual",
"version": "1.1.2",
"version": "1.1.3",
"author": "Johnny Almonte <[email protected]>",
"description": "A lightweight WYSIWYG markdown editor for Standard Notes, derived from Milkdown.",
"keywords": [
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/authenticator
## [2.2.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
### Bug Fixes
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/authenticator",
"version": "2.2.4",
"version": "2.2.5",
"main": "dist/dist.js",
"author": "Standard Notes",
"private": true,
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.23.196](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.195](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.194](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.193](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.192](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.191](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.23.191",
"version": "3.23.196",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
## [1.15.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/encryption
## [1.15.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/encryption
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.15.10",
"version": "1.16.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -44,3 +44,8 @@ export enum V004Algorithm {
EncryptionKeyLength = 256,
EncryptionNonceLength = 192,
}
export enum V005Algorithm {
AsymmetricEncryptionNonceLength = 192,
SymmetricEncryptionNonceLength = 192,
}
@@ -254,21 +254,21 @@ export class SNProtocolOperator004 implements SynchronousOperator {
encrypted: EncryptedParameters,
key: ItemsKeyInterface | SNRootKey,
): DecryptedParameters<C> | ErrorDecryptingParameters {
const itemKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
const authenticatedData = this.stringToAuthenticatedData(itemKeyComponents.authenticatedData, {
const contentKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
const authenticatedData = this.stringToAuthenticatedData(contentKeyComponents.authenticatedData, {
u: encrypted.uuid,
v: encrypted.version,
})
const useAuthenticatedString = this.authenticatedDataToString(authenticatedData)
const itemKey = this.decryptString004(
itemKeyComponents.ciphertext,
const contentKey = this.decryptString004(
contentKeyComponents.ciphertext,
key.itemsKey,
itemKeyComponents.nonce,
contentKeyComponents.nonce,
useAuthenticatedString,
)
if (!itemKey) {
if (!contentKey) {
console.error('Error decrypting itemKey parameters', encrypted)
return {
uuid: encrypted.uuid,
@@ -279,10 +279,11 @@ export class SNProtocolOperator004 implements SynchronousOperator {
const contentComponents = this.deconstructEncryptedPayloadString(encrypted.content)
const content = this.decryptString004(
contentComponents.ciphertext,
itemKey,
contentKey,
contentComponents.nonce,
useAuthenticatedString,
)
if (!content) {
return {
uuid: encrypted.uuid,
@@ -305,6 +306,7 @@ export class SNProtocolOperator004 implements SynchronousOperator {
V004Algorithm.ArgonMemLimit,
V004Algorithm.ArgonOutputKeyBytes,
)
const partitions = Utils.splitString(derivedKey, 2)
const masterKey = partitions[0]
const serverPassword = partitions[1]
@@ -0,0 +1,75 @@
import { ProtocolOperator005 } from './Operator005'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
describe('operator 005', () => {
let crypto: PureCryptoInterface
let operator: ProtocolOperator005
beforeEach(() => {
crypto = {} as jest.Mocked<PureCryptoInterface>
crypto.generateRandomKey = jest.fn().mockImplementation(() => {
return 'random-string'
})
crypto.xchacha20Encrypt = jest.fn().mockImplementation((text: string) => {
return `<e>${text}<e>`
})
crypto.xchacha20Decrypt = jest.fn().mockImplementation((text: string) => {
return text.split('<e>')[1]
})
crypto.sodiumCryptoBoxGenerateKeypair = jest.fn().mockImplementation(() => {
return { privateKey: 'private-key', publicKey: 'public-key', keyType: 'x25519' }
})
crypto.sodiumCryptoBoxEasyEncrypt = jest.fn().mockImplementation((text: string) => {
return `<e>${text}<e>`
})
crypto.sodiumCryptoBoxEasyDecrypt = jest.fn().mockImplementation((text: string) => {
return text.split('<e>')[1]
})
operator = new ProtocolOperator005(crypto)
})
it('should generateKeyPair', () => {
const result = operator.generateKeyPair()
expect(result).toEqual({ privateKey: 'private-key', publicKey: 'public-key', keyType: 'x25519' })
})
it('should asymmetricEncryptKey', () => {
const senderKeypair = operator.generateKeyPair()
const recipientKeypair = operator.generateKeyPair()
const plaintext = 'foo'
const result = operator.asymmetricEncryptKey(plaintext, senderKeypair.privateKey, recipientKeypair.publicKey)
expect(result).toEqual(`${'005_KeyAsym'}:random-string:<e>foo<e>`)
})
it('should asymmetricDecryptKey', () => {
const senderKeypair = operator.generateKeyPair()
const recipientKeypair = operator.generateKeyPair()
const plaintext = 'foo'
const ciphertext = operator.asymmetricEncryptKey(plaintext, senderKeypair.privateKey, recipientKeypair.publicKey)
const decrypted = operator.asymmetricDecryptKey(ciphertext, senderKeypair.publicKey, recipientKeypair.privateKey)
expect(decrypted).toEqual('foo')
})
it('should symmetricEncryptPrivateKey', () => {
const keypair = operator.generateKeyPair()
const symmetricKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
const encryptedKey = operator.symmetricEncryptPrivateKey(keypair.privateKey, symmetricKey)
expect(encryptedKey).toEqual(`${'005_KeySym'}:random-string:<e>${keypair.privateKey}<e>`)
})
it('should symmetricDecryptPrivateKey', () => {
const keypair = operator.generateKeyPair()
const symmetricKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
const encryptedKey = operator.symmetricEncryptPrivateKey(keypair.privateKey, symmetricKey)
const decryptedKey = operator.symmetricDecryptPrivateKey(encryptedKey, symmetricKey)
expect(decryptedKey).toEqual(keypair.privateKey)
})
})
@@ -0,0 +1,80 @@
import { ProtocolVersion } from '@standardnotes/common'
import { Base64String, HexString, PkcKeyPair, Utf8String } from '@standardnotes/sncrypto-common'
import { V005Algorithm } from '../../Algorithm'
import { SNProtocolOperator004 } from '../004/Operator004'
const VersionString = '005'
const SymmetricCiphertextPrefix = `${VersionString}_KeySym`
const AsymmetricCiphertextPrefix = `${VersionString}_KeyAsym`
export type AsymmetricallyEncryptedKey = Base64String
export type SymmetricallyEncryptedPrivateKey = Base64String
/**
* @experimental
* @unreleased
*/
export class ProtocolOperator005 extends SNProtocolOperator004 {
public override getEncryptionDisplayName(): string {
return 'XChaCha20-Poly1305'
}
override get version(): ProtocolVersion {
return VersionString as ProtocolVersion
}
generateKeyPair(): PkcKeyPair {
return this.crypto.sodiumCryptoBoxGenerateKeypair()
}
asymmetricEncryptKey(
keyToEncrypt: HexString,
senderSecretKey: HexString,
recipientPublicKey: HexString,
): AsymmetricallyEncryptedKey {
const nonce = this.crypto.generateRandomKey(V005Algorithm.AsymmetricEncryptionNonceLength)
const ciphertext = this.crypto.sodiumCryptoBoxEasyEncrypt(keyToEncrypt, nonce, senderSecretKey, recipientPublicKey)
return [AsymmetricCiphertextPrefix, nonce, ciphertext].join(':')
}
asymmetricDecryptKey(
keyToDecrypt: AsymmetricallyEncryptedKey,
senderPublicKey: HexString,
recipientSecretKey: HexString,
): Utf8String {
const components = keyToDecrypt.split(':')
const nonce = components[1]
return this.crypto.sodiumCryptoBoxEasyDecrypt(keyToDecrypt, nonce, senderPublicKey, recipientSecretKey)
}
symmetricEncryptPrivateKey(privateKey: HexString, symmetricKey: HexString): SymmetricallyEncryptedPrivateKey {
if (symmetricKey.length !== 64) {
throw new Error('Symmetric key length must be 256 bits')
}
const nonce = this.crypto.generateRandomKey(V005Algorithm.SymmetricEncryptionNonceLength)
const encryptedKey = this.crypto.xchacha20Encrypt(privateKey, nonce, symmetricKey)
return [SymmetricCiphertextPrefix, nonce, encryptedKey].join(':')
}
symmetricDecryptPrivateKey(
encryptedPrivateKey: SymmetricallyEncryptedPrivateKey,
symmetricKey: HexString,
): HexString | null {
if (symmetricKey.length !== 64) {
throw new Error('Symmetric key length must be 256 bits')
}
const components = encryptedPrivateKey.split(':')
const nonce = components[1]
return this.crypto.xchacha20Decrypt(encryptedPrivateKey, nonce, symmetricKey)
}
}
@@ -11,7 +11,7 @@ import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit'
import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit'
export interface EncryptionProvider {
export interface EncryptionProviderInterface {
encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface>
encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]>
+2 -1
View File
@@ -14,11 +14,12 @@ export * from './Operator/001/Operator001'
export * from './Operator/002/Operator002'
export * from './Operator/003/Operator003'
export * from './Operator/004/Operator004'
export * from './Operator/005/Operator005'
export * from './Operator/Functions'
export * from './Operator/Operator'
export * from './Operator/OperatorManager'
export * from './Operator/OperatorWrapper'
export * from './Service/Encryption/EncryptionProvider'
export * from './Service/Encryption/EncryptionProviderInterface'
export * from './Service/Functions'
export * from './Service/RootKey/KeyMode'
export * from './Service/RootKey/RootKeyServiceEvent'
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.23.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.23.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.23.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/filepicker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.23.10",
"version": "1.23.12",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/files
## [1.10.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/files
## [1.10.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.10.10",
"version": "1.10.12",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+30
View File
@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.40.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/mobile
# [3.40.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
## [3.39.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/mobile
## [3.39.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Bug Fixes
* add android platform check for status bar color sync ([1602d81](https://github.com/standardnotes/app/commit/1602d8157f9e61c6ad842bcce28f3aaf6a8f7154))
## [3.39.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/mobile
## [3.39.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Bug Fixes
* **mobile:** passcode timing options ([#1744](https://github.com/standardnotes/app/issues/1744)) ([6c26b96](https://github.com/standardnotes/app/commit/6c26b96cdce0d69a4a459104709880850a401a92))
## [3.39.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.39.8",
"version": "3.40.1",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+5 -5
View File
@@ -483,7 +483,7 @@ export class ApplicationState extends ApplicationService {
return
}
const hasBiometrics = this.application.hasBiometrics()
const hasBiometrics = this.application.protections.hasBiometricsEnabled()
const hasPasscode = this.application.hasPasscode()
const passcodeLockImmediately = hasPasscode && this.passcodeTiming === MobileUnlockTiming.Immediately
const biometricsLockImmediately =
@@ -571,19 +571,19 @@ export class ApplicationState extends ApplicationService {
}
private async getScreenshotPrivacyEnabled(): Promise<boolean> {
return this.application.getMobileScreenshotPrivacyEnabled()
return this.application.protections.getMobileScreenshotPrivacyEnabled()
}
private async getPasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
return this.application.getMobilePasscodeTiming()
return this.application.protections.getMobilePasscodeTiming()
}
private async getBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
return this.application.getMobileBiometricsTiming()
return this.application.protections.getMobileBiometricsTiming()
}
public async setScreenshotPrivacyEnabled(enabled: boolean) {
await this.application.setMobileScreenshotPrivacyEnabled(enabled)
await this.application.protections.setMobileScreenshotPrivacyEnabled(enabled)
this.screenshotPrivacyEnabled = enabled
await (this.application.deviceInterface as MobileDevice).setAndroidScreenshotPrivacy(enabled)
}
+1 -1
View File
@@ -460,7 +460,7 @@ export class MobileDevice implements MobileDeviceInterface {
}
reloadStatusBarStyle(animated = true) {
if (this.statusBarBgColor) {
if (this.statusBarBgColor && Platform.OS === 'android') {
StatusBar.setBackgroundColor(this.statusBarBgColor, animated)
}
StatusBar.setBarStyle(this.isDarkMode ? 'light-content' : 'dark-content', animated)
@@ -1,6 +1,7 @@
import {
Base64String,
HexString,
PkcKeyPair,
PureCryptoInterface,
SodiumConstant,
StreamDecryptorResult,
@@ -129,6 +130,28 @@ export class SNReactNativeCrypto implements PureCryptoInterface {
}
}
public sodiumCryptoBoxEasyEncrypt(
_message: Utf8String,
_nonce: HexString,
_senderSecretKey: HexString,
_recipientPublicKey: HexString,
): Base64String {
throw new Error('Not implemented')
}
public sodiumCryptoBoxEasyDecrypt(
_ciphertext: Base64String,
_nonce: HexString,
_senderPublicKey: HexString,
_recipientSecretKey: HexString,
): Utf8String {
throw new Error('Not implemented')
}
public sodiumCryptoBoxGenerateKeypair(): PkcKeyPair {
throw new Error('Not implemented')
}
public generateUUID() {
const randomBuf = Sodium.randombytes_buf(16)
const tempBuf = new Uint8Array(randomBuf.length / 2)
@@ -197,7 +197,7 @@ export const Authenticate = ({
state: AuthenticationValueStateType.Pending,
})
if (await application?.getMobileScreenshotPrivacyEnabled()) {
if (application?.protections.getMobileScreenshotPrivacyEnabled()) {
hide()
}
@@ -31,21 +31,23 @@ export const SecuritySection = (props: Props) => {
const [hasBiometrics, setHasBiometrics] = useState(false)
const [supportsBiometrics, setSupportsBiometrics] = useState(false)
const [biometricsTimingOptions, setBiometricsTimingOptions] = useState(() =>
application!.getBiometricsTimingOptions(),
application!.protections.getMobileBiometricsTimingOptions(),
)
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() =>
application!.protections.getMobilePasscodeTimingOptions(),
)
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() => application!.getPasscodeTimingOptions())
useEffect(() => {
let mounted = true
const getHasScreenshotPrivacy = async () => {
const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled()
const hasScreenshotPrivacyEnabled = application?.protections.getMobileScreenshotPrivacyEnabled()
if (mounted) {
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
}
}
void getHasScreenshotPrivacy()
const getHasBiometrics = async () => {
const appHasBiometrics = application!.hasBiometrics()
const appHasBiometrics = application!.protections.hasBiometricsEnabled()
if (mounted) {
setHasBiometrics(appHasBiometrics)
}
@@ -68,7 +70,7 @@ export const SecuritySection = (props: Props) => {
useFocusEffect(
useCallback(() => {
if (props.hasPasscode) {
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
}
}, [application, props.hasPasscode]),
)
@@ -126,12 +128,12 @@ export const SecuritySection = (props: Props) => {
const setBiometricsTiming = async (timing: MobileUnlockTiming) => {
await application?.getAppState().setBiometricsTiming(timing)
setBiometricsTimingOptions(() => application!.getBiometricsTimingOptions())
setBiometricsTimingOptions(() => application!.protections.getMobileBiometricsTimingOptions())
}
const setPasscodeTiming = async (timing: MobileUnlockTiming) => {
await application?.getAppState().setPasscodeTiming(timing)
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
}
const onScreenshotPrivacyPress = async () => {
@@ -153,14 +155,14 @@ export const SecuritySection = (props: Props) => {
void disableAuthentication('biometrics')
} else {
setHasBiometrics(true)
await application?.enableBiometrics()
await application?.protections.enableBiometrics()
await setBiometricsTiming(MobileUnlockTiming.OnQuit)
props.updateProtectionsAvailable()
}
}
const disableBiometrics = useCallback(async () => {
if (await application?.disableBiometrics()) {
if (await application?.protections.disableBiometrics()) {
setHasBiometrics(false)
props.updateProtectionsAvailable()
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.24.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
# [1.23.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Features
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
## [1.22.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/models
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.22.1",
"version": "1.24.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -24,11 +24,11 @@ export class DecryptedItem<C extends ItemContent = ItemContent>
constructor(payload: DecryptedPayloadInterface<C>) {
super(payload)
this.conflictOf = payload.content.conflict_of
const userModVal = this.getAppDomainValueWithDefault(AppDataField.UserModifiedDate, this.serverUpdatedAt || 0)
this.userModifiedDate = new Date(userModVal as number | Date)
this.conflictOf = payload.content.conflict_of
this.updatedAtString = dateToLocalizedString(this.userModifiedDate)
this.protected = useBoolean(this.payload.content.protected, false)
this.trashed = useBoolean(this.payload.content.trashed, false)
@@ -37,6 +37,7 @@ export enum PrefKey {
NewNoteTitleFormat = 'newNoteTitleFormat',
CustomNoteTitleFormat = 'customNoteTitleFormat',
UpdateSavingStatusIndicator = 'updateSavingStatusIndicator',
DarkMode = 'darkMode',
}
export enum NewNoteTitleFormat {
@@ -82,8 +83,8 @@ export type PrefValue = {
[PrefKey.NotesHideTags]: boolean
[PrefKey.NotesHideEditorIcon]: boolean
[PrefKey.UseSystemColorScheme]: boolean
[PrefKey.AutoLightThemeIdentifier]: FeatureIdentifier | 'Default'
[PrefKey.AutoDarkThemeIdentifier]: FeatureIdentifier | 'Default'
[PrefKey.AutoLightThemeIdentifier]: FeatureIdentifier | 'Default' | 'Dark'
[PrefKey.AutoDarkThemeIdentifier]: FeatureIdentifier | 'Default' | 'Dark'
[PrefKey.NoteAddToParentFolders]: boolean
[PrefKey.MobileSortNotesBy]: CollectionSortProperty
[PrefKey.MobileSortNotesReverse]: boolean
@@ -99,4 +100,5 @@ export type PrefValue = {
[PrefKey.EditorLineHeight]: EditorLineHeight
[PrefKey.EditorFontSize]: EditorFontSize
[PrefKey.UpdateSavingStatusIndicator]: boolean
[PrefKey.DarkMode]: boolean
}
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.122](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.121](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.120](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.119](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.118](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.117](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.116](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.3.116",
"version": "1.3.122",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.26.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Features
* **api:** add workspaces api ([#1765](https://github.com/standardnotes/app/issues/1765)) ([01ba715](https://github.com/standardnotes/app/commit/01ba715eba987a7da1ee062fec0b3593a7a453ed))
# [1.25.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
## [1.24.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/services
## [1.24.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/services
+1 -1
View File
@@ -12,7 +12,7 @@ module.exports = {
global: {
branches: 9,
functions: 10,
lines: 17,
lines: 16,
statements: 16
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.24.2",
"version": "1.26.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,13 +1,15 @@
import { ApplicationIdentifier, ContentType } from '@standardnotes/common'
import { BackupFile, DecryptedItemInterface, ItemStream, Platform, PrefKey, PrefValue } from '@standardnotes/models'
import { FilesClientInterface } from '@standardnotes/files'
import { AlertService } from '../Alert/AlertService'
import { AlertService } from '../Alert/AlertService'
import { ComponentManagerInterface } from '../Component/ComponentManagerInterface'
import { ApplicationEvent } from '../Event/ApplicationEvent'
import { ApplicationEventCallback } from '../Event/ApplicationEventCallback'
import { FeaturesClientInterface } from '../Feature/FeaturesClientInterface'
import { SubscriptionClientInterface } from '../Subscription/SubscriptionClientInterface'
import { DeviceInterface } from '../Device/DeviceInterface'
import { WorkspaceClientInterface } from '../Workspace/WorkspaceClientInterface'
import { ItemsClientInterface } from '../Item/ItemsClientInterface'
import { MutatorClientInterface } from '../Mutator/MutatorClientInterface'
import { StorageValueModes } from '../Storage/StorageTypes'
@@ -15,7 +17,6 @@ import { StorageValueModes } from '../Storage/StorageTypes'
import { DeinitMode } from './DeinitMode'
import { DeinitSource } from './DeinitSource'
import { UserClientInterface } from './UserClientInterface'
import { DeviceInterface } from '../Device/DeviceInterface'
export interface ApplicationInterface {
deinit(mode: DeinitMode, source: DeinitSource): void
@@ -49,6 +50,7 @@ export interface ApplicationInterface {
get user(): UserClientInterface
get files(): FilesClientInterface
get subscriptions(): SubscriptionClientInterface
get workspaces(): WorkspaceClientInterface
readonly identifier: ApplicationIdentifier
readonly platform: Platform
deviceInterface: DeviceInterface
@@ -1,5 +1,5 @@
import { ContentType, Uuid } from '@standardnotes/common'
import { EncryptionProvider } from '@standardnotes/encryption'
import { EncryptionProviderInterface } from '@standardnotes/encryption'
import { PayloadEmitSource, FileItem, CreateEncryptedBackupFileContextPayload } from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses'
import { FilesApiInterface, FileBackupMetadataFile, FileBackupsDevice, FileBackupsMapping } from '@standardnotes/files'
@@ -15,7 +15,7 @@ export class FilesBackupService extends AbstractService {
constructor(
private items: ItemManagerInterface,
private api: FilesApiInterface,
private encryptor: EncryptionProvider,
private encryptor: EncryptionProviderInterface,
private device: FileBackupsDevice,
private status: StatusServiceInterface,
protected override internalEventBus: InternalEventBusInterface,
@@ -4,7 +4,7 @@ import {
DecryptedParameters,
EncryptedParameters,
encryptedParametersFromPayload,
EncryptionProvider,
EncryptionProviderInterface,
ErrorDecryptingParameters,
findDefaultItemsKey,
FindPayloadInDecryptionSplit,
@@ -100,7 +100,7 @@ import { EncryptionServiceEvent } from './EncryptionServiceEvent'
* It also exposes public methods that allows consumers to retrieve an items key
* for a particular payload, and also retrieve all available items keys.
*/
export class EncryptionService extends AbstractService<EncryptionServiceEvent> implements EncryptionProvider {
export class EncryptionService extends AbstractService<EncryptionServiceEvent> implements EncryptionProviderInterface {
private operatorManager: OperatorManager
private readonly itemsEncryption: ItemsEncryptionService
private readonly rootKeyEncryption: RootKeyEncryptionService
@@ -714,7 +714,7 @@ export class EncryptionService extends AbstractService<EncryptionServiceEvent> i
await this.rootKeyEncryption.createNewDefaultItemsKey()
}
this.syncUnsycnedItemsKeys()
this.syncUnsyncedItemsKeys()
}
private async handleFullSyncCompletion() {
@@ -734,7 +734,7 @@ export class EncryptionService extends AbstractService<EncryptionServiceEvent> i
* items key never syncing to the account even though it is being used to encrypt synced items.
* Until we can determine its cause, this corrective function will find any such keys and sync them.
*/
private syncUnsycnedItemsKeys(): void {
private syncUnsyncedItemsKeys(): void {
if (!this.hasAccount()) {
return
}
@@ -5,7 +5,7 @@ import {
ItemsKeyContent,
RootKeyInterface,
} from '@standardnotes/models'
import { EncryptionProvider, KeyRecoveryStrings, SNRootKeyParams } from '@standardnotes/encryption'
import { EncryptionProviderInterface, KeyRecoveryStrings, SNRootKeyParams } from '@standardnotes/encryption'
import { ChallengeServiceInterface } from '../Challenge/ChallengeServiceInterface'
import { ChallengePrompt } from '../Challenge/Prompt/ChallengePrompt'
import { ChallengeReason } from '../Challenge/Types/ChallengeReason'
@@ -13,7 +13,7 @@ import { ChallengeValidation } from '../Challenge/Types/ChallengeValidation'
export async function DecryptItemsKeyWithUserFallback(
itemsKey: EncryptedPayloadInterface,
encryptor: EncryptionProvider,
encryptor: EncryptionProviderInterface,
challengor: ChallengeServiceInterface,
): Promise<DecryptedPayloadInterface<ItemsKeyContent> | 'failed' | 'aborted'> {
const decryptionResult = await encryptor.decryptSplitSingle<ItemsKeyContent>({
@@ -37,7 +37,7 @@ export async function DecryptItemsKeyWithUserFallback(
export async function DecryptItemsKeyByPromptingUser(
itemsKey: EncryptedPayloadInterface,
encryptor: EncryptionProvider,
encryptor: EncryptionProviderInterface,
challengor: ChallengeServiceInterface,
keyParams?: SNRootKeyParams,
): Promise<
@@ -1,6 +1,6 @@
import { PureCryptoInterface, StreamEncryptor } from '@standardnotes/sncrypto-common'
import { FileItem } from '@standardnotes/models'
import { EncryptionProvider } from '@standardnotes/encryption'
import { EncryptionProviderInterface } from '@standardnotes/encryption'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { ChallengeServiceInterface } from '../Challenge'
@@ -19,7 +19,7 @@ describe('fileService', () => {
let crypto: PureCryptoInterface
let challengor: ChallengeServiceInterface
let fileService: FileService
let encryptor: EncryptionProvider
let encryptor: EncryptionProviderInterface
let internalEventBus: InternalEventBusInterface
beforeEach(() => {
@@ -41,7 +41,7 @@ describe('fileService', () => {
syncService = {} as jest.Mocked<SyncServiceInterface>
syncService.sync = jest.fn()
encryptor = {} as jest.Mocked<EncryptionProvider>
encryptor = {} as jest.Mocked<EncryptionProviderInterface>
alertService = {} as jest.Mocked<AlertService>
alertService.confirm = jest.fn().mockReturnValue(true)
@@ -12,7 +12,7 @@ import {
} from '@standardnotes/models'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { UuidGenerator } from '@standardnotes/utils'
import { EncryptionProvider, SNItemsKey } from '@standardnotes/encryption'
import { EncryptionProviderInterface, SNItemsKey } from '@standardnotes/encryption'
import {
DownloadAndDecryptFileOperation,
EncryptAndUploadFileOperation,
@@ -49,7 +49,7 @@ export class FileService extends AbstractService implements FilesClientInterface
private api: FilesApiInterface,
private itemManager: ItemManagerInterface,
private syncService: SyncServiceInterface,
private encryptor: EncryptionProvider,
private encryptor: EncryptionProviderInterface,
private challengor: ChallengeServiceInterface,
private alertService: AlertService,
private crypto: PureCryptoInterface,
@@ -0,0 +1,8 @@
export interface WorkspaceClientInterface {
createWorkspace(dto: {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceName?: string
}): Promise<{ uuid: string } | null>
}
@@ -0,0 +1,32 @@
import { WorkspaceApiServiceInterface } from '@standardnotes/api'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { AbstractService } from '../Service/AbstractService'
import { WorkspaceClientInterface } from './WorkspaceClientInterface'
export class WorkspaceManager extends AbstractService implements WorkspaceClientInterface {
constructor(
private workspaceApiService: WorkspaceApiServiceInterface,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
}
async createWorkspace(dto: {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceName?: string
}): Promise<{ uuid: string } | null> {
try {
const result = await this.workspaceApiService.createWorkspace(dto)
if (result.data.error !== undefined) {
return null
}
return result.data
} catch (error) {
return null
}
}
}
+2
View File
@@ -73,3 +73,5 @@ export * from './Sync/SyncOptions'
export * from './Sync/SyncQueueStrategy'
export * from './Sync/SyncServiceInterface'
export * from './Sync/SyncSource'
export * from './Workspace/WorkspaceClientInterface'
export * from './Workspace/WorkspaceManager'
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
# [1.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/sncrypto-common",
"version": "1.12.0",
"version": "1.13.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,3 +1,4 @@
import { PkcKeyPair } from '../Types'
import { Base64String } from '../Types/Base64String'
import { Base64URLSafeString } from '../Types/Base64URLSafeString'
import { HexString } from '../Types/HexString'
@@ -27,7 +28,7 @@ export interface PureCryptoInterface {
* @param bits - Length of key in bits
* @returns A string key in hex format
*/
generateRandomKey(bits: number): string
generateRandomKey(bits: number): HexString
/**
* @legacy
@@ -98,7 +99,7 @@ export interface PureCryptoInterface {
* @param assocData
* @returns Base64 ciphertext string
*/
xchacha20Encrypt(plaintext: Utf8String, nonce: HexString, key: HexString, assocData: Utf8String): Base64String
xchacha20Encrypt(plaintext: Utf8String, nonce: HexString, key: HexString, assocData?: Utf8String): Base64String
/**
* Decrypt a message (and associated data) with XChaCha20-Poly1305
@@ -112,7 +113,7 @@ export interface PureCryptoInterface {
ciphertext: Base64String,
nonce: HexString,
key: HexString,
assocData: Utf8String | Uint8Array,
assocData?: Utf8String | Uint8Array,
): Utf8String | null
xchacha20StreamInitEncryptor(key: HexString): StreamEncryptor
@@ -132,6 +133,22 @@ export interface PureCryptoInterface {
assocData: Utf8String,
): { message: Uint8Array; tag: SodiumConstant } | false
sodiumCryptoBoxEasyEncrypt(
message: Utf8String,
nonce: HexString,
senderSecretKey: HexString,
recipientPublicKey: HexString,
): Base64String
sodiumCryptoBoxEasyDecrypt(
ciphertext: Base64String,
nonce: HexString,
senderPublicKey: HexString,
recipientSecretKey: HexString,
): Utf8String
sodiumCryptoBoxGenerateKeypair(): PkcKeyPair
/**
* Converts a plain string into base64
* @param text - A plain string
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.14.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/sncrypto-web",
"version": "1.13.0",
"version": "1.14.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+6 -6
View File
@@ -93,7 +93,7 @@ export class SNWebCrypto implements PureCryptoInterface {
return this.webCryptoDeriveBits(key, salt, iterations, length)
}
public generateRandomKey(bits: number): string {
public generateRandomKey(bits: number): HexString {
const bytes = bits / 8
const arrayBuffer = Utils.getGlobalScope().crypto.getRandomValues(new Uint8Array(bytes))
return Utils.arrayBufferToHexString(arrayBuffer)
@@ -249,14 +249,14 @@ export class SNWebCrypto implements PureCryptoInterface {
plaintext: Utf8String,
nonce: HexString,
key: HexString,
assocData: Utf8String,
assocData?: Utf8String,
): Base64String {
if (nonce.length !== 48) {
throw Error('Nonce must be 24 bytes')
}
const arrayBuffer = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
plaintext,
assocData,
assocData || null,
null,
Utils.hexStringToArrayBuffer(nonce),
Utils.hexStringToArrayBuffer(key),
@@ -268,7 +268,7 @@ export class SNWebCrypto implements PureCryptoInterface {
ciphertext: Base64String,
nonce: HexString,
key: HexString,
assocData: Utf8String | Uint8Array,
assocData?: Utf8String | Uint8Array,
): Utf8String | null {
if (nonce.length !== 48) {
throw Error('Nonce must be 24 bytes')
@@ -277,7 +277,7 @@ export class SNWebCrypto implements PureCryptoInterface {
return sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
null,
Utils.base64ToArrayBuffer(ciphertext),
assocData,
assocData || null,
Utils.hexStringToArrayBuffer(nonce),
Utils.hexStringToArrayBuffer(key),
'text',
@@ -368,7 +368,7 @@ export class SNWebCrypto implements PureCryptoInterface {
nonce: HexString,
senderPublicKey: HexString,
recipientSecretKey: HexString,
): Base64String {
): Utf8String {
const result = sodium.crypto_box_open_easy(
Utils.base64ToArrayBuffer(ciphertext),
Utils.hexStringToArrayBuffer(nonce),
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.136.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Features
* **api:** add workspaces api ([#1765](https://github.com/standardnotes/app/issues/1765)) ([01ba715](https://github.com/standardnotes/app/commit/01ba715eba987a7da1ee062fec0b3593a7a453ed))
# [2.135.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
## [2.134.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Bug Fixes
* **mobile:** passcode timing options ([#1744](https://github.com/standardnotes/app/issues/1744)) ([6c26b96](https://github.com/standardnotes/app/commit/6c26b96cdce0d69a4a459104709880850a401a92))
## [2.134.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/snjs
+32 -53
View File
@@ -14,6 +14,10 @@ import {
WebSocketApiServiceInterface,
WebSocketServer,
WebSocketServerInterface,
WorkspaceApiService,
WorkspaceApiServiceInterface,
WorkspaceServer,
WorkspaceServerInterface,
} from '@standardnotes/api'
import * as Common from '@standardnotes/common'
import * as ExternalServices from '@standardnotes/services'
@@ -45,7 +49,8 @@ import {
FileService,
SubscriptionClientInterface,
SubscriptionManager,
StorageValueModes,
WorkspaceClientInterface,
WorkspaceManager,
} from '@standardnotes/services'
import { FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateWorkspaceIdentifier } from '@standardnotes/encryption'
@@ -65,7 +70,6 @@ import { SNLog } from '../Log'
import { Challenge, ChallengeResponse } from '../Services'
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
import { ApplicationOptionsDefaults } from './Options/Defaults'
import { MobileUnlockTiming } from '@Lib/Services/Protection/MobileUnlockTiming'
/** How often to automatically sync, in milliseconds */
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
@@ -112,6 +116,9 @@ export class SNApplication
private declare subscriptionApiService: SubscriptionApiServiceInterface
private declare subscriptionServer: SubscriptionServerInterface
private declare subscriptionManager: SubscriptionClientInterface
private declare workspaceApiService: WorkspaceApiServiceInterface
private declare workspaceServer: WorkspaceServerInterface
private declare workspaceManager: WorkspaceClientInterface
private declare webSocketApiService: WebSocketApiServiceInterface
private declare webSocketServer: WebSocketServerInterface
private sessionManager!: InternalServices.SNSessionManager
@@ -213,6 +220,10 @@ export class SNApplication
return this.subscriptionManager
}
get workspaces(): ExternalServices.WorkspaceClientInterface {
return this.workspaceManager
}
public get files(): FilesClientInterface {
return this.fileService
}
@@ -899,24 +910,6 @@ export class SNApplication
return this.launched
}
public hasBiometrics(): boolean {
return this.protectionService.hasBiometricsEnabled()
}
/**
* @returns whether the operation was successful or not
*/
public enableBiometrics(): boolean {
return this.protectionService.enableBiometrics()
}
/**
* @returns whether the operation was successful or not
*/
public disableBiometrics(): Promise<boolean> {
return this.protectionService.disableBiometrics()
}
public hasPasscode(): boolean {
return this.protocolService.hasPasscode()
}
@@ -936,38 +929,6 @@ export class SNApplication
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
}
setBiometricsTiming(timing: MobileUnlockTiming) {
return this.protectionService.setBiometricsTiming(timing)
}
getMobileScreenshotPrivacyEnabled(): boolean {
return this.protectionService.getMobileScreenshotPrivacyEnabled()
}
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
return this.protectionService.setMobileScreenshotPrivacyEnabled(isEnabled)
}
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as
| MobileUnlockTiming
| undefined
}
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as
| MobileUnlockTiming
| undefined
}
getBiometricsTimingOptions() {
return this.protectionService.getBiometricsTimingOptions()
}
getPasscodeTimingOptions() {
return this.protectionService.getPasscodeTimingOptions()
}
isNativeMobileWeb() {
return this.environment === Environment.NativeMobileWeb
}
@@ -1099,6 +1060,9 @@ export class SNApplication
this.createWebSocketServer()
this.createWebSocketApiService()
this.createSubscriptionManager()
this.createWorkspaceServer()
this.createWorkspaceApiService()
this.createWorkspaceManager()
this.createWebSocketsService()
this.createSessionManager()
this.createHistoryManager()
@@ -1139,9 +1103,12 @@ export class SNApplication
;(this.userServer as unknown) = undefined
;(this.subscriptionApiService as unknown) = undefined
;(this.subscriptionServer as unknown) = undefined
;(this.subscriptionManager as unknown) = undefined
;(this.workspaceApiService as unknown) = undefined
;(this.workspaceServer as unknown) = undefined
;(this.workspaceManager as unknown) = undefined
;(this.webSocketApiService as unknown) = undefined
;(this.webSocketServer as unknown) = undefined
;(this.subscriptionManager as unknown) = undefined
;(this.sessionManager as unknown) = undefined
;(this.syncService as unknown) = undefined
;(this.challengeService as unknown) = undefined
@@ -1356,6 +1323,18 @@ export class SNApplication
this.subscriptionManager = new SubscriptionManager(this.subscriptionApiService, this.internalEventBus)
}
private createWorkspaceServer() {
this.workspaceServer = new WorkspaceServer(this.httpService)
}
private createWorkspaceApiService() {
this.workspaceApiService = new WorkspaceApiService(this.workspaceServer)
}
private createWorkspaceManager() {
this.workspaceManager = new WorkspaceManager(this.workspaceApiService, this.internalEventBus)
}
private createItemManager() {
this.itemManager = new InternalServices.ItemManager(this.payloadManager, this.options, this.internalEventBus)
this.services.push(this.itemManager)
@@ -1,7 +1,7 @@
import { ContentType } from '@standardnotes/common'
import { ItemsKeyInterface } from '@standardnotes/models'
import { dateSorted } from '@standardnotes/utils'
import { SNRootKeyParams, EncryptionProvider } from '@standardnotes/encryption'
import { SNRootKeyParams, EncryptionProviderInterface } from '@standardnotes/encryption'
import { DecryptionQueueItem, KeyRecoveryOperationResult } from './Types'
import { serverKeyParamsAreSafe } from './Utils'
import { ChallengeServiceInterface, DecryptItemsKeyByPromptingUser } from '@standardnotes/services'
@@ -11,7 +11,7 @@ export class KeyRecoveryOperation {
constructor(
private queueItem: DecryptionQueueItem,
private itemManager: ItemManager,
private protocolService: EncryptionProvider,
private protocolService: EncryptionProviderInterface,
private challengeService: ChallengeServiceInterface,
private clientParams: SNRootKeyParams | undefined,
private serverParams: SNRootKeyParams | undefined,
@@ -8,7 +8,7 @@ import {
ChallengeReason,
MutatorClientInterface,
} from '@standardnotes/services'
import { EncryptionProvider } from '@standardnotes/encryption'
import { EncryptionProviderInterface } from '@standardnotes/encryption'
import { ClientDisplayableError } from '@standardnotes/responses'
import { ContentType, ProtocolVersion, compareVersions } from '@standardnotes/common'
import { ItemManager } from '../Items'
@@ -49,7 +49,7 @@ export class MutatorService extends AbstractService implements MutatorClientInte
private itemManager: ItemManager,
private syncService: SNSyncService,
private protectionService: SNProtectionService,
private encryption: EncryptionProvider,
private encryption: EncryptionProviderInterface,
private payloadManager: PayloadManager,
private challengeService: ChallengeService,
private componentManager: SNComponentManager,
@@ -1,8 +1,19 @@
import { ChallengeReason } from '@standardnotes/services'
import { DecryptedItem } from '@standardnotes/models'
import { TimingDisplayOption, MobileUnlockTiming } from './MobileUnlockTiming'
export interface ProtectionsClientInterface {
authorizeProtectedActionForItems<T extends DecryptedItem>(files: T[], challengeReason: ChallengeReason): Promise<T[]>
authorizeItemAccess(item: DecryptedItem): Promise<boolean>
getMobileBiometricsTiming(): MobileUnlockTiming | undefined
getMobilePasscodeTiming(): MobileUnlockTiming | undefined
setMobileBiometricsTiming(timing: MobileUnlockTiming): void
setMobilePasscodeTiming(timing: MobileUnlockTiming): void
setMobileScreenshotPrivacyEnabled(isEnabled: boolean): void
getMobileScreenshotPrivacyEnabled(): boolean
getMobilePasscodeTimingOptions(): TimingDisplayOption[]
getMobileBiometricsTimingOptions(): TimingDisplayOption[]
hasBiometricsEnabled(): boolean
enableBiometrics(): boolean
disableBiometrics(): Promise<boolean>
}
@@ -2,3 +2,9 @@ export enum MobileUnlockTiming {
Immediately = 'immediately',
OnQuit = 'on-quit',
}
export type TimingDisplayOption = {
title: string
key: MobileUnlockTiming
selected: boolean
}
@@ -18,7 +18,7 @@ import {
} from '@standardnotes/services'
import { ProtectionsClientInterface } from './ClientInterface'
import { ContentType } from '@standardnotes/common'
import { MobileUnlockTiming } from './MobileUnlockTiming'
import { MobileUnlockTiming, TimingDisplayOption } from './MobileUnlockTiming'
export enum ProtectionEvent {
UnprotectedSessionBegan = 'UnprotectedSessionBegan',
@@ -64,8 +64,8 @@ export const ProtectionSessionDurations = [
*/
export class SNProtectionService extends AbstractService<ProtectionEvent> implements ProtectionsClientInterface {
private sessionExpiryTimeout = -1
private mobilePasscodeTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.Immediately
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.Immediately
private mobilePasscodeTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
constructor(
private protocolService: EncryptionService,
@@ -87,8 +87,8 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
override handleApplicationStage(stage: ApplicationStage): Promise<void> {
if (stage === ApplicationStage.LoadedDatabase_12) {
this.updateSessionExpiryTimer(this.getSessionExpiryDate())
this.mobilePasscodeTiming = this.getPasscodeTiming()
this.mobileBiometricsTiming = this.getBiometricsTiming()
this.mobilePasscodeTiming = this.getMobilePasscodeTiming()
this.mobileBiometricsTiming = this.getMobileBiometricsTiming()
}
return Promise.resolve()
}
@@ -229,7 +229,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
})
}
getPasscodeTimingOptions() {
getMobilePasscodeTimingOptions(): TimingDisplayOption[] {
return [
{
title: 'Immediately',
@@ -244,7 +244,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
]
}
getBiometricsTimingOptions() {
getMobileBiometricsTimingOptions(): TimingDisplayOption[] {
return [
{
title: 'Immediately',
@@ -259,25 +259,32 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
]
}
private getBiometricsTiming(): MobileUnlockTiming | undefined {
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
return this.storageService.getValue<MobileUnlockTiming | undefined>(
StorageKey.MobileBiometricsTiming,
StorageValueModes.Nonwrapped,
MobileUnlockTiming.OnQuit,
)
}
private getPasscodeTiming(): MobileUnlockTiming | undefined {
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
return this.storageService.getValue<MobileUnlockTiming | undefined>(
StorageKey.MobilePasscodeTiming,
StorageValueModes.Nonwrapped,
MobileUnlockTiming.OnQuit,
)
}
async setBiometricsTiming(timing: MobileUnlockTiming) {
setMobileBiometricsTiming(timing: MobileUnlockTiming): void {
this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
this.mobileBiometricsTiming = timing
}
setMobilePasscodeTiming(timing: MobileUnlockTiming): void {
this.storageService.setValue(StorageKey.MobilePasscodeTiming, timing, StorageValueModes.Nonwrapped)
this.mobilePasscodeTiming = timing
}
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default)
}
@@ -33,7 +33,7 @@ import {
* key can decrypt wrapped storage.
*/
export class DiskStorageService extends Services.AbstractService implements Services.StorageServiceInterface {
private encryptionProvider!: Encryption.EncryptionProvider
private encryptionProvider!: Encryption.EncryptionProviderInterface
private storagePersistable = false
private persistencePolicy!: Services.StoragePersistencePolicies
private encryptionPolicy!: Services.StorageEncryptionPolicy
@@ -53,7 +53,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
void this.setEncryptionPolicy(Services.StorageEncryptionPolicy.Default, false)
}
public provideEncryptionProvider(provider: Encryption.EncryptionProvider): void {
public provideEncryptionProvider(provider: Encryption.EncryptionProviderInterface): void {
this.encryptionProvider = provider
}
+5 -3
View File
@@ -39,12 +39,14 @@ describe('basic auth', function () {
let error = null
try {
await this.application.register(this.email, password)
} catch(caughtError) {
} catch (caughtError) {
error = caughtError
}
expect(error.message).to.equal('Your password must be at least 8 characters in length. '
+ 'For your security, please choose a longer password or, ideally, a passphrase, and try again.')
expect(error.message).to.equal(
'Your password must be at least 8 characters in length. ' +
'For your security, please choose a longer password or, ideally, a passphrase, and try again.',
)
expect(await this.application.protocolService.getRootKey()).to.not.be.ok
})
+1 -1
View File
@@ -62,7 +62,7 @@ describe('device authentication', function () {
const passcode = 'foobar'
const wrongPasscode = 'barfoo'
await application.addPasscode(passcode)
await application.protectionService.enableBiometrics()
await application.protections.enableBiometrics()
expect(await application.hasPasscode()).to.equal(true)
expect((await application.protectionService.createLaunchChallenge()).prompts.length).to.equal(2)
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly)
+36 -2
View File
@@ -69,7 +69,8 @@ export default class FakeWebCrypto {
}
generateRandomKey(bits) {
const length = bits / 8
const bitsPerHexChar = 4
const length = bits / bitsPerHexChar
return this.randomString(length)
}
@@ -107,7 +108,13 @@ export default class FakeWebCrypto {
}
argon2(password, salt, iterations, bytes, length) {
return btoa(password)
const bitsPerHexChar = 4
const bitsInByte = 8
const encoded = btoa(password)
const desiredLength = length * (bitsInByte / bitsPerHexChar)
const missingLength = desiredLength - encoded.length
const result = `${encoded}${encoded.repeat(Math.ceil(missingLength / encoded.length))}`.slice(0, desiredLength)
return result
}
xchacha20Encrypt(plaintext, nonce, key, assocData) {
@@ -128,6 +135,33 @@ export default class FakeWebCrypto {
return data.plaintext
}
sodiumCryptoBoxEasyEncrypt(message, nonce, senderSecretKey, recipientPublicKey) {
const data = {
message,
nonce,
senderSecretKey,
recipientPublicKey,
}
return btoa(JSON.stringify(data))
}
sodiumCryptoBoxEasyDecrypt(ciphertext, nonce, senderPublicKey, recipientSecretKey) {
const data = JSON.parse(atob(ciphertext))
if (
data.senderPublicKey !== senderPublicKey ||
data.recipientSecretKey !== recipientSecretKey ||
data.nonce !== nonce ||
data.assocData !== assocData
) {
return undefined
}
return data.message
}
sodiumCryptoBoxGenerateKeypair() {
return { publicKey: this.randomString(64), privateKey: this.randomString(64), keyType: 'x25519' }
}
generateOtpSecret() {
return 'WQVV2GFBRQWU3UQZWQFZC37PSNRXKTA6'
}
+4 -4
View File
@@ -311,7 +311,7 @@ describe('protections', function () {
it('no account, no passcode, biometrics', async function () {
application = await Factory.createInitAppWithFakeCrypto()
await application.enableBiometrics()
await application.protections.enableBiometrics()
expect(application.hasProtectionSources()).to.be.true
})
@@ -324,7 +324,7 @@ describe('protections', function () {
it('no account, passcode, biometrics', async function () {
application = await Factory.createInitAppWithFakeCrypto()
await application.addPasscode('passcode')
await application.enableBiometrics()
await application.protections.enableBiometrics()
expect(application.hasProtectionSources()).to.be.true
})
@@ -345,7 +345,7 @@ describe('protections', function () {
email: UuidGenerator.GenerateUuid(),
password: UuidGenerator.GenerateUuid(),
})
await application.enableBiometrics()
await application.protections.enableBiometrics()
expect(application.hasProtectionSources()).to.be.true
})
@@ -372,7 +372,7 @@ describe('protections', function () {
})
Factory.handlePasswordChallenges(application, password)
await application.addPasscode('passcode')
await application.enableBiometrics()
await application.protections.enableBiometrics()
expect(application.hasProtectionSources()).to.be.true
})
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.134.8",
"version": "2.136.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.5.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Features
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
## [1.4.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-06-30)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/styles",
"version": "1.4.2",
"version": "1.5.0",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
+3 -3
View File
@@ -20,14 +20,14 @@
--sn-stylekit-background-color: #ffffff;
// For borders inside background-color
--sn-stylekit-border-color: #dfe1e4;
--sn-stylekit-foreground-color: #19191C;
--sn-stylekit-foreground-color: #19191c;
// Colors for layers placed on top of non-prefixed background, border, and foreground
--sn-stylekit-contrast-background-color: rgba(244, 245, 247, 1.0);
--sn-stylekit-contrast-background-color: rgba(244, 245, 247, 1);
--sn-stylekit-contrast-foreground-color: #2e2e2e;
--sn-stylekit-contrast-border-color: #e3e3e3; // For borders inside contrast-background-color
// Alternative set of background and contrast options
--sn-stylekit-secondary-background-color: #EEEFF1;
--sn-stylekit-secondary-background-color: #eeeff1;
--sn-stylekit-secondary-foreground-color: #2e2e2e;
--sn-stylekit-secondary-border-color: #e3e3e3;
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.6.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Bug Fixes
* dark mode toggling ([1377846](https://github.com/standardnotes/app/commit/1377846f3f3a23a405c96b8a733d439947abfefe))
# [1.6.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Features
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
## [1.5.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.5.2",
"version": "1.6.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+32 -7
View File
@@ -20,6 +20,7 @@ import {
const CachedThemesKey = 'cachedThemes'
const TimeBeforeApplyingColorScheme = 5
const DefaultThemeIdentifier = 'Default'
const DarkThemeIdentifier = 'Dark'
export class ThemeManager extends AbstractService {
private activeThemes: Uuid[] = []
@@ -90,11 +91,13 @@ export class ThemeManager extends AbstractService {
private handlePreferencesChangeEvent(): void {
const useDeviceThemeSettings = this.application.getPreference(PrefKey.UseSystemColorScheme, false)
if (useDeviceThemeSettings !== this.lastUseDeviceThemeSettings) {
const hasPreferenceChanged = useDeviceThemeSettings !== this.lastUseDeviceThemeSettings
if (hasPreferenceChanged) {
this.lastUseDeviceThemeSettings = useDeviceThemeSettings
}
if (useDeviceThemeSettings) {
if (hasPreferenceChanged && useDeviceThemeSettings) {
const prefersDarkColorScheme = window.matchMedia('(prefers-color-scheme: dark)')
this.setThemeAsPerColorScheme(prefersDarkColorScheme.matches)
@@ -159,7 +162,11 @@ export class ThemeManager extends AbstractService {
}
private colorSchemeEventHandler(event: MediaQueryListEvent) {
this.setThemeAsPerColorScheme(event.matches)
const shouldChangeTheme = this.application.getPreference(PrefKey.UseSystemColorScheme, false)
if (shouldChangeTheme) {
this.setThemeAsPerColorScheme(event.matches)
}
}
private showColorSchemeToast(setThemeCallback: () => void) {
@@ -192,19 +199,35 @@ export class ThemeManager extends AbstractService {
private setThemeAsPerColorScheme(prefersDarkColorScheme: boolean) {
const preference = prefersDarkColorScheme ? PrefKey.AutoDarkThemeIdentifier : PrefKey.AutoLightThemeIdentifier
const preferenceDefault =
preference === PrefKey.AutoDarkThemeIdentifier ? DarkThemeIdentifier : DefaultThemeIdentifier
const themes = this.application.items
.getDisplayableComponents()
.filter((component) => component.isTheme()) as SNTheme[]
const activeTheme = themes.find((theme) => theme.active && !theme.isLayerable())
const activeThemeIdentifier = activeTheme ? activeTheme.identifier : DefaultThemeIdentifier
const activeThemeIdentifier = activeTheme
? activeTheme.identifier
: this.application.getPreference(PrefKey.DarkMode, false)
? DarkThemeIdentifier
: DefaultThemeIdentifier
const themeIdentifier = this.application.getPreference(preference, DefaultThemeIdentifier) as string
const themeIdentifier = this.application.getPreference(preference, preferenceDefault) as string
const toggleActiveTheme = () => {
if (activeTheme) {
void this.application.mutator.toggleTheme(activeTheme)
}
}
const setTheme = () => {
if (themeIdentifier === DefaultThemeIdentifier && activeTheme) {
this.application.mutator.toggleTheme(activeTheme).catch(console.error)
if (themeIdentifier === DefaultThemeIdentifier) {
toggleActiveTheme()
void this.application.setPreference(PrefKey.DarkMode, false)
} else if (themeIdentifier === DarkThemeIdentifier) {
toggleActiveTheme()
void this.application.setPreference(PrefKey.DarkMode, true)
} else {
const theme = themes.find((theme) => theme.package_info.identifier === themeIdentifier)
if (theme && !theme.active) {
@@ -300,6 +323,8 @@ export class ThemeManager extends AbstractService {
}
}
document.getElementsByTagName('head')[0].appendChild(link)
void this.application.setPreference(PrefKey.DarkMode, false)
}
private getBackgroundColor() {
+33
View File
@@ -3,6 +3,39 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.70.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/web
## [3.70.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Bug Fixes
* dark mode toggling ([1377846](https://github.com/standardnotes/app/commit/1377846f3f3a23a405c96b8a733d439947abfefe))
* **mobile:** reenable bouncing in lists ([c13dd88](https://github.com/standardnotes/app/commit/c13dd883a41d21d46350676f350ac95b5ae5d5a6))
## [3.70.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Bug Fixes
* increase default editor font size on mobile ([c944eb9](https://github.com/standardnotes/app/commit/c944eb9365e5835494724724d04c7dc3e20b8f61))
## [3.70.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Bug Fixes
* add fallback for initial window size ([#1749](https://github.com/standardnotes/app/issues/1749)) ([05aff27](https://github.com/standardnotes/app/commit/05aff2776ba84f2988ebc7b3fca5ab9a18a58430))
# [3.70.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Bug Fixes
* **mobile:** passcode timing options ([#1744](https://github.com/standardnotes/app/issues/1744)) ([6c26b96](https://github.com/standardnotes/app/commit/6c26b96cdce0d69a4a459104709880850a401a92))
### Features
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
## [3.69.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.69.1",
"version": "3.70.4",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
+13 -4
View File
@@ -42,10 +42,19 @@ const getKey = () => {
}
const setViewportHeight = () => {
document.documentElement.style.setProperty(
'--viewport-height',
`${visualViewport ? visualViewport.height : window.innerHeight}px`,
)
const currentValue = parseInt(document.documentElement.style.getPropertyValue('--viewport-height'))
const newValue = visualViewport && visualViewport.height > 0 ? visualViewport.height : window.innerHeight
if (currentValue && !newValue) {
return
}
if (!currentValue && !newValue) {
document.documentElement.style.setProperty('--viewport-height', '100vh')
return
}
document.documentElement.style.setProperty('--viewport-height', `${newValue}px`)
}
const setDefaultMonospaceFont = (platform?: Platform) => {
@@ -32,7 +32,7 @@ import { PrefDefaults } from '@/Constants/PrefDefaults'
type WebServices = {
viewControllerManager: ViewControllerManager
desktopService?: DesktopManager
autolockService: AutolockService
autolockService?: AutolockService
archiveService: ArchiveManager
themeService: ThemeManager
io: IOService
@@ -237,7 +237,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
async handleMobileGainingFocusEvent(): Promise<void> {}
async handleMobileLosingFocusEvent(): Promise<void> {
if (this.getMobileScreenshotPrivacyEnabled()) {
if (this.protections.getMobileScreenshotPrivacyEnabled()) {
this.mobileDevice().stopHidingMobileInterfaceFromScreenshots()
}
@@ -245,7 +245,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
}
async handleMobileResumingFromBackgroundEvent(): Promise<void> {
if (this.getMobileScreenshotPrivacyEnabled()) {
if (this.protections.getMobileScreenshotPrivacyEnabled()) {
this.mobileDevice().hideMobileInterfaceFromScreenshots()
}
}
@@ -256,10 +256,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return
}
const hasBiometrics = this.hasBiometrics()
const hasBiometrics = this.protections.hasBiometricsEnabled()
const hasPasscode = this.hasPasscode()
const passcodeTiming = await this.getMobilePasscodeTiming()
const biometricsTiming = await this.getMobileBiometricsTiming()
const passcodeTiming = this.protections.getMobilePasscodeTiming()
const biometricsTiming = this.protections.getMobileBiometricsTiming()
const passcodeLockImmediately = hasPasscode && passcodeTiming === MobileUnlockTiming.Immediately
const biometricsLockImmediately = hasBiometrics && biometricsTiming === MobileUnlockTiming.Immediately
@@ -34,7 +34,6 @@ const createApplication = (
const archiveService = new ArchiveManager(application)
const io = new IOService(platform === Platform.MacWeb || platform === Platform.MacDesktop)
const internalEventBus = new InternalEventBus()
const autolockService = new AutolockService(application, internalEventBus)
const themeService = new ThemeManager(application, internalEventBus)
application.setWebServices({
@@ -42,7 +41,7 @@ const createApplication = (
archiveService,
desktopService: isDesktopDevice(device) ? new DesktopManager(application, device) : undefined,
io,
autolockService,
autolockService: application.isNativeMobileWeb() ? undefined : new AutolockService(application, internalEventBus),
themeService,
})

Some files were not shown because too many files have changed in this diff Show More